跟着官方文档学 SpringBoot 二:使用 spring boot
使用 spring boot
本文会更加深入使用 spring boot 的细节,包含 如何构建应用,自动配置以及启动应用几个话题。尽管 spring boot 并没有太多特殊的地方(其实你可以把它当作一个类库使用),只是如果有如下的一些提示,开发过程会更简单而已。
一、【应用构建】
强烈建议使用支持依赖管理的构建工具,提议使用 Maven 或者 Gradle。也可以使用其他构建系统,如 Ant,但是它们并没有得到 spring boot 很好的支持。
1.1 依赖管理
每一次 spring boot 版本的推出都会提供该版本支持的依赖列表。在实践中,你不需要为配置的依赖提供版本号(想这么做也可以)。当项目升级 spring boot 版本时, 这些依赖也会随之持续升级。
每个 spring boot 版本都关联一个基础的 spring framework 版本,最好不要手动指定 spring 版本。
1.2 Maven
Maven 用户可以从 spring-boot-starter-parent 项目继承,从而获得良好的默认配置。这个项目有一下特点:
- Java 1.8 作为默认编译版本
- 使用 UTF-8 编码
- 从 spring-boot-starter-parent 继承,这使得你可以忽略版本号(version)标签
- 良好的资源文件过滤(resource filtering)
- 良好的插件配置
- 对 application.properties 和 application.yml 进行资源过滤,包括特定于配置文件的文件(例如,application-dev.properties 和 application-dev.yml)
需要注意,由于 application.properties 和 application.yml 文件支持 spring 风格的占位符“${...}”,Maven 的占位符改为“@...@”。通过设置一个叫“resource.delimiter”的 maven 属性可以修改这个符号。
1.2.1 继承父工程
这个现在我们已经非常熟悉了,只需要注意版本标签即可
<version>2.0.0.RELEASE</version>
想要知道 spring boot 都支持哪些依赖,依赖的写法,可以上网找答案,也可以点这里查找。
1.2.2 不继承父工程
所谓众口难调,并不是所有人都满意使用父工程的配置,也可能企业有自己的标准,需要自己明确声明 maven 配置。
那么不继承父工程,可以这样做
<dependencyManagement> <dependencies> <dependency> <!-- Import dependency management from Spring Boot --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
通过将范围(scope)指定为“import”,这样可以保持 spring boot 良好的配置(不包括插件管理)。
那如何修改 spring boot 已经默认指定的依赖版本?比如,修改 spring data releasetrain 版本。
方法:增加你要修改的依赖,注意,要在 spring-boot-dependencies 声明之前,同时,将 scope 设置为 import 即可,如下
<dependencyManagement> <dependencies> <!-- Override Spring Data release train provided by Spring Boot --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-releasetrain</artifactId> <version>Fowler-SR2</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
1.2.3 使用 spring boot Maven 插件
spring boot 包含了一个可以将工程打包为可执行 jar 的 maven 插件。如下使用即可
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
1.3 那些“Starters”
“Starters”是一些依赖描述符的集合。简单来说,一个“Starters”声明的依赖可能包含了许多的分散的依赖,在以前,我们可能需要定义许许多多的 maven 坐标来引入依赖,无法避免地,需要从网上七拼八凑的复制粘贴,spring boot 的 “Starts”就解决了这个问题。
如果你要使用 spring 和 spring JPA 作为数据库访问技术,那么你只要声明 spring-boot-starter-data-jpa 依赖就 ok 了。
如果想要使用某个依赖,但不知道明确名称是什么,那么可以在 pom 文件中使用“spring-boot-starter-”作为前缀寻找。
二、【代码构建】
spring boot 并不要求任何特殊的代码分层,即使如此,还是有一些有帮助的好的实践方式。
1.1 不要使用“default”包
当一个类没有包括一个包的声明,它会被放置于默认包中。默认包是不推荐使用,也是应该避免的。这对于 spring boot 应用可能造成一些问题,如果使用 @ComponentSacn,@EntityScan 或者 @SpringBootApplication 注解的话, 因为每个 jar 包中的每个类都会被读取。
1.2 启动类的位置
推荐将应用启动类(SpringApplication.run()所在的那个类)放置于根包(root package)目录下。
一个典型的目录分层如下:
上图中的 Application.java 声明了 main 方法,并且使用了 @Configuration,如下
package com.example.myapplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @EnableAutoConfiguration @ComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
1.3 配置类
spring boot 推崇基于 Java 的配置方式。尽管可以通过 XML 文件的方式开发 spring 应用,但是一般还是推荐将你的主要资源作为一个 @Configuration 注解的类。通常,定义了 main 方法的类是作为主要资源配置类的选择。
1.3.1 导入其他配置类
你无需将所有的配置都放在一个类中。@Import 注解可以用来导入其余的配置类。此外,也可以使用 @ComponentScan 来自动识别 spring 组件,包括 @Configuration 注解的类。
1.3.2 导入 XML 配置
如果一定要使用 XML 配置文件,可以通过将一个类用 @Configuration 注解,然后使用 @ImportResource 注解来加载 XML 配置文件。
1.4 自动配置
你需要在 其中一个配置类(@Configuration classes)中添加 @EnableAutoConfiguration 或者 @SpringBootApplication 注解,这样即可开启自动配置。需要注意的是,@EnableAutoConfiguration 注解应该只使用一次,因此推荐在主配置文件上添加。
1.4.1 替换自动配置
Auto-configuration 是无侵入性的,你可以定义自己的配置来替换自动配置的部分。比如,添加 DataSource bean(数据源类),那么默认的数据库支持就会失效了。
可以通过在启动项中加入“--debug”选项,你将会得到一个“条件评估报告”,它显示了哪些配置正在使用和为什么使用,哪些没有在使用等等:
1.4.2 禁用某些自动配置类
如果发现有你并不想要配置的类被应用了,可以使用 @EnableAutoConfiguration 的排除属性。
如下案例:
import org.springframework.boot.autoconfigure.*; import org.springframework.boot.autoconfigure.jdbc.*; import org.springframework.context.annotation.*; @Configuration @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) public class MyConfiguration { }
如果需要被排除的类不在 classpath 中,可以使用该注解的 excludeName 属性,并指定类的全限定名。此外,还可以通过使用 spring.autoconfigure.exclude 属性来控制要排除的自动配置类的列表。
1.5 spring bean 和 依赖注入
在 spring boot 中,你可以随意地使用 spring 框架的标准技术,比如说使用 @ComponentScan 来发现 bean,使用 @Autowired 进行注入。如果你将代码做了如上文建议的构建(将启动类放在根包),那么你可以只添加 @ComponentScan 注解即可,不必加任何参数。所有的 spring 应用组件都将会被作为 spring bean 自动注册。
下面这个示例展示了一个 @Service bean 通过构造注入获得一个 RiskAssessor bean 的过程:
package com.example.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class DatabaseAccountService implements AccountService { private final RiskAssessor riskAssessor; @Autowired public DatabaseAccountService(RiskAssessor riskAssessor) { this.riskAssessor = riskAssessor; } // ... }
如果一个 bean 只有一个构造方法,你也可以忽略 @Autowired 注解,但是要注意, RiskAssessor 是被 final 修饰的:
@Service public class DatabaseAccountService implements AccountService { private final RiskAssessor riskAssessor; public DatabaseAccountService(RiskAssessor riskAssessor) { this.riskAssessor = riskAssessor; } // ... }
1.6 使用 @SpringBootApplication
很多的开发人员都会将 @Configuration,@EnableAutoConfiguration 和 @ComponentScan 放在一起使用,因此,spring boot 提供了 @SpringBootApplication 这么个方便的注解。@EnableAutoConfiguration 和 @ComponentScan 都有自己的属性,在 @SpringBootApplication 也有相应的属性可以设置。
1.7 启动应用
以下基于上一篇“原生Hello World”案例演示。
1.7.1 在开发工具中启动
在本系列第一篇“准备”中,我们已经写过一个示例“Hello World”,我们将它导入到开发工具中来。如果无法直接导入,请检查开发工具中是否安装了 maven 插件。导入完成之后,直接点击 debug 按钮即可启动(或者 run)。
1.7.2 作为打包应用启动
这种方式在上一篇中提到过,在 pom 文件所在目录,执行以下命令即可:
java -jar target/myapplication-0.0.1-SNAPSHOT.jar
也可以在启用远程调试支持的情况下运行打包的应用程序,这样可以将调试器附加到打包的应用程序:
java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \ -jar target/myapplication-0.0.1-SNAPSHOT.jar
1.7.3 使用 Maven 插件启动(推荐)
以 Idea 工具为例,有两种方式可以通过 Maven 插件运行程序。
① 点击最右侧的“Maven Projects”,选择“myproject>Plugins>spring-boot>spring-boot:run”进行启动:
② 通过添加新的 maven 配置,并在命令行中输入指令:
1.8 开发者工具(Developer Tools)
spring-boot-devtools 模块提供了额外的开发阶段的特性,它所需要声明的依赖如下:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies>
1.8.1 默认的属性
许多类库都会使用缓存来提高响应性能,比如一些模版引擎会缓存编译好的模版,避免重复解析模版文件;比如 Spring MVC 在处理静态资源时,可以将 HTTP 缓存头添加到 response 中。这些对于用户友好的性能在开发中却往往适得其反,因为我们总是想要及时看到那些被我们修改过的东西。因此,在 spring-boot-devtools 中已经默认禁止了缓存。
缓存是否启用的选项往往会写在 application.properties 文件中,比起手动设置这些值,spring-boot-devtools 会自动应用开发阶段的配置。点击这里查看详细的默认配置。
1.8.2 自动重启
使用 spring-boot-devtools 模块后,当 classpath 路径中的文件(多指配置文件)发生改变时,应用(服务器)会自动重启,这对于开发阶段的调试会提高很大效率。对于一些静态资源的修改,并不需要重启应用,默认情况下,/META-INF/maven,/META-INF/resources,/resources,/static,/public 目录下的资源修改不会触发重启。如果浏览器添加了 live load 扩展,则会触发浏览器的刷新。
可以通过在配置文件中自定义不触发重启的资源位置,比如:
spring.devtools.restart.exclude=static/**,public/**
如果想在默认的配置上再添加其他目录,可以这样配置:
spring.devtools.restart.additional-exclude=/js/**,/img/**
如果不想启用 live load,可以将 spring.devtools.livereload.enable 设为 false。
1.8.3 其他路径上的改变
如果想要当其他路径(非 classpath)上的文件发生改变重启应用,需要使用spring.devtools.restart.additional-paths 属性指定。
1.8.4 禁用重启
在配置文件中指定 spring.devtools.restart.enabled 属性值为 false。如果因为重启应用这个特性,与第三方类库不兼容,需要彻底禁用,则需要在 SpringApplication.run() 方法前设置系统属性为 false,如下:
public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(MyApp.class, args); }
1.8.5 选择触发文件
使用开发工具开发时,往往会持续性地编译经过修改的文件,那么可能不太希望服务器太频繁重启。我们可以在配置文件中指定 spring.devtools.restart.trigger-file 的值,其值为触发文件的路径。最好将该文件作为全局设置,这样触发文件的修改会应用到整个项目。
如何设置为全局文件?
在“家目录($HOME)”文件夹下创建一个名为“.spring-boot-devtools.properties”属性文件,在该文件中添加以下内容即可:
spring.devtools.reload.trigger-file=.reloadtrigger
简单解释下“家目录”:在 windows 系统中,Vista 及以上版本的家目录位于 <根目录>\Users\<用户名>,根目录为系统盘符,如我的系统盘为 C 盘;在 Linux 系统中,家目录位于 ~/ 下,这个想必大家都知道。
1.8.6 定制重启类加载器
在开始这个小节之前,我们先简单了解一下 “Restart” 和 “Reload” 有哪些不同之处。
回到本小节。为什么要定制重启类加载器呢?在很多应用中,通过使用这两种类加载器实现的重启可以正常发挥作用,但是在多模块应用中,由于并不是每个模块都会导入到开发工具中来,有时候会出现问题,那么就需要我们做一些定制的工作。
在 META-INF 下创建一个名为“spring-devtools.properties” 的属性文件,其中的属性名前缀 为 restart.exclude 和 restart.include。include 元素对应的值意味着哪些类(或 jar)会被加载到 restart classloader 中,exclude 元素对应的则表示被加载到 base classloader 中。键必须唯一。
示例如下,属性的值是一个相对于 classpath 的正则表达式:
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
1.8.7 重启功能的局限
重启功能对于那些使用标准的 ObjectInputStream(对象输入流) 反序列化的对象支持的不太好。如果想要反序列化数据,需要结合使用 spring 提供的 ConfigurableObjectInputStream 和 Thread.currentThread().getContextClassLoader()。
不巧的是,某些第三方类库做反序列化操作时没有考虑到 context classloader。如果遇到这样的问题,你需要向类库作者反馈并请求解决。
1.9 远程操作
待补充...