1 构建环境
我们强烈推荐你选择支持依赖管理的构建系统,例如 Maven or Gradle。Spring Boot在别的构建环境中也能运行(例如Ant),但是支持得不是特别好。
1.1 Maven
Maven用户可以通过继承spring-boot-starter-parent来获取合适的默认配置。该父项目提供了以下特性:
- 默认编译级别为Java 1.6
- 源码编码为UTF-8
- 一个继承于spring-boot-dependencies POM的依赖管理节点,允许你忽略一些普通的依赖的版本号
- 合理的资源过滤
- 合理的插件配置(例如 plugin, surefire, Git commit ID, shade)
- 针对application.properties和application.yml,特别是(e.g. application-foo.properties and application-foo.yml)这些特殊文件的合理资源过滤
最后一点:由于默认的配置文件接受了Spring风格的占位符(${…}),所以Maven filtering改用@..@占位符(你可以使用Maven的性质resource.delimiter来重写它)
1.1.1 继承starter parent
如果想要继承spring-boot-starter-parent,你只需要像下面的代码一样配置项目的parent
<!-- Inherit defaults from Spring Boot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.BUILD-SNAPSHOT</version> </parent> |
注:如果你继承了spring-boot-starter-parent,那么你只需要在这个依赖中声明Spring Boot的版本号。如果你再引入其他的starters,你可以忽略这些starters的版本号。
引入了parent依赖后,你可以通过重写项目中的一些property(配置)来实现依赖的个性化。举个例子,为了更新另外一个Spring Data的发布版本,你必须在pom.xml文件中加入以下内容:
<properties> <spring-data-releasetrain.version>Fowler-SR2</spring-data-releasetrain.version> </properties> |
1.1.2 不继承parent POM的Spring Boot应用
并不是所有人都喜欢在pom文件中继承spring-boot-starter-parent。可能是你所在的公司有自己的parent需要继承,或者你希望明确地声明你的所有Maven配置。
如果你不想使用spring-boot-starter-parent,你也可以通过使用scope=import的依赖来获取版本管理的好处(但是这种方式没有插件管理的功能)。
<dependencyManagement> <dependencies> <dependency> <!-- Import dependency management from Spring Boot --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.0.BUILD-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> |
而这个配置并不能让你像继承parent那样通过重写某些配置来重写个性化依赖。为了达到同样的效果,你必须在dependencyManagement模块里并且是spring-boot-dependencies的前面加入整个需要升级的依赖。举例说明,为了更新Spring Data版本,你需要在pom.xml文件中加入以下内容:
<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> <scope>import</scope> <type>pom</type> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.0.BUILD-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> |
1.1.3 改变Java版本
spring-boot-starter-parent选择相当保守的Java兼容版本。如果你想遵循我们的推荐,使用最新的Java版本,你可以在property中加入java.version:
<properties> <java.version>1.8</java.version> </properties> |
1.1.4 使用Spring Boot的Maven插件
Spring Boot包含了可以把项目打包成jar包的Maven插件。如果你想使用,只需要在<plugins>标签中加入该插件:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> |
注:如果你使用Spring Boot starter parent配置pom.xml,你只需要引入这个插件。除非你想改变这些配置在parent中的定义,否则,你并不需要去配置它。
1.2 Starters
Starters是一组很方便的依赖描述,你可以直接加入到项目中。通过这些Starters,你可以得到加载到所有Spring以及相关的技术,而无需去寻找示例代码以及复制粘贴大量的依赖描述。举个例子,如果你需要使用Spring和JPA作为数据库连接,你只需要引入spring-boot-starter-data-jpa的依赖就可以了。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> </dependencies> |
2 组织你的代码
Spring Boot并不需要任何特殊的代码结构就可以运行,然而,这里有一些有用的最佳实践
2.1 使用默认的包
当一个类没有声明package时,它通常会被默认声明为“default package”。常情况下,我们不建议甚至是避免使用"default package"。当使用@ComponentScan, @EntityScan或@SpringBootApplication注解时,"default package"会Spring Boot程序中读取每个jar包的每一个class类,这可能会造成一些奇怪的问题。
注:我们建议你遵循java建议的包命名规范(例如,com.example.project)
2.2 放置Main应用类
我们通常建议你把Main应用类放在其他类之上的根包中。@EnableAutoConfiguration注解通常放在你的main类中,并且它暗中地为某些项定义了一个基础的"search package"。举例说明,如果你正在编写一个JPA的程序,带有@EnableAutoConfiguration注解的类所在的包会被用来寻找有@Entity的项。
使用根包允许使用@ComponentScan注解而不需要指定一个basePackage属性。如果你的main类放置在根包下面,你也可以使用@SpringBootApplication注解。
以下是典型的布局:
com +- example +- myproject +- Application.java | +- domain | +- Customer.java | +- CustomerRepository.java | +- service | +- CustomerService.java | +- web +- CustomerController.java |
在Application.java文件中,我们将会定义一个main方法,还有基础的注解@Configuration
package com.example.myproject; import org.springframework.boot.SpringApplication; @Configuration public static void main(String[] args) { } |
3 配置类
Spring Boot支持基础的java配置。尽管你可以通过xml源文件来调用SpringApplication.run(),但是我们通常建议你使用@Configuration注解的类作为主要源文件。
注:大多数发布在线上的Spring配置的例子都是使用XML配置文件的。然而我们建议你尽可能地尝试使用等价的基于java的配置。你可以先从enable*注解开始。
3.1导入外部的配置类
你不必把所有的@Configuration放在一个单独的类里。@Import注解可以被用来引入外部配置类。另外,你也可以使用@ComponentScan去扫描并加载所有Spring的组件,包括有@Configuration注解的类。
3.2导入XML配置
如果你坚持要使用XML配置,我们建议你仍然从一个@Configuration类开始。然后你再使用@ImportResource注解去加载XML配置文件。
4 自动配置
Spring Boot自动配置致力于依据你加入的依赖jar包去自动配置你的Spring应用。举个例子,如果你的classpath目录下存在HSQLDB,我们就会自动配置一个内存数据库,你并不需要手动地配置任何的数据库连接beans。你可以在一个@Configuration注解的类中加入@EnableAutoConfiguration或者@SpringBootApplication来实现自动配置。
注:你只需要加入一个@EnableAutoConfiguration,我们建议你添加在主@Configuration类上。
4.1 逐步地更换自动配置
自动配置是非入侵的,你可以在任何时候自定义配置去覆盖特定模块的自动配置。举例说明,如果你加入自动的数据库bean,默认嵌入的数据库会退出使用。如果你想知道现在正在使用哪个自动配置,以及为什么,你可以在启动应用时加入--debug开关。然后你就可以看到自动配置的日志输出。
4.2 禁用特定的自动配置
如果你发现某个正在使用的特定的自动配置并非你想使用的,你可以在@EnableAutoConfiguration里使用exclude属性来禁用。
import org.springframework.boot.autoconfigure.*; @Configuration |
5 Spring类和依赖注入
你可以使用任意标准的Spring框架技术去定义你的类以及他们的依赖注入。最简单的,我们通常会使用@ComponentScan去寻找你的类,并使用@Autowired构造器注入。
如果你按照以上的建议组织你的代码(在跟包放置你的应用类),你可以在不添加任何参数的情况下加入@ComponentScan注解。你的所有应用组件(@Component, @Service, @Repository, @Controller etc),会被自动地注册为Spring类。
下面是一个@Service类的例子,它使用构造器注入来获取需要的RiskAssessor类。
package com.example.service; import org.springframework.beans.factory.annotation.Autowired; @Service private final RiskAssessor riskAssessor; @Autowired // ... } |
注:注意使用构造器注入,riskAssessor类会被标志位final,表明它后续不能被修改。
6 使用@SpringBootApplication注解
大多数的Spring Boot开发者会使用@Configuration, @EnableAutoConfiguration和@ComponentScan注解他们的主类。由于这些注解经常在一起使用(特别是如果你按照我们以上建议的方式使用Spring Boot),Spring Boot提供了一个更方便的@SpringBootApplication注解以供选择。
@SpringBootApplication等价于使用@Configuration,@EnableAutoConfiguration和@ComponentScan的默认属性。
package com.example.myproject; import org.springframework.boot.SpringApplication; @SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan public static void main(String[] args) { } |
注:尽管@SpringBootApplication使用了默认属性,但是它也支持重复自定义@EnableAutoConfiguration和@ComponentScan的属性。
7 运行你的程序
把你的程序打包成jar和使用内嵌HTTP服务器的其中一个最大的好处是,你可以随时随地的运行你的程序。
注:这部分仅仅是基于jar包的,如果你需要把程序打包成war包,你应该参考你的服务器和IDE文档。
7.1 在IDE中运行
在IDE中,导入项目后,你就可以像运行一般的java程序一样运行Spring Boot程序。导入过程中因你的IDE和构建环境而异。大多数的IDE可以直接导入Maven项目,Eclipse使用者通常用使用Import… → Existing Maven Projects来导入项目。
7.2 运行打包程序
如果你使用Spring Boot的Maven或者Gradle来打jar包,你可以使用java -jar来运行你的程序。例如:
$ java -jar target/myproject-0.0.1-SNAPSHOT.jar |
你也可以通过启动远程调试支持来运行一个打包好的程序。这允许你在打包程序上附加一个调试器。
$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar target/myproject-0.0.1-SNAPSHOT.jar |
你可能想使用可用的操作系统环境变量:
$ export MAVEN_OPTS=-Xmx1024m -XX:MaxPermSize=128M |
8 开发者工具
Spring Boot提供了一系列使用方便的开发者工具。在你的项目引入spring-boot-devtools模块可以提供额外的调试时间特性:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> |
注:当运行一个完整的打包应用程序时,Developer tools会被自动禁用。如果你是使用java -jar或者特殊的类加载器启动,该程序就会被认为是一个"生产的应用程序"。
8.1 属性默认值
好几个Spring Boot提供的库都使用了缓存来提供性能。举例说明,模板引擎会缓存编译后的模板来避免反复地解析模板文件。同样地,当访问静态资源文件,Spring MVC会在响应中加入HTTP缓存头,让浏览器缓存静态文件。
同时,缓存在生产服务上也是非常有用的。但是相反的,在开发的时缓存会导致你看不到刚刚修改的东西。所以,spring-boot-devtools默认会禁用这些缓存设置。
缓存的设置通常是在application.properties中配置的。例如,Thymeleaf(一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发)提供了spring.thymeleaf.cache参数。并不需要手动设置这些参数,spring-boot-devtools模块会自动选择最适合开发的配置。
8.2 自动重启
任何时候类路径下的文件有改变,使用了spring-boot-devtools的程序都会自动重启。当你使用IDE编码时,这是非常有用的一个属性,因为它能让你快速地看到代码修改后的效果。默认地,类路径下的所有类都会被检测是否有修改。注意确定的资源文件例如静态资源文件和视图模板的修改并不需要重启程序。
触发重启 因为DevTools会检测类路径的资源文件,所以触发重启的唯一办法就是更新类路径下的资源。如何更新类路径下的内容,这取决于你正在使用的IDE。在Eclipse,只要你保存了修改的文件,类路径下的内容就会被修改并触发程序重启。在IntelliJ IDEA,构建项目(Build → Make Project)可以达到同样的效果。 |
注:你可以通过支持分支的构建插件来启动程序(例如Maven和Gradle),因为DevTools需要一个独立的程序文件类加载器去正常运作。当检测到类路径下有DevTolls时,Gradle和Maven会默认做这些。
注:LiveReload也很好地支持了自动重启。
注:DevTools在重启的过程中,依赖于程序的上下文注册的关机钩子去关机。如果你禁用了这个关机钩子,自动重启将不能使用(SpringApplication.setRegisterShutdownHook(false))
注:DevTools会自动忽略命名为spring-boot,spring-boot-devtools,spring-boot-autoconfigure,spring-boot-actuator,and spring-boot-starter的项目上的修改,并且不会自动重启。
重启和重载 Spring Boot提供的重启技术使用了两个类加载器来运行。那些不会被修改的类(例如第三方库)会被加载到一个基础的类加载器中。那些你经常改动的会被加载到一个重启类加载器中。当程序被重启时,重启加载器会扔掉旧的类,直接创建新的类。这个途径意味着程序重启会比传统的程序启动要快,因为重启时,基础类加载器已经存在并且可以可用。 如果你觉得重启还不够快,或者你遇到了类加载的问题,你可以考虑使用重载技术,例如JRebel。他们通过重写被加载的类来重载程序。 |
8.2.1 排除资源文件
确定的资源文件发生修改时,没必要触发重启。例如,Thymeleaf模板可以就地编辑。默认地,改变/META-INF/maven, /META-INF/resources,/resources,/static,/public或者/templates不会触发重启但会触发重载。如果你想自定义这些排除资源文件,可以使用spring.devtools.restart.exclude参数。例如,只想排除/static和/public文件夹下的资源,你可以像下面这样设置:
spring.devtools.restart.exclude=static/**,public/** |
注:如果你想使用默认配置,并加上额外的排除,可以使用spring.devtools.restart.additional-exclude参数。
8.2.2 观察外加路径的文件
你可能希望你的程序被重启或者重启时,你修改的文件不要出现在类路径下。为了实现这个需求,使用spring.devtools.restart.additional-paths参数去配置外加的路径去观察这些变化。你可以使用spring.devtools.restart.exclude参数去控制外加路径的修改是重启还是重载。
8.2.3 禁用重启
如果想禁用重启特性,你可以使用spring.devtools.restart.enabled参数。大多数情况下,你可以在application.properties文件中设置。(加了禁用参数后,重启类加载器虽然仍会初始化,但是它不会去检测文件的变化。)
如果你想完全禁止重启支持,因为他不支持特定的第三方,你只能在调用SpringApplication.run(…)的前面设置System的参数。例如:
public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(MyApp.class, args); } |
8.2.4 使用触发文件
如果你使用IDE编辑器编辑代码,你可能更趋向于仅在某个特定的时间重启。为了实现这个需求,你可以使用"trigger file"。如果你想按照实际情况去触发重启检测,你必须修改这个文件。仅在需要触发检查的时候改变这个文件,然后Devtools去检测有没有文件更新,如果有就触发重启。这个触发文件可以手动去设置,或者通过IDE插件。
使用spring.devtools.restart.trigger-file参数来使用触发文件。
注:你可以设置spring.devtools.restart.trigger-file为全局属性,然后你的所有项目都能通过这种方式重启。
8.2.5 自定义重启类加载器
正如上面重启和重载的章节所说的,重启功能是通过使用两个类加载器来实现的。这种方式在大多数应用都是正常的,但是某些时候可能会出现类加载的问题。
默认地,你在IDE打开的项目都会使用重启类加载器来加载,同时jar文件会被基础类加载器加载。如果你正在编写一个混合模块的项目,并且不会在IDE中引入所有模块。你可能需要自定义一些东西。这种情况下为了实现重启,你可以创建一个META-INF/spring-devtools.properties文件。
spring-devtools.properties文件包含了restart.exclude.和restart.include.前缀的属性。包含的原理是所有文件都应该被放入重启类加载器中,而排除的原理是所有文件都应该放到基础类加载器中。这个属性的值是一个应用在类路径下的正则表达式。例如:
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar |
注:只要是以restart.include.或者restart.exclude.开头的属性,那么他们的名字必须是唯一的。
注:类路径下所有的META-INF/spring-devtools.properties都会被加载。你可以在项目里面打包好这些文件,或者通过项目自定义的第三方库打包。
8.2.6 了解局限性
对于那些使用标准ObjectInputStream来并行化对象,重启功能并不能很好地支持。如果你需要并行化数据,你可能需要配合使用Spring的ConfigurableObjectInputStream和Thread.currentThread().getContextClassLoader()。
不幸地,很大一部分第三方库在并行化的时候并没有考虑上下文的类加载器。如果你发现了这样的问题,请联系原作者寻求解决办法。
8.3 活重载
当资源文件发生变化时,spring-boot-devtools模块内嵌的一个活重载服务器去触发浏览器更新。谷歌,火狐和Safari浏览器可以从http://livereload.com/extensions/网站中免费扩展。
如果不想在程序中启用活重载服务器,你可以设置spring.devtools.livereload.enabled属性为false。
注:每次只能运行一个活重载服务器。在程序启动之前,确保没有别的活重启服务器在运行。如果你运行的是复合的应用程序,仅仅第一个程序会获得活重载支持。
8.4 全局配置
你可以在$HOME文件夹下加入命名为.spring-boot-devtools.properties的文件来配置全局开发者工具属性(注意文件名是以"."开头的)。任何在这个文件加入的属性都会被应用到你机器上的所有使用开发者工具的Spring Boot程序中。例如,配置重启通常使用一个触发文件,你可以这样配置:
~/.spring-boot-devtools.properties.
spring.devtools.reload.trigger-file=.reloadtrigger |
8.5 远程应用
Spring Boot的开发者工具不仅适用于本地开发环境,你同样可以在远端使用。为了实现远端调试支持,你必须确保打包代码中加入了devtools。
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludeDevtools>false</excludeDevtools> </configuration> </plugin> </plugins> </build> |
然后你需要去设置spring.devtools.remote.secret属性,例如:
spring.devtools.remote.secret=mysecret |
注:在远程应用中启用spring-boot-devtools会有安全风险。你最好永远别在生产应用上开启远程工具的支持。
远程工具支持会在两个地方启用:接收请求的服务器端,你运行在IDE的客户端。设置了spring.devtools.remote.secret属性后,服务器端组件会自动启用。但是客户端需要手动启动。
8.5.1 运行远程客户端应用
远程客户端应用被设计成可以在你的IDE运行。你必须使用跟你连接的远程项目一样的类路径去运行org.springframework.boot.devtools.RemoteSpringApplication。传递到应用程序的非选择参数应该是远程URL。
例如,如果你使用的是Eclipse或者STS,并且你有一个命名为my-app的项目部署在云平台,你需要做以下这些操作:
- 右键项目,在Run菜单选择Run Configurations…
- 创建一个新的Java Application
- 浏览并选择my-app项目
- 使用org.springframework.boot.devtools.RemoteSpringApplication作为主类
- Program arguments参数列表中加入https://myapp.cfapps.io(或者你的远程项目URL)
运行成功情况:
. ____ _ __ _ _ 2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code/spring-boot-samples/spring-boot-sample-devtools) |
注:因为远程客户端使用和真实应用一样的类路径,所以它可以直接读取应用的配置文件。这就是为什么spring.devtools.remote.secret为什么能被读取并通过服务器验证。
注:选择https://作为连接协议是非常明智的,它可以对通信进行加密并且保护密码不被拦截。
注:如果你必须使用代理去访问远程应用,配置spring.devtools.remote.proxy.host和spring.devtools.remote.proxy.port属性。
8.5.2 远程更新
远程客户端会像本地重启一样检测你的应用程序类路径的变化。任何更新的资源都会被推送到远程应用程序(如果需要)并触发重启。当使用一个你本地不存在的云服务来迭代某个特性时,这是相当有用的。通常地,远程更新和重启都会一个完整的重启和部署要快。
注:只要在远程客户端运行的时候,文件才能处于检测状态。如果你在启动远程客户端之前改变了文件,这个文件并不会被推送到远程服务器。
8.5.3 远程调试通道
当诊断远程应用程序的问题时,Java远程调试时非常有用的。不幸地,当你的项目部署在数据中心之外时,远程调试并不一直可用。如果你使用想Docker这样的基础技术容器,远程调试的设置会变得相当棘手。为了解决这些限制,开发者工具提供了通过HTTP进行远程调试的通道。远程客户端提供了一个8000端口的本地服务器连接远程调试工具。一旦连接建立,调试通信就会通过HTTP发送到远程应用程序。如果你想使用不同的端口,可以设置spring.devtools.remote.debug.local-port属性。
你需要确保你的远程应用程序启用了远程调试服务。通常地,可以通过配置JAVA_OPTS来实现。例如,在云平台中,你可以在你的manifest.yml中加入以下语句:
--- env: JAVA_OPTS: "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n" |
注:你不必在为-Xrunjdwp设置一个address=NNNN选项。如果不设置,Java会选择一个随机的空闲端口。
注:通过网络调试远程的服务可能会响应缓慢,你需要增加你的IDE的请求超时时间。例如,在Eclipse选择Preferences → Java → Debug,然后把Debugger timeout (ms)设大(大多数情况下设置为60000)。
9 打包生产应用程序
可执行的jar包可以部署到生产环境中。由于他们是独立的,他们同样适用基于云的部署。