spring_reference/all in one/spring_boot_features.md

86 KiB
Raw Blame History

Spring Boot特性

SpringApplication

SpringApplication类提供了一种从main()方法启动Spring应用的便捷方式。在很多情况下你只需委托给SpringApplication.run这个静态方法

public static void main(String[] args){
    SpringApplication.run(MySpringConfiguration.class, args);
}
  • 自定义Banner

通过在classpath下添加一个banner.txt或设置banner.location来指定相应的文件可以改变启动过程中打印的banner。如果这个文件有特殊的编码你可以使用banner.encoding设置它默认为UTF-8

在banner.txt中可以使用如下的变量

变量 描述
${application.version} MANIFEST.MF中声明的应用版本号例如1.0
${application.formatted-version} MANIFEST.MF中声明的被格式化后的应用版本号被括号包裹且以v作为前缀用于显示例如(v1.0)
${spring-boot.version} 正在使用的Spring Boot版本号例如1.2.2.BUILD-SNAPSHOT
${spring-boot.formatted-version} 正在使用的Spring Boot被格式化后的版本号被括号包裹且以v作为前缀, 用于显示,例如(v1.2.2.BUILD-SNAPSHOT)

如果想以编程的方式产生一个banner可以使用SpringBootApplication.setBanner(…)方法。使用org.springframework.boot.Banner接口实现你自己的printBanner()方法。

  • 自定义SpringApplication

如果默认的SpringApplication不符合你的口味你可以创建一个本地的实例并自定义它。例如关闭banner你可以这样写

public static void main(String[] args){
    SpringApplication app = new SpringApplication(MySpringConfiguration.class);
    app.setShowBanner(false);
    app.run(args);
}

传递给SpringApplication的构造器参数是spring beans的配置源。在大多数情况下这些将是@Configuration类的引用但它们也可能是XML配置或要扫描包的引用。

你也可以使用application.properties文件来配置SpringApplication。具体参考[Externalized 配置](#Externalized 配置)。查看配置选项的完整列表,可参考SpringApplication Javadoc.

  • 流畅的构建API

如果你需要创建一个分层的ApplicationContext多个具有父子关系的上下文或你只是喜欢使用流畅的构建API你可以使用SpringApplicationBuilder。SpringApplicationBuilder允许你以链式方式调用多个方法包括可以创建层次结构的parent和child方法。

new SpringApplicationBuilder()
    .showBanner(false)
    .sources(Parent.class)
    .child(Application.class)
    .run(args);

创建ApplicationContext层次时有些限制比如Web组件(components)必须包含在子上下文(child context)中且相同的Environment即用于父上下文也用于子上下文中。具体参考SpringApplicationBuilder javadoc

  • Application事件和监听器

除了常见的Spring框架事件比如ContextRefreshedEvent一个SpringApplication也发送一些额外的应用事件。一些事件实际上是在ApplicationContext被创建前触发的。

你可以使用多种方式注册事件监听器最普通的是使用SpringApplication.addListeners(…)方法。在你的应用运行时,应用事件会以下面的次序发送:

  1. 在运行开始但除了监听器注册和初始化以外的任何处理之前会发送一个ApplicationStartedEvent。
  2. 在Environment将被用于已知的上下文但在上下文被创建前会发送一个ApplicationEnvironmentPreparedEvent。
  3. 在refresh开始前但在bean定义已被加载后会发送一个ApplicationPreparedEvent。
  4. 启动过程中如果出现异常会发送一个ApplicationFailedEvent。

你通常不需要使用应用程序事件但知道它们的存在会很方便在某些场合可能会使用到。在Spring内部Spring Boot使用事件处理各种各样的任务。

  • Web环境

一个SpringApplication将尝试为你创建正确类型的ApplicationContext。在默认情况下使用AnnotationConfigApplicationContext或AnnotationConfigEmbeddedWebApplicationContext取决于你正在开发的是否是web应用。

用于确定一个web环境的算法相当简单基于是否存在某些类。如果需要覆盖默认行为你可以使用setWebEnvironment(boolean webEnvironment)。通过调用setApplicationContextClass(…)你可以完全控制ApplicationContext的类型。

当JUnit测试里使用SpringApplication时调用setWebEnvironment(false)是可取的。

  • 命令行启动器

如果你想获取原始的命令行参数或一旦SpringApplication启动你需要运行一些特定的代码你可以实现CommandLineRunner接口。在所有实现该接口的Spring beans上将调用run(String… args)方法。

import org.springframework.boot.*
import org.springframework.stereotype.*

@Component
public class MyBean implements CommandLineRunner {
    public void run(String... args) {
        // Do something...
    }
}

如果一些CommandLineRunner beans被定义必须以特定的次序调用你可以额外实现org.springframework.core.Ordered接口或使用org.springframework.core.annotation.Order注解。

  • Application退出

每个SpringApplication在退出时为了确保ApplicationContext被优雅的关闭将会注册一个JVM的shutdown钩子。所有标准的Spring生命周期回调比如DisposableBean接口或@PreDestroy注解都能使用。

此外如果beans想在应用结束时返回一个特定的退出码exit code可以实现org.springframework.boot.ExitCodeGenerator接口。

外化配置

Spring Boot允许外化externalize你的配置这样你能够在不同的环境下使用相同的代码。你可以使用properties文件YAML文件环境变量和命令行参数来外化配置。使用@Value注解可以直接将属性值注入到你的beans中并通过Spring的Environment抽象或绑定到结构化对象来访问。

Spring Boot使用一个非常特别的PropertySource次序来允许对值进行合理的覆盖需要以下面的次序考虑属性

  1. 命令行参数
  2. 来自于java:comp/env的JNDI属性
  3. Java系统属性System.getProperties()
  4. 操作系统环境变量
  5. 只有在random.*里包含的属性会产生一个RandomValuePropertySource
  6. 在打包的jar外的应用程序配置文件application.properties包含YAML和profile变量
  7. 在打包的jar内的应用程序配置文件application.properties包含YAML和profile变量
  8. 在@Configuration类上的@PropertySource注解
  9. 默认属性使用SpringApplication.setDefaultProperties指定

下面是一个具体的示例假设你开发一个使用name属性的@Component

import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*

@Component
public class MyBean {
    @Value("${name}")
    private String name;
    // ...
}

你可以将一个application.properties文件捆绑到jar内用来提供一个合理的默认name属性值。当运行在生产环境时可以在jar外提供一个application.properties文件来覆盖name属性。对于一次性的测试你可以使用特定的命令行开关启动比如java -jar app.jar --name="Spring")。

RandomValuePropertySource在注入随机值比如密钥或测试用例时很有用。它能产生整数longs或字符串比如

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

random.int*语法是OPEN value (,max) CLOSE此处OPENCLOSE可以是任何字符并且valuemax是整数。如果提供max那么value是最小的值max是最大的值不包含在内

  • 访问命令行属性

默认情况下SpringApplication将任何可选的命令行参数以'--'开头,比如,--server.port=9000转化为property并将其添加到Spring Environment中。如上所述命令行属性总是优先于其他属性源。

如果你不想将命令行属性添加到Environment里你可以使用SpringApplication.setAddCommandLineProperties(false)来禁止它们。

  • Application属性文件

SpringApplication将从以下位置加载application.properties文件并把它们添加到Spring Environment中

  1. 当前目录下的一个/config子目录
  2. 当前目录
  3. 一个classpath下的/config包
  4. classpath根路径root

这个列表是按优先级排序的(列表中位置高的将覆盖位置低的)。

你可以使用YAML'.yml')文件替代'.properties'。

如果不喜欢将application.properties作为配置文件名你可以通过指定spring.config.name环境属性来切换其他的名称。你也可以使用spring.config.location环境属性来引用一个明确的路径目录位置或文件路径列表以逗号分割

$ java -jar myproject.jar --spring.config.name=myproject
//or
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

如果spring.config.location包含目录相对于文件那它们应该以/结尾在加载前spring.config.name产生的名称将被追加到后面。不管spring.config.location是什么值默认的搜索路径classpath:,classpath:/config,file:,file:config/总会被使用。以这种方式你可以在application.properties中为应用设置默认值然后在运行的时候使用不同的文件覆盖它同时保留默认配置。

如果你使用环境变量而不是系统配置大多数操作系统不允许以句号分割period-separated的key名称但你可以使用下划线underscores代替比如使用SPRING_CONFIG_NAME代替spring.config.name。如果你的应用运行在一个容器中那么JNDI属性java:comp/env或servlet上下文初始化参数可以用来取代环境变量或系统属性当然也可以使用环境变量或系统属性。

  • 特定的Profile属性

除了application.properties文件特定配置属性也能通过命令惯例application-{profile}.properties来定义。特定Profile属性从跟标准application.properties相同的路径加载并且特定profile文件会覆盖默认的配置。

  • 属性占位符

当application.properties里的值被使用时它们会被存在的Environment过滤所以你能够引用先前定义的值比如系统属性

app.name=MyApp
app.description=${app.name} is a Spring Boot application

你也能使用相应的技巧为存在的Spring Boot属性创建'短'变量,具体参考Section 63.3, “Use short command line arguments”

  • 使用YAML代替Properties

YAML是JSON的一个超集也是一种方便的定义层次配置数据的格式。无论你何时将SnakeYAML 库放到classpath下SpringApplication类都会自动支持YAML作为properties的替换。

:如果你使用'starter POMs'spring-boot-starter会自动提供SnakeYAML。

  • 加载YAML

Spring框架提供两个便利的类用于加载YAML文档YamlPropertiesFactoryBean会将YAML作为Properties来加载YamlMapFactoryBean会将YAML作为Map来加载。

示例:

environments:
    dev:
        url: http://dev.bar.com
        name: Developer Setup
    prod:
        url: http://foo.bar.com
        name: My Cool App

上面的YAML文档会被转化到下面的属性中

environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App

YAML列表被表示成使用[index]间接引用作为属性keys的形式例如下面的YAML

my:
   servers:
       - dev.bar.com
       - foo.bar.com

将会转化到下面的属性中:

my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com

使用Spring DataBinder工具绑定那样的属性这是@ConfigurationProperties做的事你需要确定目标bean中有个java.util.List或Set类型的属性并且需要提供一个setter或使用可变的值初始化它比如下面的代码将绑定上面的属性

@ConfigurationProperties(prefix="my")
public class Config {
    private List<String> servers = new ArrayList<String>();
    public List<String> getServers() {
        return this.servers;
    }
}
  • 在Spring环境中使用YAML暴露属性

YamlPropertySourceLoader类能够用于将YAML作为一个PropertySource导出到Sprig Environment。这允许你使用熟悉的@Value注解和占位符语法访问YAML属性。

  • Multi-profile YAML文档

你可以在单个文件中定义多个特定配置profile-specific的YAML文档并通过一个spring.profiles key标示应用的文档。例如

server:
    address: 192.168.1.100
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: production
server:
    address: 192.168.1.120

在上面的例子中如果development配置被激活那server.address属性将是127.0.0.1。如果development和production配置profiles没有启用则该属性的值将是192.168.1.100。

  • YAML缺点

YAML文件不能通过@PropertySource注解加载。所以在这种情况下如果需要使用@PropertySource注解的方式加载值那就要使用properties文件。

  • 类型安全的配置属性

使用@Value("${property}")注解注入配置属性有时可能比较笨重特别是需要使用多个properties或你的数据本身有层次结构。为了控制和校验你的应用配置Spring Boot提供一个允许强类型beans的替代方法来使用properties。

示例:

@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings {
    private String username;
    private InetAddress remoteAddress;
    // ... getters and setters
}

当@EnableConfigurationProperties注解应用到你的@Configuration时任何被@ConfigurationProperties注解的beans将自动被Environment属性配置。这种风格的配置特别适合与SpringApplication的外部YAML配置进行配合使用。

# application.yml
connection:
    username: admin
    remoteAddress: 192.168.1.1
# additional configuration as required

为了使用@ConfigurationProperties beans你可以使用与其他任何bean相同的方式注入它们。

@Service
public class MyService {
    @Autowired
    private ConnectionSettings connection;
     //...
    @PostConstruct
    public void openConnection() {
        Server server = new Server();
        this.connection.configure(server);
    }
}

你可以通过在@EnableConfigurationProperties注解中直接简单的列出属性类来快捷的注册@ConfigurationProperties bean的定义。

@Configuration
@EnableConfigurationProperties(ConnectionSettings.class)
public class MyConfiguration {
}

:使用@ConfigurationProperties能够产生可被IDEs使用的元数据文件。具体参考Appendix B, Configuration meta-data

  • 第3方配置

正如使用@ConfigurationProperties注解一个类你也可以在@Bean方法上使用它。当你需要绑定属性到不受你控制的第三方组件时这种方式非常有用。

为了从Environment属性配置一个bean将@ConfigurationProperties添加到它的bean注册过程

@ConfigurationProperties(prefix = "foo")
@Bean
public FooComponent fooComponent() {
    ...
}

和上面ConnectionSettings的示例方式相同任何以foo为前缀的属性定义都会被映射到FooComponent上。

  • 松散的绑定Relaxed binding

Spring Boot使用一些宽松的规则用于绑定Environment属性到@ConfigurationProperties beans所以Environment属性名和bean属性名不需要精确匹配。常见的示例中有用的包括虚线分割比如context--path绑定到contextPath和将环境属性转为大写字母比如PORT绑定port

示例:

@Component
@ConfigurationProperties(prefix="person")
public class ConnectionSettings {
    private String firstName;
}

下面的属性名都能用于上面的@ConfigurationProperties类

属性 说明
person.firstName 标准驼峰规则
person.first-name 虚线表示,推荐用于.properties和.yml文件中
PERSON_FIRST_NAME 大写形式,使用系统环境变量时推荐

Spring会尝试强制外部的应用属性在绑定到@ConfigurationProperties beans时类型是正确的。如果需要自定义类型转换你可以提供一个ConversionService beanbean id为conversionService或自定义属性编辑器通过一个CustomEditorConfigurer bean

  • @ConfigurationProperties校验

Spring Boot将尝试校验外部的配置默认使用JSR-303如果在classpath路径中。你可以轻松的为你的@ConfigurationProperties类添加JSR-303 javax.validation约束注解

@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings {
    @NotNull
    private InetAddress remoteAddress;
    // ... getters and setters
}

你也可以通过创建一个叫做configurationPropertiesValidator的bean来添加自定义的Spring Validator。

spring-boot-actuator模块包含一个暴露所有@ConfigurationProperties beans的端点。简单地将你的web浏览器指向/configprops或使用等效的JMX端点。具体参考Production ready features

Profiles

Spring Profiles提供了一种隔离应用程序配置的方式并让这些配置只能在特定的环境下生效。任何@Component或@Configuration都能被@Profile标记从而限制加载它的时机。

@Configuration
@Profile("production")
public class ProductionConfiguration {
    // ...
}

以正常的Spring方式你可以使用一个spring.profiles.active的Environment属性来指定哪个配置生效。你可以使用平常的任何方式来指定该属性例如可以将它包含到你的application.properties中

spring.profiles.active=dev,hsqldb

或使用命令行开关:

--spring.profiles.active=dev,hsqldb
  • 添加激活的配置(profiles)

spring.profiles.active属性和其他属性一样都遵循相同的排列规则最高的PropertySource获胜。也就是说你可以在application.properties中指定生效的配置然后使用命令行开关替换它们。

有时将特定的配置属性添加到生效的配置中而不是替换它们是有用的。spring.profiles.include属性可以用来无条件的添加生效的配置。SpringApplication的入口点也提供了一个用于设置额外配置的Java API比如在那些通过spring.profiles.active属性生效的配置之上参考setAdditionalProfiles()方法。

示例:当一个应用使用下面的属性,并用--spring.profiles.active=prod开关运行那proddb和prodmq配置也会生效

---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include: proddb,prodmq

spring.profiles属性可以定义到一个YAML文档中用于决定什么时候该文档被包含进配置中。具体参考Section 63.6, “Change configuration depending on the environment”

  • 以编程方式设置profiles

在应用运行前你可以通过调用SpringApplication.setAdditionalProfiles(…)方法以编程的方式设置生效的配置。使用Spring的ConfigurableEnvironment接口激动配置也是可行的。

  • Profile特定配置文件

application.properties或application.yml和通过@ConfigurationProperties引用的文件这两种配置特定变种都被当作文件来加载的具体参考Section 23.3, “Profile specific properties”

日志

Spring Boot内部日志系统使用的是Commons Logging,但开放底层的日志实现。默认为会Java Util Logging, Log4J, Log4J2Logback提供配置。每种情况下都会预先配置使用控制台输出,也可以使用可选的文件输出。

默认情况下,如果你使用'Starter POMs'那么就会使用Logback记录日志。为了确保那些使用Java Util Logging, Commons Logging, Log4J或SLF4J的依赖库能够正常工作正确的Logback路由也被包含进来。

如果上面的列表看起来令人困惑不要担心Java有很多可用的日志框架。通常你不需要改变日志依赖Spring Boot默认的就能很好的工作。

  • 日志格式

Spring Boot默认的日志输出格式如下

2014-03-05 10:57:51.112  INFO 45469 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.52
2014-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-03-05 10:57:51.253  INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1358 ms
2014-03-05 10:57:51.698  INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-03-05 10:57:51.702  INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]

输出的节点items如下

  1. 日期和时间 - 精确到毫秒,且易于排序。
  2. 日志级别 - ERROR, WARN, INFO, DEBUG 或 TRACE。
  3. Process ID。
  4. 一个用于区分实际日志信息开头的---分隔符。
  5. 线程名 - 包括在方括号中(控制台输出可能会被截断)。
  6. 日志名 - 通常是源class的类名缩写
  7. 日志信息。
  • 控制台输出

默认的日志配置会在写日志消息时将它们回显到控制台。默认ERROR, WARN和INFO级别的消息会被记录。可以在启动应用时通过--debug标识开启控制台的DEBUG级别日志记录。

$ java -jar myapp.jar --debug

如果你的终端支持ANSI为了增加可读性将会使用彩色的日志输出。你可以设置spring.output.ansi.enabled为一个支持的值来覆盖自动检测。

  • 文件输出

默认情况下Spring Boot只会将日志记录到控制台而不会写进日志文件。如果除了输出到控制台你还想写入到日志文件那你需要设置logging.filelogging.path属性例如在你的application.properties中

下表显示如何组合使用logging.*

logging.file logging.path 示例 描述
(none) (none) 只记录到控制台
Specific file (none) my.log 写到特定的日志文件里,名称可以是一个精确的位置或相对于当前目录
(none) Specific folder /var/log 写到特定文件夹下的spring.log里名称可以是一个精确的位置或相对于当前目录

日志文件每达到10M就会被轮换分割和控制台一样默认记录ERROR, WARN和INFO级别的信息。

  • 日志级别

所有支持的日志系统在Spring的Environment例如在application.properties里都有通过'logging.level.*=LEVEL''LEVEL'是TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF中的一个设置的日志级别。

示例application.properties

logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
  • 自定义日志配置

通过将适当的库添加到classpath可以激活各种日志系统。然后在classpath的根目录(root)或通过Spring Environment的logging.config属性指定的位置提供一个合适的配置文件来达到进一步的定制注意由于日志是在ApplicationContext被创建之前初始化的所以不可能在Spring的@Configuration文件中通过@PropertySources控制日志。系统属性和平常的Spring Boot外部配置文件能正常工作

根据你的日志系统,下面的文件会被加载:

日志系统 定制
Logback logback.xml
Log4j log4j.properties或log4j.xml
Log4j2 log4j2.xml
JDK (Java Util Logging) logging.properties

为了帮助定制一些其他的属性从Spring的Envrionment转换到系统属性

Spring Environment System Property 评价
logging.file LOG_FILE 如果定义,在默认的日志配置中使用
logging.path LOG_PATH 如果定义,在默认的日志配置中使用
PID PID 当前的处理进程(process)ID如果能够被发现且还没有作为操作系统环境变量被定义

所有支持的日志系统在解析它们的配置文件时都能查询系统属性。具体可以参考spring-boot.jar中的默认配置。

在运行可执行的jar时Java Util Logging有类加载问题我们建议你尽可能避免使用它。

开发Web应用

Spring Boot非常适合开发web应用程序。你可以使用内嵌的TomcatJetty或Undertow轻轻松松地创建一个HTTP服务器。大多数的web应用都使用spring-boot-starter-web模块进行快速搭建和运行。

  • Spring Web MVC框架

Spring Web MVC框架通常简称为"Spring MVC")是一个富"模型,视图,控制器"的web框架。 Spring MVC允许你创建特定的@Controller或@RestController beans来处理传入的HTTP请求。 使用@RequestMapping注解可以将控制器中的方法映射到相应的HTTP请求。

示例:

@RestController
@RequestMapping(value="/users")
public class MyRestController {

    @RequestMapping(value="/{user}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long user) {
        // ...
    }

    @RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
    List<Customer> getUserCustomers(@PathVariable Long user) {
        // ...
    }

    @RequestMapping(value="/{user}", method=RequestMethod.DELETE)
    public User deleteUser(@PathVariable Long user) {
        // ...
    }
}
  • Spring MVC自动配置

Spring Boot为Spring MVC提供适用于多数应用的自动配置功能。在Spring默认基础上自动配置添加了以下特性

  1. 引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。
  2. 对静态资源的支持包括对WebJars的支持。
  3. 自动注册ConverterGenericConverterFormatter beans。
  4. 对HttpMessageConverters的支持。
  5. 自动注册MessageCodeResolver。
  6. 对静态index.html的支持。
  7. 对自定义Favicon的支持。

如果想全面控制Spring MVC你可以添加自己的@Configuration并使用@EnableWebMvc对其注解。如果想保留Spring Boot MVC的特性并只是添加其他的MVC配置(拦截器formatters视图控制器等)你可以添加自己的WebMvcConfigurerAdapter类型的@Bean不使用@EnableWebMvc注解

  • HttpMessageConverters

Spring MVC使用HttpMessageConverter接口转换HTTP请求和响应。合理的缺省值被包含的恰到好处out of the box例如对象可以自动转换为JSON使用Jackson库或XML如果Jackson XML扩展可用则使用它否则使用JAXB。字符串默认使用UTF-8编码。

如果需要添加或自定义转换器你可以使用Spring Boot的HttpMessageConverters类

import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }
}

任何在上下文中出现的HttpMessageConverter bean将会添加到converters列表你可以通过这种方式覆盖默认的转换器converters

  • MessageCodesResolver

Spring MVC有一个策略用于从绑定的errors产生用来渲染错误信息的错误码MessageCodesResolver。如果设置spring.mvc.message-codes-resolver.format属性为PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE(具体查看DefaultMessageCodesResolver.Format枚举值Spring Boot会为你创建一个MessageCodesResolver。

  • 静态内容

默认情况下Spring Boot从classpath下一个叫/static/public/resources或/META-INF/resources的文件夹或从ServletContext根目录提供静态内容。这使用了Spring MVC的ResourceHttpRequestHandler所以你可以通过添加自己的WebMvcConfigurerAdapter并覆写addResourceHandlers方法来改变这个行为加载静态文件

在一个单独的web应用中容器默认的servlet是开启的如果Spring决定不处理某些请求默认的servlet作为一个回退降级将从ServletContext根目录加载内容。大多数时候这不会发生除非你修改默认的MVC配置因为Spring总能够通过DispatcherServlet处理请求。

此外,上述标准的静态资源位置有个例外情况是Webjars内容。任何在/webjars/**路径下的资源都将从jar文件中提供只要它们以Webjars的格式打包。

如果你的应用将被打包成jar那就不要使用src/main/webapp文件夹。尽管该文件夹是一个共同的标准但它仅在打包成war的情况下起作用并且如果产生一个jar多数构建工具都会静悄悄的忽略它。

  • 模板引擎

正如REST web服务你也可以使用Spring MVC提供动态HTML内容。Spring MVC支持各种各样的模板技术包括Velocity, FreeMarker和JSPs。很多其他的模板引擎也提供它们自己的Spring MVC集成。

Spring Boot为以下的模板引擎提供自动配置支持

  1. FreeMarker
  2. Groovy
  3. Thymeleaf
  4. Velocity

如果可能的话应该忽略JSPs因为在内嵌的servlet容器使用它们时存在一些已知的限制

当你使用这些引擎的任何一种并采用默认的配置你的模板将会从src/main/resources/templates目录下自动加载。

IntelliJ IDEA根据你运行应用的方式会对classpath进行不同的整理。在IDE里通过main方法运行你的应用跟从Maven或Gradle或打包好的jar中运行相比会导致不同的顺序。这可能导致Spring Boot不能从classpath下成功地找到模板。如果遇到这个问题你可以在IDE里重新对classpath进行排序将模块的类和资源放到第一位。或者你可以配置模块的前缀为classpath*:/templates/这样会查找classpath下的所有模板目录。

  • 错误处理

Spring Boot默认提供一个/error映射用来以合适的方式处理所有的错误并且它在servlet容器中注册了一个全局的 错误页面。对于机器客户端相对于浏览器而言浏览器偏重于人的行为它会产生一个具有详细错误HTTP状态异常信息的JSON响应。对于浏览器客户端它会产生一个白色标签样式whitelabel的错误视图该视图将以HTML格式显示同样的数据可以添加一个解析为erro的View来自定义它。为了完全替换默认的行为你可以实现ErrorController并注册一个该类型的bean定义或简单地添加一个ErrorAttributes类型的bean以使用现存的机制只是替换显示的内容。

如果在某些条件下需要比较多的错误页面内嵌的servlet容器提供了一个统一的Java DSL领域特定语言来自定义错误处理。 示例:

@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
    return new MyCustomizer();
}

// ...
private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }
}

你也可以使用常规的Spring MVC特性来处理错误比如@ExceptionHandler方法@ControllerAdvice。ErrorController将会捡起任何没有处理的异常。

N.B. 如果你为一个路径注册一个ErrorPage最终被一个过滤器Filter处理对于一些非Spring web框架像Jersey和Wicket这很常见然后过滤器需要显式注册为一个ERROR分发器dispatcher

@Bean
public FilterRegistrationBean myFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter());
    ...
    registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
    return registration;
}

默认的FilterRegistrationBean没有包含ERROR分发器类型。

  • Spring HATEOAS

如果你正在开发一个使用超媒体的RESTful APISpring Boot将为Spring HATEOAS提供自动配置这在多数应用中都工作良好。自动配置替换了对使用@EnableHypermediaSupport的需求并注册一定数量的beans来简化构建基于超媒体的应用这些beans包括一个LinkDiscoverer和配置好的用于将响应正确编排为想要的表示的ObjectMapper。ObjectMapper可以根据spring.jackson.*属性或一个存在的Jackson2ObjectMapperBuilder bean进行自定义。

通过使用@EnableHypermediaSupport你可以控制Spring HATEOAS的配置。注意这会禁用上述的对ObjectMapper的自定义。

  • JAX-RS和Jersey

如果喜欢JAX-RS为REST端点提供的编程模型你可以使用可用的实现替代Spring MVC。如果在你的应用上下文中将Jersey 1.x和Apache Celtix的Servlet或Filter注册为一个@Bean那它们工作的相当好。Jersey 2.x有一些原生的Spring支持所以我们会在Spring Boot为它提供自动配置支持连同一个启动器starter

想要开始使用Jersey 2.x只需要加入spring-boot-starter-jersey依赖然后你需要一个ResourceConfig类型的@Bean用于注册所有的端点endpoints

@Component
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(Endpoint.class);
    }
}

所有注册的端点都应该被@Components和HTTP资源annotations比如@GET注解。

@Component
@Path("/hello")
public class Endpoint {
    @GET
    public String message() {
        return "Hello";
    }
}

由于Endpoint是一个Spring组件@Component所以它的生命周期受Spring管理并且你可以使用@Autowired添加依赖及使用@Value注入外部配置。Jersey servlet将被注册并默认映射到/*。你可以将@ApplicationPath添加到ResourceConfig来改变该映射。

默认情况下Jersey将在一个ServletRegistrationBean类型的@Bean中被设置成名称为jerseyServletRegistration的Servlet。通过创建自己的相同名称的bean你可以禁止或覆盖这个bean。你也可以通过设置spring.jersey.type=filter来使用一个Filter代替Servlet在这种情况下被覆盖或替换的@Bean是jerseyFilterRegistration。该servlet有@Order属性你可以通过spring.jersey.filter.order进行设置。不管是Servlet还是Filter注册都可以使用spring.jersey.init.*定义一个属性集合作为初始化参数传递过去。

这里有一个Jersey示例,你可以查看如何设置相关事项。

  • 内嵌servlet容器支持

Spring Boot支持内嵌的Tomcat, Jetty和Undertow服务器。多数开发者只需要使用合适的'Starter POM'来获取一个完全配置好的实例即可。默认情况下内嵌的服务器会在8080端口监听HTTP请求。

1.Servlets和Filters

当使用内嵌的servlet容器时你可以直接将servlet和filter注册为Spring的beans。在配置期间如果你想引用来自application.properties的值这是非常方便的。默认情况下如果上下文只包含单一的Servlet那它将被映射到根路径/。在多Servlet beans的情况下bean的名称将被用作路径的前缀。过滤器会被映射到/*。

如果基于约定convention-based的映射不够灵活你可以使用ServletRegistrationBean和FilterRegistrationBean类实现完全的控制。如果你的bean实现了ServletContextInitializer接口也可以直接注册它们。

2.EmbeddedWebApplicationContext

Spring Boot底层使用了一个新的ApplicationContext类型用于对内嵌servlet容器的支持。EmbeddedWebApplicationContext是一个特殊类型的WebApplicationContext它通过搜索一个单一的EmbeddedServletContainerFactory bean来启动自己。通常TomcatEmbeddedServletContainerFactoryJettyEmbeddedServletContainerFactory或UndertowEmbeddedServletContainerFactory将被自动配置。

你通常不需要知道这些实现类。大多数应用将被自动配置并根据你的行为创建合适的ApplicationContext和EmbeddedServletContainerFactory。

3.自定义内嵌servlet容器

常见的Servlet容器设置可以通过Spring Environment属性进行配置。通常你会把这些属性定义到application.properties文件中。 常见的服务器设置包括:

  1. server.port - 进来的HTTP请求的监听端口号
  2. server.address - 绑定的接口地址
  3. server.sessionTimeout - session超时时间

具体参考ServerProperties

编程方式的自定义

如果需要以编程的方式配置内嵌的servlet容器你可以注册一个实现EmbeddedServletContainerCustomizer接口的Spring bean。EmbeddedServletContainerCustomizer提供对ConfigurableEmbeddedServletContainer的访问ConfigurableEmbeddedServletContainer包含很多自定义的setter方法。

import org.springframework.boot.context.embedded.*;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements EmbeddedServletContainerCustomizer {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.setPort(9000);
    }
}

直接自定义ConfigurableEmbeddedServletContainer

如果上面的自定义手法过于受限你可以自己注册TomcatEmbeddedServletContainerFactoryJettyEmbeddedServletContainerFactory或UndertowEmbeddedServletContainerFactory。

@Bean
public EmbeddedServletContainerFactory servletContainer() {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
    factory.setPort(9000);
    factory.setSessionTimeout(10, TimeUnit.MINUTES);
    factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html");
    return factory;
}

很多可选的配置都提供了setter方法也提供了一些受保护的钩子方法以满足你的某些特殊需求。具体参考相关文档。

4.JSP的限制

在内嵌的servlet容器中运行一个Spring Boot应用时并打包成一个可执行的存档archive容器对JSP的支持有一些限制。

  1. tomcat只支持war的打包方式不支持可执行的jar。
  2. 内嵌的Jetty目前不支持JSPs。
  3. Undertow不支持JSPs。

这里有个JSP示例,你可以查看如何设置相关事项。

安全

如果Spring Security在classpath下那么web应用默认对所有的HTTP路径也称为终点端点表示API的具体网址使用'basic'认证。为了给web应用添加方法级别的保护你可以添加@EnableGlobalMethodSecurity并使用想要的设置。其他信息参考Spring Security Reference

默认的AuthenticationManager有一个单一的user'user'的用户名和随机密码会在应用启动时以INFO日志级别打印出来。如下

Using default security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35

:如果你对日志配置进行微调,确保org.springframework.boot.autoconfigure.security类别能记录INFO信息否则默认的密码不会被打印。

你可以通过提供security.user.password改变默认的密码。这些和其他有用的属性通过SecurityProperties以security为前缀的属性被外部化了。

默认的安全配置security configuration是在SecurityAutoConfiguration和导入的类中实现的SpringBootWebSecurityConfiguration用于web安全AuthenticationManagerConfiguration用于与非web应用也相关的认证配置。你可以添加一个@EnableWebSecurity bean来彻底关掉Spring Boot的默认配置。为了对它进行自定义你需要使用外部的属性配置和WebSecurityConfigurerAdapter类型的beans比如添加基于表单的登陆。在Spring Boot示例里有一些安全相关的应用可以带你体验常见的用例。

在一个web应用中你能得到的基本特性如下

  1. 一个使用内存存储的AuthenticationManager bean和唯一的user查看SecurityProperties.User获取user的属性
  2. 忽略(不保护)常见的静态资源路径(/css/**, /js/**, /images/****/favicon.ico)。
  3. 对其他的路径实施HTTP Basic安全保护。
  4. 安全相关的事件会发布到Spring的ApplicationEventPublisher成功和失败的认证拒绝访问
  5. Spring Security提供的常见底层特性HSTS, XSS, CSRF, 缓存)默认都被开启。

上述所有特性都能打开和关闭或使用外部的配置进行修改security.*。为了覆盖访问规则access rules而不改变其他自动配置的特性你可以添加一个使用@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)注解的WebSecurityConfigurerAdapter类型的@Bean。

如果Actuator也在使用你会发现

  1. 即使应用路径不受保护,被管理的路径也会受到保护。
  2. 安全相关的事件被转换为AuditEvents审计事件并发布给AuditService。
  3. 默认的用户有ADMIN和USER的角色。

使用外部属性能够修改Actuator执行器的安全特性management.security.*。为了覆盖应用程序的访问规则你可以添加一个WebSecurityConfigurerAdapter类型的@Bean。同时如果不想覆盖执行器的访问规则你可以使用@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)注解该bean否则使用@Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER)注解该bean。

使用SQL数据库

Spring框架为使用SQL数据库提供了广泛的支持。从使用JdbcTemplate直接访问JDBC到完全的对象关系映射技术比如Hibernate。Spring Data提供一个额外的功能直接从接口创建Repository实现并使用约定从你的方法名生成查询。

  • 配置DataSource

Java的javax.sql.DataSource接口提供了一个标准的使用数据库连接的方法。传统做法是一个DataSource使用一个URL连同相应的证书去初始化一个数据库连接。

1.对内嵌数据库的支持

开发应用时使用内存数据库是很实用的。显而易见地,内存数据库不需要提供持久化存储。你不需要在应用启动时填充数据库,也不需要在应用结束时丢弃数据。

Spring Boot可以自动配置的内嵌数据库包括H2, HSQLDerby。你不需要提供任何连接URLs只需要简单的添加你想使用的内嵌数据库依赖。

示例典型的POM依赖如下

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>runtime</scope>
</dependency>

对于自动配置的内嵌数据库你需要依赖spring-jdbc。在示例中它通过spring-boot-starter-data-jpa被传递地拉过来了。

2.连接到一个生产环境数据库

在生产环境中数据库连接可以使用DataSource池进行自动配置。下面是选取一个特定实现的算法

  • 由于Tomcat数据源连接池的性能和并发在tomcat可用时我们总是优先使用它。
  • 如果HikariCP可用我们将使用它。
  • 如果Commons DBCP可用我们将使用它但在生产环境不推荐使用它。
  • 最后如果Commons DBCP2可用我们将使用它。

如果你使用spring-boot-starter-jdbc或spring-boot-starter-data-jpa 'starter POMs'你将会自动获取对tomcat-jdbc的依赖。

其他的连接池可以手动配置。如果你定义自己的DataSource bean自动配置不会发生。

DataSource配置通过外部配置文件的spring.datasource.*属性控制。示例中你可能会在application.properties中声明下面的片段

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

其他可选的配置可以查看DataSourceProperties。同时注意你可以通过spring.datasource.*配置任何DataSource实现相关的特定属性具体参考你使用的连接池实现的文档。

既然Spring Boot能够从大多数数据库的url上推断出driver-class-name那么你就不需要再指定它了。对于一个将要创建的DataSource连接池我们需要能够验证Driver是否可用所以我们会在做任何事情之前检查它。比如如果你设置spring.datasource.driverClassName=com.mysql.jdbc.Driver然后这个类就会被加载。

3.连接到一个JNDI数据库

如果正在将Spring Boot应用部署到一个应用服务器你可能想要用应用服务器内建的特性来配置和管理你的DataSource并使用JNDI访问它。

spring.datasource.jndi-name属性可以用来替代spring.datasource.urlspring.datasource.username和spring.datasource.password去从一个特定的JNDI路径访问DataSource。比如下面application.properties中的片段展示了如何获取JBoss定义的DataSource

spring.datasource.jndi-name=java:jboss/datasources/customers
  • 使用JdbcTemplate

Spring的JdbcTemplate和NamedParameterJdbcTemplate类是被自动配置的你可以在自己的beans中通过@Autowire直接注入它们。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public MyBean(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    // ...
}
  • JPA和Spring Data

Java持久化API是一个允许你将对象映射为关系数据库的标准技术。spring-boot-starter-data-jpa POM提供了一种快速上手的方式。它提供下列关键的依赖

  • Hibernate - 一个非常流行的JPA实现。
  • Spring Data JPA - 让实现基于JPA的repositories更容易。
  • Spring ORMs - Spring框架的核心ORM支持。

我们不想在这涉及太多关于JPA或Spring Data的细节。你可以参考来自spring.io的指南使用JPA获取数据,并阅读Spring Data JPAHibernate的参考文档。

1.实体类

传统上JPA实体类被定义到一个persistence.xml文件中。在Spring Boot中这个文件不是必需的并被'实体扫描'替代。默认情况下在你主main配置类被@EnableAutoConfiguration或@SpringBootApplication注解的类下的所有包都将被查找。

任何被@Entity@Embeddable或@MappedSuperclass注解的类都将被考虑。一个普通的实体类看起来像下面这样

package com.example.myapp.domain;

import java.io.Serializable;
import javax.persistence.*;

@Entity
public class City implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String state;

    // ... additional members, often include @OneToMany mappings

    protected City() {
        // no-args constructor required by JPA spec
        // this one is protected since it shouldn't be used directly
    }

    public City(String name, String state) {
        this.name = name;
        this.country = country;
    }

    public String getName() {
        return this.name;
    }

    public String getState() {
        return this.state;
    }
    // ... etc
}

:你可以使用@EntityScan注解自定义实体扫描路径。具体参考Section 67.4, “Separate @Entity definitions from Spring configuration”

2.Spring Data JPA仓库

Spring Data JPA仓库repositories是用来定义访问数据的接口。根据你的方法名JPA查询会被自动创建。比如一个CityRepository接口可能声明一个findAllByState(String state)方法,用来查找给定状态的所有城市。

对于比较复杂的查询你可以使用Spring Data的Query来注解你的方法。

Spring Data仓库通常继承自RepositoryCrudRepository接口。如果你使用自动配置,包括在你的主配置类(被@EnableAutoConfiguration或@SpringBootApplication注解的类的包下的仓库将会被搜索。

下面是一个传统的Spring Data仓库

package com.example.myapp.domain;

import org.springframework.data.domain.*;
import org.springframework.data.repository.*;

public interface CityRepository extends Repository<City, Long> {

    Page<City> findAll(Pageable pageable);

    City findByNameAndCountryAllIgnoringCase(String name, String country);
}

我们仅仅触及了Spring Data JPA的表面。具体查看它的参考指南

3.创建和删除JPA数据库

默认情况下只有在你使用内嵌数据库H2, HSQL或DerbyJPA数据库才会被自动创建。你可以使用spring.jpa.*属性显示的设置JPA。比如为了创建和删除表你可以将下面的配置添加到application.properties中

spring.jpa.hibernate.ddl-auto=create-drop

Hibernate自己内部对创建删除表支持如果你恰好记得这回事更好的属性是hibernate.hbm2ddl.auto。使用spring.jpa.properties.*前缀在被添加到实体管理器之前会被剥离掉你可以设置Hibernate本身的属性比如hibernate.hbm2ddl.auto。示例spring.jpa.properties.hibernate.globally_quoted_identifiers=true将传递hibernate.globally_quoted_identifiers到Hibernate实体管理器。

默认情况下DDL执行或验证被延迟到ApplicationContext启动。这也有一个spring.jpa.generate-ddl标识如果Hibernate自动配置被激活那该标识就不会被使用因为ddl-auto设置粒度更细。

使用NoSQL技术

Spring Data提供其他项目用来帮你使用各种各样的NoSQL技术包括MongoDB, Neo4J, Elasticsearch, Solr, Redis, Gemfire, CouchbaseCassandra。Spring Boot为Redis, MongoDB, Elasticsearch, Solr和Gemfire提供自动配置。你可以充分利用其他项目但你需要自己配置它们。具体查看projects.spring.io/spring-data.中合适的参考文档。

  • Redis

Redis是一个缓存消息中间件及具有丰富特性的键值存储系统。Spring Boot为Jedis客户端库和由Spring Data Redis提供的基于Jedis客户端的抽象提供自动配置。spring-boot-starter-redis'Starter POM'为收集依赖提供一种便利的方式。

  1. 连接Redis

你可以注入一个自动配置的RedisConnectionFactoryStringRedisTemplate或普通的跟其他Spring Bean相同的RedisTemplate实例。默认情况下这个实例将尝试使用localhost:6379连接Redis服务器。

@Component
public class MyBean {

    private StringRedisTemplate template;

    @Autowired
    public MyBean(StringRedisTemplate template) {
        this.template = template;
    }
    // ...
}

如果你添加一个你自己的任何自动配置类型的@Bean它将替换默认的除了RedisTemplate的情况它是根据bean的名称'redisTemplate'而不是它的类型进行排除的。如果在classpath路径下存在commons-pool2默认你会获得一个连接池工厂。

  • MongoDB

MongoDB是一个开源的NoSQL文档数据库它使用一个JSON格式的模式schema替换了传统的基于表的关系数据。Spring Boot为使用MongoDB提供了很多便利包括spring-boot-starter-data-mongodb'Starter POM'。

1. 连接MongoDB数据库

你可以注入一个自动配置的org.springframework.data.mongodb.MongoDbFactory来访问Mongo数据库。默认情况下该实例将尝试使用URLmongodb://localhost/test连接一个MongoDB服务器。

import org.springframework.data.mongodb.MongoDbFactory;
import com.mongodb.DB;

@Component
public class MyBean {

    private final MongoDbFactory mongo;

    @Autowired
    public MyBean(MongoDbFactory mongo) {
        this.mongo = mongo;
    }

    // ...
    public void example() {
        DB db = mongo.getDb();
        // ...
    }
}

你可以通过设置spring.data.mongodb.uri来改变该url或指定一个host/port。比如你可能会在你的application.properties中设置如下的属性

spring.data.mongodb.host=mongoserver
spring.data.mongodb.port=27017

:如果没有指定spring.data.mongodb.port那将使用默认的端口27017。你可以简单的从上面的示例中删除这一行。如果不使用Spring Data Mongo你可以注入com.mongodb.Mongo beans而不是使用MongoDbFactory。

如果想全面控制MongoDB连接的建立你也可以声明自己的MongoDbFactory或Mongo@Beans。

2. MongoDBTemplate

Spring Data Mongo提供了一个MongoTemplate它的设计和Spring的JdbcTemplate很相似。正如JdbcTemplate一样Spring Boot会为你自动配置一个bean你只需简单的注入它即可

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final MongoTemplate mongoTemplate;

    @Autowired
    public MyBean(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
    // ...
}

具体参考MongoOperations Javadoc。

3. Spring Data MongoDB仓库

Spring Data的仓库包括对MongoDB的支持。正如上面讨论的JPA仓库基本的原则是查询会自动基于你的方法名创建。

实际上不管是Spring Data JPA还是Spring Data MongoDB都共享相同的基础设施。所以你可以使用上面的JPA示例并假设那个City现在是一个Mongo数据类而不是JPA @Entity它将以同样的方式工作。

package com.example.myapp.domain;

import org.springframework.data.domain.*;
import org.springframework.data.repository.*;

public interface CityRepository extends Repository<City, Long> {

    Page<City> findAll(Pageable pageable);

    City findByNameAndCountryAllIgnoringCase(String name, String country);

}
  • Gemfire

Spring Data Gemfire为使用Pivotal Gemfire数据管理平台提供了方便的Spring友好的工具。Spring Boot提供了一个用于聚集依赖的spring-boot-starter-data-gemfire'Starter POM'。目前不支持Gemfire的自动配置但你可以使用一个单一的注解使Spring Data仓库支持它。

  • Solr

Apache Solr是一个搜索引擎。Spring Boot为solr客户端库及Spring Data Solr提供的基于solr客户端库的抽象提供了基本的配置。Spring Boot提供了一个用于聚集依赖的spring-boot-starter-data-solr'Starter POM'。

1. 连接Solr

你可以像其他Spring beans一样注入一个自动配置的SolrServer实例。默认情况下该实例将尝试使用localhost:8983/solr连接一个服务器。

@Component
public class MyBean {

    private SolrServer solr;

    @Autowired
    public MyBean(SolrServer solr) {
        this.solr = solr;
    }
    // ...
}

如果你添加一个自己的SolrServer类型的@Bean它将会替换默认的。

2. Spring Data Solr仓库

Spring Data的仓库包括了对Apache Solr的支持。正如上面讨论的JPA仓库基本的原则是查询会自动基于你的方法名创建。

实际上不管是Spring Data JPA还是Spring Data Solr都共享相同的基础设施。所以你可以使用上面的JPA示例并假设那个City现在是一个@SolrDocument类而不是JPA @Entity它将以同样的方式工作。

:具体参考Spring Data Solr文档

  • Elasticsearch

Elastic Search是一个开源的分布式实时搜索和分析引擎。Spring Boot为Elasticsearch及Spring Data Elasticsearch提供的基于它的抽象提供了基本的配置。Spring Boot提供了一个用于聚集依赖的spring-boot-starter-data-elasticsearch'Starter POM'。

1. 连接Elasticsearch

你可以像其他Spring beans那样注入一个自动配置的ElasticsearchTemplate或Elasticsearch客户端实例。默认情况下该实例将尝试连接到一个本地内存服务器在Elasticsearch项目中的一个NodeClient但你可以通过设置spring.data.elasticsearch.clusterNodes为一个以逗号分割的host:port列表来将其切换到一个远程服务器比如TransportClient

@Component
public class MyBean {

    private ElasticsearchTemplate template;

    @Autowired
    public MyBean(ElasticsearchTemplate template) {
        this.template = template;
    }
    // ...
}

如果你添加一个你自己的ElasticsearchTemplate类型的@Bean它将替换默认的。

2. Spring Data Elasticseach仓库

Spring Data的仓库包括了对Elasticsearch的支持。正如上面讨论的JPA仓库基本的原则是查询会自动基于你的方法名创建。

实际上不管是Spring Data JPA还是Spring Data Elasticsearch都共享相同的基础设施。所以你可以使用上面的JPA示例并假设那个City现在是一个Elasticsearch @Document类而不是JPA @Entity它将以同样的方式工作。

:具体参考Spring Data Elasticsearch文档

消息

Spring Framework框架为集成消息系统提供了扩展extensive支持从使用JmsTemplate简化JMS API到实现一个完整异步消息接收的底层设施。Spring AMQP提供一个相似的用于'高级消息队列协议'的特征集并且Spring Boot也为RabbitTemplate和RabbitMQ提供了自动配置选项。Spring Websocket提供原生的STOMP消息支持并且Spring Boot通过starters和一些自动配置也提供了对它的支持。

  • JMS

javax.jms.ConnectionFactory接口提供了一个标准的用于创建一个javax.jms.Connection的方法javax.jms.Connection用于和JMS代理broker交互。尽管为了使用JMSSpring需要一个ConnectionFactory但通常你不需要直接使用它而是依赖于上层消息抽象具体参考Spring框架的相关章节。Spring Boot也会自动配置发送和接收消息需要的设施infrastructure

  1. HornetQ支持

如果在classpath下发现HornetQSpring Boot会自动配置ConnectionFactory。如果需要代理将会开启一个内嵌的已经自动配置好的代理除非显式设置mode属性。支持的modes有embedded显式声明使用一个内嵌的代理如果该代理在classpath下不可用将导致一个错误native使用netty传输协议连接代理。当后者被配置Spring Boot配置一个连接到一个代理的ConnectionFactory该代理运行在使用默认配置的本地机器上。

如果使用spring-boot-starter-hornetq连接到一个已存在的HornetQ实例所需的依赖都会被提供同时还有用于集成JMS的Spring基础设施。将org.hornetq:hornetq-jms-server添加到你的应用中你就可以使用embedded模式。

HornetQ配置被spring.hornetq.*中的外部配置属性所控制。例如你可能在application.properties声明以下片段

spring.hornetq.mode=native
spring.hornetq.host=192.168.1.210
spring.hornetq.port=9876

当内嵌代理时你可以选择是否启用持久化并且列表中的目标都应该是可用的。这些可以通过一个以逗号分割的列表来指定一些默认的配置项或定义org.hornetq.jms.server.config.JMSQueueConfiguration或org.hornetq.jms.server.config.TopicConfiguration类型的bean(s)来配置更高级的队列和主题。具体参考HornetQProperties

没有涉及JNDI查找目标是通过名字解析的名字即可以使用HornetQ配置中的name属性也可以是配置中提供的names。

  1. ActiveQ支持

如果发现ActiveMQ在classpath下可用Spring Boot会配置一个ConnectionFactory。如果需要代理将会开启一个内嵌的已经自动配置好的代理只要配置中没有指定代理URL

ActiveMQ配置是通过spring.activemq.*中的外部配置来控制的。例如你可能在application.properties中声明下面的片段

spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret

具体参考ActiveMQProperties

默认情况下如果目标还不存在ActiveMQ将创建一个所以目标是通过它们提供的名称解析出来的。

  1. 使用JNDI ConnectionFactory

如果你在一个应用服务器中运行你的应用Spring Boot将尝试使用JNDI定位一个JMS ConnectionFactory。默认情况会检查java:/JmsXA和java:/ XAConnectionFactory。如果需要的话你可以使用spring.jms.jndi-name属性来指定一个替代位置。

spring.jms.jndi-name=java:/MyConnectionFactory
  1. 发送消息

Spring的JmsTemplate会被自动配置你可以将它直接注入到你自己的beans中

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final JmsTemplate jmsTemplate;
@Autowired
public MyBean(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
// ...
}

JmsMessagingTemplate(Spring4.1新增的)也可以使用相同的方式注入。

  1. 接收消息

当JMS基础设施能够使用时任何bean都能够被@JmsListener注解以创建一个监听者端点。如果没有定义JmsListenerContainerFactory一个默认的将会被自动配置。下面的组件在someQueue目标上创建一个监听者端点。

@Component
public class MyBean {
@JmsListener(destination = "someQueue")
public void processMessage(String content) {
// ...
}
}

具体查看@EnableJms javadoc

发送邮件

Spring框架使用JavaMailSender接口为发送邮件提供了一个简单的抽象并且Spring Boot也为它提供了自动配置和一个starter模块。 具体查看JavaMailSender参考文档

如果spring.mail.host和相关的库通过spring-boot-starter-mail定义都存在一个默认的JavaMailSender将被创建。该sender可以通过spring.mail命名空间下的配置项进一步自定义具体参考MailProperties

使用JTA处理分布式事务

Spring Boot使用一个AtomkosBitronix的内嵌事务管理器来支持跨多个XA资源的分布式JTA事务。当部署到一个恰当的J2EE应用服务器时也会支持JTA事务。

当发现一个JTA环境时Spring Boot将使用Spring的JtaTransactionManager来管理事务。自动配置的JMSDataSource和JPA beans将被升级以支持XA事务。你可以使用标准的Spring idioms比如@Transactional来参与到一个分布式事务中。如果你处于JTA环境里但仍旧想使用本地事务你可以将spring.jta.enabled属性设置为false来禁用JTA自动配置功能。

  • 使用一个Atomikos事务管理器

Atomikos是一个非常流行的开源事务管理器它可以嵌入到你的Spring Boot应用中。你可以使用spring-boot-starter-jta-atomikosStarter POM去获取正确的Atomikos库。Spring Boot会自动配置Atomikos并将合适的depends-on应用到你的Spring Beans上确保它们以正确的顺序启动和关闭。

默认情况下Atomikos事务日志将被记录在应用home目录你的应用jar文件放置的目录下的transaction-logs文件夹中。你可以在application.properties文件中通过设置spring.jta.log-dir属性来自定义该目录。以spring.jta.开头的属性能用来自定义Atomikos的UserTransactionServiceIml实现。具体参考AtomikosProperties javadoc

为了确保多个事务管理器能够安全地和相应的资源管理器配合每个Atomikos实例必须设置一个唯一的ID。默认情况下该ID是Atomikos实例运行的机器上的IP地址。为了确保生产环境中该ID的唯一性你需要为应用的每个实例设置不同的spring.jta.transaction-manager-id属性值。

  • 使用一个Bitronix事务管理器

Bitronix是另一个流行的开源JTA事务管理器实现。你可以使用spring-boot-starter-jta-bitronixstarter POM为项目添加合适的Birtronix依赖。和Atomikos类似Spring Boot将自动配置Bitronix并对beans进行后处理post-process以确保它们以正确的顺序启动和关闭。

默认情况下Bitronix事务日志将被记录到应用home目录下的transaction-logs文件夹中。通过设置spring.jta.log-dir属性你可以自定义该目录。以spring.jta.开头的属性将被绑定到bitronix.tm.Configuration bean你可以通过这完成进一步的自定义。具体参考Bitronix文档

为了确保多个事务管理器能够安全地和相应的资源管理器配合每个Bitronix实例必须设置一个唯一的ID。默认情况下该ID是Bitronix实例运行的机器上的IP地址。为了确保生产环境中该ID的唯一性你需要为应用的每个实例设置不同的spring.jta.transaction-manager-id属性值。

  • 使用一个J2EE管理的事务管理器

如果你将Spring Boot应用打包为一个war或ear文件并将它部署到一个J2EE的应用服务器中那你就能使用应用服务器内建的事务管理器。Spring Boot将尝试通过查找常见的JNDI路径java:comp/UserTransaction, java:comp/TransactionManager等来自动配置一个事务管理器。如果使用应用服务器提供的事务服务你通常需要确保所有的资源都被应用服务器管理并通过JNDI暴露出去。Spring Boot通过查找JNDI路径java:/JmsXA或java:/XAConnectionFactory获取一个ConnectionFactory来自动配置JMS并且你可以使用spring.datasource.jndi-name属性配置你的DataSource。

  • 混合XA和non-XA的JMS连接

当使用JTA时主要的JMS ConnectionFactory bean将是XA aware并参与到分布式事务中。有些情况下你可能需要使用non-XA的ConnectionFactory去处理一些JMS消息。例如你的JMS处理逻辑可能比XA超时时间长。

如果想使用一个non-XA的ConnectionFactory你可以注入nonXaJmsConnectionFactory bean而不是@Primary jmsConnectionFactory bean。为了保持一致jmsConnectionFactory bean将以别名xaJmsConnectionFactor来被使用。

示例如下:

// Inject the primary (XA aware) ConnectionFactory
@Autowired
private ConnectionFactory defaultConnectionFactory;
// Inject the XA aware ConnectionFactory (uses the alias and injects the same as above)
@Autowired
@Qualifier("xaJmsConnectionFactory")
private ConnectionFactory xaConnectionFactory;
// Inject the non-XA aware ConnectionFactory
@Autowired
@Qualifier("nonXaJmsConnectionFactory")
private ConnectionFactory nonXaConnectionFactory;
  • 支持可替代的内嵌事务管理器

XAConnectionFactoryWrapperXADataSourceWrapper接口用于支持可替换的内嵌事务管理器。该接口用于包装XAConnectionFactory和XADataSource beans并将它们暴露为普通的ConnectionFactory和DataSource beans这样在分布式事务中可以透明使用。

Spring集成

Spring集成提供基于消息和其他协议的比如HTTPTCP等的抽象。如果Spring集成在classpath下可用它将会通过@EnableIntegration注解被初始化。如果classpath下'spring-integration-jmx'可用则消息处理统计分析将被通过JMX发布出去。具体参考IntegrationAutoConfiguration类

基于JMX的监控和管理

Java管理扩展JMX提供了一个标准的用于监控和管理应用的机制。默认情况下Spring Boot将创建一个id为mbeanServer的MBeanServer并导出任何被Spring JMX注解@ManagedResource,@ManagedAttribute,@ManagedOperation的beans。具体参考JmxAutoConfiguration类

测试

Spring Boot提供很多有用的测试应用的工具。spring-boot-starter-test POM提供Spring TestJUnitHamcrest和Mockito的依赖。在spring-boot核心模块org.springframework.boot.test包下也有很多有用的测试工具。

  • 测试作用域依赖

如果使用spring-boot-starter-test Starter POM在test作用域内你将发现下列被提供的库

  • Spring Test - 对Spring应用的集成测试支持
  • JUnit - 事实上的de-facto标准用于Java应用的单元测试。
  • Hamcrest - 一个匹配对象的库也称为约束或前置条件它允许assertThat等JUnit类型的断言。
  • Mockito - 一个Java模拟框架。

这也有一些我们写测试用例时经常用到的库。如果它们不能满足你的要求,你可以随意添加其他的测试用的依赖库。

  • 测试Spring应用

依赖注入最大的优点就是它能够让你的代码更容易进行单元测试。你只需简单的通过new操作符实例化对象而不需要涉及Spring。你也可以使用模拟对象替换真正的依赖。

你常常需要在进行单元测试后开始集成测试在这个过程中只需要涉及到Spring的ApplicationContext。在执行集成测试时不需要部署应用或连接到其他基础设施是非常有用的。

Spring框架包含一个dedicated测试模块用于这样的集成测试。你可以直接声明对org.springframework:spring-test的依赖或使用spring-boot-starter-test Starter POM以透明的方式拉取它。

如果你以前没有使用过spring-test模块可以查看Spring框架参考文档中的相关章节

  • 测试Spring Boot应用

一个Spring Boot应用只是一个Spring ApplicationContext所以在测试它时除了正常情况下处理一个vanilla Spring context外不需要做其他特别事情。唯一需要注意的是如果你使用SpringApplication创建上下文外部配置日志和Spring Boot的其他特性只会在默认的上下文中起作用。

Spring Boot提供一个@SpringApplicationConfiguration注解用来替换标准的spring-test @ContextConfiguration注解。如果使用@SpringApplicationConfiguration来设置你的测试中使用的ApplicationContext它最终将通过SpringApplication创建并且你将获取到Spring Boot的其他特性。

示例如下:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleDataJpaApplication.class)
public class CityRepositoryIntegrationTests {
@Autowired
CityRepository repository;
// ...
}	

提示:上下文加载器会通过查找@WebIntegrationTest或@WebAppConfiguration注解来猜测你想测试的是否是web应用例如是否使用MockMVCMockMVC和@WebAppConfiguration是spring-test的一部分

如果想让一个web应用启动并监听它的正常的端口你可以使用HTTP来测试它比如使用RestTemplate并使用@WebIntegrationTest注解你的测试类或它的一个父类。这很有用因为它意味着你可以对你的应用进行全栈测试但在一次HTTP交互后你需要在你的测试类中注入相应的组件并使用它们断言应用的内部状态。

示例:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleDataJpaApplication.class)
@WebIntegrationTest
public class CityRepositoryIntegrationTests {
@Autowired
CityRepository repository;
RestTemplate restTemplate = new TestRestTemplate();
// ... interact with the running server
}

Spring测试框架在每次测试时会缓存应用上下文。因此只要你的测试共享相同的配置不管你实际运行多少测试开启和停止服务器只会发生一次。

你可以为@WebIntegrationTest添加环境变量属性来改变应用服务器端口号比如@WebIntegrationTest("server.port:9000")。此外你可以将server.port和management.port属性设置为来让你的集成测试使用随机的端口号例如

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class)
@WebIntegrationTest({"server.port=0", "management.port=0"})
public class SomeIntegrationTests {
// ...
}

可以查看Section 64.4, “Discover the HTTP port at runtime”它描述了如何在测试期间发现分配的实际端口。

  1. 使用Spock测试Spring Boot应用

如果期望使用Spock测试一个Spring Boot应用你应该将Spock的spock-spring模块依赖添加到应用的构建中。spock-spring将Spring的测试框架集成到了Spock里。

注意你不能使用上述提到的@SpringApplicationConfiguration注解因为Spock找不到@ContextConfiguration元注解。为了绕过该限制,你应该直接使用@ContextConfiguration注解并使用Spring Boot特定的上下文加载器来配置它。

@ContextConfiguration(loader = SpringApplicationContextLoader.class)
class ExampleSpec extends Specification {
// ...
}

上面描述的注解在Spock中可以使用比如你可以使用@WebIntegrationTest注解你的Specification以满足测试需要。

  • 测试工具

打包进spring-boot的一些有用的测试工具类。

  1. ConfigFileApplicationContextInitializer

ConfigFileApplicationContextInitializer是一个ApplicationContextInitializer可以用来测试加载Spring Boot的application.properties文件。当不需要使用@SpringApplicationConfiguration提供的全部特性时你可以使用它。

@ContextConfiguration(classes = Config.class,initializers = ConfigFileApplicationContextInitializer.class)
```  
2. EnvironmentTestUtils

EnvironmentTestUtils允许你快速添加属性到一个ConfigurableEnvironmentConfigurableApplicationContext。只需简单的使用key=value字符串调用它:
```java
EnvironmentTestUtils.addEnvironment(env, "org=Spring", "name=Boot");
  1. OutputCapture

OutputCapture是一个JUnit Rule用于捕获System.out和System.err输出。只需简单的将捕获声明为一个@Rule并使用toString()断言:

import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.OutputCapture;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

public class MyTest {
@Rule
public OutputCapture capture = new OutputCapture();
@Test
public void testName() throws Exception {
System.out.println("Hello World!");
assertThat(capture.toString(), containsString("World"));
}
}
  1. TestRestTemplate

TestRestTemplate是一个方便进行集成测试的Spring RestTemplate子类。你会获取到一个普通的模板或一个发送基本HTTP认证使用用户名和密码的模板。在任何情况下这些模板都表现出对测试友好不允许重定向这样你可以对响应地址进行断言忽略cookies这样模板就是无状态的对于服务端错误不会抛出异常。推荐使用Apache HTTP Client(4.3.2或更好的版本)但不强制这样做。如果在classpath下存在Apache HTTP ClientTestRestTemplate将以正确配置的client进行响应。

public class MyTest {
RestTemplate template = new TestRestTemplate();
@Test
public void testRequest() throws Exception {
HttpHeaders headers = template.getForEntity("http://myhost.com", String.class).getHeaders();
assertThat(headers.getLocation().toString(), containsString("myotherhost"));
}
}

开发自动配置和使用条件

如果你在一个开发者共享库的公司工作或你在从事一个开源或商业型的库你可能想要开发自己的auto-configuration。Auto-configuration类能够在外部的jars中绑定并仍能被Spring Boot发现。

  • 理解auto-configured beans

从底层来讲auto-configured是使用标准的@Configuration实现的类另外的@Conditional注解用来约束在什么情况下使用auto-configuration。通常auto-configuration类使用@ConditionalOnClass和@ConditionalOnMissingBean注解。这是为了确保只有在相关的类被发现和你没有声明自己的@Configuration时才应用auto-configuration。

你可以浏览spring-boot-autoconfigure的源码查看我们提供的@Configuration类查看META-INF/spring.factories文件

  • 定位auto-configuration候选者

Spring Boot会检查你发布的jar中是否存在META-INF/spring.factories文件。该文件应该列出以EnableAutoConfiguration为key的配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

如果配置需要应用特定的顺序,你可以使用@AutoConfigureAfter@AutoConfigureBefore注解。例如你想提供web-specific配置你的类就需要应用在WebMvcAutoConfiguration后面。

  • Condition注解

你几乎总是需要在你的auto-configuration类里添加一个或更多的@Condition注解。@ConditionalOnMissingBean注解是一个常见的示例它经常用于允许开发者覆盖auto-configuration如果他们不喜欢你提供的默认行为。

Spring Boot包含很多@Conditional注解你可以在自己的代码中通过注解@Configuration类或单独的@Bean方法来重用它们。

  1. Class条件

@ConditionalOnClass和@ConditionalOnMissingClass注解允许根据特定类是否出现来跳过配置。由于注解元数据是使用ASM来解析的你实际上可以使用value属性来引用真正的类即使该类可能实际上并没有出现在运行应用的classpath下。如果你倾向于使用一个String值来指定类名你也可以使用name属性。

  1. Bean条件

@ConditionalOnBean和@ConditionalOnMissingBean注解允许根据特定beans是否出现来跳过配置。你可以使用value属性来指定beansby type也可以使用name来指定beansby name。search属性允许你限制搜索beans时需要考虑的ApplicationContext的层次。

:当@Configuration类被解析时@Conditional注解会被处理。Auto-configure @Configuration总是最后被解析在所有用户定义beans后面然而如果你将那些注解用到常规的@Configuration类需要注意不能引用那些还没有创建好的bean定义。

  1. Property条件

@ConditionalOnProperty注解允许根据一个Spring Environment属性来决定是否包含配置。可以使用prefix和name属性指定要检查的配置属性。默认情况下任何存在的只要不是false的属性都会匹配。你也可以使用havingValue和matchIfMissing属性创建更高级的检测。

  1. Resource条件

@ConditionalOnResource注解允许只有在特定资源出现时配置才会被包含。资源可以使用常见的Spring约定命名例如file:/home/user/test.dat。

  1. Web Application条件

@ConditionalOnWebApplication和@ConditionalOnNotWebApplication注解允许根据应用是否为一个'web应用'来决定是否包含配置。一个web应用是任何使用Spring WebApplicationContext定义一个session作用域或有一个StandardServletEnvironment的应用。

  1. SpEL表达式条件

@ConditionalOnExpression注解允许根据SpEL表达式结果来跳过配置。

WebSockets

Spring Boot为内嵌的Tomcat(8和7)Jetty 9和Undertow提供WebSockets自动配置。如果你正在将一个war包部署到一个单独的容器Spring Boot会假设该容器会对它的WebSocket支持相关的配置负责。

Spring框架提供丰富的WebSocket支持通过spring-boot-starter-websocket模块可以轻易获取到。