使用Spring Boot搭建你的第一个应用程序
文章目录
- 依赖配置
- main程序配置
- MVC配置
- 安全配置
- 存储
- Web 页面和Controller
- 异常处理
- 测试
- 结论
Spring Boot是Spring平台的约定式的应用框架,使用Spring Boot可以更加方便简洁的开发基于Spring的应用程序,本篇文章通过一个实际的例子,来一步一步的演示如何创建一个基本的Spring Boot程序。
依赖配置
本例子使用Maven来做包的依赖管理,在pom.xml文件中我们需要添加Spring boot依赖:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.2.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent>
同时我们要构建一个web应用程序,所以需要添加web依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
OOM框架,我们使用spring自带的jpa,数据库使用内存数据库H2:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency>
main程序配置
接下来我们需要创建一个应用程序的主类:
@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}
这里我们使用了注解: @SpringBootApplication。 它等同于三个注解:@Configuration, @EnableAutoConfiguration, 和 @ComponentScan同时使用。
最后,我们需要在resources目录中添加属性文件:application.properties。 在其中我们定义程序启动的端口:
server.port=8081
MVC配置
spring MVC可以配合很多模板语言使用,这里我们使用Thymeleaf。
首先需要添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
然后在application.properties中添加如下配置:
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.htmlspring.application.name=Bootstrap Spring Boot
然后创建一个home页面:
<html>
<head><title>Home Page</title></head>
<body>
<h1>Hello !</h1>
<p>Welcome to <span th:text="${appName}">Our App</span></p>
</body>
</html>
最后创建一个Controller指向这个页面:
@Controller
public class SimpleController {@Value("${spring.application.name}")String appName;@GetMapping("/")public String homePage(Model model) {model.addAttribute("appName", appName);return "home";}
}
安全配置
本例主要是搭一个基本完整的框架,所以必须的安全访问控制也是需要的。我们使用Spring Security来做安全控制,加入依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId>
</dependency>
当spring-boot-starter-security加入依赖之后,应用程序所有的入库会被默认加入权限控制,在本例中,我们还用不到这些权限控制,所以需要自定义SecurityConfig,放行所有的请求:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();}
}
上例中,我们permit all请求。
后面我又会详细的关于Spring Security的教程。这里先不做深入讨论。
存储
本例中,我们定义一个Book类,那么需要定义相应的Entity类:
@Entity
public class Book {@Id@GeneratedValue(strategy = GenerationType.AUTO)private long id;@Column(nullable = false, unique = true)private String title;@Column(nullable = false)private String author;
}
和相应的Repository类:
public interface BookRepository extends CrudRepository<Book, Long> {List<Book> findByTitle(String title);
}
最后,我们需要让应用程序发现我们配置的存储类,如下:
@EnableJpaRepositories("com.flydean.learn.repository")
@EntityScan("com.flydean.learn.entity")
@SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}
这里,我们使用@EnableJpaRepositories 来扫描repository类。
使用@EntityScan来扫描JPA entity类。
为了方便起见,我们使用内存数据库H2. 一旦H2在依赖包里面,Spring boot会自动检测到,并使用它。 我们需要配置一些H2的属性:
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:bootapp;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
和安全一样,存储也是一个非常重要和复杂的课题,我们也会在后面的文章中讨论。
Web 页面和Controller
有了Book entity, 我们需要为Book写一个Controller,主要做增删改查的操作,如下所示:
@RestController
@RequestMapping("/api/books")
public class BookController {@Autowiredprivate BookRepository bookRepository;@GetMappingpublic Iterable findAll() {return bookRepository.findAll();}@GetMapping("/title/{bookTitle}")public List findByTitle(@PathVariable String bookTitle) {return bookRepository.findByTitle(bookTitle);}@GetMapping("/{id}")public Book findOne(@PathVariable Long id) {return bookRepository.findById(id).orElseThrow(BookNotFoundException::new);}@PostMapping@ResponseStatus(HttpStatus.CREATED)public Book create(@RequestBody Book book) {return bookRepository.save(book);}@DeleteMapping("/{id}")public void delete(@PathVariable Long id) {bookRepository.findById(id).orElseThrow(BookNotFoundException::new);bookRepository.deleteById(id);}@PutMapping("/{id}")public Book updateBook(@RequestBody Book book, @PathVariable Long id) {if (book.getId() != id) {throw new BookIdMismatchException("ID mismatch!");}bookRepository.findById(id).orElseThrow(BookNotFoundException::new);return bookRepository.save(book);}
}
这里我们使用@RestController 注解,表示这个Controller是一个API,不涉及到页面的跳转。
@RestController是@Controller 和 @ResponseBody 的集合。
异常处理
基本上我们的程序已经完成了,但是在Controller中,我们定义了一些自定义的异常:
public class BookNotFoundException extends RuntimeException {public BookNotFoundException(String message, Throwable cause) {super(message, cause);}// ...
}
那么怎么处理这些异常呢?我们可以使用@ControllerAdvice来拦截这些异常:
@ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {@ExceptionHandler({ BookNotFoundException.class })protected ResponseEntity<Object> handleNotFound(Exception ex, WebRequest request) {return handleExceptionInternal(ex, "Book not found", new HttpHeaders(), HttpStatus.NOT_FOUND, request);}@ExceptionHandler({ BookIdMismatchException.class, ConstraintViolationException.class, DataIntegrityViolationException.class })public ResponseEntity<Object> handleBadRequest(Exception ex, WebRequest request) {return handleExceptionInternal(ex, ex.getLocalizedMessage(), new HttpHeaders(), HttpStatus.BAD_REQUEST, request);}
}
这种异常捕获也叫做全局异常捕获。
测试
我们的Book API已经写好了,接下来我们需要写一个测试程序来测试一下。
这里我们使用@SpringBootTest :
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class SpringContextTest {@Testpublic void contextLoads() {log.info("contextLoads");}
}
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT的作用是表示测试时候使用的Spring boot应用程序端口使用自定义在application.properties中的端口。
接下来我们使用RestAssured来测试BookController:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class SpringBootBootstrapTest {private static final String API_ROOT= "http://localhost:8081/api/books";private Book createRandomBook() {Book book = new Book();book.setTitle(randomAlphabetic(10));book.setAuthor(randomAlphabetic(15));return book;}private String createBookAsUri(Book book) {Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_ROOT);return API_ROOT + "/" + response.jsonPath().get("id");}@Testpublic void whenGetAllBooks_thenOK() {Response response = RestAssured.get(API_ROOT);assertEquals(HttpStatus.OK.value(), response.getStatusCode());}@Testpublic void whenGetBooksByTitle_thenOK() {Book book = createRandomBook();createBookAsUri(book);Response response = RestAssured.get(API_ROOT + "/title/" + book.getTitle());assertEquals(HttpStatus.OK.value(), response.getStatusCode());assertTrue(response.as(List.class).size() > 0);}@Testpublic void whenGetCreatedBookById_thenOK() {Book book = createRandomBook();String location = createBookAsUri(book);Response response = RestAssured.get(location);assertEquals(HttpStatus.OK.value(), response.getStatusCode());assertEquals(book.getTitle(), response.jsonPath().get("title"));}@Testpublic void whenGetNotExistBookById_thenNotFound() {Response response = RestAssured.get(API_ROOT + "/" + randomNumeric(4));assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());}@Testpublic void whenCreateNewBook_thenCreated() {Book book = createRandomBook();Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_ROOT);assertEquals(HttpStatus.CREATED.value(), response.getStatusCode());}@Testpublic void whenInvalidBook_thenError() {Book book = createRandomBook();book.setAuthor(null);Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_ROOT);assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatusCode());}@Testpublic void whenUpdateCreatedBook_thenUpdated() {Book book = createRandomBook();String location = createBookAsUri(book);book.setId(Long.parseLong(location.split("api/books/")[1]));book.setAuthor("newAuthor");Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).put(location);assertEquals(HttpStatus.OK.value(), response.getStatusCode());response = RestAssured.get(location);assertEquals(HttpStatus.OK.value(), response.getStatusCode());assertEquals("newAuthor", response.jsonPath().get("author"));}@Testpublic void whenDeleteCreatedBook_thenOk() {Book book = createRandomBook();String location = createBookAsUri(book);Response response = RestAssured.delete(location);assertEquals(HttpStatus.OK.value(), response.getStatusCode());response = RestAssured.get(location);assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());}
}
写好了测试类,运行就行了。
结论
你的第一个Spring Boot程序就完成了,后面的文章我们会继续丰富和改善这个基本框架,欢迎继续关注。
本文章的例子代码可以参考github: bootstrap-sample-app
更多精彩内容且看:
- 区块链从入门到放弃系列教程-涵盖密码学,超级账本,以太坊,Libra,比特币等持续更新
- Spring Boot 2.X系列教程:七天从无到有掌握Spring Boot-持续更新
- Spring 5.X系列教程:满足你对Spring5的一切想象-持续更新
- java程序员从小工到专家成神之路(2020版)-持续更新中,附详细文章教程
更多教程请参考 flydean的博客
使用Spring Boot搭建你的第一个应用程序相关推荐
- spring boot 搭建 和 全局异常处理
spring boot 搭建: java -jar -Dserver.port=10000 -Dlogging.path=/var/logs xxx.jar & -- 默认在/var/lo ...
- maven 聚合工程 用spring boot 搭建 spring cloud 微服务 模块式开发项目
项目的简单介绍: 项目采用maven聚合工程 用spring boot 搭建 spring cloud的微服务 模块式开发 项目的截图: 搭建开始: 能上图 我少打字 1.首先搭建maven的聚合工程 ...
- Spring Boot 搭建应用实现登陆实例,页面使用bootstrap
2019独角兽企业重金招聘Python工程师标准>>> Spring boot 搭建web应用集成了thymeleaf模板实现登陆 下面是pom.xml的配置 <?xml v ...
- move_uploaded_file返回false但实际成功_023 Spring Boot 搭建实际项目开发框架
前面的课程中,我主要给大家讲解了 Spring Boot 中常用的一些技术点,这些技术点在实际项目中可能不会全部用得到,因为不同的项目可能使用的技术不同,但是希望大家都能掌握如何使用,并能自己根据实际 ...
- spring boot 搭建的一个企业级快速开发脚手架
源码地址 https://github.com/javanan/slife slife spring boot 搭建的一个企业级快速开发脚手架. 技术栈 Spring Boot MySQL Freem ...
- Spring Boot搭建简易spring clound框架 (一)
Spring Boot搭建简易spring clound框架 (一) 1.搭建之前先了解微服务 微服务架构介绍 微服务架构(Microservice Architecture)是一种架构概念,旨在通过 ...
- Spring boot 搭建个人博客系统(二)——登录注册功能
Spring boot 搭建个人博客系统(二)--登录注册功能 一直想用Spring boot 搭建一个属于自己的博客系统,刚好前段时间学习了叶神的牛客项目课受益匪浅,乘热打铁也主要是学习,好让自己熟 ...
- Spring Boot:构建一个RESTful Web应用程序
介绍: REST代表表示状态传输 ,是API设计的体系结构指南. 我们假设您已经具有构建RESTful API的背景. 在本教程中,我们将设计一个简单的Spring Boot RESTful Web应 ...
- PX4-AutoPilot教程--搭建并运行第一个应用程序
搭建并运行第一个应用程序 本文主要说明如何搭建并运行你的第一个板载应用程序. 1.编写px4_simple_app应用程序文件 我们创建一个很小的应用程序,只是打印出来Hello Sky!. 这包括一 ...
最新文章
- 深度学习(2)基础2 -- 分类:得分函数损失函数(损失、正则化惩罚项、梯度下降、学习率)概率
- Blog博客系统数据库设计
- 战胜 Flash ,HTML5 还需要什么?
- Fast Walsh-Hadamard Transform——快速沃尔什变换
- 2018-2019-1 20165237 《信息安全系统设计基础》第四周学习总结
- P4396 [AHOI2013]作业
- 28岁成中科院课题组长,短短半年他接连在Science和Nature发论文
- 速成pytorch学习——2天
- 魅族Flyme5系统内置原生铃声免费下载
- [工具] Sublime Text 使用指南
- java 字符串 数字个数_Java 求一串字符串中字符,字母,数字的个数
- WRF-Chem笔记——MOZBC边界场制作
- 形式语言与自动机 Part.6 图灵机
- alert的使用方法
- 58. 最后一个单词的长度(水题)
- [DonkeyCar][树莓派]基础01 - 首次配置 - WIFI
- 数字是有绝对值的,负数的绝对值是它本身取反,非负数的绝对值是它本身。 请定义一个方法,方法能够得到小数类型数字的绝对值并返回。 请定义方法并测试
- 惟伊·京汉方内部启动会圆满成功
- 实习生两大杀手之一:Git 引入
- 利用过滤器处理字符,解决中文乱码问题