MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

深入理解Java中的Spring Boot自动配置

2024-03-242.2k 阅读

一、Spring Boot自动配置的基础概念

Spring Boot的自动配置是其核心特性之一,它极大地简化了Spring应用的开发过程。在传统的Spring应用开发中,开发者需要手动配置大量的Bean,包括数据源、事务管理器、MVC相关配置等。这些配置不仅繁琐,而且容易出错。Spring Boot通过自动配置机制,根据项目的依赖情况和用户的少量配置,自动为应用创建和配置所需的Bean。

以Web应用为例,当我们在项目中引入了Spring Boot的Web Starter依赖时,Spring Boot会自动配置好Tomcat(或其他Servlet容器)、Spring MVC等相关组件,使得我们可以快速搭建一个可用的Web应用,而无需手动编写大量的XML配置或Java配置类。

1.1 自动配置的核心原理 - 条件化配置

Spring Boot自动配置的核心基于Spring的条件化配置(Conditional Configuration)。Spring 4.0引入了@Conditional注解,Spring Boot在此基础上进行了扩展。通过条件化配置,Spring容器可以根据特定的条件来决定是否创建某个Bean。

比如,@ConditionalOnClass注解表示只有当类路径下存在指定的类时,才会创建对应的Bean。假设我们有一个配置类MyServiceConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyServiceConfig {

    @Bean
    @Conditional(MyCondition.class)
    public MyService myService() {
        return new MyService();
    }
}

这里MyService Bean的创建依赖于MyCondition条件的满足。如果MyConditionmatches方法返回true,则会创建MyService Bean。

Spring Boot提供了大量预定义的条件注解,如@ConditionalOnProperty(根据配置属性判断)、@ConditionalOnWebApplication(根据是否是Web应用判断)等。这些注解是实现自动配置的重要工具。

1.2 自动配置的启动流程

当Spring Boot应用启动时,会经历一系列步骤来完成自动配置。首先,Spring Boot会加载META-INF/spring.factories文件。在这个文件中,定义了各种自动配置类。

例如,在Spring Boot的Web Starter依赖的spring-boot-starter-web模块的META-INF/spring.factories文件中,有如下配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

Spring Boot会读取这个文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration键对应的值,这些值就是自动配置类的全限定名。然后,Spring Boot会根据条件化配置的规则,对这些自动配置类进行筛选和实例化。

例如,WebMvcAutoConfiguration会在满足@ConditionalOnWebApplication(type = Type.SERVLET)条件(即应用是基于Servlet的Web应用)时被实例化,进而为应用配置Spring MVC相关的Bean,如DispatcherServletHandlerMapping等。

二、深入剖析Spring Boot自动配置类

2.1 常见自动配置类分析 - WebMvcAutoConfiguration

WebMvcAutoConfiguration是Spring Boot中与Spring MVC相关的自动配置类。它为Spring MVC应用提供了一系列默认配置。

首先,它配置了DispatcherServlet,这是Spring MVC的核心Servlet,负责接收所有的HTTP请求并将其分发给合适的处理器。

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@ConditionalOnMissingBean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
    dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    dispatcherServlet.setPublishEvents(this.webMvcProperties.isPublishRequestHandledEvents());
    return dispatcherServlet;
}

这里通过@ConditionalOnWebApplication(type = Type.SERVLET)确保只有在基于Servlet的Web应用中才会创建DispatcherServlet。同时,@ConditionalOnMissingBean注解保证了如果用户没有手动定义名为dispatcherServlet的Bean,Spring Boot才会创建它。

其次,WebMvcAutoConfiguration还配置了HandlerMapping,用于将请求映射到对应的处理器方法。

@Bean
@ConditionalOnMissingBean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
        final WebMvcRegistrations webMvcRegistrations,
        final ObjectProvider<RequestMappingHandlerAdapter> requestMappingHandlerAdapterProvider) {
    // 创建RequestMappingHandlerMapping实例并进行配置
}

这里同样使用了@ConditionalOnMissingBean,如果用户没有自定义RequestMappingHandlerMapping,Spring Boot会自动创建一个。

2.2 数据源相关自动配置类 - DataSourceAutoConfiguration

DataSourceAutoConfiguration负责自动配置数据源。在传统的Java EE应用中,配置数据源需要编写大量的XML或Java代码,而Spring Boot通过这个自动配置类大大简化了这一过程。

当项目中引入了数据库连接相关的依赖,如spring-boot-starter-jdbc和具体数据库驱动(如mysql-connector-java)时,DataSourceAutoConfiguration会根据配置文件中的数据库连接信息(如spring.datasource.urlspring.datasource.usernamespring.datasource.password)来创建数据源Bean。

@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@EnableConfigurationProperties(DataSourceProperties.class)
public static class PooledDataSourceConfiguration {

    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    @ConditionalOnClass(HikariDataSource.class)
    public DataSource dataSource(DataSourceProperties properties) {
        return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }
}

在上述代码中,@ConditionalOnClass(HikariDataSource.class)表示只有当类路径下存在HikariDataSource类时,才会创建基于HikariCP的数据源。@ConfigurationProperties("spring.datasource.hikari")则将配置文件中spring.datasource.hikari开头的属性绑定到数据源的配置上。

三、自定义Spring Boot自动配置

3.1 创建自定义自动配置类

有时候,Spring Boot提供的默认自动配置不能满足我们的需求,这时就需要自定义自动配置。假设我们有一个自定义的服务MyCustomService,我们希望Spring Boot能自动配置它。

首先,创建MyCustomService类:

public class MyCustomService {
    public void doSomething() {
        System.out.println("MyCustomService is doing something.");
    }
}

然后,创建自动配置类MyCustomServiceAutoConfiguration

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(MyCustomService.class)
@EnableConfigurationProperties(MyCustomServiceProperties.class)
public class MyCustomServiceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyCustomService myCustomService() {
        return new MyCustomService();
    }
}

这里@ConditionalOnClass(MyCustomService.class)确保只有当项目中存在MyCustomService类时,才会进行自动配置。@ConditionalOnMissingBean保证如果用户没有手动定义MyCustomService Bean,Spring Boot会自动创建一个。

3.2 配置属性绑定

我们还可以为自定义的自动配置添加配置属性。创建MyCustomServiceProperties类来定义配置属性:

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "my.custom.service")
public class MyCustomServiceProperties {
    private String customProperty;

    public String getCustomProperty() {
        return customProperty;
    }

    public void setCustomProperty(String customProperty) {
        this.customProperty = customProperty;
    }
}

MyCustomServiceAutoConfiguration中,我们可以注入MyCustomServiceProperties并使用这些属性来配置MyCustomService

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(MyCustomService.class)
@EnableConfigurationProperties(MyCustomServiceProperties.class)
public class MyCustomServiceAutoConfiguration {

    private final MyCustomServiceProperties properties;

    public MyCustomServiceAutoConfiguration(MyCustomServiceProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean
    public MyCustomService myCustomService() {
        MyCustomService service = new MyCustomService();
        // 使用配置属性进行一些配置
        service.setCustomProperty(properties.getCustomProperty());
        return service;
    }
}

最后,在application.properties文件中添加配置:

my.custom.service.customProperty=SomeValue

这样,我们就完成了一个简单的自定义Spring Boot自动配置,既可以根据项目情况自动创建Bean,又能通过配置文件灵活调整Bean的属性。

四、自动配置与Spring Boot Starter

4.1 Starter的作用与原理

Spring Boot Starter是一组方便的依赖描述符,它将相关的依赖聚合在一起,使得开发者可以通过引入一个Starter依赖,快速添加一组功能到项目中。例如,spring-boot-starter-web包含了Spring MVC、Tomcat等依赖,引入这个Starter就可以快速搭建一个Web应用。

Starter的原理与自动配置紧密相关。每个Starter通常会包含对应的自动配置类。当我们引入一个Starter时,相关的依赖被添加到项目中,同时Spring Boot会根据这些依赖和条件化配置规则,自动加载对应的自动配置类。

spring-boot-starter-data-jpa为例,它引入了Spring Data JPA、Hibernate等依赖。在其自动配置类HibernateJpaAutoConfiguration中,会根据项目的配置和依赖情况,自动配置数据源、EntityManagerFactory、事务管理器等与JPA相关的Bean。

4.2 创建自定义Starter

我们也可以创建自己的Starter。假设我们有一个自定义的模块my-custom-module,我们想为它创建一个Starter。

首先,创建my-custom-starter项目,在pom.xml中添加对my-custom-module的依赖:

<dependencies>
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>my-custom-module</artifactId>
        <version>1.0.0</version>
    </dependency>
</dependencies>

然后,在my-custom-starter项目中创建自动配置类,就像前面自定义自动配置时那样。假设我们的自动配置类是MyCustomStarterAutoConfiguration

接下来,在my-custom-starter项目的src/main/resources/META-INF/spring.factories文件中添加:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyCustomStarterAutoConfiguration

这样,当其他项目引入my-custom-starter依赖时,Spring Boot会自动加载MyCustomStarterAutoConfiguration,从而实现对my-custom-module的自动配置。

五、自动配置的调试与问题排查

5.1 开启自动配置日志

在开发过程中,有时需要了解Spring Boot自动配置的详细过程,以便排查问题。我们可以通过在application.properties文件中添加以下配置来开启自动配置日志:

debug=true

开启调试模式后,Spring Boot会在启动过程中输出详细的自动配置日志,包括哪些自动配置类被加载、哪些条件不满足导致某些配置未生效等信息。例如,我们可能会看到类似如下的日志:

o.s.b.a.w.s.WebMvcAutoConfiguration     : WebMvcAutoConfiguration matched:
       - @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
       - @ConditionalOnClass found required classes 'org.springframework.web.servlet.DispatcherServlet', 'org.springframework.web.servlet.config.annotation.WebMvcConfigurer' (OnClassCondition)
       - @ConditionalOnMissingBean (types: org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; SearchStrategy: all) did not find any beans (OnBeanCondition)

这表明WebMvcAutoConfiguration满足了相关条件,被成功加载。

5.2 排查自动配置问题

如果自动配置没有按照预期工作,首先查看自动配置日志,确定哪些条件未满足。例如,如果某个Bean没有被自动创建,可能是对应的条件注解不满足。

假设我们自定义了一个自动配置类MyAutoConfiguration,但是相关的Bean没有被创建。通过查看日志发现@ConditionalOnClass注解指定的类在类路径下不存在,那么我们需要确保相关的依赖已经正确引入。

另外,配置属性的错误也可能导致自动配置失败。比如在数据源自动配置中,如果spring.datasource.url配置错误,数据源将无法正确创建。这时需要仔细检查配置文件中的属性是否正确。

在排查问题时,还可以使用断点调试。在自动配置类的关键方法上设置断点,如@Bean方法,查看在创建Bean过程中的变量值和执行流程,以确定问题所在。

通过以上方法,我们可以有效地调试和排查Spring Boot自动配置过程中出现的各种问题,确保应用能够按照预期进行配置和运行。