• 20220608 Howto Guides


    前言

    文档地址

    1. Spring Boot 应用程序

    1.1. 创建自己的 FailureAnalyzer

    FailureAnalyzer 是在启动时截获异常并将其转换为包装在 FailureAnalysis 中的人类可读消息的好方法。Spring Boot 为与应用上下文相关的异常、 JSR-303 验证等提供了这样的分析器。你也可以创建你自己的。

    AbstractFailureAnalyzer 是一个方便的 FailureAnalyzer 扩展,它检查要处理的异常中是否存在指定的异常类型。您可以对此进行扩展,以便您的实现只有在指定的异常出现时才有机会处理该异常。如果由于某种原因无法处理该异常,请返回 null 以使另一个实现有机会处理该异常。

    FailureAnalyzer 实现必须在 META-INF/spring.factories 中注册。以下示例注册 ProjectConstraintViolationFailureAnalyzer

    org.springframework.boot.diagnostics.FailureAnalyzer=\
    com.example.ProjectConstraintViolationFailureAnalyzer
    

    如果您需要访问 BeanFactoryEnvironment ,则 FailureAnalyzer 可以分别实现 BeanFactoryAwareEnvironmentAware

    源码学习

    org.springframework.boot.SpringApplication#run(java.lang.String...)
        org.springframework.boot.SpringApplication#handleRunFailure
        	org.springframework.boot.SpringApplication#getExceptionReporters
        		getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    					new Class<?>[] { ConfigurableApplicationContext.class }, context);
    				List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    					org.springframework.boot.diagnostics.FailureAnalyzers#FailureAnalyzers(org.springframework.context.ConfigurableApplicationContext, java.lang.ClassLoader)
          
    

    spring.factories 里读取 org.springframework.boot.SpringBootExceptionReporter 后,实例化 FailureAnalyzers(通过构造函数将 spring.factories 中所有 org.springframework.boot.diagnostics.FailureAnalyzer 的值作为 FailureAnalyzers 构造器的参数)

    org.springframework.boot.SpringBootExceptionReporter=\
    org.springframework.boot.diagnostics.FailureAnalyzers
    
    org.springframework.boot.diagnostics.FailureAnalyzer=\
    xxx
    
    org.springframework.boot.diagnostics.FailureAnalyzers#FailureAnalyzers(org.springframework.context.ConfigurableApplicationContext)
    

    org.springframework.boot.SpringApplication 中有两个 getSpringFactoriesInstances 方法,都是用来获取实例的,第一个方法使用的是无参构造函数,第二个使用有参构造函数(可以为多个)。例如,FailureAnalyzers 就是使用的第二个方法,在构造器中调用方法获取 FailureAnalyzer

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }
    
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    

    1.2. 自动配置故障排除

    Spring Boot 自动配置会尽力“做正确的事”,但是有时事情会失败,并且很难说出原因。

    任何 Spring Boot ApplicationContext 都有一个非常有用的功能可用, ConditionEvaluationReport 。如果启用 DEBUG 日志记录输出,则可以看到它。如果使用 spring-boot-actuator ,那么还有一个 conditions 端点,该端点以 JSON 形式呈现报告。使用该端点来调试应用程序,并在运行时查看 Spring Boot 添加(或未添加)了哪些功能。

    通过查看源代码和 Javadoc ,可以回答更多问题。阅读代码时,请记住以下 经验法则

    • 查找名称为 *AutoConfiguration 的类并阅读其源代码。特别注意 @Conditional* 注解,以了解它们启用了哪些功能以及何时启用。添加 --debug 到命令行或系统属性 -Ddebug 以在控制台上获取在您的应用中做出的所有自动配置决策的日志。在启用了执行器的运行应用程序中,查看 conditions 端点( /actuator/conditions 或等效的 JMX 选项)以获取相同信息
    • 查找 @ConfigurationProperties(例如 ServerProperties )的类,然后从中读取可用的外部配置选项。 @ConfigurationProperties 注解具有一个 name 充当前缀外部性能属性。因此,ServerProperties 拥有 prefix="server" 并且它的配置属性是 server.portserver.address 以及其他。在启用了执行器的运行应用程序中,查看 configprops 端点
    • 请查看 Binder 上的 bind 方法的使用情况,以便以轻松的方式显式地从 Environment 中提取配置值。它通常与前缀一起使用。
    • 查找直接绑定到 Environment@Value 注解
    • 寻找 @ConditionalOnExpression 注解以响应 SpEL 表达式来开启或关闭功能,这些注解通常使用从 Environment 中解析的占位符进行计算

    源码学习

    Binder 的简单使用:

    Binder binder = Binder.get(ctx.getEnvironment()); //绑定简单配置
    MyProperties myProperties = binder.bind("my", Bindable.of(MyProperties.class)).get();
    System.out.println(myProperties);
    

    @ConfigurationProperties(prefix = "my") 使用原理:

    通过后置处理器,在实例化 Bean 之前,调用 org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization ,底层也是使用 Binder 实现的。

    1.3. 启动之前自定义 EnvironmentApplicationContext

    SpringApplication 具有 ApplicationListenersApplicationContextInitializers ,可以被用于应用自定义的 context 或 environment 。Spring Boot 从 META-INF/spring.factories 加载了许多此类自定义项,以供内部使用。注册其他自定义项的方法有多种:

    • 在运行之前,通过对每个应用程序进行编程,方法是调用 SpringApplicationaddListenersaddInitializers 方法
    • 通过设置 context.initializer.classescontext.listener.classes 属性
    • 通过添加 META-INF/spring.factories ,并打包一个 jar 文件

    SpringApplication 发送一些特殊 ApplicationEvents 给监听器(一些甚至在创建上下文之前),然后注册了监听 ApplicationContext 发布事件的监听器。参考文档

    还可以使用 EnvironmentPostProcessor 在刷新应用上下文之前来自定义 Environment 。每个实现都应在 META-INF/spring.factories 中注册,如以下示例所示:

    org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor
    

    该实现可以加载任意文件并将其添加到 Environment 中。例如,以下示例从类路径加载 YAML 配置文件:

    import java.io.IOException;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.env.EnvironmentPostProcessor;
    import org.springframework.boot.env.YamlPropertySourceLoader;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.PropertySource;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    import org.springframework.util.Assert;
    
    public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
    
        private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    
        @Override
        public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
            Resource path = new ClassPathResource("com/example/myapp/config.yml");
            PropertySource<?> propertySource = loadYaml(path);
            environment.getPropertySources().addLast(propertySource);
        }
    
        private PropertySource<?> loadYaml(Resource path) {
            Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist");
            try {
                return this.loader.load("custom-resource", path).get(0);
            }
            catch (IOException ex) {
                throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
            }
        }
    
    }
    

    Environment 已经用所有 Spring Boot 默认加载的常见的属性来源准备好。因此可以从 Environment 中获取文件的位置。前面的示例将 custom-resource 属性源添加到列表的末尾,以便在其他任何常见位置定义的键具有优先权。自定义实现可以定义另一个顺序。

    @SpringBootApplication 上使用 @PropertySource 似乎是 Environment 加载一个自定义资源的便利和简单的方法,然而我们不建议这样做,因为 Spring Boot 准备 EnvironmentApplicationContext 被刷新之前。用 @PropertySource 定义的任何键加载得太晚,对自动配置没有任何影响。

    源码学习

    ApplicationListenerApplicationContextInitializer 被设置到 SpringApplication

    ApplicationListenerApplicationContextInitializer 被设置到 SpringApplication 中,是通过构造器:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = new ArrayList<>(
            getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    

    可以看出,这里的两个集合的值,是通过 spring.factories 里定义的属性获取,通过无参构造函数实例化。

    使用 ApplicationContextInitializer

    接口定义:

    @FunctionalInterface
    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    
    	void initialize(C applicationContext);
    
    }
    

    使用地点:

    org.springframework.boot.SpringApplication#run(java.lang.String...)
    	prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    		applyInitializers(context);
    

    部分 ApplicationContextInitializer 实现,本身也实现了 ApplicationListener ,在 initialize 方法中,将自身添加到 SpringApplicationApplicationListener 列表中。

    注意:有些事件已经在 prepareContext 方法之前就已经发布。

    使用 ApplicationListener

    SpringApplication 里的 ApplicationListener ,部分事件是在上下文刷新前就已经发布,所以需要提前实例化出来

    org.springframework.boot.SpringApplication#run(java.lang.String...)	
    	SpringApplicationRunListeners listeners = getRunListeners(args);
    
    	private SpringApplicationRunListeners getRunListeners(String[] args) {
    		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    		return new SpringApplicationRunListeners(logger,
    				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
    				this.applicationStartup);
        }
    

    这里获取了 spring.factories 里定义的 SpringApplicationRunListener 实现,默认是 EventPublishingRunListener 。这个类中存在 SimpleApplicationEventMulticaster 事件多播器,并将 SpringApplication 里的 ApplicationListener 添加到这个事件多播器中。

    SpringApplicationRunListener 定义了多个方法,这些方法在 SpringApplication#run 过程中的不同时期分别被调用。SpringApplicationRunListener 的实现 EventPublishingRunListener ,在每个方法里发布一个事件,调用事件监听器的方法。

    EnvironmentPostProcessor

    接口定义:

    @FunctionalInterface
    public interface EnvironmentPostProcessor {
    
    	void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
    
    }
    

    使用地点:

    org.springframework.boot.SpringApplication#run(java.lang.String...)	
    	ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    		listeners.environmentPrepared(bootstrapContext, environment);
    			EnvironmentPostProcessorApplicationListener#onApplicationEnvironmentPreparedEvent
    

    这个接口用来操作操作 Environment ,一般是向其中添加 PropertySource ,例如 RandomValuePropertySourceEnvironmentPostProcessor 向其中添加了一个 RandomValuePropertySource

    1.4. 建立 ApplicationContext 层次结构(添加父上下文或根上下文)

    您可以使用 ApplicationBuilder 类创建父/子 ApplicationContext 层次结构。有关更多信息,请参见 Spring Boot 功能部分中的 Fluent Builder API

    1.5. 创建一个非 Web 应用程序

    并非所有的Spring应用程序都必须是 Web 应用程序(或 Web 服务)。如果要在 main 方法中执行一些代码,又要引导 Spring 应用程序以设置要使用的基础设施,则可以使用 Spring Boot 的 SpringApplication 功能。SpringApplication 根据是否需要 Web 应用程序来更改其 ApplicationContext 类。您可以做的第一件事是让服务器相关的依赖项(例如 Servlet API )脱离类路径。如果你不能做到这一点(例如,您使用相同的代码库的运行两个应用程序),那么你可以显式调用 SpringApplication 实例的 setWebApplicationType(WebApplicationType.NONE) 或设置 applicationContextClass 属性(通过 Java API 或与外部属性)。您可以将要作为业务逻辑运行的应用程序代码实现为CommandLineRunner 并作为 @Bean 定义放到上下文中。

    2. 属性 (Properties) 和配置 (Configuration)

    本节包括有关设置和读取属性和配置设置以及它们与 Spring Boot 应用程序交互的主题。

    2.1. 在构建时自动扩展属性

    您可以使用现有的构建配置自动扩展它们,而不是对项目的构建配置中也指定的某些属性进行硬编码。在 Maven 和 Gradle 中都是可行的。

    2.1.1. 使用 Maven 自动扩展属性

    您可以使用资源过滤从 Maven 项目自动扩展属性。如果使用 spring-boot-starter-parent ,则可以使用 @..@ 占位符引用 Maven 的“项目属性” ,如以下示例所示:

    app.encoding=@project.build.sourceEncoding@
    app.java.version=@java.version@
    

    这样只会过滤生产配置(也就是说,不会对 src/test/resources 进行过滤)。

    如果启用 addResources 标志,则 spring-boot:run 目标可以将 src/main/resources 直接添加到类路径中(用于热重载)。这样做避免了资源过滤和此功能。相反,您可以使用 exec:java goal 或自定义插件的配置。有关更多详细信息,请参阅 插件使用页面

    如果您不使用 parent 启动器,则需要在 pom.xml<build/> 标签中包括以下标签:

    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    

    您还需要在其中包含以下 <plugins/> 标签:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.7</version>
        <configuration>
            <delimiters>
                <delimiter>@</delimiter>
            </delimiters>
            <useDefaultDelimiters>false</useDefaultDelimiters>
        </configuration>
    </plugin>
    

    如果在配置中使用标准的 Spring 占位符(例如 ${placeholder} ),则 useDefaultDelimiters 属性很重要。如果该属性未设置为 false ,则可以通过构建扩展它们。

    2.1.2. 使用 Gradle 自动扩展属性

    您可以通过配置 Java 插件的 processResources 任务来自动扩展 Gradle 项目中的属性,如以下示例所示:

    tasks.named('processResources') {
        expand(project.properties)
    }
    

    然后,您可以使用占位符来引用 Gradle 项目的属性,如以下示例所示:

    app.name=${name}
    app.description=${description}
    

    Gradle 的 expand 方法使用 Groovy 的 SimpleTemplateEngine 方法来转换 ${..} 标记。${..} 样式与 Spring 自己的属性占位符机制冲突。要将 Spring 属性占位符与自动扩展一起使用,请按如下方式对 Spring 属性占位符进行转义:\${..}

    2.2. 外部化 SpringApplication 的配置

    SpringApplication 具有 bean 属性设置器(setter),因此在创建应用程序时可以使用其 Java API 修改其行为。或者,您可以通过在 spring.main.* 中设置属性来外部化配置。例如,在 application.properties 中,您可能具有以下设置:

    spring.main.web-application-type=none
    spring.main.banner-mode=off
    

    这样设置后,启动时不会打印 Spring Boot 横幅,并且应用程序也没有启动嵌入式 Web 服务器。

    外部配置中定义的属性会覆盖用 Java API 指定的值,特别是主要来源。主要来源是提供给 SpringApplication 构造函数的来源。考虑以下应用程序:

    import org.springframework.boot.Banner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class MyApplication {
    
        public static void main(String[] args) {
            SpringApplication application = new SpringApplication(MyApplication.class);
            application.setBannerMode(Banner.Mode.OFF);
            application.run(args);
        }
    
    }
    

    或者是使用 SpringApplicationBuildersources(…) 方法:

    import org.springframework.boot.Banner;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    
    public class MyApplication {
    
        public static void main(String[] args) {
            new SpringApplicationBuilder()
                .bannerMode(Banner.Mode.OFF)
                .sources(MyApplication.class)
                .run(args);
        }
    
    }
    

    给定上面的例子,如果我们有以下配置:

    spring.main.sources=com.example.MyDatabaseConfig,com.example.MyJmsConfig
    spring.main.banner-mode=console
    

    实际的应用程序将显示横幅(被配置覆盖)并为 ApplicationContext 使用三个 sources 。应用程序属性 sources 是:

    1. MyApplication(来自代码)
    2. MyDatabaseConfig(来自外部配置)
    3. MyJmsConfig(来自外部配置)

    源码学习

    spring.main 属性绑定到 SpringApplication 的字段上
    SpringApplication#run
    	SpringApplication#prepareEnvironment
    		bindToSpringApplication(environment);
    			Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
    

    因为是在 run 方法中调用的,所以在 run 方法前,通过 setter 方法设置的字段值,会被外部属性源中的配置覆盖

    关于 sources

    SpringApplication 中有两个字段

    private Set<Class<?>> primarySources;
    
    private Set<String> sources = new LinkedHashSet<>();
    

    sources 有 getter/setter 方法,primarySources 没有

    通过 spring.main.sources 可以设置 sources

    通过 SpringApplication#getAllSources 方法可以获取 primarySourcessources 的合集。获取到的合集,会被加载到应用上下文中

    SpringApplication#run
    	prepareContext
    		Set<Object> sources = getAllSources();
    		Assert.notEmpty(sources, "Sources must not be empty");
    		load(context, sources.toArray(new Object[0]));
    

    这里可以看到必须至少设置一个 primarySourcessources ,否则启动会失败,报错 Sources must not be empty

    2.3. 更改应用程序外部属性的位置

    默认情况下,来自不同来源的属性会以定义的顺序添加到 Spring Environment 中。具体的顺序可以参考 Externalized Configuration

    您还可以提供以下系统属性(或环境变量)来更改行为:

    • spring.config.nameSPRING_CONFIG_NAME ):默认 application 作为根文件名
    • spring.config.locationSPRING_CONFIG_LOCATION ):要加载的文件(例如类路径资源或 URL )。为此文档设置了单独的 Environment 属性源,可以通过系统属性,环境变量或命令行来覆盖它

    无论您在 Environment 中进行什么设置,Spring Boot 都将始终如上所述进行加载 application.properties 。默认情况下,如果使用 YAML ,则扩展名为 .yml 的文件也将添加到列表中。

    Spring Boot 在 DEBUG 级别记录加载的配置文件以及在 TRACE 级别记录找不到的候选文件。

    请参阅 ConfigFileApplicationListener

    2.4. 使用 “Short” 命令行参数

    有些人喜欢使用 --port=9000 而不是 --server.port=9000 在命令行上设置配置属性。您可以通过在 application.properties 中使用占位符来启用此行为,如以下示例所示:

    server.port=${port:8080}
    

    如果您从 spring-boot-starter-parent POM 继承,则 maven-resources-plugins 的默认过滤器标记从 ${*} 更改为 @ (即,@maven.token@ 而不是 ${maven.token}),以防止与 Spring 样式的占位符冲突。如果直接为 application.properties 启用了 Maven 过滤,则可能还需要更改默认过滤器标记以使用其他分隔符。

    在这种特定情况下,端口绑定在 Heroku 或 Cloud Foundry 等 PaaS 环境中工作。在这两个平台中,PORT 环境变量是自动设置的,Spring 可以绑定到大写的 Environment 属性同义词。

    源码学习

    spring-boot-starter-parent-2.6.7.pom 中存在以下定义

    <properties>
        <java.version>1.8</java.version>
        <resource.delimiter>@</resource.delimiter>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>
    
    
    
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <configuration>
            <propertiesEncoding>${project.build.sourceEncoding}</propertiesEncoding>
            <delimiters>
                <delimiter>${resource.delimiter}</delimiter>
            </delimiters>
            <useDefaultDelimiters>false</useDefaultDelimiters>
        </configuration>
    </plugin>
    

    2.5. 对外部属性使用 YAML

    YAML 是 JSON 的超集,因此是一种方便的语法,用于以分层格式存储外部属性,如以下示例所示:

    spring:
      application:
        name: "cruncher"
      datasource:
        driver-class-name: "com.mysql.jdbc.Driver"
        url: "jdbc:mysql://localhost/test"
    server:
      port: 9000
    

    创建一个名为 application.yml 的文件,并将其放在类路径的根目录中。然后添加 snakeyaml 到您的依赖项(Maven 坐标 org.yaml:snakeyaml ,如果使用 spring-boot-starter ,则已经包含在内)。将 YAML 文件解析为 Java Map<String,Object>(就像 JSON 对象),然后 Spring Boot 展开 Map,使其具有一层深度并具有以句点分隔的键,正如许多人习惯于使用 Java 中的 Properties 文件一样。

    前面的示例 YAML 对应于以下 application.properties 文件:

    spring.application.name=cruncher
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost/test
    server.port=9000
    

    请参阅 Working with YAML

    2.6. 设置激活的 Spring Profiles

    Spring Environment 为此提供了一个 API ,但是您通常会设置一个系统属性( spring.profiles.active )或 OS 环境变量( SPRING_PROFILES_ACTIVE )。另外,您可以使用 -D 参数启动应用程序(请记住将其放在主类或 jar 之前),如下所示:

    $ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
    

    在 Spring Boot 中,您还可以在 application.properties 中设置激活的 profiles ,如以下示例所示:

    spring.profiles.active=production
    

    以这种方式设置的值将被系统属性或环境变量设置代替,但不会被 SpringApplicationBuilder.profiles() 方法替代。因此,后一种 Java API 可用于扩充配置文件,而不是更改默认值。

    请参阅 Profiles

    源码学习

    在这里读取了 spring.profiles.active 属性

    SpringApplication#run
    	prepareEnvironment
    		listeners.environmentPrepared(bootstrapContext, environment);
    			EnvironmentPostProcessorApplicationListener#onApplicationEvent
    				ConfigDataEnvironmentPostProcessor#postProcessEnvironment
    

    2.7. 设置默认 profile 名称

    默认 profile 是在没有 profile 处于活动状态时启用的 profile 。默认情况下,默认 profile 的名称是 default ,但可以使用系统属性 ( spring.profiles.default ) 或操作系统环境变量 ( SPRING_PROFILES_DEFAULT ) 更改它。

    在 Spring Boot 中,您还可以在 application.properties 中设置默认 profile 名称,如下例所示:

    spring.profiles.default=dev
    

    请参阅 Profiles

    2.8. 根据 Environment 更改配置

    Spring Boot 支持多文档 YAML 和 Properties 文件(请参阅 Working with Multi-Document Files ),这些文件可以根据活动 profile 有条件地激活。

    如果文档包含 spring.config.activate.on-profile 键,则将 profile 值(以逗号分隔的 profile 列表或 profile 表达式)输入 Spring Environment.acceptsProfiles() 方法。如果 profile 表达式匹配,则该文档将包含在最终合并中(否则不包含),如以下示例所示:

    server.port=9000
    #---
    spring.config.activate.on-profile=development
    server.port=9001
    #---
    spring.config.activate.on-profile=production
    server.port=0
    
    server:
      port: 9000
    ---
    spring:
      config:
        activate:
          on-profile: "development"
    server:
      port: 9001
    ---
    spring:
      config:
        activate:
          on-profile: "production"
    server:
      port: 0
    

    在前面的示例中,默认端口为 9000 。但是,如果名为 development 的 Spring profile 处于活动状态,则端口为 9001 。如果 production 为活动状态,则该端口为 0

    文档按照遇到的顺序合并,后面的值会覆盖前面的值。

    2.9. 发现外部属性的内置选项

    Spring Boot 在运行时将 application.properties (或 .yml 文件和其他位置)的外部属性绑定到应用程序中。在一个位置没有(技术上也不可能)所有受支持属性的详尽列表,因为可能有来自类路径上的其他 jar 文件的属性。

    具有 Actuator 功能的正在运行的应用程序具有 configprops 端点,该端点显示了可通过 @ConfigurationProperties 访问的所有绑定和可绑定属性。

    附录中包含一个 application.properties 示例,其中列出了 Spring Boot 支持的最常见属性。最终列表来自搜索源代码中的 @ConfigurationProperties@Value 注解,以及偶尔使用的 Binder

    请参阅 Externalized Configuration

    3. 嵌入式 Web 服务器

    每个 Spring Boot Web 应用程序都包含一个嵌入式 Web 服务器。此功能会导致许多操作方法问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。本节回答了这些问题。

    3.1. 使用其他 Web 服务器

    许多 Spring Boot 启动器都包含默认的嵌入式容器

    • 对于 servlet 堆栈应用,通过 spring-boot-starter-web 引入 spring-boot-starter-tomcat 来包括 Tomcat ,但是您可以使用 spring-boot-starter-jettyspring-boot-starter-undertow 代替
    • 对于反应式堆栈应用,spring-boot-starter-webflux 通过包括 spring-boot-starter-reactor-netty 包括 Reactor Netty ,但你可以使用 spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow 代替

    切换到其他 HTTP 服务器时,除了包括所需的依赖关系之外,还需要排除默认的依赖关系。Spring Boot 为 HTTP 服务器提供了单独的启动器,以帮助简化此过程。

    以下 Maven 示例显示了如何排除 Tomcat 并包括 Spring MVC 的 Jetty

    <properties>
        <servlet-api.version>3.1.0</servlet-api.version>
    </properties>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <!-- Exclude the Tomcat dependency -->
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- Use Jetty instead -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
    

    Servlet API 的版本已被覆盖,因为与 Tomcat 9 和 Undertow 2 不同,Jetty 9.4 不支持 Servlet 4.0

    如果您希望使用支持 Servlet 4.0 的 Jetty 10,您可以按照以下示例进行操作:

    <properties>
        <jetty.version>10.0.8</jetty.version>
    </properties>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <!-- Exclude the Tomcat dependency -->
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- Use Jetty instead -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
        <exclusions>
            <!-- Exclude the Jetty-9 specific dependencies -->
            <exclusion>
                <groupId>org.eclipse.jetty.websocket</groupId>
                <artifactId>websocket-server</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.eclipse.jetty.websocket</groupId>
                <artifactId>javax-websocket-server-impl</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    

    请注意,除了排除 Tomcat 启动器之外,还需要排除一些特定于 Jetty 9 的依赖项。

    以下 Gradle 示例配置了必要的依赖项和 模块替换 ,以使用 Undertow 代替 Spring WebFlux 的 Reactor Netty

    dependencies {
        implementation "org.springframework.boot:spring-boot-starter-undertow"
        implementation "org.springframework.boot:spring-boot-starter-webflux"
        modules {
            module("org.springframework.boot:spring-boot-starter-reactor-netty") {
                replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty")
            }
        }
    }
    

    spring-boot-starter-reactor-netty 对于使用 WebClient 类是必需的,因此即使您需要包括其他 HTTP 服务器,也可能需要保持对 Netty 的依赖。

    3.2. 禁用W eb 服务器

    如果您的类路径包含启动 Web 服务器所需的库,则 Spring Boot 将自动启动它。要禁用此行为,请在 application.properties 中配置 WebApplicationType ,如以下示例所示:

    spring.main.web-application-type=none
    

    3.3. 更改 HTTP 端口

    在独立应用程序中,主 HTTP 端口默认为 8080 ,但可以使用 server.port(例如,在 application.properties 中或作为系统属性)进行设置。由于 Environment 值的宽松绑定,因此还可以使用 SERVER_PORT(例如,作为 OS 环境变量)。

    要完全关闭 HTTP 端点,但仍创建一个 WebApplicationContext ,请使用 server.port=-1(这样做有时对测试很有用)。

    有关更多详细信息,请参阅 ServerPropertiesCustomizing Embedded Servlet Containers

    3.4. 使用随机未分配的 HTTP 端口

    要扫描可用端口(使用 OS 本机来防止冲突),请使用 server.port=0

    3.5. 在运行时发现 HTTP 端口

    您可以从日志输出或通过 WebServerWebServerApplicationContext 访问服务器正在运行的端口。获得它并确保它已被初始化的最佳方法是添加一个 ApplicationListener<WebServerInitializedEvent> 类型的 @Bean 并在发布时将容器从事件中拉出。

    使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) 的测试也可以通过 @LocalServerPort 注解将实际端口注入到字段中,如下例所示:

    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
    import org.springframework.boot.web.server.LocalServerPort;
    
    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    public class MyWebIntegrationTests {
    
        @LocalServerPort
        int port;
    
        // ...
    
    }
    

    @LocalServerPort@Value("${local.server.port}") 作为元注解。不要尝试在常规应用程序中注入端口。如我们所见,仅在初始化容器之后才设置该值。与测试相反,应早处理应用程序代码回调(在值实际可用之前)。

    3.6. 启用 HTTP 响应压缩

    Jetty,Tomcat 和 Undertow 支持 HTTP 响应压缩。可以在 application.properties 中启用它,如下所示:

    server.compression.enabled=true
    

    默认情况下,响应的长度必须至少为 2048 个字节才能执行压缩。您可以通过设置 server.compression.min-response-size 属性来配置此行为。

    默认情况下,仅当响应的内容类型为以下之一时,才被压缩:

    • text/html
    • text/xml
    • text/plain
    • text/css
    • text/javascript
    • application/javascript
    • application/json
    • application/xml

    您可以通过设置 server.compression.mime-types 属性来配置此行为。

    3.7. 配置 SSL

    可以通过设置 server.ssl.* 属性来声明性地配置 SSL ,通常在 application.propertiesapplication.yml 中。以下示例显示了在 application.properties 中设置 SSL 属性:

    server.port=8443
    server.ssl.key-store=classpath:keystore.jks
    server.ssl.key-store-password=secret
    server.ssl.key-password=another-secret
    

    有关所有受支持属性的详细信息,请参见 Ssl

    使用上述示例的配置意味着应用程序不再在端口 8080 上支持纯 HTTP 连接器。Spring Boot 不支持通过 application.properties 同时进行 HTTP 连接器和 HTTPS 连接器的配置。如果要同时拥有两者,则需要以编程方式配置其中之一。我们建议您使用 application.properties 进行 HTTPS 配置,因为 HTTP 连接器是两者中以编程方式进行配置的较容易方式。

    3.8. 配置 HTTP/2

    您可以使用 server.http2.enabled 配置属性在 Spring Boot 应用程序中启用 HTTP/2 支持。支持 h2(基于 TLS 的 HTTP/2)和 h2c(基于 TCP 的 HTTP/2)。要使用 h2 ,还必须启用 SSL 。未启用 SSL 时,将使用 h2ch2 支持的详细信息取决于所选的 Web 服务器和应用程序环境,因为并非所有 JDK 8 版本都开箱即用地支持该协议。

    3.8.1. Tomcat 与 HTTP/2

    Spring Boot 默认随 Tomcat 9.0.x 一起提供,当使用 JDK 9 或更高版本时,它支持开箱即用的 h2c 和开箱即用的 h2 。或者,如果 libtcnative 库及其依赖项安装在主机操作系统上,则可以在 JDK 8 上使用 h2

    库目录必须对 JVM 库路径可用(如果还没有的话)。您可以使用诸如 -Djava.library.path=/usr/local/opt/tomcat-native/lib 之类的 JVM 参数来执行此操作。有关更多信息,请参见 Tomcat 官方文档

    在启用了 HTTP/2 和 SSL 但没有原生支持的情况下在 JDK 8 上启动 Tomcat 9.0.x 会记录以下错误:

    ERROR 8787 --- [           main] o.a.coyote.http11.Http11NioProtocol      : The upgrade handler [org.apache.coyote.http2.Http2Protocol] for [h2] only supports upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"] connector that does not support ALPN.
    

    此错误不是致命错误,并且该应用程序仍以 HTTP/1.1 SSL 支持启动。

    3.8.2. Jetty 与 HTTP/2

    对于 HTTP/2 支持,Jetty 需要额外的 org.eclipse.jetty.http2:http2-server 依赖项。不需要使用 h2c 其他依赖项。要使用 h2 ,您还需要根据您的部署选择以下依赖项之一:

    • org.eclipse.jetty:jetty-alpn-java-server 适用于在 JDK 9+ 上运行的应用程序
    • org.eclipse.jetty:jetty-alpn-openjdk8-server 适用于在 JDK8u252+ 上运行的应用程序
    • org.eclipse.jetty:jetty-alpn-conscrypt-server 和没有 JDK 要求的 Conscrypt 库

    3.8.3. Reactor Netty 与 HTTP/2

    默认情况下 spring-boot-webflux-starter 使用 Reactor Netty 作为服务器。Reactor Netty 支持 h2c 使用 JDK 8 或更高版本,无需额外依赖。Reactor Netty 支持 h2 在 JDK 9 或更高版本中使用 JDK 支持。对于 JDK 8 环境,或为了获得最佳运行时性能,该服务器还支持 h2 原生库。要启用它,您的应用程序需要有一个额外的依赖项。

    Spring Boot 管理 io.netty:netty-tcnative-boringssl-static “uber jar” 的版本,包含所有平台的本地库。开发人员可以选择使用分类器仅导入所需的依赖项(参见 Netty 官方文档)。

    3.8.4. Undertow 与 HTTP/2

    从 Undertow 1.4.0+ 开始, JDK 8 支持h2h2c,没有任何额外的依赖项。

    3.9. 配置 Web 服务器

    通常,您首先应该考虑使用许多可用的配置键之一,并通过在您的 application.propertiesapplication.yml 中添加新条目来自定义 Web 服务器。请参阅 发现外部属性的内置选项server.* 命名空间是非常有用的在这里,它包括命名空间像 server.tomcat.*server.jetty.* 和其他特定服务器的功能。请参阅 Common Application Properties

    前面的部分已经介绍了许多常见的用例,例如压缩,SSL 或 HTTP/2 。但是,如果您的用例不存在配置 key ,则应查看 WebServerFactoryCustomizer 。您可以声明一个这样的组件,并访问与您选择的服务器相关的工厂:您应该为所选服务器(Tomcat,Jetty,Reactor Netty,Undertow)和所选 Web 堆栈(Servlet 或 Reactive)选择变体。

    以下示例适用于带有 Tomcat 的 spring-boot-starter-web(Servlet 堆栈)

    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    
        @Override
        public void customize(TomcatServletWebServerFactory factory) {
            // customize the factory here
        }
    
    }
    

    Spring Boot 在内部使用该基础设施来自动配置服务器。自动配置的 WebServerFactoryCustomizer bean 顺序为 0 ,并且将在任何用户定义的定制器之前处理,除非它有明确的顺序说明。

    使用定制器访问 WebServerFactory 后,您可以使用它来配置特定部分,例如连接器、服务器资源或服务器本身——所有这些都使用特定于服务器的 API。

    此外,Spring Boot 还提供:

    Server Servlet stack Reactive stack
    Tomcat TomcatServletWebServerFactory TomcatReactiveWebServerFactory
    Jetty JettyServletWebServerFactory JettyReactiveWebServerFactory
    Undertow UndertowServletWebServerFactory UndertowReactiveWebServerFactory
    Reactor N/A NettyReactiveWebServerFactory

    作为最后的手段,您还可以声明自己的 WebServerFactory bean ,它将覆盖 Spring Boot 提供的 bean。当您这样做时,自动配置的定制器仍会应用于您的自定义工厂,因此请谨慎使用该选项。

    3.10. 将 Servlet ,Filter 或 Listener 添加到应用程序

    在一个使用 spring-boot-starter-web 的 servlet 堆栈的应用,有两种方法可以添加 ServletFilterServletContextListener,和 Servlet API 支持的其他监听器:

    • 使用 Spring Bean 添加 Servlet ,Filter 或 Listener
    • 使用类路径扫描添加 Servlet ,Filter 和 Listener

    3.10.1. 使用 Spring Bean 添加 Servlet ,Filter 或 Listener

    要使用 Spring bean 添加 ServletFilterListener ,你必须提供一个它的 @Bean 定义。当您要注入配置或依赖项时,这样做非常有用。但是,您必须非常小心,以免引起过多其他 bean 的急切 (Eager) 初始化,因为必须在应用程序生命周期的早期就将它们安装在容器中。(例如,让它们依赖于您 DataSource 或 JPA 配置不是一个好主意。)您可以通过在首次使用 bean 时而不是在初始化时对 bean 进行初始化(即懒加载)来解决这些限制。

    在 Filters 和 Servlets 的情况下,还可以通过添加 FilterRegistrationBeanServletRegistrationBean 来添加映射和初始化参数。

    如果在过滤器注册上未指定 dispatcherType ,则使用 REQUEST 。这符合 Servlet 规范的默认调度程序类型。

    像其他任何 Spring bean 一样,您可以定义 Servlet 过滤器 bean 的顺序。请参阅 将 Servlet、过滤器和监听器注册为 Spring Bean

    禁用 Servlet 或 Filter 的注册

    正如前面所述,任何 ServletFilter bean 自动注册进 servlet 容器。

    要禁用特定 FilterServlet bean 的注册,请为其创建注册 bean 并将其标记为禁用,如以下示例所示:

    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyFilterConfiguration {
    
        @Bean
        public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
            FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
            registration.setEnabled(false);
            return registration;
        }
    
    }
    

    3.10.2. 使用类路径扫描添加Servlet,Filter和Listener

    @WebServlet@WebFilter@WebListener 可以通过向注解 @ServletComponentScan@Configuration 类并指定包含要注册的组件的包来自动向嵌入式 servlet 容器注册。默认情况下,从带 @ServletComponentScan 注解的类的包进行扫描。

    3.11. 配置访问日志

    可以通过它们各自的名称空间为 Tomcat ,Undertow 和 Jetty 配置访问日志。

    例如,以下设置使用 自定义模式 记录对 Tomcat 的访问

    server.tomcat.basedir=my-tomcat
    server.tomcat.accesslog.enabled=true
    server.tomcat.accesslog.pattern=%t %a %r %s (%D ms)
    

    日志的默认位置是相对于 Tomcat 基本目录的 logs 目录。默认情况下,logs 目录是一个临时目录,因此您可能需要修复 Tomcat 的基本目录或对日志使用绝对路径。在前面的示例中,日志位于相对于应用程序工作目录的 my-tomcat/logs 中。

    可以用类似的方式配置 Undertow 的访问日志,如以下示例所示:

    server.undertow.accesslog.enabled=true
    server.undertow.accesslog.pattern=%t %a %r %s (%D ms)
    

    日志存储在相对于应用程序工作目录的 logs 目录中。您可以通过设置 server.undertow.accesslog.dir 属性来自定义此位置。

    最后,Jetty 的访问日志也可以配置如下:

    server.jetty.accesslog.enabled=true
    server.jetty.accesslog.filename=/var/log/jetty-access.log
    

    默认情况下,日志重定向到 System.err

    3.12. 在前端代理服务器后面运行

    如果您的应用程序在代理、负载均衡器或云中运行,请求信息(如主机、端口、方案……)可能会在此过程中发生变化。您的应用程序可能在 10.10.10.10:8080 上运行,但 HTTP 客户端应该只能看到 example.org

    RFC7239 "Forwarded Headers" 定义了 Forwarded HTTP 标头;代理可以使用此标头来提供有关原始请求的信息。您可以将应用程序配置为读取这些标头,并在创建链接并将它们发送到 HTTP 302 响应、JSON 文档或 HTML 页面中的客户端时自动使用该信息。还有非标准标头,如 X-Forwarded-Host , X-Forwarded-Port , X-Forwarded-Proto , X-Forwarded-Ssl , 和X-Forwarded-Prefix

    如果代理添加常用 X-Forwarded-ForX-Forwarded-Proto 标头,设置 server.forward-headers-strategyNATIVE 足以支持这些。有了这个选项,Web 服务器本身就支持这个特性;您可以查看他们的特定文档以了解特定行为。

    如果这还不够,Spring Framework 提供了 ForwardedHeaderFilter 。您可以通过将 server.forward-headers-strategy 设置为 FRAMEWORK 将其注册为应用程序中的 servlet 过滤器。

    如果您使用 Tomcat 并在代理上终止 SSL ,则应设置 server.tomcat.redirect-context-rootfalse 。这允许在执行任何重定向之前遵守 X-Forwarded-Proto 标头。

    如果您的应用程序在 Cloud Foundry 或 Heroku 中运行,则 server.forward-headers-strategy 属性默认为 NATIVE 。在所有其他情况下,它默认为 NONE

    3.12.1. 自定义 Tomcat 的代理配置

    如果使用 Tomcat ,则可以另外配置用于携带 “forwarded” 信息的标头名称,如以下示例所示:

    server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header
    server.tomcat.remoteip.protocol-header=x-your-protocol-header
    

    Tomcat 还配置有一个默认正则表达式,该正则表达式与要信任的内部代理匹配。有关其默认值,请参见 附录中的 server.tomcat.remoteip.internal-proxies 条目。 您可以通过向 application.properties 中添加条目来自定义阀门(valve)的配置,如下例所示:

    server.tomcat.remoteip.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
    

    您可以通过将 internal-proxies 设置为空来信任所有代理(但在生产环境中不要这样做)。

    您可以完全控制 Tomcat 的 RemoteIpValve 的配置,方法是关闭自动配置(为此,请设置 server.forward-headers-strategy=NONE)并使用 WebServerFactoryCustomizer bean 添加新的 valve 实例。

    3.13. 使用 Tomcat 启用多个连接器

    您可以在 TomcatServletWebServerFactory 中添加 org.apache.catalina.connector.Connector 以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如以下示例所示:

    import java.io.IOException;
    import java.net.URL;
    
    import org.apache.catalina.connector.Connector;
    import org.apache.coyote.http11.Http11NioProtocol;
    
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.util.ResourceUtils;
    
    @Configuration(proxyBeanMethods = false)
    public class MyTomcatConfiguration {
    
        @Bean
        public WebServerFactoryCustomizer<TomcatServletWebServerFactory> sslConnectorCustomizer() {
            return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createSslConnector());
        }
    
        private Connector createSslConnector() {
            Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
            Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
            try {
                URL keystore = ResourceUtils.getURL("keystore");
                URL truststore = ResourceUtils.getURL("truststore");
                connector.setScheme("https");
                connector.setSecure(true);
                connector.setPort(8443);
                protocol.setSSLEnabled(true);
                protocol.setKeystoreFile(keystore.toString());
                protocol.setKeystorePass("changeit");
                protocol.setTruststoreFile(truststore.toString());
                protocol.setTruststorePass("changeit");
                protocol.setKeyAlias("apitester");
                return connector;
            }
            catch (IOException ex) {
                throw new IllegalStateException("Fail to create ssl connector", ex);
            }
        }
    
    }
    

    3.14. 使用 Tomcat 的 LegacyCookieProcessor

    默认情况下,Spring Boot 使用的嵌入式 Tomcat 不支持 Cookie 格式的 “Version 0” ,因此您可能会看到以下错误:

    java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
    

    如果可能的话,您应该考虑更新您的代码以仅存储符合以后 Cookie 规范的值。但是,如果您无法更改 cookie 的写入方式,则可以将 Tomcat 配置为使用 LegacyCookieProcessor 。要切换到 LegacyCookieProcessor,请使用添加 TomcatContextCustomizerWebServerFactoryCustomizer bean,如以下示例所示:

    import org.apache.tomcat.util.http.LegacyCookieProcessor;
    
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyLegacyCookieProcessorConfiguration {
    
        @Bean
        public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
            return (factory) -> factory
                    .addContextCustomizers((context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
        }
    
    }
    

    3.15. 启用 Tomcat 的 MBean Registry

    默认情况下,嵌入式 Tomcat 的 MBean 注册表是禁用的。这样可以最大程度地减少 Tomcat 的内存占用。例如,如果要使用 Tomcat 的 MBean ,以便可以通过 Micrometer 公开它们,则必须使用 server.tomcat.mbeanregistry.enabled 属性,如以下示例所示:

    server.tomcat.mbeanregistry.enabled=true
    

    3.16. 使用 Undertow 启用多个监听器

    UndertowServletWebServerFactory 中添加一个 UndertowBuilderCustomizer ,并向 Builder 中添加一个监听器,如以下示例所示:

    import io.undertow.Undertow.Builder;
    
    import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyUndertowConfiguration {
    
        @Bean
        public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
            return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
        }
    
        private Builder addHttpListener(Builder builder) {
            return builder.addHttpListener(8080, "0.0.0.0");
        }
    
    }
    

    3.17. 使用 @ServerEndpoint 创建 WebSocket 端点

    如果要在使用嵌入式容器的 Spring Boot 应用程序中使用 @ServerEndpoint ,必须声明一个单独的 ServerEndpointExporter @Bean ,如以下示例所示:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    @Configuration(proxyBeanMethods = false)
    public class MyWebSocketConfiguration {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    
    }
    

    前面示例中显示的 bean 将所有带 @ServerEndpoint 注解的 bean 注册到基础 WebSocket 容器中。当部署到独立 servlet 容器时,此角色由 servlet 容器初始化程序执行,并且不需要 ServerEndpointExporter bean

    4. Spring MVC

    文档地址

    Spring Boot 有许多启动器包含 Spring MVC 。请注意,一些启动器包含对 Spring MVC 的依赖项,而不是直接包含它。本节回答有关 Spring MVC 和 Spring Boot 的常见问题。

    4.1. 编写 JSON REST 服务

    只要 Jackson2 在类路径上,Spring Boot 应用程序中的任何 Spring @RestController 默认情况下都应呈现 JSON 响应,如以下示例所示:

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class MyController {
    
        @RequestMapping("/thing")
        public MyThing thing() {
            return new MyThing();
        }
    
    }
    

    只要 MyThing 可以由 Jackson2 序列化(对于普通的 POJO 或 Groovy 对象为 true ),则 localhost:8080/thing 默认情况下会为其提供 JSON 表示。请注意,在浏览器中,有时可能会看到 XML 响应,因为浏览器倾向于发送 XML 的接受标头。

    4.2. 编写 XML REST 服务

    如果类路径上具有 Jackson XML 扩展( jackson-dataformat-xml ),则可以使用它来呈现 XML 响应。我们之前用于 JSON 的示例可以工作。要使用 Jackson XML 渲染器,请将以下依赖项添加到您的项目中:

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
    

    如果无法使用 Jackson 的 XML 扩展而可以使用 JAXB ,则可以将 MyThing 注解为 @XmlRootElement ,如以下示例所示:

    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement
    public class MyThing {
    
        private String name;
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    

    JAXB 仅在 Java 8 中是开箱即用的。如果您使用的是较新的 Java 版本,请在项目中添加以下依赖项:

    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
    </dependency>
    

    要使服务器呈现 XML 而不是 JSON ,您可能必须发送 Accept: text/xml 标头(或使用浏览器)。

    4.3. 自定义 Jackson ObjectMapper

    Spring MVC(客户端和服务器端)使用 HttpMessageConverters 来协商 HTTP 交换中的内容转换。如果 Jackson 在类路径中,则您已经获得 Jackson2ObjectMapperBuilder 所提供的默认转换器,该转换器的实例已为您自动配置。

    ObjectMapper(或 Jackson XML 转换器 XmlMapper )实例(默认创建)具有以下定义的属性:

    • MapperFeature.DEFAULT_VIEW_INCLUSION 被禁用
    • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 被禁用
    • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 被禁用

    Spring Boot 还具有一些功能,可以更轻松地自定义此行为。

    您可以使用 Environment 配置 ObjectMapperXmlMapper 实例。Jackson 提供了一套广泛的简单的开/关功能,可用于配置其处理的各个方面。在六个枚举(在 Jackson 中)中描述了这些功能,这些枚举映射到环境中的属性:

    枚举 属性 值域
    com.fasterxml.jackson.databind.DeserializationFeature spring.jackson.deserialization.<feature_name> true
    false
    com.fasterxml.jackson.core.JsonGenerator.Feature spring.jackson.generator.<feature_name> true
    false
    com.fasterxml.jackson.databind.MapperFeature spring.jackson.mapper.<feature_name> true
    false
    com.fasterxml.jackson.core.JsonParser.Feature spring.jackson.parser.<feature_name> true
    false
    com.fasterxml.jackson.databind.SerializationFeature spring.jackson.serialization.<feature_name> true
    false
    com.fasterxml.jackson.annotation.JsonInclude.Include spring.jackson.default-property-inclusion always
    non_null
    non_absent
    non_default
    non_empty

    例如,要启用格式化打印,请设置 spring.jackson.serialization.indent_output=true 。请注意,由于使用了宽松绑定,indent_output 的大小写不必与相应的枚举常量 INDENT_OUTPUT 的大小写匹配。

    这种基于 Environment 的配置适用于自动配置的 Jackson2ObjectMapperBuilder Bean,并且适用于使用构建器创建的任何映射器,包括自动配置的ObjectMapper Bean。

    上下文中的 Jackson2ObjectMapperBuilder 可以由一个或多个 Jackson2ObjectMapperBuilderCustomizer bean 定制。可以对此类定制器 beans 进行排序(Spring Boot 自己的定制器的顺序为 0 ),从而可以在 Spring Boot 定制之前和之后应用其他的定制。

    任何类型的 com.fasterxml.jackson.databind.Module bean 都会自动向自动配置的 Jackson2ObjectMapperBuilder 中注册,并应用于它创建的任何 ObjectMapper 实例。当您向应用程序中添加新功能时,这提供了一种用于贡献自定义模块的全局机制。

    如果要完全替换默认 ObjectMapper ,请定义该类型的 @Bean 并将其标记为 @Primary ,或者,如果您更喜欢基于构建器的方法,请定义一个 Jackson2ObjectMapperBuilder @Bean 。请注意,无论哪种情况,这样做都会禁用 ObjectMapper 的所有自动配置。

    如果提供 MappingJackson2HttpMessageConverter 类型的任何 @Bean ,它们将替换 MVC 配置中的默认值。另外,还提供了一个 HttpMessageConverters 类型的便捷 bean (如果使用默认的 MVC 配置,该 bean 始终可用)。它提供了一些有用的方法来访问默认的和用户增强的消息转换器。

    有关更多详细信息,请参见 WebMvcAutoConfiguration

    源码学习:

    org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
    
    org.springframework.boot.autoconfigure.jackson.JacksonProperties
    

    4.4. 自定义 @ResponseBody 渲染

    Spring 使用 HttpMessageConverters 渲染 @ResponseBody(或 @RestController 的响应)。您可以通过在 Spring Boot 上下文中添加适当类型的 bean 来贡献额外的转换器。如果您添加的 bean 的类型是默认包含的(例如用于 JSON 转换的 MappingJackson2HttpMessageConverter ),它将替换默认值。如果您使用默认的 MVC 配置,HttpMessageConverters 类型的便捷 bean 将始终可用。它提供了一些有用的方法来访问默认的和用户增强的消息转换器(例如,如果您想将它们手动注入到自定义 RestTemplate 中,可能会很有用)。

    与正常的 MVC 用法一样,您提供的任何 WebMvcConfigurer bean 也可以通过重写 configureMessageConverters 方法来贡献转换器。但是,与普通的 MVC 不同,您只能提供所需的其他转换器(因为 Spring Boot 使用相同的机制来提供其默认值)。最后,如果您通过提供自己的 @EnableWebMvc 配置来退出 Spring Boot 默认MVC 配置,则可以使用 WebMvcConfigurationSupportgetMessageConverters 方法完全控制并手动完成所有操作。

    请参阅 WebMvcAutoConfiguration

    4.5. 处理多部分文件上传

    Spring Boot 包含 Servlet 3 的 javax.servlet.http.Part API 以支持上传文件。默认情况下,Spring Boot 将 Spring MVC 配置为单个请求中每个文件最大大小为 1 MB,最大文件数据为 10 MB。您可以覆盖这些值,使用 MultipartProperties 类公开的属性覆盖这些值,中间数据的存储位置(例如,存储到 /tmp 目录)以及将数据刷新到磁盘的阈值。例如,如果您要指定文件不受限制,请将 spring.servlet.multipart.max-file-size 属性设置为 -1

    当您想在Spring MVC控制器处理 器方法中将多部分编码的文件数据作为带 @RequestParam 注解的 MultipartFile 类型的参数来接收时,多部分支持会很有帮助。

    请参阅 MultipartAutoConfiguration

    建议使用容器的内置支持进行分段上传,而不要引入其他依赖项,例如 Apache Commons File Upload

    4.6. 关闭 Spring MVC DispatcherServlet

    默认情况下,所有内容均从应用程序的根目录( / )提供。如果您希望映射到其他路径,则可以如下配置:

    spring.mvc.servlet.path=/mypath
    

    如果您有其他 servlet,您可以为每个 ServletServletRegistrationBean 类型声明一个 @Bean ,Spring Boot 会将它们透明地注册到容器中。因为 servlet 是以这种方式注册的,所以它们可以映射到 DispatcherServlet 的子上下文而不调用它。

    配置您自己的 DispatcherServlet 是不寻常的,但是如果您确实需要这样做,则还必须提供一个 DispatcherServletPath 类型的 @Bean 来提供自定义 DispatcherServlet 的路径。

    4.7. 关闭默认的 MVC 配置

    完全控制 MVC 配置的最简单方法是为您自己的 @Configuration 提供 @EnableWebMvc 注解。这样做会使您掌握所有 MVC 配置。

    4.8. 自定义 ViewResolver

    ViewResolver 是 Spring MVC 的核心组件,将 @Controller 中的视图名称转换为实际的 View 实现。请注意,ViewResolver 主要用于 UI 应用程序,而不是 REST 风格的服务( View 不用于渲染 @ResponseBody )。有很多 ViewResolver 实现可供选择,Spring 本身对是否应使用哪个没有意见。另一方面,Spring Boot 根据在类路径和应用上下文中找到的内容为您安装一个或两个。DispatcherServlet 会使用它在应用上下文中找到的所有解析器,依次尝试每个解析器,直到获得结果为止。如果添加自己的解析器,则必须知道其顺序以及解析器的添加位置。

    WebMvcAutoConfiguration 将以下 ViewResolver 添加到您的上下文中:

    • 名为 defaultViewResolverInternalResourceViewResolver 。它定位可以通过使用 DefaultServlet 渲染的物理资源(包括静态资源和 JSP 页面)。它将一个前缀和一个后缀应用于视图名称,然后在 Servlet 上下文中查找具有该路径的物理资源(默认值均为空,但可以通过 spring.mvc.view.prefixspring.mvc.view.suffix 进行外部配置访问)。您可以通过提供相同类型的 bean 覆盖它
    • 名为 beanNameViewResolverBeanNameViewResolver 。这是视图解析器链的有用成员,它会拾取任何与正在解析的 View 名称相同的 bean。没有必要覆盖或替换它
    • 仅当实际 存在 类型为 View 的 bean 的情况下,才添加名为 viewResolverContentNegotiatingViewResolver 。这是一个复合解析器,委托给所有其他解析器并尝试找到与客户端发送的 Accept HTTP 标头的匹配项。您可以通过定义一个名为 viewResolverContentNegotiatingViewResolver bean 来关闭自动配置
    • 如果您使用 Thymeleaf ,则还有一个名为 thymeleafViewResolverThymeleafViewResolver 。它通过在视图名称前后加上前缀和后缀来查找资源。前缀为 spring.thymeleaf.prefix ,后缀为 spring.thymeleaf.suffix 。前缀和后缀的值分别默认为 classpath/templates/.html 。您可以通过提供相同名称的 bean 来覆盖 ThymeleafViewResolver
    • 如果您使用 FreeMarker ,则还有一个名为 freeMarkerViewResolverFreeMarkerViewResolver 。它通过用前缀和后缀包围视图名称来在加载器路径(外部化为 spring.freemarker.templateLoaderPath 并具有默认值 classpath:/templates/ )中查找资源。前缀被外部化为 spring.freemarker.prefix ,后缀被外部化为 spring.freemarker.suffix 。前缀和后缀的默认值分别为空和 .ftlh 。您可以通过提供相同名称的 bean 来覆盖 FreeMarkerViewResolver
    • 如果您使用 Groovy 模板(实际上,如果 groovy-templates 在类路径中),则还具有一个名为 groovyMarkupViewResolverGroovyMarkupViewResolver 。它通过在视图名称周围加上前缀和后缀(并扩展为 spring.groovy.template.prefixspring.groovy.template.suffix )来在加载器路径中查找资源。前缀和后缀分别具有默认值 classpath:/templates/.tpl 。您可以通过提供相同名称的 bean 来覆盖 GroovyMarkupViewResolver
    • 如果您使用 Mustache ,那么您还将有一个名为 mustacheViewResolverMustacheViewResolver 。它通过在视图名称前后加上前缀和后缀来查找资源。前缀为 spring.mustache.prefix ,后缀为 spring.mustache.suffix 。前缀和后缀的值分别默认为 classpath:/templates/.mustache 。您可以通过提供相同名称的 bean 来覆盖 MustacheViewResolver

    有关更多详细信息,请参见以下部分:

    5. Jersey

    5.1. 使用 Spring Security 保护 Jersey 端点

    可以使用 Spring Security 来保护基于 Jersey 的 Web 应用程序,其方式与用来保护基于 Spring MVC 的 Web 应用程序的方式几乎相同。但是,如果您想在 Jersey 上使用 Spring Security 的方法级安全性,则必须将 Jersey 配置为使用 setStatus(int) 而不是 sendError(int) 。这可以防止 Jersey 在 Spring Security 有机会向客户端报告身份验证或授权失败之前提交响应。

    jersey.config.server.response.setStatusOverSendError 属性必须在应用程序的 ResourceConfig Bean 上设置为 true ,如以下示例所示:

    import java.util.Collections;
    
    import org.glassfish.jersey.server.ResourceConfig;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {
    
        public JerseySetStatusOverSendErrorConfig() {
            register(Endpoint.class);
            setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true));
        }
    
    }
    

    5.2. 将 Jersey 与另一个 Web 框架一起使用

    要将 Jersey 与另一个 Web 框架(例如 Spring MVC)一起使用,它应该被配置为允许另一个框架处理它无法处理的请求。首先,将 Jersey 配置为使用过滤器而不是 servlet ,方法是将 spring.jersey.type 应用程序属性配置为 filter 。其次,配置您的 ResourceConfig 以转发可能导致 404 的请求,如以下示例所示。

    import org.glassfish.jersey.server.ResourceConfig;
    import org.glassfish.jersey.servlet.ServletProperties;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class JerseyConfig extends ResourceConfig {
    
        public JerseyConfig() {
            register(Endpoint.class);
            property(ServletProperties.FILTER_FORWARD_ON_404, true);
        }
    
    }
    

    6. HTTP 客户端

    Spring Boot 提供了许多可与 HTTP 客户端一起使用的启动器。

    6.1. 配置 RestTemplate 使用代理

    您可以使用带 RestTemplateCustomizerRestTemplateBuilder 来构建自定义 RestTemplate 。这是创建 RestTemplate 配置为使用代理的推荐方法。参考 RestTemplate Customization

    代理配置的确切详细信息取决于所使用的底层客户端请求工厂。

    6.2. 配置基于R eactor Netty 的 WebClient 使用的 TcpClient

    Reactor Netty 在类路径上时,将自动配置基于 Reactor Netty 的 WebClient 。要自定义客户端对网络连接的处理,请提供一个 ClientHttpConnector bean 。以下示例配置 60 秒的连接超时并添加 ReadTimeoutHandler

    import io.netty.channel.ChannelOption;
    import io.netty.handler.timeout.ReadTimeoutHandler;
    import reactor.netty.http.client.HttpClient;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.client.reactive.ClientHttpConnector;
    import org.springframework.http.client.reactive.ReactorClientHttpConnector;
    import org.springframework.http.client.reactive.ReactorResourceFactory;
    
    @Configuration(proxyBeanMethods = false)
    public class MyReactorNettyClientConfiguration {
    
        @Bean
        ClientHttpConnector clientHttpConnector(ReactorResourceFactory resourceFactory) {
            HttpClient httpClient = HttpClient.create(resourceFactory.getConnectionProvider())
                    .runOn(resourceFactory.getLoopResources())
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
                    .doOnConnected((connection) -> connection.addHandlerLast(new ReadTimeoutHandler(60)));
            return new ReactorClientHttpConnector(httpClient);
        }
    
    }
    

    请注意 ReactorResourceFactory 对连接提供器和事件循环资源的使用。这确保了用于服务器接收请求和客户端发出请求的资源的有效共享。

    7. 日志记录

    除了通常由 Spring Framework 的 spring-jcl 模块提供的 Commons Logging API 之外,Spring Boot 没有强制性的日志记录依赖项。要使用 Logback ,您需要将其和 spring-jcl 添加到类路径中。最简单的方法是通过启动器, spring-boot-starter-logging 。对于 Web 应用程序,您仅需要 spring-boot-starter-web ,因为它可传递地依赖于日志记录启动器。如果使用 Maven ,则以下依赖项会为您添加日志记录:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    Spring Boot 有一个 LoggingSystem 抽象,它试图根据类路径的内容来配置日志记录。如果 Logback 可用,则它是首选。

    如果您只需要对日志记录进行的唯一更改是设置各种记录器的级别,则可以在 application.properties 里使用 logging.level 前缀来进行设置,如以下示例所示:

    logging.level.org.springframework.web=debug
    logging.level.org.hibernate=error
    

    您还可以使用 logging.file.name 来设置要写入日志的文件的位置(除了控制台)。

    要配置日志记录系统的更细粒度的设置,您需要使用 LoggingSystem 所支持的原生配置格式。默认情况下,Spring Boot 从系统的默认位置(例如 Logback 的 classpath:logback.xml )拾取本地配置,但是您可以使用logging.config 属性设置配置文件的位置。

    7.1. 配置 Logback 以进行日志记录

    如果您的自定义设置需要超出 application.properties 可以设置的范围,则需要添加标准的 Logback 配置文件。您可以将 logback.xml 文件添加到类路径的根目录中,以供 Logback 查找。如果您想使用 Spring Boot Logback 扩展,也可以使用 logback-spring.xml

    Logback 文档有一个专用部分,其中详细介绍了 配置

    Spring Boot 提供了许多可以包含在您自己配置中的 Logback 配置。这些 includes 旨在允许重新应用某些常见的 Spring Boot 约定。

    org/springframework/boot/logging/logback/ 下提供了以下文件:

    • defaults.xml - 提供转换规则,模式属性和通用记录器配置
    • console-appender.xml - 使用 CONSOLE_LOG_PATTERN 添加一个 ConsoleAppender
    • file-appender.xml - 使用具有适当设置的 FILE_LOG_PATTERNROLLING_FILE_NAME_PATTERN 添加 RollingFileAppender

    此外,还提供了一个过时的 base.xml 文件以与早期版本的 Spring Boot 兼容。

    一个典型的自定义 logback.xml 文件如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
        <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
        </root>
        <logger name="org.springframework.web" level="DEBUG"/>
    </configuration>
    

    您的 Logback 配置文件也可以利用 LoggingSystem 为您创建的系统属性:

    • ${PID} :当前进程 ID
    • ${LOG_FILE} :是否在 Boot 的外部配置中设置了 logging.file.name
    • ${LOG_PATH} :是否在 Boot 的外部配置中设置了 logging.file.path(表示用于存放日志文件的目录)
    • ${LOG_EXCEPTION_CONVERSION_WORD} :是否在 Boot 的外部配置中设置了 logging.exception-conversion-word
    • ${ROLLING_FILE_NAME_PATTERN} :是否在 Boot 的外部配置中设置了 logging.pattern.rolling-file-name

    通过使用自定义的 Logback 转换器,Spring Boot 还可以在控制台上提供一些不错的 ANSI 颜色终端输出(但不在日志文件中)。例如在 defaults.xml 中的 CONSOLE_LOG_PATTERN 配置。

    如果 Groovy 在类路径上,那么您也应该能够使用 logback.groovy 配置 Logback 。如果存在,则优先考虑此设置。

    Groovy 配置不支持 Spring 扩展。将不会检测 logback-spring.groovy 文件。

    7.1.1. 配置仅文件输出的 Logback

    如果要禁用控制台日志记录并且仅将输出写入文件,则需要一个自定义 logback-spring.xml 导入 file-appender.xml 但不导入 console-appender.xml ,如以下示例所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <include resource="org/springframework/boot/logging/logback/defaults.xml" />
        <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
        <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
        <root level="INFO">
            <appender-ref ref="FILE" />
        </root>
    </configuration>
    

    您还需要将 logging.file.name 添加到 application.properties 中,如以下示例所示:

    logging.file.name=myapplication.log
    

    7.2. 配置 Log4j 进行日志记录

    如果 Log4j2 在类路径上,则 Spring Boot 支持 Log4j2 进行日志记录配置。如果使用启动器来组装依赖项,则必须排除 Logback ,然后改为包括 Log4j2 。如果您不使用启动器,至少需要提供 spring-jclLog4j2

    推荐的路径是通过启动器,即使它需要对排除对象进行微调。以下示例显示了如何在 Maven 中设置启动器:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>
    

    Gradle 提供了几种不同的方法来设置启动器。一种方法是使用 模块更换 。为此,请声明对 Log4j 2 启动器的依赖,并告诉 Gradle 任何出现的默认日志记录启动器都应替换为 Log4j 2 启动器,如以下示例所示:

    dependencies {
        implementation "org.springframework.boot:spring-boot-starter-log4j2"
        modules {
            module("org.springframework.boot:spring-boot-starter-logging") {
                replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")
            }
        }
    }
    

    Log4j 启动器将依赖关系集中在一起,以满足常见的日志记录要求(例如 Tomcat 使用 java.util.logging ,但配置 Log4j2 输出)。

    为确保将使用 java.util.logging 进行的调试日志记录路由到 Log4j2 中,请将系统属性 java.util.logging.manager 设置为 org.apache.logging.log4j.jul.LogManager ,以配置其 JDK 日志记录适配器

    7.2.1. 使用 YAML 或 JSON 配置 Log4j 2

    除了默认的 XML 配置格式外,Log4j 2 还支持 YAML 和 JSON 配置文件。要将 Log4j2 配置为使用备用配置文件格式,请将适当的依赖项添加到类路径中,并命名您的配置文件以匹配您选择的文件格式,如以下示例所示:

    格式 依赖项 文件名
    YAML com.fasterxml.jackson.core:jackson-databind + com.fasterxml.jackson.dataformat:jackson-dataformat-yaml log4j2.yaml + log4j2.yml
    JSON com.fasterxml.jackson.core:jackson-databind log4j2.json + log4j2.jsn

    7.2.2. 使用复合配置来配置 Log4j 2

    Log4j 2 支持将多个配置文件组合成一个复合配置。要在 Spring Boot 中使用此支持,请使用一个或多个辅助配置文件的位置进行配置 logging.log4j2.config.override 。辅助配置文件将与主配置合并,无论主配置的源是 Spring Boot 的默认值、标准位置(例如 log4j.xml )还是 logging.config 属性配置的位置

    8. Data Access

    文档地址

    8.1. 配置自定义数据源

    要配置您自己的 DataSource ,请在配置中定义该类型的 @Bean 。Spring Boot 可以在任何需要的地方重用 DataSource ,包括数据库初始化。如果需要外部化某些设置,则可以将 DataSource 绑定到环境。参考 Third-party Configuration

    以下示例显示了如何在 bean 中定义数据源:

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyDataSourceConfiguration {
    
        @Bean
        @ConfigurationProperties(prefix = "app.datasource")
        public SomeDataSource dataSource() {
            return new SomeDataSource();
        }
    
    }
    

    以下示例显示如何通过设置属性来定义数据源:

    app.datasource.url=jdbc:h2:mem:mydb
    app.datasource.username=sa
    app.datasource.pool-size=30
    

    假设 SomeDataSource 具有 URL,用户名和池大小的常规 JavaBean 属性,则在将 DataSource 用于其他组件之前,将自动绑定这些设置。

    Spring Boot 还提供了一个名为 DataSourceBuilder 的实用程序构建器类,可用于创建标准数据源之一(如果它位于类路径上)。构建器可以根据类路径中可用的内容来检测要使用的内容。它还基于 JDBC URL 自动检测驱动程序。

    以下示例显示如何使用 DataSourceBuilder 来创建数据源:

    import javax.sql.DataSource;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyDataSourceConfiguration {
    
        @Bean
        @ConfigurationProperties("app.datasource")
        public DataSource dataSource() {
            return DataSourceBuilder.create().build();
        }
    
    }
    

    要使用 DataSource 运行应用程序,您需要的只是连接信息。还可以提供特定于池的设置。有关更多详细信息,请检查将在运行时使用的实现。

    以下示例显示如何通过设置属性来定义 JDBC 数据源:

    app.datasource.url=jdbc:mysql://localhost/test
    app.datasource.username=dbuser
    app.datasource.password=dbpass
    app.datasource.pool-size=30
    

    但是,有一个问题。由于未公开连接池的实际类型,因此在自定义 DataSource 的元数据中不会生成任何键,并且在 IDE 中也无法完成操作(因为 DataSource 接口未公开任何属性)。另外,如果您碰巧在类路径上有 Hikari ,则此基本设置将不起作用,因为 Hikari 没有 url 属性(但确实具有 jdbcUrl 属性)。在这种情况下,您必须按照以下方式重写配置:

    app.datasource.jdbc-url=jdbc:mysql://localhost/test
    app.datasource.username=dbuser
    app.datasource.password=dbpass
    app.datasource.pool-size=30
    

    您可以通过强制连接池使用并返回专用的实现而不是 DataSource 来解决此问题。您无法在运行时更改实现,但是选项列表将是明确的。

    下面的示例演示如何使用 DataSourceBuilder 创建 HikariDataSource

    import com.zaxxer.hikari.HikariDataSource;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyDataSourceConfiguration {
    
        @Bean
        @ConfigurationProperties("app.datasource")
        public HikariDataSource dataSource() {
            return DataSourceBuilder.create().type(HikariDataSource.class).build();
        }
    
    }
    

    您甚至可以利用 DataSourceProperties 为您提供服务的功能(即,如果没有提供 URL ,则通过提供具有合理用户名和密码的默认嵌入式数据库)。您可以轻松地从任何 DataSourceProperties 对象的状态初始化 DataSourceBuilder ,因此您还可以注入Spring Boot自动创建的 DataSource 。然而,这将配置分为两个命名空间:spring.datasource 中的url,username,password,type 和 driver ;您的自定义命名空间中的其余部分(app.datasource)。为避免这种情况,可以在自定义名称空间上重新定义一个自定义 DataSourceProperties ,如以下示例所示:

    import com.zaxxer.hikari.HikariDataSource;
    
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    @Configuration(proxyBeanMethods = false)
    public class MyDataSourceConfiguration {
    
        @Bean
        @Primary
        @ConfigurationProperties("app.datasource")
        public DataSourceProperties dataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        @ConfigurationProperties("app.datasource.configuration")
        public HikariDataSource dataSource(DataSourceProperties properties) {
            return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        }
    
    }
    

    默认情况下,该设置使您与 Spring Boot 为您所做的同步,不同的是,已选择(以代码形式)专用的连接池,并且其设置在 app.datasource.configuration 子名称空间中公开。因为 DataSourceProperties 为您处理 url / jdbcUrl 转换,所以可以按以下方式配置它:

    app.datasource.url=jdbc:mysql://localhost/test
    app.datasource.username=dbuser
    app.datasource.password=dbpass
    app.datasource.configuration.maximum-pool-size=30
    

    Spring Boot 会将 Hikari 特定的设置公开给 spring.datasource.hikari 。本示例使用更通用的 configuration 子名称空间,因为该示例不支持多个数据源实现。

    由于您的自定义配置选择与 Hikari 一起使用,因此 app.datasource.type 无效。实际上,构建器会使用 .type() 的任何值。

    有关更多详细信息,请参见 Configure a DataSourceDataSourceAutoConfiguration

    8.2. 配置两个数据源

    如果需要配置多个数据源,则可以应用上一节中介绍的相同技巧。但是,您必须将其中一个 DataSource 实例标记为 @Primary ,因为将来各种自动配置都希望能够按类型获得一个 DataSource

    如果您创建自己的 DataSource ,则自动配置将退出。在以下示例中,我们提供与自动配置在主数据源上提供的功能完全相同的功能集:

    import com.zaxxer.hikari.HikariDataSource;
    import org.apache.commons.dbcp2.BasicDataSource;
    
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    @Configuration(proxyBeanMethods = false)
    public class MyDataSourcesConfiguration {
    
        @Bean
        @Primary
        @ConfigurationProperties("app.datasource.first")
        public DataSourceProperties firstDataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        @Primary
        @ConfigurationProperties("app.datasource.first.configuration")
        public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
            return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        }
    
        @Bean
        @ConfigurationProperties("app.datasource.second")
        public BasicDataSource secondDataSource() {
            return DataSourceBuilder.create().type(BasicDataSource.class).build();
        }
    
    }
    

    firstDataSourceProperties 必须标记为 @Primary ,以便数据库初始化程序功能使用您的副本(如果使用初始化程序)。

    这两个数据源也都必须进行高级定制。例如,您可以按以下方式配置它们:

    app.datasource.first.url=jdbc:mysql://localhost/first
    app.datasource.first.username=dbuser
    app.datasource.first.password=dbpass
    app.datasource.first.configuration.maximum-pool-size=30
    
    app.datasource.second.url=jdbc:mysql://localhost/second
    app.datasource.second.username=dbuser
    app.datasource.second.password=dbpass
    app.datasource.second.max-total=30
    

    您也可以将相同的概念应用于辅助服务器 DataSource ,如以下示例所示:

    import com.zaxxer.hikari.HikariDataSource;
    import org.apache.commons.dbcp2.BasicDataSource;
    
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    
    @Configuration(proxyBeanMethods = false)
    public class MyCompleteDataSourcesConfiguration {
    
        @Bean
        @Primary
        @ConfigurationProperties("app.datasource.first")
        public DataSourceProperties firstDataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        @Primary
        @ConfigurationProperties("app.datasource.first.configuration")
        public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
            return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
        }
    
        @Bean
        @ConfigurationProperties("app.datasource.second")
        public DataSourceProperties secondDataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        @ConfigurationProperties("app.datasource.second.configuration")
        public BasicDataSource secondDataSource(
                @Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
            return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build();
        }
    
    }
    

    上面的示例在自定义名称空间上配置两个数据源,其逻辑与 Spring Boot 在自动配置中使用的逻辑相同。请注意,每个 configuration 子名称空间都基于所选实现提供高级设置。

    8.3. 使用 Spring Data Repositories

    Spring Data 可以创建各种类型的 @Repository 接口的实现。只要 @Repositories 包含在 @EnableAutoConfiguration 类的同一个包(或子包)中,Spring Boot 就会为您处理所有这些操作。

    对于许多应用程序,您所需要做的就是在类路径上放置正确的 Spring Data 依赖项。JPA 有一个 spring-boot-starter-data-jpa ,Mongodb 有一个 spring-boot-starter-data-mongodb 等。要开始使用,请创建一些存储库接口来处理您的 @Entity 对象。

    Spring Boot 会根据 @EnableAutoConfiguration 发现的内容尝试猜测您的 @Repository 定义的位置。要获得更多控制权,请使用 @EnableJpaRepositories 注解(来自 Spring Data JPA)。

    有关Spring Data的更多信息,请参见 Spring Data

    8.4. 将 @Entity 定义与 Spring 配置分开

    Spring Boot 会根据 @EnableAutoConfiguration 发现的内容尝试猜测您定义的 @Entity 的位置。要获得更多控制,可以使用 @EntityScan 注解,如以下示例所示:

    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.domain.EntityScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    @EnableAutoConfiguration
    @EntityScan(basePackageClasses = City.class)
    public class MyApplication {
    
        // ...
    
    }
    

    8.5. 配置 JPA 属性

    Spring Data JPA 已经提供了一些独立于供应商的配置选项(例如用于 SQL 日志记录的那些),并且 Spring Boot 公开了这些选项,还为 Hibernate 提供了更多选项作为外部配置属性。其中的一些会根据上下文自动检测,因此您不必进行设置。

    spring.jpa.hibernate.ddl-auto 是一种特殊情况,因为根据运行时条件,它具有不同的默认值。如果使用嵌入式数据库并且没有模式管理器(例如 Liquibase 或 Flyway)正在处理 DataSource ,则默认为 create-drop 。在所有其他情况下,默认为 none

    JPA 供应商检测要使用的方言。如果您希望自己设置方言,请设置 spring.jpa.database-platform 属性。

    下例显示了最常用的设置选项:

    spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
    spring.jpa.show-sql=true
    

    此外,创建本地 EntityManagerFactory 时,所有 spring.jpa.properties.* 属性均作为常规 JPA 属性(前缀被去除)传递。

    您需要确保在 spring.jpa.properties.* 下定义的名称与您的 JPA 供应商期望的名称完全匹配。Spring Boot 不会尝试对这些条目进行任何形式的宽松绑定。

    例如,如果要配置 Hibernate 的批处理大小,则必须使用 spring.jpa.properties.hibernate.jdbc.batch_size 。如果您使用其他形式,例如 batchSizebatch-size ,则 Hibernate 将不会应用该设置。

    如果您需要对 Hibernate 属性应用高级定制,请考虑在创建 EntityManagerFactory 之前注册将被调用的 HibernatePropertiesCustomizer bean 。这优先于自动配置应用的任何内容。

    8.6. 配置 Hibernate 命名策略

    Hibernate 使用 两种不同的命名策略 将名称从对象模型映射到相应的数据库名称。可以分别通过设置 spring.jpa.hibernate.naming.physical-strategyspring.jpa.hibernate.naming.implicit-strategy 属性来配置物理和隐式策略实现的完全限定类名。或者,如果 ImplicitNamingStrategyPhysicalNamingStrategy bean 在应用上下文是可用的,Hibernate 会被自动配置为使用它们。

    默认情况下,Spring Boot 配置物理命名策略为 CamelCaseToUnderscoresNamingStrategy 。使用此策略,所有点都被下划线替换,驼峰式大小写也被下划线替换。此外,默认情况下,所有表名都以小写形式生成。例如,将 TelephoneNumber 实体映射到 telephone_number 表。如果您的模式需要混合大小写的标识符,请定义一个自定义 CamelCaseToUnderscoresNamingStrategy bean,如以下示例所示:

    import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
    import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyHibernateConfiguration {
    
        @Bean
        public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
            return new CamelCaseToUnderscoresNamingStrategy() {
    
                @Override
                protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
                    return false;
                }
    
            };
        }
    
    }
    

    如果您更喜欢使用 Hibernate 5 的默认设置,请设置以下属性:

    spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    

    另外,您可以配置以下 bean :

    import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    class MyHibernateConfiguration {
    
        @Bean
        PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
            return new PhysicalNamingStrategyStandardImpl();
        }
    
    }
    

    请参阅 HibernateJpaAutoConfigurationJpaBaseConfiguration

    8.7. 配置 Hibernate 二级缓存

    可以为一系列缓存供应商配置 Hibernate 二级缓存。与其将 Hibernate 配置为再次查找缓存供应商,不如尽可能提供在上下文中可用的缓存供应商。

    如果您使用的是 JCache ,这非常简单。首先,确保 org.hibernate:hibernate-jcache 在类路径上可用。然后,添加一个 HibernatePropertiesCustomizer bean,如以下示例所示:

    import org.hibernate.cache.jcache.ConfigSettings;
    
    import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
    import org.springframework.cache.jcache.JCacheCacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyHibernateSecondLevelCacheConfiguration {
    
        @Bean
        public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
            return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
        }
    
    }
    

    该定制器将配置 Hibernate 以使其与应用程序使用的 CacheManager 相同。也可以使用单独的 CacheManager 实例。有关详细信息,请参阅 Hibernate 用户指南

    8.8. 在 Hibernate 组件中使用依赖注入

    默认情况下,Spring Boot 注册使用 BeanFactoryBeanContainer 实现,以便转换器和实体监听器可以使用常规依赖项注入。

    您可以通过注册 HibernatePropertiesCustomizer 删除或更改 hibernate.resource.beans.container 属性来禁用或调整此行为。

    8.9. 使用自定义 EntityManagerFactory

    要完全控制 EntityManagerFactory 的配置,您需要添加一个名为 entityManagerFactory@Bean 。如果存在这种类型的 bean ,Spring Boot 自动配置将关闭其实体管理器。

    8.10. 使用多个 EntityManagerFactories

    如果您需要针对多个数据源使用 JPA,您可能需要每个数据源一个 EntityManagerFactory 。来自 Spring ORM 的 LocalContainerEntityManagerFactoryBean 允许您根据需要配置一个 EntityManagerFactory 。您还可以重用 JpaProperties 为每个 EntityManagerFactory 绑定设置,如以下示例所示:

    import javax.sql.DataSource;
    
    import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.orm.jpa.JpaVendorAdapter;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    
    @Configuration(proxyBeanMethods = false)
    public class MyEntityManagerFactoryConfiguration {
    
        @Bean
        @ConfigurationProperties("app.jpa.first")
        public JpaProperties firstJpaProperties() {
            return new JpaProperties();
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource,
                JpaProperties firstJpaProperties) {
            EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties);
            return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build();
        }
    
        private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
            JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
            return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null);
        }
    
        private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
            // ... map JPA properties as needed
            return new HibernateJpaVendorAdapter();
        }
    
    }
    

    上面的示例使用名为 firstDataSourceDataSource bean 创建了一个 EntityManagerFactory 。它扫描与 Order 位于同一包中的实体。可以使用 app.first.jpa 命名空间映射其他 JPA 属性。

    当您自己创建 LocalContainerEntityManagerFactoryBean bean 时,在创建自动配置的 LocalContainerEntityManagerFactoryBean 期间应用的任何自定义都将丢失。例如,在 Hibernate 的情况下,spring.jpa.hibernate 前缀下的任何属性都不会自动应用于您的 LocalContainerEntityManagerFactoryBean 。如果您依赖这些属性来配置命名策略或 DDL 模式等内容,则需要在创建 LocalContainerEntityManagerFactoryBean bean 时显式配置它们。

    您应该为需要 JPA 访问权限的任何其他数据源提供类似的配置。要完成 picture ,您还需要为每个 EntityManagerFactory 配置一个 JpaTransactionManager 。或者,您可以使用跨越两者的 JTA 事务管理器。

    如果使用 Spring Data ,则需要进行相应的配置 @EnableJpaRepositories ,如下例所示:

    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    
    @Configuration(proxyBeanMethods = false)
    @EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
    public class OrderConfiguration {
    
    }
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    
    @Configuration(proxyBeanMethods = false)
    @EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
    public class CustomerConfiguration {
    
    }
    

    8.11. 使用传统 persistence.xml 文件

    默认情况下,Spring Boot 不会搜索或使用 META-INF/persistence.xml 。如果您喜欢使用传统的 persistence.xml ,则需要定义自己的 LocalEntityManagerFactoryBean 类型的 @Bean(ID 为 entityManagerFactory )并在其中设置持久层单元名称。

    请参阅 JpaBaseConfiguration

    8.12. 使用 Spring Data JPA 和 Mongo 存储库

    Spring Data JPA 和 Spring Data Mongo 都可以自动为您创建 Repository 实现。如果它们都存在于类路径中,则可能必须做一些额外的配置以告诉 Spring Boot 要创建哪个存储库。最明确的方法是使用标准的 Spring Data @EnableJpaRepositories@EnableMongoRepositories 注解并提供 Repository 接口的位置。

    您还可以使用标志( spring.data.*.repositories.enabledspring.data.*.repositories.type )在外部配置中打开和关闭自动配置的存储库。这样做很有用,例如,如果您想关闭 Mongo 存储库并仍然使用自动配置的 MongoTemplate

    对于其他自动配置的 Spring Data 存储库类型(Elasticsearch,Solr等),存在相同的障碍和相同的功能。要使用它们,请相应地更改注解和标志的名称。

    8.13. 自定义 Spring Data 的 Web 支持

    Spring Data 提供了 Web 支持,简化了 Web 应用程序中 Spring Data 存储库的使用。Spring Boot 在 spring.data.web 名称空间中提供了用于自定义其配置的属性。请注意,如果您使用的是 Spring Data REST ,则必须改为使用 spring.data.rest 名称空间中的属性。

    8.14. 将 Spring Data 存储库公开为 REST 端点

    如果已为应用程序启用了 Spring MVC ,则 Spring Data REST 可以为您将 Repository 实现公开为 REST 端点。

    Spring Boot 公开了一组有用的属性(来自 spring.data.rest 名称空间),这些属性来自定义 RepositoryRestConfiguration 。如果需要提供其他定制,则应使用 RepositoryRestConfigurer bean

    如果您没有在自定义 RepositoryRestConfigurer 上指定任何顺序,它将在 Spring Boot 内部使用的 RepositoryRestConfigurer 之后运行。如果您需要指定顺序,请确保 order 大于 0 。

    8.15. 配置 JPA 使用的组件

    如果要配置 JPA 使用的组件,则需要确保在 JPA 之前初始化该组件。当组件被自动配置后,Spring Boot 会为您处理。例如,当自动配置 Flyway 时,会将 Hibernate 配置为依赖 Flyway ,以便 Flyway 有机会在 Hibernate 尝试使用它之前对数据库进行初始化。

    如果您自己配置组件,则可以使用 EntityManagerFactoryDependsOnPostProcessor 子类作为设置必要依赖项的便捷方法。例如,如果您使用带有 Elasticsearch 作为其索引管理器的 Hibernate Search ,则必须将任何 EntityManagerFactory bean 配置为依赖于 elasticsearchClient bean,如以下示例所示:

    import javax.persistence.EntityManagerFactory;
    
    import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor;
    import org.springframework.stereotype.Component;
    
    /**
     * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
     * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
     */
    @Component
    public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
            extends EntityManagerFactoryDependsOnPostProcessor {
    
        public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
            super("elasticsearchClient");
        }
    
    }
    

    8.16. 使用两个数据源配置 jOOQ

    如果需要将 jOOQ 与多个数据源一起使用,则应该为每个数据源创建自己的 DSLContext 。请参阅 JooqAutoConfiguration

    特别是,JooqExceptionTranslatorSpringTransactionProvider 可以重用以对单个 DataSource 提供与自动配置相似的功能。

    9. Database Initialization

    文档地址

    可以根据堆栈的不同以不同的方式初始化 SQL 数据库。当然,如果数据库是一个单独的进程,您也可以手动执行。建议使用单一机制生成模式。

    9.1. 使用 JPA 初始化数据库

    JPA 具有用于 DDL 生成的功能,可以将其设置为在启动时针对数据库运行。这是通过两个外部属性控制的:

    • spring.jpa.generate-ddl (布尔值)打开和关闭该功能,并且与供应商无关
    • spring.jpa.hibernate.ddl-auto(枚举)是一种 Hibernate 功能,可以更精细地控制行为

    9.2. 使用 Hibernate 初始化数据库

    您可以显式设置 spring.jpa.hibernate.ddl-auto 为标准的 Hibernate 属性值:nonevalidateupdatecreatecreate-drop 。Spring Boot 根据您的数据库是否为嵌入式来为您选择默认值。如果未检测到任何模式管理器,默认为 create-drop ,或在所有其他情况下默认为 none 。通过查看 Connection 类型和 JDBC url 可以检测到嵌入式数据库。 hsqldbh2derby 是嵌入式的,而其他不是。从内存数据库转换为“真实”数据库时,请不要对新平台中表和数据的存在做出假设。您必须显式设置 ddl-auto 或使用其他机制之一来初始化数据库。

    您可以通过启用 org.hibernate.SQL 日志记录器来输出模式创建。如果启用调试模式,此操作将自动为您完成。

    另外,如果 Hibernate 从头开始创建模式(即,如果 ddl-auto 属性设置为 createcreate-drop ),则在启动时将执行在类路径的根目录中命名为 import.sql 的文件。如果您小心的话,这对于演示和测试很有用,但可能不希望出现在生产环境的类路径中。这是一个 Hibernate 功能(与 Spring 无关)。

    9.3. 使用基本 SQL 脚本初始化数据库

    Spring Boot 可以自动创建 JDBC DataSource 或 R2DBC ConnectionFactory 的模式(DDL 脚本)并对其进行初始化(DML 脚本)。它分别从标准根类路径位置加载 SQL :schema.sqldata.sql 。此外,Spring Boot 处理 schema-${platform}.sqldata-${platform}.sql 文件(如果存在),其中 platformspring.sql.init.platform 的值。这允许您在必要时切换到特定于数据库的脚本。例如,您可以选择将其设置为数据库的供应商名称(hsqldbh2oraclemysqlpostgresql 等)。默认情况下,SQL 数据库初始化仅在使用嵌入式内存数据库时执行。要始终初始化 SQL 数据库,无论其类型如何,设置 spring.sql.init.modealways 。同样,要禁用初始化,请设置 spring.sql.init.modenever 。默认情况下,Spring Boot 启用其基于脚本的数据库初始化程序的快速失败特性。这意味着,如果脚本导致异常,应用程序将无法启动。您可以通过设置 spring.sql.init.continue-on-error 来调整该行为。

    默认情况下,在 EntityManagerFactory 创建任何 JPA bean 之前执行基于脚本的 DataSource 初始化。schema.sql 可用于为 JPA 管理的实体创建模式,data.sql 可用于填充它。虽然我们不建议使用多数据源初始化技术,但如果您希望基于脚本的 DataSource 初始化能够建立在 Hibernate 执行的模式创建之上,请设置 spring.jpa.defer-datasource-initializationtrue 。这将推迟数据源初始化,直到创建和初始化任何 EntityManagerFactory bean 之后。 然后 schema.sql 可用于对 Hibernate 执行的任何模式创建进行添加,data.sql 可用于填充它。

    如果您使用的是 高级数据库迁移工具 ,例如 Flyway 或 Liquibase ,则应单独使用它们来创建和初始化架构。不建议将基本 schema.sqldata.sql 脚本与 Flyway 或 Liquibase 一起使用,并且将在未来的版本中删除支持。

    9.4. 初始化 Spring Batch 数据库

    如果您使用 Spring Batch ,则它随大多数流行的数据库平台一起预包装了 SQL 初始化脚本。Spring Boot 可以检测您的数据库类型并在启动时执行这些脚本。如果您使用嵌入式数据库,则默认情况下会发生这种情况。您还可以为任何数据库类型启用它,如以下示例所示:

    spring.batch.jdbc.initialize-schema=always
    

    您还可以通过设置 spring.batch.initialize-schema=never 显式关闭初始化。

    9.5. 使用高级数据库迁移工具

    Spring Boot支持两种更高级别的迁移工具:FlywayLiquibase

    9.5.1. 在启动时执行 Flyway 数据库迁移

    要在启动时自动运行 Flyway 数据库迁移,请将 org.flywaydb:flyway-core 添加到您的类路径中。

    通常,迁移是采用以下形式的脚本 V<VERSION>__<NAME>.sql(带有 <VERSION> 下划线分隔的版本,例如 12_1 )。默认情况下,它们位于名为 classpath:db/migration 的文件夹中,但是您可以通过设置 spring.flyway.locations 修改该位置。这是一个或多个 classpath:filesystem: 位置的逗号分隔列表。例如,以下配置将在默认类路径位置和 /opt/migration 目录中搜索脚本:

    spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
    

    您还可以添加特殊的 {vendor} 占位符以使用特定于供应商的脚本。假设以下内容:

    spring.flyway.locations=classpath:db/migration/{vendor}
    

    前面的配置不是使用 db/migration ,而是根据数据库的类型(例如,db/migration/mysql 对于 MySQL)来设置要使用的文件夹。支持的数据库列表在 DatabaseDriver 中提供。

    迁移也可以用 Java 编写。Flyway 将使用任何实现 JavaMigration 的 bean 自动配置。

    FlywayProperties 提供 Flyway 的大多数设置以及少量的其他属性,这些属性可用于禁用迁移或关闭位置检查。如果需要对配置进行更多控制,考虑注册 FlywayConfigurationCustomizer bean 。

    Spring Boot 调用 Flyway.migrate() 以执行数据库迁移。如果您想要更多控制,请提供实现 FlywayMigrationStrategy@Bean

    Flyway 支持 SQL 和 Java 回调。要使用基于 SQL 的回调,请将回调脚本放在 classpath:db/migration 目录中。要使用基于 Java 的回调,请创建一个或多个实现 Callback 的 bean 。任何此类 bean 都会自动向 Flyway 进行注册。可以通过使用 @Order 或实现 Ordered 来调整顺序。也可以检测到实现了已弃用的 FlywayCallback 接口的Bean ,但是不能与 Callback Bean 一起使用。

    默认情况下,Flyway 自动在您的上下文中自动装配(@PrimaryDataSource 并将其用于迁移。如果您想使用其他 DataSource ,则可以创建一个并将其标记为 @FlywayDataSource@Bean 。如果这样做并想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary 。另外,您可以通过设置 spring.flyway.[url,user,password] 外部属性来使用 Flyway 的原生 DataSource 。设置 spring.flyway.urlspring.flyway.user 足以使 Flyway 使用自己的 DataSource 。如果未设置这三个属性中的任何一个,则将使用其等效属性 spring.datasource 的值。

    您还可以使用 Flyway 为特定场景提供数据。例如,您可以在 src/test/resources 中放入特定于测试的迁移,并且仅在您的应用程序开始进行测试时才运行它们。另外,您可以使用特定于 profile 的配置进行自定义 spring.flyway.locations ,以便仅在特定 profile 处于活动状态时才运行某些迁移。例如,在 application-dev.properties 中,您可以指定以下设置:

    spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration
    

    使用该设置,dev/db/migration 的迁移仅在 dev profile 处于活动状态时才运行。

    9.5.2. 在启动时执行 Liquibase 数据库迁移

    要在启动时自动运行 Liquibase 数据库迁移,请将 org.liquibase:liquibase-core 添加到您的类路径中。

    当您添加 org.liquibase:liquibase-core 到您的类路径时,默认情况下,在应用程序启动期间和测试运行之前都会运行数据库迁移。可以通过使用 spring.liquibase.enabled 属性来自定义此行为,在 maintest 配置中设置不同的值。不可能使用两种不同的方式来初始化数据库(例如,用于应用程序启动的 Liquibase ,用于测试运行的 JPA)。

    默认情况下,从 db/changelog/db.changelog-master.yaml 中读取主更改日志,但是您可以通过设置 spring.liquibase.change-log 来更改位置。除了 YAML ,Liquibase 还支持 JSON,XML 和 SQL 更改日志格式。

    默认情况下,Liquibase 在您的上下文中自动装配(@PrimaryDataSource 并将其用于迁移。如果需要使用其他 DataSource ,则可以创建一个并将其标记为@LiquibaseDataSource@Bean 。如果这样做,并且想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary 。另外,您可以通过设置 spring.liquibase.[url,user,password] 外部属性来使用 Liquibase 的原生 DataSource 。设置 spring.liquibase.urlspring.liquibase.user 足以使 Liquibase 使用其自己的 DataSource 。如果未设置这三个属性中的任何一个,则将使用其等效属性 spring.datasource 的值。

    请参阅 LiquibaseProperties

    9.6. 依赖一个初始化的数据库

    作为应用上下文刷新的一部分,在应用程序启动时执行数据库初始化。为了允许在启动期间访问已初始化的数据库,会自动检测充当数据库初始化程序的 bean 和需要已初始化该数据库的 bean。初始化依赖于已初始化数据库的 Bean 被配置为依赖于那些初始化它的 Bean。如果在启动期间,您的应用程序尝试访问数据库但尚未初始化,您可以配置对初始化数据库并要求数据库已初始化的 bean 的额外检测。

    9.6.1. 检测数据库初始化器

    Spring Boot 将自动检测初始化 SQL 数据库的以下类型的 bean :

    • DataSourceScriptDatabaseInitializer
    • EntityManagerFactory
    • Flyway
    • FlywayMigrationInitializer
    • R2dbcScriptDatabaseInitializer
    • SpringLiquibase

    如果您使用第三方启动器进行数据库初始化库,它可能会提供一个检测器,这样其他类型的 bean 也会被自动检测到。要检测其他 bean,请在 META-INF/spring-factories 中注册 DatabaseInitializerDetector 的实现。

    9.6.2. 检测依赖于数据库初始化的 Bean

    Spring Boot 将根据数据库初始化自动检测以下类型的 bean:

    • AbstractEntityManagerFactoryBean(除非 spring.jpa.defer-datasource-initialization 设置为 true
    • DSLContext(JOOQ )
    • EntityManagerFactory(除非 spring.jpa.defer-datasource-initialization 设置为 true
    • JdbcOperations
    • NamedParameterJdbcOperations

    如果您使用的是第三方启动器数据访问库,它可能会提供一个检测器,以便也自动检测其他类型的 bean 。要检测其他 bean ,请在 META-INF/spring-factories 中注册 DependsOnDatabaseInitializationDetector 实现。或者,使用 @DependsOnDatabaseInitialization 注解 bean 的类或其 @Bean 方法。

    10. Messaging

    Spring Boot 提供了许多支持消息传递的启动器。

    10.1. 禁用事务处理的 JMS Session

    如果您的 JMS 代理不支持事务处理会话,则必须完全禁用对事务的支持。如果您创建自己的 JmsListenerContainerFactory ,则无需执行任何操作,因为默认情况下无法进行交易。如果要使用 DefaultJmsListenerContainerFactoryConfigurer 来重用 Spring Boot 的默认值,则可以禁用事务处理会话,如下所示:

    import javax.jms.ConnectionFactory;
    
    import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
    
    @Configuration(proxyBeanMethods = false)
    public class MyJmsConfiguration {
    
        @Bean
        public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory,
                DefaultJmsListenerContainerFactoryConfigurer configurer) {
            DefaultJmsListenerContainerFactory listenerFactory = new DefaultJmsListenerContainerFactory();
            configurer.configure(listenerFactory, connectionFactory);
            listenerFactory.setTransactionManager(null);
            listenerFactory.setSessionTransacted(false);
            return listenerFactory;
        }
    
    }
    

    前面的示例将覆盖默认工厂,并且应将其应用于应用程序定义的任何其他工厂(如果有)。

    11. Batch Applications

    当人们在 Spring Boot 应用程序中使用 Spring Batch 时,经常会出现许多问题。本节将解决这些问题。

    11.1. 指定批处理数据源。

    默认情况下,批处理应用程序需要一个 DataSource 来存储作业详细信息。Spring Batch 默认需要一个 DataSource 。要让 Batch 使用应用程序的 main DataSource 之外的其他 DataSource ,请声明一个 DataSource bean ,并用 @BatchDataSource 注解其 @Bean 方法。如果这样做并想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary 。要获得更大的控制权,请实现 BatchConfigurer 。有关更多详细信息,请参见 @EnableBatchProcessing

    有关 Spring Batch 的更多信息,请参见 Spring Batch

    11.2. 在启动时执行 Spring Batch 作业

    通过添加 @EnableBatchProcessing 到您的一个 @Configuration 类来启用 Spring Batch 自动配置。

    默认情况下,它在启动时在应用上下文中执行所有 Jobs(有关详细信息,请参见 JobLauncherCommandLineRunner )。您可以通过指定 spring.batch.job.names(使用逗号分隔的作业名称模式列表)缩小到一个或多个特定作业。

    有关更多详细信息,请参见 BatchAutoConfiguration@EnableBatchProcessing

    11.3. 从命令行运行

    Spring Boot 将任何以 -- 开头的命令行参数转换为要添加到的 Environment 属性,请参阅 访问命令行属性 。这不应该用于将参数传递给批处理作业。要在命令行上指定批处理参数,请使用常规格式(即不带 -- ),如以下示例所示:

    $ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue
    

    如果您在命令行上指定 Environment 的属性,作业将忽略它。考虑以下命令:

    $ java -jar myapp.jar --server.port=7070 someParameter=someValue
    

    这仅为批处理作业提供了一个参数:someParameter=someValue

    11.4. 存储作业存储库

    Spring Batch 需要 Job 存储库的数据存储。如果使用 Spring Boot ,则必须使用实际的数据库。请注意,它可以是内存数据库,请参阅 配置作业存储库

    12. Actuator

    Spring Boot 包括 Spring Boot Actuator 。本节回答了经常因使用而引起的问题。

    12.1. 更改执行器端点的 HTTP 端口或地址

    在独立应用程序中,Actuator HTTP 端口默认与主 HTTP 端口相同。要使应用程序在其他端口上监听,请设置外部属性: management.server.port 。要监听完全不同的网络地址(例如,当您有一个用于管理的内部网络,还有一个用于用户应用程序的外部网络)时,还可以设置 management.server.address 为服务器可以绑定到的有效 IP 地址。

    有关更多详细信息,请参阅 ManagementServerPropertiesCustomizing the Management Server Port

    12.2. 自定义 “whitelabel” 错误页面

    如果遇到服务器错误,Spring Boot 会安装一个 “whitelabel” 错误页面,您会在浏览器客户端中看到该错误页面(使用 JSON 和其他媒体类型的机器客户端应该看到带有正确错误代码的合理响应)。

    设置 server.error.whitelabel.enabled=false 以关闭默认错误页面。这样做将还原您正在使用的 servlet 容器的默认值。请注意,Spring Boot 仍然尝试解决错误视图,因此您应该添加自己的错误页面,而不是完全禁用它。

    用自己的错误页面覆盖错误页面取决于您使用的模板技术。例如,如果您使用 Thymeleaf ,则可以添加 error.html 模板。如果使用 FreeMarker ,则可以添加 error.ftlh 模板。通通常,您需要一个使用错误名称解析的 View 或一个处理 /error 路径的 @Controller 。除非你更换了一些默认配置,你应该在 ApplicationContext 中找到一个 BeanNameViewResolver ,所以将 @Bean 命名 error 是一个简单的方法。请参阅 ErrorMvcAutoConfiguration

    有关如何在 servlet 容器中注册处理程序的详细信息,请参见 Error Handling

    12.3. 清理敏感值

    envconfigprops 端点返回的信息可能有些敏感,因此默认情况下会清理与某个模式匹配的键(即,它们的值将替换为 ****** )。

    Spring Boot 对此类键使用合理的默认值:例如,对任何以单词 passwordsecretkeytokenvcap_servicessun.java.command 结尾的键进行清理。此外,包含单词 credentials(配置为正则表达式,即 .*credentials.* )作为键一部分的任何键也被完全清理。

    此外,Spring Boot 对具有以下结尾之一的键的类似 URI 的值的敏感部分进行清理:

    • address
    • addresses
    • uri
    • uris
    • url
    • urls

    URI 的敏感部分使用 <scheme>://<username>:<password>@<host>:<port>/ 格式标识。例如,对于属性 myclient.uri=http://user1:password1@localhost:8081 ,经过清理的结果值为 http://user1:******@localhost:8081

    12.3.1. 自定义清理

    清理可以通过两种不同的方式进行定制。

    envconfigprops 端点使用的默认模式可以分别使用 management.endpoint.env.keys-to-sanitizemanagement.endpoint.configprops.keys-to-sanitize 替换。或者,可以使用 management.endpoint.env.additional-keys-to-sanitizemanagement.endpoint.configprops.additional-keys-to-sanitize 配置其他模式。

    要对清理进行更多控制,请定义一个 SanitizingFunction bean。调用该函数的 SanitizableData 提供了对键和值以及它们的 PropertySource 来源的访问。例如,这允许您清理来自特定属性源的每个值。每个 SanitizingFunction 在内置的基于键的清理之前和之外调用。

    12.4. 将健康指标映射到 Micrometer Metrics

    Spring Boot 健康指标返回一个 Status 类型来指示整个系统的健康状况。如果您想监控或提醒特定应用程序的健康水平,您可以使用 Micrometer 将这些状态导出为指标。默认情况下,Spring Boot 使用状态码 UPDOWNOUT_OF_SERVICEUNKNOWN 。要导出这些,您需要将这些状态转换为一组数字,以便它们可以与 Micrometer Gauge 一起使用。

    以下示例显示了编写此类导出器的一种方法:

    import io.micrometer.core.instrument.Gauge;
    import io.micrometer.core.instrument.MeterRegistry;
    
    import org.springframework.boot.actuate.health.HealthEndpoint;
    import org.springframework.boot.actuate.health.Status;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration(proxyBeanMethods = false)
    public class MyHealthMetricsExportConfiguration {
    
        public MyHealthMetricsExportConfiguration(MeterRegistry registry, HealthEndpoint healthEndpoint) {
            // This example presumes common tags (such as the app) are applied elsewhere
            Gauge.builder("health", healthEndpoint, this::getStatusCode).strongReference(true).register(registry);
        }
    
        private int getStatusCode(HealthEndpoint health) {
            Status status = health.health().getStatus();
            if (Status.UP.equals(status)) {
                return 3;
            }
            if (Status.OUT_OF_SERVICE.equals(status)) {
                return 2;
            }
            if (Status.DOWN.equals(status)) {
                return 1;
            }
            return 0;
        }
    
    }
    

    13. Security

    本部分解决有关使用 Spring Boot 时的安全性的问题,包括因将 Spring Security 与 Spring Boot 一起使用而引起的问题。

    有关 Spring Security 的更多信息,请参见 Spring Security

    13.1. 关闭 Spring Boot 安全配置

    如果在应用程序中用 WebSecurityConfigurerAdapterSecurityFilterChain bean 定义 @Configuration ,它将关闭 Spring Boot 中的默认 webapp 安全设置。

    13.2. 更改 UserDetailsService 并添加用户帐户

    如果你提供了一个 AuthenticationManagerAuthenticationProvider 或者 UserDetailsService 类型的 @Bean ,默认 InMemoryUserDetailsManager@Bean 不创建。这意味着您拥有完整的 Spring Security 功能集(例如 各种身份验证选项 )。

    添加用户帐户的最简单方法是提供您自己的 UserDetailsService bean

    13.3. 在代理服务器后面运行时启用 HTTPS

    对于所有应用程序而言,确保所有主要端点仅可通过 HTTPS 进行访问都是一项重要的工作。如果您将 Tomcat 用作 servlet 容器,如果检测到某些环境设置,则 Spring Boot 会自动添加 Tomcat 自己的 RemoteIpValve ,并且您应该能够依靠 HttpServletRequest 报告其是否安全(甚至在处理真正的 SSL 终端代理服务器的下游)。标准行为由某些请求标头( x-forwarded-forx-forwarded-proto )的存在或不存在决定,它们的名称是常规名称,因此它应适用于大多数前端代理。您可以通过向 application.properties 中添加一些条目来打开开关,如以下示例所示:

    server.tomcat.remoteip.remote-ip-header=x-forwarded-for
    server.tomcat.remoteip.protocol-header=x-forwarded-proto
    

    (这些属性中的任何一个的存在都会打开阀门。或者,您可以通过使用 WebServerFactoryCustomizer bean 自定义 TomcatServletWebServerFactory 添加 RemoteIpValve

    要将 Spring Security 配置为要求所有(或部分)请求的安全通道,请考虑添加您自己的 SecurityFilterChain bean,该 bean 添加以下 HttpSecurity 配置:

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.web.SecurityFilterChain;
    
    @Configuration
    public class MySecurityConfig {
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            // Customize the application security ...
            http.requiresChannel().anyRequest().requiresSecure();
            return http.build();
        }
    
    }
    

    14. 热部署

    Spring Boot 支持热部署。本部分回答有关其工作方式的问题。

    14.1. 重新加载静态内容

    有几种热加载选项。推荐的方法是使用 spring-boot-devtools ,因为它提供了其他的开发时功能,例如对快速应用程序重新启动和 LiveReload 的支持,以及合理的开发时配置(例如模板缓存)。Devtools 通过监视类路径的更改来工作。这意味着必须“构建”静态资源更改才能使更改生效。默认情况下,当您保存更改时,这在 Eclipse 中自动发生。在 IntelliJ IDEA 中,Make Project 命令将触发必要的构建。由于默认的重新启动排除项,对静态资源的更改不会触发应用程序的重新启动。但是,它们确实会触发实时重新加载。

    另外,在 IDE 中运行(特别是在调试时)是进行开发的好方法(所有现代 IDE 都允许重新加载静态资源,并且通常还允许热加载 Java 类更改)。

    最后,可以配置 Maven 和 Gradle 插件(请参阅 addResources 属性)以支持从命令行运行,并直接从源中重新加载静态文件。如果要使用高级工具编写该代码,则可以将其与外部 css/js 编译器进程一起使用。

    14.2. 在不重启容器的情况下重新加载模板

    Spring Boot 支持的大多数模板技术都包含禁用缓存的配置选项。如果使用 spring-boot-devtools 模块,则在开发时会为您 自动配置 这些属性。

    14.2.1. Thymeleaf 模板

    如果您使用 Thymeleaf ,请将 spring.thymeleaf.cache 设置为 false 。有关其他 Thymeleaf 自定义选项,请参见 ThymeleafAutoConfiguration

    14.2.2. FreeMarker 模板

    如果使用 FreeMarker ,请设置 spring.freemarker.cachefalse 。有关其他FreeMarker定制选项,请参见 FreeMarkerAutoConfiguration

    14.2.3. Groovy 模板

    如果使用 Groovy 模板,请设置 spring.groovy.template.cachefalse 。请参阅 GroovyTemplateAutoConfiguration

    14.3. 快速应用重启

    spring-boot-devtools 模块包括对应用程序自动重启的支持。尽管不如 JRebel 这样的技术快,但通常比“冷启动”要快得多。在研究本文档后面讨论的一些更复杂的重载选项之前,您可能应该先尝试一下。

    请参阅 Developer Tools

    14.4. 在不重新启动容器的情况下重新加载 Java 类

    许多现代的 IDE(Eclipse,IDEA 等)都支持字节码的热交换。因此,如果所做的更改不影响类或方法的签名,则应干净地重新加载而没有副作用。

    todo 15. Testing

    16. Build

    Spring Boot 包括 Maven 和 Gradle 的构建插件。本部分回答有关这些插件的常见问题。

    16.1. 生成构建信息

    Maven 插件和 Gradle 插件都允许生成包含项目的坐标,名称和版本的构建信息。还可以将插件配置为通过配置添加其他属性。当存在这样的文件时,Spring Boot会自动配置一个 BuildProperties bean 。

    要使用 Maven 生成构建信息,请为 build-info 目标添加执行,如以下示例所示:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.7</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    

    有关更多详细信息,请参见 Spring Boot Maven 插件文档

    下面的示例对 Gradle 执行相同的操作:

    springBoot {
        buildInfo()
    }
    

    有关更多详细信息,请参见 Spring Boot Gradle 插件文档

    16.2. 生成 Git 信息

    Maven 和 Gradle 都允许生成一个 git.properties 文件,其中包含有关构建项目时 git 源代码存储库状态的信息。

    对于 Maven 用户,spring-boot-starter-parent POM 包含一个预配置的插件来生成 git.properties 文件。要使用它,请将以下声明添加到您的 POM 中:

    <build>
        <plugins>
            <plugin>
                <groupId>pl.project13.maven</groupId>
                <artifactId>git-commit-id-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    

    Gradle 用户可以通过使用 gradle-git-properties 插件获得相同的结果,如以下示例所示:

    plugins {
        id "com.gorylenko.gradle-git-properties" version "2.3.2"
    }
    

    Maven 和 Gradle 插件都允许配置 git.properties 中包含的属性。

    git.properties 内的提交时间应与以下格式匹配:yyyy-MM-dd’T’HH:mm:ssZ 。这是上面列出的两个插件的默认格式。使用此格式,可以将时间解析为 Date ,其格式在序列化为 JSON 时,由 Jackson 的日期序列化配置设置控制。

    16.3. 自定义依赖版本

    spring-boot-dependencies POM 管理通用依赖项的版本。Maven 和 Gradle 的 Spring Boot 插件允许使用构建属性自定义这些托管依赖版本。

    每个 Spring Boot 版本都针对这组特定的第三方依赖项进行设计和测试。覆盖版本可能会导致兼容性问题。

    要使用 Maven 覆盖依赖版本,请参阅Maven 插件文档的 Using the Plugin

    要覆盖 Gradle 中的依赖版本,请参阅Gradle 插件文档的 Customizing Managed Versions

    16.4. 使用 Maven 创建可执行 JAR

    spring-boot-maven-plugin 可用于创建可执行的 JAR 。如果使用 spring-boot-starter-parent POM,则可以声明插件,然后将 jar 重新打包:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    

    如果您不使用父 POM ,仍然可以使用该插件。但是,您必须另外添加一个 <executions> 部分,如下所示:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>{spring-boot-version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    

    有关完整用法的详细信息,请参阅 插件文档

    16.5. 使用 Spring Boot 应用程序作为依赖项

    像 war 文件一样,Spring Boot 应用程序也不打算被用作依赖项。如果您的应用程序包含要与其他项目共享的类,则建议的方法是将该代码移到单独的模块中。然后,您的应用程序和其他项目可以依赖单独的模块。

    如果您不能按照上面的建议重新排列代码,则必须将 Spring Boot 的 Maven 和 Gradle 插件配置为生成适合用作依赖项的单独工件。可执行存档不能用作依赖项,因为 可执行 jar 格式 将打包应用程序类到 BOOT-INF/classes 中。这意味着当将可执行 jar 用作依赖项时,找不到它们。

    要生成两个工件,一个可以用作依赖项,另一个可以执行,必须指定分类器。该分类器应用于可执行归档文件的名称,保留默认归档文件以用作依赖项。

    要在 Maven 中配置 exec 分类器,可以使用以下配置:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>exec</classifier>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    16.6. 运行可执行 jar 时提取特定的库

    可执行 jar 中的大多数嵌套库不需要解压即可运行。但是,某些库可能会有问题。例如,JRuby 包含自己的嵌套 jar 支持,它假定 jruby-complete.jar 总是可以直接以文件的形式直接使用。

    为了处理任何有问题的库,您可以标记在可执行 jar 首次运行时应自动解压缩特定的嵌套 jar 。这些嵌套的 jar 会写在系统属性 java.io.tmpdir 标识的临时目录下。

    应注意确保已配置您的操作系统,以便在应用程序仍在运行时,它不会删除已解压缩到临时目录中的 jar

    例如,为了指示应该使用 Maven 插件将 JRuby 标记为要解包,您可以添加以下配置:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <requiresUnpack>
                        <dependency>
                            <groupId>org.jruby</groupId>
                            <artifactId>jruby-complete</artifactId>
                        </dependency>
                    </requiresUnpack>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    16.7. 创建带有排除项的不可执行的 JAR

    通常,如果您将可执行文件和不可执行 jar 作为两个单独的构建产品,则可执行版本具有库 jar 中不需要的附加配置文件。例如,application.yml 配置文件可能被排除在不可执行的 JAR 中。

    在 Maven 中,可执行 jar 必须是主要工件,您可以为库添加分类 jar ,如下所示:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <id>lib</id>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                        <configuration>
                            <classifier>lib</classifier>
                            <excludes>
                                <exclude>application.yml</exclude>
                            </excludes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    

    16.8. 使用 Maven 远程调试 Spring Boot 应用程序

    要将远程调试器附加到使用 Maven 启动的 Spring Boot 应用程序,可以使用 maven pluginjvmArguments 属性。

    请参阅 Debug the Application

    16.9. 在不使用 spring-boot-antlib 的情况下从 Ant 构建可执行存档

    要使用 Ant 进行构建,您需要获取依赖项,进行编译,然后创建一个 jar 或 war 存档。要使其可执行,可以使用 spring-boot-antlib 模块,也可以按照以下说明进行操作:

    1. 如果要构建 jar ,请将应用程序的类和资源打包到嵌套 BOOT-INF/classes 目录中。如果要构建 war ,请照常将应用程序的类打包在嵌套 WEB-INF/classes 目录中。
    2. 在嵌套的 BOOT-INF/libjarWEB-INF/libwar 目录中添加运行时依赖项。切记不要压缩存档中的条目。
    3. provided(嵌入式容器)依赖项添加到 jar 中的嵌套目录 BOOT-INF/lib 或 war 中的 WEB-INF/lib-provided 。切记不要压缩存档中的条目。
    4. spring-boot-loader 类添加到存档的根目录(使 Main-Class 可用)。
    5. 使用适当的启动器(例如 jar 文件的 JarLauncher )作为清单中的 Main-Class 属性,并指定其所需的其他属性作为清单条目(主要是通过设置 Start-Class 属性)。

    以下示例显示了如何使用 Ant 构建可执行归档文件:

    <target name="build" depends="compile">
        <jar destfile="target/${ant.project.name}-${spring-boot.version}.jar" compress="false">
            <mappedresources>
                <fileset dir="target/classes" />
                <globmapper from="*" to="BOOT-INF/classes/*"/>
            </mappedresources>
            <mappedresources>
                <fileset dir="src/main/resources" erroronmissingdir="false"/>
                <globmapper from="*" to="BOOT-INF/classes/*"/>
            </mappedresources>
            <mappedresources>
                <fileset dir="${lib.dir}/runtime" />
                <globmapper from="*" to="BOOT-INF/lib/*"/>
            </mappedresources>
            <zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
            <manifest>
                <attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" />
                <attribute name="Start-Class" value="${start-class}" />
            </manifest>
        </jar>
    </target>
    

    17. 传统部署

    Spring Boot 支持传统部署以及更现代的部署形式。本节回答有关传统部署的常见问题。

    17.1. 创建可部署的 war 文件

    由于 Spring WebFlux 不严格依赖 Servlet API,并且默认情况下将应用程序部署在嵌入式 Reactor Netty 服务器上,因此 WebFlux 应用程序不支持 War 部署。

    生成可部署 war 文件的第一步是提供一个 SpringBootServletInitializer 子类并覆盖其 configure 方法。这样做利用了 Spring Framework 的 Servlet 3.0 支持,并允许您在由 Servlet 容器启动应用程序时对其进行配置。通常,应将应用程序的主类更新为继承 SpringBootServletInitializer ,如以下示例所示:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    @SpringBootApplication
    public class MyApplication extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(MyApplication.class);
        }
    
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        }
    
    }
    

    下一步是更新构建配置,以使您的项目生成 war 文件而不是 jar 文件。如果您使用 Maven 和 spring-boot-starter-parent(为您配置 Maven 的 war 插件),那么您要做的就是修改 pom.xml 以将包装更改为 war,如下所示:

    <packaging>war</packaging>
    

    如果使用 Gradle,则需要修改 build.gradle 以将 war 插件应用于项目,如下所示:

    apply plugin: 'war'
    

    该过程的最后一步是确保嵌入式 servlet 容器不干扰 war 文件所部署到的 servlet 容器。为此,您需要将嵌入式 Servlet 容器依赖性标记为 provided 。

    如果使用 Maven,则以下示例将 servlet 容器(在本例中为 Tomcat)标记为已提供:

    <dependencies>
        <!-- ... -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- ... -->
    </dependencies>
    

    如果使用 Gradle ,则以下示例将 servlet 容器(在本例中为 Tomcat )标记为 provided :

    dependencies {
        // ...
        providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
        // ...
    }
    

    providedRuntime 优于 Gradle 的 compileOnly 配置。除其他限制外,compileOnly 依赖项不在测试类路径上,因此任何基于 Web 的集成测试都将失败。

    如果您使用 Spring Boot 构建工具 ,则将提供的嵌入式 servlet 容器依赖关系标记为 provided 将生成可执行的 war 文件,并将提供的依赖关系打包在 lib-provided 目录中。这意味着,除了可以部署到 servlet 容器之外,还可以通过 java -jar 在命令行上使用运行应用程序。

    17.2. 将现有应用程序转换为 Spring Boot

    对于非 Web 应用程序,将现有的 Spring 应用程序转换为 Spring Boot 应用程序应该很容易。为此,请丢弃您的创建 ApplicationContext 代码,并用 SpringApplicationSpringApplicationBuilder 调用替换它。Spring MVC Web 应用程序通常可以先创建可部署的 war 应用程序,然后再将其迁移到可执行的 war 或 jar 。请参阅 将 jar 转换为 war 的入门指南

    要通过扩展 SpringBootServletInitializer(例如,在名为 Application 的类中)并添加 Spring Boot 的 @SpringBootApplication 注解来创建可部署的 war ,请使用类似于以下示例所示的代码:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    @SpringBootApplication
    public class MyApplication extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            // Customize the application or call application.sources(...) to add sources
            // Since our example is itself a @Configuration class (via @SpringBootApplication)
            // we actually do not need to override this method.
            return application;
        }
    
    
    }
    

    请记住,无论您放置什么在 sources 里,都只是 Spring ApplicationContext 。通常,任何已经工作的东西都应该在这里工作。可能有些 bean 可以在以后删除,并让 Spring Boot 为它们提供自己的默认值,但在您需要这样做之前应该可以让某些东西工作。

    可以将静态资源移到类路径根目录下 /public(或 /static/resources/META-INF/resources )。这同样适用于messages.properties(Spring Boot 自动在类路径的根目录中检测到)。

    在 Spring 和 Spring Security 中普通使用 DispatcherServlet 不需要进一步更改。如果您的应用程序具有其他功能(例如,使用其他 servlet 或过滤器),则可能需要通过从 web.xml 中替换这些元素来为上下文添加一些配置,如下所示:

    • ServletServletRegistrationBean 类型的 @Bean 将安装 Bean 在容器中,好像它是 web.xml 中的一个 <servlet/><servlet-mapping/>
    • FilterFilterRegistrationBean 类型的 @Bean ,表现得类似(作为 <filter/><filter-mapping/>
    • XML 文件中的 ApplicationContext 可以通过 @ImportResource 被添加在你的 Application 中。另外,一些简单的情况下,注解配置已被大量使用可以在几行中作为 @Bean 定义重新创建。

    war 文件运行后,可以通过向 Application 中添加一个 main 方法使其变为可执行文件,如以下示例所示:

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
    

    如果打算以 war 或可执行应用程序的形式启动应用程序,则需要在 SpringBootServletInitializer 回调可用的方法和类似于以下的类的 main 方法中共享构建器的自定义:

    import org.springframework.boot.Banner;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    @SpringBootApplication
    public class MyApplication extends SpringBootServletInitializer {
    
     @Override
     protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
         return customizerBuilder(builder);
     }
    
     public static void main(String[] args) {
         customizerBuilder(new SpringApplicationBuilder()).run(args);
     }
    
     private static SpringApplicationBuilder customizerBuilder(SpringApplicationBuilder builder) {
         return builder.sources(MyApplication.class).bannerMode(Banner.Mode.OFF);
     }
    
    }
    

    应用程序可以分为多个类别:

    • 没有 web.xml 的 Servlet 3.0+ 应用程序
    • 带有 web.xml 的应用程序
    • 具有上下文层次结构的应用程序
    • 没有上下文层次结构的应用程序

    所有这些都应该可以转换,但是每种可能都需要稍微不同的技术。

    如果 Servlet 3.0+ 应用程序已经使用了 Spring Servlet 3.0+ 初始化器支持类,那么它们可能会很容易转换。通常,来自现有 WebApplicationInitializer 的所有代码都可以移入 SpringBootServletInitializer 。如果您现有的应用程序有多个 ApplicationContext(例如,如果使用 AbstractDispatcherServletInitializer ),则可以将所有上下文源组合到一个 SpringApplication 中。您可能会遇到的主要复杂情况是,如果组合不起作用并且您需要维护上下文层次结构。有关示例,请参见 有关构建层次结构的条目 。通常,需要拆分包含特定于 Web 的功能的现有父上下文,以便所有 ServletContextAware 组件都位于子上下文中。

    还不是 Spring 应用程序的应用程序可以转换为 Spring Boot 应用程序,前面提到的指南可能会有所帮助。但是,您可能仍然遇到问题。在这种情况下,建议您使用 spring-boot 标签在 Stack Overflow 上提问。

    17.3. 将 WAR 部署到 WebLogic

    要将 Spring Boot 应用程序部署到 WebLogic ,必须确保 servlet 初始化程序 直接实现 WebApplicationInitializer(即使从已经实现了它的基类扩展)。

    WebLogic 的典型初始化程序应类似于以下示例:

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    import org.springframework.web.WebApplicationInitializer;
    
    @SpringBootApplication
    public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
    
    }
    

    如果使用 Logback ,则还需要告诉 WebLogic 首选打包版本,而不是服务器预装的版本。您可以通过添加具有以下内容的 WEB-INF/weblogic.xml 文件来做到这一点:

    <?xml version="1.0" encoding="UTF-8"?>
    <wls:weblogic-web-app
        xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
            https://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
            http://xmlns.oracle.com/weblogic/weblogic-web-app
            https://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
        <wls:container-descriptor>
            <wls:prefer-application-packages>
                <wls:package-name>org.slf4j</wls:package-name>
            </wls:prefer-application-packages>
        </wls:container-descriptor>
    </wls:weblogic-web-app>
    
  • 相关阅读:
    1553: Good subsequence (很奇妙的set模拟题,也可以直接暴力)
    1550: Simple String (做得少的思维题,两个字符串能否组成另外一个字符串问题)
    1549: Navigition Problem (几何计算+模拟 细节较多)
    1548: Design road (思维题 做法:三分找极值)
    Python 正则表达式入门(初级篇)
    Python中的正则表达式教程
    软件自动化测试的学习步骤
    自动化测试的Selenium的python版安装与使用
    自动化测试一些问题
    自动化测试
  • 原文地址:https://www.cnblogs.com/huangwenjie/p/16354188.html
Copyright © 2020-2023  润新知