Spring Boot 自动配置(auto configure)的原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/CmdSmith/article/details/86741705

在向应用程序加入Spring Boot时,有个名为spring-boot-autoconfigure的JAR文件,其中包含了很多配置类。

它们利用了Spring 的条件化配置,这是Spring 4.0引入的新特征。条件化配置允许配置存在应用程序中,但在满足某些条件之前都忽略这个配置

在Spring里可以很方便地编写你自己的条件,所要做的就是实现Condition接口,覆盖它的matches()方法。

举例来说,下面这个简单的条件类只有在Classpath里存在JdbcTemplate时才会生效:

package readinglist;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class JdbcTemplateCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        try {
            context.getClassLoader().loadClass("org.springframework.jdbc.core.JdbcTemplate");
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

当你用Java来声明Bean的时候,可以使用这个自定义条件类:

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

只有当JdbcTemplateCondition 类的条件成立时才会创建MyService这个Bean。也就是说MyService Bean创建的条件是Classpath里有JdbcTemplate。否则这个Bean的声明就会被忽略掉。

Spring Boot定义了很多更有趣的条件,并把它们运用到了配置类上,这些配置类构成了Spring Boot的自动配置。Spring Boot运用条件化配置的方法是,定义多个特殊的条件化注解,并将它们用到配置类上。

表 2-1 自动配置中使用的条件化注解

条件化注解 配置生效条件
@ConditionalOnBean 配置了某个特定的Bean
@ConditionalOnMissingBean 没有配置某个特定的Bean
@ConditionalOnClass Classpath 里有指定的类
@ConditionalOnMissingClass Classpathh 里缺少指定的类
@ConditionalOnExpression 给定的Spring Expression Language(SpEL)表达式结果为true
@ConditionalOnJava Java的版本匹配特定值或者一个范围值
@ConditionalOnJndi 参数中给定的JNDI位置必须存在一个,如果没有参数,则要有JNDIInitialContext
@ConditionalOnProperty 指定的配置属性要有一个明确的值
@ConditionalOnResource Classpath 里有指定的资源
@ConditionalOnWebApplication 这是一个Web应用程序
@ConditionalOnNotWebApplication 这不是一个Web应用程序

DataSourceAutoConfiguration的片段(Spring Boot自动配置库的一部分):

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
        DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
    ...
}

DataSourceAutoConfiguration 添加了@Configuration 注解。

最重要的是,DataSourceAutoConfiguration 添加了@ConditionalOnClass 注解,要求Classpath里必须要有DataSource和EmbeddedDataBaseType。如果它们不存在,条件就不成立,DataSourceAutoConfiguration提供的配置都会被忽略掉。

DataSourceAutoConfiguration 里嵌入了一个JdbcTemplateConfiguration 类,自动配置了一个JdbcTemplate Bean :

@Configuration
@Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class)
protected static class JdbcTemplateConfiguration {
    @Autowired(required = false)
    private DataSource dataSource;

    @Bean
    @ConditionalOnMissingBean(JdbcOperations.class)
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(this.dataSource);
    }

    ...
}

自动配置会做出一下配置决策

  • 因为Classpath里有H2,所以会创建一个嵌入式的H2数据库Bean,它的类型是javax.sql.DataSource,JPA实现(Hibernate)需要它来访问数据库。
  • 因为Classpath里有Hibernate(Spring Data Jpa 传递引入的)的实体管理器,所以会自动配置与hibernate相关的Bean,包括Spring的LocalContainerEntityManagerFactoryBean 和 JpaVendorAdapter。
  • 因为Classpath里有Spring Data JPA,所以它会自动配置为根据仓库的接口创建仓库实现。
  • 因为Classpath里有Thymeleaf,所以Thymeleaf会配置为Spring MVC的视图,包括一个Thymeleaf的模板解析器、模板引擎及视图解析器。视图解析器会解析相对于Classpath根目录的/templates目录里的模板。
  • 因为这是一个Spring MVC Web应用程序,所以会注册一个资源管理器,把相对于Classpath根目录的/static 目录里的静态内容提供出来(这个资源处理器还能处理/public、/resources和/META-INF/resources的静态内容。)
  • 因为Classpath 里有Tomcat(通过Web起步依赖传递引用),所以会启动一个嵌入式的Tomcat容器,监听8080端口。

由此可见,Spring Boot自动配置承担起了配置Spring 的重任,因此你能专注于编写自己的应用程序。

覆盖自动配置很简单,就当自动配置不存在,直接显式地写一段配置

想要覆盖Spring Boot的自动配置,所要做的仅仅是编写一个显式的配置,Spring会自动发现你的配置,随后降低自动配置的优先级,以你的配置为准

大部分情况下 @ConditionalOnMissingBean 注解是覆盖自动配置的关键

@Bean
@ConditionalOnMissingBean(JdbcOperation.class)
public JdbcTemplate jdbcTemplate() {
    return new JdbcTemplate(this.dataSource);
}

在需要时可以配置出一个JdbcTemplate Bean,但是上面还有@ConditionalOnMissingBean 注解,要求当前不存在JdbcOperations 类型(JdbcTemplate实现了该接口)的Bean才生效。如果当前已经有了一个JdbcOperations Bean了,条件即不满足,不会执行jdbcTemplate()方法。

Spring Boot的设计是加载应用级配置,随后再考虑自动配置类。因此,如果你已经配置了一个JdbcTemplate Bean,那么在执行自动配置时就已经存在一个JdbcTemplate类型的Bean了,于是忽略自动配置的JdbcTemplate Bean。

Spring Security,自动配置会考虑几个配置类。

@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({EnableWebSecurity.class})
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
@ConditionalOnWebApplication
public class SpringBootWebSecurityConfiguration {
    ...
}

SpringBootWebSecurityConfiguration 上有好几个注解。

@ConditionalOnClass 说明Classpath里必须要有@EnableWebSecurity注解。

@ConditionalOnWebApplication 说明这必须是个Web应用程序

@ConditionalOnMissingBean 才是我们的安全配置类代替SpringBootSecurityConfiguration的关键所在

Spring Boot 的自动配置和 @ConditionalOnMissingBean 能让你显式地覆盖自动配置的Bean。

猜你喜欢

转载自blog.csdn.net/CmdSmith/article/details/86741705