开发第一个Spring Boot应用程序

开发第一个Spring Boot应用程序

要用Spring创建应用程序,你需要为框架做很多事情。当然,Spring提供了很
多优秀的特性,用于开发令人惊讶的应用程序。但是,你需要自己往项目的构建说明文件里添加各种库依赖,还要自己写配置文件,告诉Spring要做什么。

Spring Boot的出现就是为了解决以上的问题,让你集中精力开发应用程序,而不是繁琐的配置。

以下实践基于spring boot的2.1.5发行版

1.使用 Spring Initializr 初始化 Spring Boot 项目

spring initializer初始化项目方法有以下四种:

  • 通过Web界面使用。
  • 通过Spring Tool Suite使用。
  • 通过IntelliJ IDEA使用。
  • 使用Spring Boot CLI使用。
    要使用Spring Initializr,最直接的办法就是用浏览器打开http://start.spring.io,这里使用Web界面的方式创建项目。
    在这里插入图片描述配置包括项目名、开发语言、Spring Boot的版本(本例选择的是2.1.5版本)、项目属性选项和依赖的Jar。填写完毕后会生成一个maven项目的压缩包,下载导入到工程里即可。

2.运用Spring Boot

我们会使用Spring Boot构建一个简单的阅读列表应用程序。在这个程序里,用户可以输入想读的图书信息,查看列表,用Spring MVC来处理Web请求,用Thymeleaf来定义Web视图,用Spring Data JPA来把阅读列表持久化到数据库里,数据库用Mysql,构建工具用Maven。
项目的结构和遵循传统Maven布局:
DemoApplication.java 项目的启动引导类,也是Spring配置类,稍后会将配置的加载方式
application.properties 用于传统项目的配置信息和Spring Boot的属性
pom.xml maven构建文件,用于管理依赖的jar

2.1 启动引导类

引导类也即刚才的DemoApplication.java,有配置和引导启动两个作用。虽然Spring Boot的自动配置的目的是尽量不写配置,但是还是需要少量配置来启动自动配置。

@Controller
@SpringBootApplication
public class DemoApplication {

	@Autowired
	private SystemProperties systemProperties;
	
	@RequestMapping("/home")
	public String home() {
		return "home";
	}
	
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

@SpringBootApplication开启了Spring的组件扫描和Spring Boot的自动配置功能。实际
上,@SpringBootApplication将三个有用的注解组合在了一起:

  • Spring的@Configuration:标明该类使用Spring基于Java的配置。虽然本书不会写太多配置,但我们会更倾向于使用基于Java注解而不是XML的配置。
  • Spring的@ComponentScan:启用组件扫描,这样你写的Web控制器类和其他组件才能被自动发现并注册为Spring应用程序上下文里的Bean。本章稍后会写一个简单的Spring MVC控制器,使用@Controller进行注解,这样组件扫描才能找到它。
  • Spring Boot 的 @EnableAutoConfiguration :就是这一行配置开启了Spring Boot自动配置的魔力,让你不用再写成篇的配置了。

上面的例子会看到,DemoApplication类不仅是配置和引导启动类,加上@Controller注解后还是注册为一个SpringMVC控制器,路径"/home"返回的home会去templates文件加下寻找home.html静态资源文件并返回给前端,通过地址http://localhost:8080/home可以访问。

端口可以通过在application.properties里设置server.port属性来指定,默认是8000

2.2 起步依赖

要理解Spring Boot起步依赖带来的好处,先让我们假设它们尚不存在。如果没用Spring Boot的话,你会向项目里添加哪些依赖呢?要用Spring MVC的话,你需要哪个Spring依赖?你还记得Thymeleaf的Group和Artifact ID吗?你应该用哪个版本的Spring Data JPA呢?它们放在一起兼容吗?这样一来,有不少配置功课要做。
即使你能记得所有需要的依赖,但你是怎么知道的?你怎么保证你选的这些版本能相互兼容?也许可以,但构建并运行应用程序之前你是不知道的。再说了,你怎么知道这个列表是完整的?在一行代码都没写的情况下,你离开始构建还有很长的路要走。

2.2.1 指定功能的依赖

将spring-boot-starter-parent作为上一级,这样一来就能利用Maven的依赖管理功能,继承很多常用库的依赖版本,在你声明依赖时就不用再去指定版本号了。

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.5.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
</parent>

Spring Boot通过提供众多起步依赖降低项目依赖的复杂度。起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。很多起步依赖的命名都暗示了它们提供的某种或某类功能。
我们还想以Thymeleaf为Web视图,用JPA来实现数据持久化,因此在构建文件里还需要Thymeleaf和Spring Data JPA的起步依赖。
为了能进行测试,我们还需要能在Spring Boot上下文里运行集成测试的库,因此要添加Spring Boot的test起步依赖,这是一个测试时依赖。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency> 
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>6.0.5</version>
		</dependency>

这几个起步依赖只是Spring Boot众多起步依赖中的沧海一粟,还有很多可选择的起步依赖,可以自由选择使用的依赖和组合。
现在我们有了一个空的项目结构,构建说明文件也准备好了,是时候开发应用程序了。我们会让Spring Boot来处理配置细节,而我们自己则专注于编写阅读列表功能相关的代码。

2.3 自动配置

自动配置会做出以下配置决策,它们和之前的例子息息相关。

  • 因 为 Classpath 里 有 mysql ,所以会创建一个嵌入式的 H2 数据库 Bean ,它的类型是javax.sql.DataSource,JPA实现(Hibernate)需要它来访问数据库。
  • 因为Classpath里有Hibernate(Spring Data JPA传递引入的)的实体管理器,所以自动配置会配置与 Hibernate 相关的 Bean ,包括 Spring LocalContainerEntityManagerFactoryBean和JpaVendorAdapter。
  • 因为Classpath里有Spring Data JPA,所以它会自动配置为根据仓库的接口创建仓库实现。
  • 因为Classpath里有Thymeleaf,所以Thymeleaf会配置为Spring MVC的视图,包括一个Thymeleaf的模板解析器、模板引擎及视图解析器。视图解析器会解析相对于Classpath根目录的/templates目录里的模板。
  • 因 为 Classpath 里 有 Spring MVC (归功于 Web 起 步 依 赖 ), 所 以 会 配 置 Spring 的DispatcherServlet并启用Spring MVC。
  • 因为这是一个Spring MVC Web应用程序,所以会注册一个资源处理器,把相对于Classpath根目录的/static目录里的静态内容提供出来。(这个资源处理器还能处理/public、/resources和/META-INF/resources的静态内容。)
  • 因为Classpath里有Tomcat(通过Web起步依赖传递引用),所以会启动一个嵌入式的Tomcat容器,监听8080端口。
    以上是Spring Boot自动帮我们配置的,只要启动Spring Boot的自动配置,也就是@EnableAutoConfiguration注解的功能,这个后面会说到。
2.4 自定义配置

自动配置里比较简单的还是在配置文件里面覆盖配置选项

2.4.1 通过属性文件外置配置

Spring Boot自动配置的Bean提供了300多个用于微调的属性。当你调整设置时,只
要在环境变量、Java系统属性、JNDI(Java Naming and Directory Interface)、命令行参数或者属性文件里进行指定就好了。
Spring Boot应用程序有多种设置途径。Spring Boot能从多种属性源获得属性,包括
如下几处:
(1) 命令行参数
(2) java:comp/env里的JNDI属性
(3) JVM系统属性
(4) 操作系统环境变量
(5) 随机生成的带random.*前缀的属性(在设置其他属性时,可以引用它们,比如${random. long})
(6) 应用程序以外的application.properties或者appliaction.yml文件
(7) 打包在应用程序内的application.properties或者appliaction.yml文件
(8) 通过@PropertySource标注的属性源
(9) 默认属性
这个列表按照优先级排序,也就是说,任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性。例如,命令行参数会覆盖其他属性源里的属性。

application.properties和application.yml文件能放在以下四个位置,优先级同理。
(1) 外置,在相对于应用程序运行目录的/config子目录里。
(2) 外置,在应用程序运行的目录里。
(3) 内置,在config包内。
(4) 内置,在Classpath根目录。

2.4.2 自动配置微调

开发的过程中你会发现,Thymeleaf模板的变更是不会立即生效的。这是因为Thymeleaf模板默认缓存。这有助于改善应用程序的性能,因为模板只需编译一次,但在开发过程中就不能实时看到变更的效果了。
将spring.thymeleaf.cache设置为false就能禁用Thymeleaf模板缓存。这里使用在application.properties里设置属性:

spring.thymeleaf.cache=false

此处使用Thymeleaf作为应用程序的视图,Spring Boot支持的其他模板也能关闭模板缓存,设置这些属性就好了:

  • spring.freemarker.cache(Freemarker)
  • spring.groovy.template.cache(Groovy模板)
  • spring.velocity.cache(Velocity)
2.4.3 配置嵌入式服务器

要是所有应用程序都试着让Tomcat服务器监听同一个端口,在启动第二个应用程
序时就会有冲突。这时需要给每个服务器不同的配置。可以设置server.port属性,和spring.thymeleaf.cache一样设置在Classpath目录里的application.properties。

server.port=8080

除了服务器的端口,你还可能希望服务器提供HTTPS服务,这里只是介绍基本的配置服务,不做介绍了,如果有需要可以自行搜索相关配置方法。

2.4.4 配置日志

Spring Boot会用Logback(http://logback.qos.ch)来记录日志,并用INFO级别输
出到控制台。在运行应用程序和其他例子时,你应该已经看到很多INFO级别的日志了。
可以在指定目录创建logback.xml配置文件,示例内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
      <encoder charset="GBK">
          <pattern>[Consociate] %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
  </appender>  
 
  <root level="DEBUG">
    <appender-ref ref="stdout" />
  </root>
</configuration>

application.properties需要配置日志的级别和指定logback.xml的路径:

logging.level.root=INFO
logging.config.classpath=logback.xml
logging.path=/var/logs/ 
logging.file=BookWorm.log 
logging.level.root.org.springframework.security=DEBUG

还可以指定日志的输出文件,如果日志的模式是写文件模式。如果根日志级别要设置为WARN,但Spring Security的日志要用DEBUG级别,可以logging.level.root.org.
springframework.security指定Spring Security的日志级别。

2.4.5 配置数据源

虽然你可以显式配置自己的DataSource Bean,但通常并不用这么做,只需简单地通过属性配置数据库的URL和身份信息就可以了。举例来说,如果你用的是MySQL数据库,你的基本连接配置可以是下面这样:

spring.datasource.url=jdbc:mysql://localhost:3306/books_stadium?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=
spring.datasource.drive-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource

这里用的mysql-connector是6+版本,驱动类变成了com.mysql.cj.jdbc.Driver,数据库连接池是默认用的Hikari。

通常你都无需指定JDBC驱动,Spring Boot会根据数据库URL识别出需要的驱动

在自动配置DataSource Bean的时候,Spring Boot会使用这里的连接数据。DataSourceBean是一个连接池,如果Classpath里有Tomcat的连接池DataSource,那么就会使用这个连接池;否则,Spring Boot会在Classpath里查找以下连接池:

  • HikariCP
  • Commons DBCP
  • Commons DBCP 2
    据说HikariCP在性能方面相比其他连接池还是有不小的优势,我们就使用默认的HikariCP。
2.4.6 用Profile进行配置

当应用程序需要部署到不同的运行环境时,一些配置细节通常会有所不同。比如,数据库连接的细节在开发环境下和测试环境下就会不一样,在生产环境下又不一样。Spring Framework从Spring 3.1开始支持基于Profile的配置。Profile是一种条件化配置,基于运行时激活的Profile,会使用或者忽略不同的Bean或配置类。
设置spring.profiles.active属性就能激活Profile,任意设置配置属性的方式都能用于
设置这个值。Spring Boot支持为application.properties和application.yml里的属性配置
Profile。
为了演示区分Profile的属性,假设你希望针对生产环境和开发环境能有不同的日志配置。在生产环境中,你只关心WARN或更高级别的日志项,想把日志写到日志文件里。在开发环境中,你只想把日志输出到控制台,记录DEBUG或更高级别。
如果你正在使用application.properties,可以创建额外的属性文件,遵循application-{profile}. properties这种命名格式,这样就能提供特定于Profile的属性了。

spring.profiles.active=development

对应application-development.properties里的配置就是:

server.port=8081
logging.level.root=DEBUG

这是把开发环境的端口设置为8081,与正式环境区分开来,以便部署的时候不出差错。

2.4.7 定制错误页面

Spring Boot自动配置的默认错误处理器会查找名为error的视图,如果找不到就用默认的白标错误视图,会显示一个默认错误页面。

  • 实现了Spring的View接口的Bean,其 ID为error(由Spring的BeanNameViewResolver
    所解析)。
  • 如果配置了Thymeleaf,则有名为error.html的Thymeleaf模板。
  • 如果配置了FreeMarker,则有名为error.ftl的FreeMarker模板。
  • 如果配置了Velocity,则有名为error.vm的Velocity模板。
  • 如果是用JSP视图,则有名为error.jsp的JSP模板。

3.Spring Boot 测试

3.1集成测试

Spring自1.1.1版就向集成测试提供了极佳的支持。自Spring 2.5开始,集成测试支持的形式就变成了SpringJUnit4ClassRunner。这是一个JUnit类运行器,会为JUnit测试加载Spring应用程序上下文,并为测试类自动织入所需的Bean。
如你所见,SpringTest类上加注了@RunWith和@SpringBootTest注解。@RunWith的参数是SpringJUnit4ClassRunner.class,开启了Spring集成测试支持。与此同时,
@SpringBootTest指定了如何加载应用程序上下文。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SpringTest {
	@Autowired
	private WebApplicationContext context;
	
	private MockMvc mvc;
	
	@Before
	public void setupMockMvc() throws ClassNotFoundException {
		mvc = MockMvcBuilders.webAppContextSetup(context)
				.build();
	}
	
	@Test
	public void test() {
	}
}
3.2 测试Web应用程序

Spring MVC有一个优点:它的编程模型是围绕POJO展开的,在POJO上添加注解,声明如何处理Web请求。这种编程模型不仅简单,还让你能像对待应用程序中的其他组件一样对待这些控制器。你还可以针对这些控制器编写测试,就像测试POJO一样。
像下面的添加图书的一个方法:

 @RequestMapping(value="/{reader}/add", method=RequestMethod.POST) 
 public String addToReadingList( 
	 @PathVariable("reader") String reader, Book book) { 
	 log.info("{}添加书单{}",reader,book.getTitle());
	 book.setReader(reader); 
	 readingListRepository.save(book); 
	 return "redirect:/readingList/"+reader; 
 }

如果没有@RequestMapping,这只是一个简单的方法,通过直接传参可以测试代码本身有没有问题,但是不能真实体现表单提交的完整过程,也不能测试出真实添加过程中可能出现的问题,所以Spring提供了Mock MVC来模拟一个真实的Servlet容器来测试控制器,而不用启动服务器,这也是非常方便的做法。
Spring的Mock MVC框架模拟了Spring MVC的很多功能。它几乎和运行在Servlet容器里的应用程序一样,尽管实际情况并非如此。
要在测试里设置Mock MVC,可以使用MockMvcBuilders,该类提供了两个静态方法。

  • standaloneSetup():构建一个Mock MVC,提供一个或多个手工创建并配置的控制器。
  • webAppContextSetup():使用Spring应用程序上下文来构建Mock MVC,该上下文里可以包含一个或多个配置好的控制器。
    两者的区别就是前者更适合单元测试,后者更适合集成测试。我们这里使用后者,示例代码:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class MockMvcTest {

	Logger log = (Logger)LoggerFactory.getLogger(MockMvcTest.class);
	@Autowired
	private WebApplicationContext context;
	
	private MockMvc mvc;
	
	@Before
	public void setupMockMvc() throws ClassNotFoundException {
		mvc = MockMvcBuilders.webAppContextSetup(context)
				.build();
	}
	
	@Test
	public void test(){
		ResultActions result = null;
		try {
			result = (ResultActions) mvc.perform(get("/readingList/lzy"))
					.andExpect(model().attributeExists("reader"))
					.andExpect(status().isOk());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Spring完成了ReadingListController的初始化,并从Spring Boot自动配置的应用程序上下文里将其注入。setupMockMvc()方法上添加了JUnit的@Before注解,表明它应该在测试方法之前执行。它将WebApplicationContext注入webAppContextSetup()方法,然后调用build()产生了一个MockMvc实例,该实例赋给了一个实例变量,供测试方法使用。
示例中像/readingList/lzy地址发送了一个GET请求,判断模型属性和状态是否满足期望。其中使用了几个静态方法,可以通过静态导入对应的类,使代码看起来简便优美一些。

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

这样,一个简单的Spring Boot应用程序已经开发完成了。
希望这篇文章对初学Spring Boot的朋友起到帮助作用,因为篇幅的原因,很多代码没有贴出来,有兴趣看的同学可以下载源代码自己调试看看第一个Spring Boot应用程序源代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值