文章目录

  • 依赖配置
  • 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搭建你的第一个应用程序相关推荐

  1. spring boot 搭建 和 全局异常处理

    spring boot 搭建: java -jar -Dserver.port=10000 -Dlogging.path=/var/logs xxx.jar &   -- 默认在/var/lo ...

  2. maven 聚合工程 用spring boot 搭建 spring cloud 微服务 模块式开发项目

    项目的简单介绍: 项目采用maven聚合工程 用spring boot 搭建 spring cloud的微服务 模块式开发 项目的截图: 搭建开始: 能上图 我少打字 1.首先搭建maven的聚合工程 ...

  3. Spring Boot 搭建应用实现登陆实例,页面使用bootstrap

    2019独角兽企业重金招聘Python工程师标准>>> Spring boot 搭建web应用集成了thymeleaf模板实现登陆  下面是pom.xml的配置 <?xml v ...

  4. move_uploaded_file返回false但实际成功_023 Spring Boot 搭建实际项目开发框架

    前面的课程中,我主要给大家讲解了 Spring Boot 中常用的一些技术点,这些技术点在实际项目中可能不会全部用得到,因为不同的项目可能使用的技术不同,但是希望大家都能掌握如何使用,并能自己根据实际 ...

  5. spring boot 搭建的一个企业级快速开发脚手架

    源码地址 https://github.com/javanan/slife slife spring boot 搭建的一个企业级快速开发脚手架. 技术栈 Spring Boot MySQL Freem ...

  6. Spring Boot搭建简易spring clound框架 (一)

    Spring Boot搭建简易spring clound框架 (一) 1.搭建之前先了解微服务 微服务架构介绍 微服务架构(Microservice Architecture)是一种架构概念,旨在通过 ...

  7. Spring boot 搭建个人博客系统(二)——登录注册功能

    Spring boot 搭建个人博客系统(二)--登录注册功能 一直想用Spring boot 搭建一个属于自己的博客系统,刚好前段时间学习了叶神的牛客项目课受益匪浅,乘热打铁也主要是学习,好让自己熟 ...

  8. Spring Boot:构建一个RESTful Web应用程序

    介绍: REST代表表示状态传输 ,是API设计的体系结构指南. 我们假设您已经具有构建RESTful API的背景. 在本教程中,我们将设计一个简单的Spring Boot RESTful Web应 ...

  9. PX4-AutoPilot教程--搭建并运行第一个应用程序

    搭建并运行第一个应用程序 本文主要说明如何搭建并运行你的第一个板载应用程序. 1.编写px4_simple_app应用程序文件 我们创建一个很小的应用程序,只是打印出来Hello Sky!. 这包括一 ...

最新文章

  1. 深度学习(2)基础2 -- 分类:得分函数损失函数(损失、正则化惩罚项、梯度下降、学习率)概率
  2. Blog博客系统数据库设计
  3. 战胜 Flash ,HTML5 还需要什么?
  4. Fast Walsh-Hadamard Transform——快速沃尔什变换
  5. 2018-2019-1 20165237 《信息安全系统设计基础》第四周学习总结
  6. P4396 [AHOI2013]作业
  7. 28岁成中科院课题组长,短短半年他接连在Science和Nature发论文
  8. 速成pytorch学习——2天
  9. 魅族Flyme5系统内置原生铃声免费下载
  10. [工具] Sublime Text 使用指南
  11. java 字符串 数字个数_Java 求一串字符串中字符,字母,数字的个数
  12. WRF-Chem笔记——MOZBC边界场制作
  13. 形式语言与自动机 Part.6 图灵机
  14. alert的使用方法
  15. 58. 最后一个单词的长度(水题)
  16. [DonkeyCar][树莓派]基础01 - 首次配置 - WIFI
  17. 数字是有绝对值的,负数的绝对值是它本身取反,非负数的绝对值是它本身。 请定义一个方法,方法能够得到小数类型数字的绝对值并返回。 请定义方法并测试
  18. 惟伊·京汉方内部启动会圆满成功
  19. 实习生两大杀手之一:Git 引入
  20. 利用过滤器处理字符,解决中文乱码问题

热门文章

  1. Duilib教程-控件练习
  2. 利用 AVDictionary 配置参数
  3. 漫游Kafka设计篇之数据持久化
  4. Python 中的url,Base64和MD5编码解码的使用
  5. MySQL(七)关于MySQL不同版本下临键锁锁定范围不同
  6. 如何从零开始写一个 web 框架?
  7. 对话阿里云:解锁视频云的新技术、新场景
  8. 音视频技术开发周刊 | 189
  9. 音视频技术开发周刊 | 144
  10. 音视频技术开发周刊(第126期)