Spring Boot自动配置如何工作

通过完善的MongoMySQL数据库访问类实现示例深入了解Spring Boot @Conditional注释世界


在我的文章“ 为什么使用Spring Boot?“对Spring Boot应用程序的创建进行了审查,您几乎无法从中了解幕后情况。也许您想了解Spring Boot自动配置的魔力。


在此之前,您应该了解有关Spring的@Conditional批注,所有Spring Boot自动配置魔术都依赖于此


探索@Conditional功能


在基于Spring开发应用程序时,您可能需要有条件地注册bean组件。


例如,您可以注册一个DataSource bean,该bean在应用程序本地启动时指向DEV数据库,而在生产环境中工作时则指向另一个PRODUCTION数据库。


您可以将数据库连接设置转移到属性文件,并使用与环境匹配的文件,但是每当需要指定其他环境并创建应用程序时,都需要更改配置。


为了解决这个问题,Spring 3.1引入了Profiles(配置文件)的概念您可以注册多个相同类型的bean,并将它们与一个或多个概要文件关联。启动应用程序时,您可以激活必要的配置文件以及与已激活的配置文件相关联的组件,并且只会注册这些配置文件。


@Configuration
public class AppConfig
{
     @Bean
     @Profile("DEV")
     public DataSource devDataSource() {
        ...
     }

     @Bean
     @Profile("PROD")
     public DataSource prodDataSource() {
        ...
     }
}

然后,您可以使用系统属性-Dspring.profiles.active = DEV指定活动配置文件


该方法适用于简单情况,例如基于激活的配置文件启用或禁用组件注册。但是,如果您想基于某些条件逻辑来注册bean组件,那么概要方法本身是不够的。


bean- Spring, Spring 4 @Conditional. @Conditional, , .


, , :


  • classpath
  • Spring bean ApplicationContext
  • /

, , .


, Spring's @Conditional.


, UserDAO . UserDAO, JdbcUserDAO, MySQL, MongoUserDAO, MongoDB.


JdbcUserDAO MongoUserDAO System Property, , dbType.


: java -jar myapp.jar -DdbType = MySQL, JdbcUserDAO. , : java -jar myapp.jar -DdbType = MONGO, MongoUserDAO.


JdbcUserDAO MongoUserDAO UserDAO :


public interface UserDAO
{
    List<String> getAllUserNames();
}
public class JdbcUserDAO implements UserDAO
{
    @Override
    public List<String> getAllUserNames()
    {
          System.out.println("**** Getting usernames from RDBMS *****");
          return Arrays.asList("Siva","Prasad","Reddy");
    }
}
public class MongoUserDAO implements UserDAO
{
    @Override
    public List<String> getAllUserNames()
    {
          System.out.println("**** Getting usernames from MongoDB *****");
          return Arrays.asList("Bond","James","Bond");
    }
}

Condition MySQLDatabaseTypeCondition, , System Property dbType «MYSQL» :


public class MySQLDatabaseTypeCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata)
    {
        String enabledDBType = System.getProperty("dbType");
        return (enabledDBType != null && enabledDBType.equalsIgnoreCase("MYSQL"));
    }
}

Condition MongoDBDatabaseTypeCondition, , System Property dbType «MONGODB» :


public class MongoDBDatabaseTypeCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata)
    {
        String enabledDBType = System.getProperty("dbType");
        return (enabledDBType != null && enabledDBType.equalsIgnoreCase("MONGODB"));
    }
}

bean- JdbcUserDAO MongoUserDAO, @Conditional :


@Configuration
public class AppConfig
{
    @Bean
    @Conditional(MySQLDatabaseTypeCondition.class)
    public UserDAO jdbcUserDAO(){
        return new JdbcUserDAO();
    }

    @Bean
    @Conditional(MongoDBDatabaseTypeCondition.class)
    public UserDAO mongoUserDAO(){
        return new MongoUserDAO();
    }
}

: java -jar myapp.jar -DdbType = MYSQL, bean- JdbcUserDAO.


System property: -DdbType = MONGODB, bean- MongoUserDAO.


, System Property.


, bean- MongoUserDAO , classpath Java MongoDB "com.mongodb.Server", , bean- JdbcUserDAO.


Condition MongoDB "com.mongodb.Server" :


public class MongoDriverPresentsCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata)
    {
        try {
            Class.forName("com.mongodb.Server");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

public class MongoDriverNotPresentsCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata)
    {
        try {
            Class.forName("com.mongodb.Server");
            return false;
        } catch (ClassNotFoundException e) {
            return true;
        }
    }
}

, bean- classpath.


bean- MongoUserDAO, Spring- UserDAO .


Condition, , - bean- , :


public class UserDAOBeanNotPresentsCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata)
    {
         UserDAO userDAO = conditionContext.getBeanFactory().getBean(UserDAO.class);
         return (userDAO == null);
    }
}

bean- MongoUserDAO, app.dbType = MONGO?


Condition :


public class MongoDbTypePropertyCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext,
    AnnotatedTypeMetadata metadata)
    {
        String dbType = conditionContext.getEnvironment()
                            .getProperty("app.dbType");
        return "MONGO".equalsIgnoreCase(dbType);
    }
}

Condition. Condition . Condition MYSQL MongoDB DatabaseType :


@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(DatabaseTypeCondition.class)
public @interface DatabaseType
{
    String value();
}

DatabaseTypeCondition, DatabaseType :


public class DatabaseTypeCondition implements Condition
{
    @Override
    public boolean matches(ConditionContext conditionContext,
 AnnotatedTypeMetadata metadata)
    {
        Map<String, Object> attributes = metadata.getAnnotationAttributes(DatabaseType.class.getName());
        String type = (String) attributes.get("value");
        String enabledDBType = System.getProperty("dbType","MYSQL");
        return (enabledDBType != null && type != null && enabledDBType.equalsIgnoreCase(type));
    }
}

@DatabaseType :


@Configuration
@ComponentScan
public class AppConfig
{
    @Bean
    @DatabaseType("MYSQL")
    public UserDAO jdbcUserDAO(){
        return new JdbcUserDAO();
    }

    @Bean
    @DatabaseType("MONGO")
    public UserDAO mongoUserDAO(){
        return new MongoUserDAO();
    }
}

DatabaseType dbType , , .


, , , @Conditional.


Spring Boot @Conditional bean- .


Condition, SpringBoot org.springframework.boot.autoconfigure spring-boot-autoconfigure-{version}.jar.


Spring Boot @Conditional , .


?


, .


Spring Boot AutoConfiguration


Spring Boot — @EnableAutoConfiguration. , , @SpringBootApplication, , , :


@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application
{

}

@EnableAutoConfiguration Spring ApplicationContext , .


Spring Boot AutoConfiguration spring-boot-autoconfigure-{version}.jar, .


AutoConfiguration @Configuration, Spring, @EnableConfigurationProperties .


, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.


@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration 
{
    ...
    ...
    @Conditional(DataSourceAutoConfiguration.EmbeddedDataSourceCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedConfiguration {

    }

    @Configuration
    @ConditionalOnMissingBean(DataSourceInitializer.class)
    protected static class DataSourceInitializerConfiguration {
        @Bean
        public DataSourceInitializer dataSourceInitializer() {
        return new DataSourceInitializer();
        }
    }

    @Conditional(DataSourceAutoConfiguration.NonEmbeddedDataSourceCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    protected static class NonEmbeddedConfiguration {
        @Autowired
        private DataSourceProperties properties;

        @Bean
        @ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
        public DataSource dataSource() {
            DataSourceBuilder factory = DataSourceBuilder
                    .create(this.properties.getClassLoader())
                    .driverClassName(this.properties.getDriverClassName())
                    .url(this.properties.getUrl()).username(this.properties.getUsername())
                    .password(this.properties.getPassword());
            if (this.properties.getType() != null) {
                factory.type(this.properties.getType());
            }
            return factory.build();
        }
    }
    ...
    ...
    @Configuration
    @ConditionalOnProperty(prefix = "spring.datasource", name = "jmx-enabled")
    @ConditionalOnClass(name = "org.apache.tomcat.jdbc.pool.DataSourceProxy")
    @Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class)
    @ConditionalOnMissingBean(name = "dataSourceMBean")
    protected static class TomcatDataSourceJmxConfiguration {
        @Bean
        public Object dataSourceMBean(DataSource dataSource) {
        ....
        ....
        }
    }
    ...
    ...
}

DataSourceAutoConfiguration @ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class}), , bean- DataSourceAutoConfiguration , DataSource.class EmbeddedDatabaseType.class classpath.


@EnableConfigurationProperties(DataSourceProperties.class), application.properties DataSourceProperties.


@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
public class DataSourceProperties implements BeanClassLoaderAware, EnvironmentAware, InitializingBean {

    public static final String PREFIX = "spring.datasource";
    ...
    ...
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    ...
    //setters and getters
}

, spring.datasource.*, DataSourceProperties.


spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

bean-, SpringBoot, @ConditionalOnMissingBean, @ConditionalOnClass @ConditionalOnProperty ..


bean- ApplicationContext, .


spring-boot-autoconfigure-{version}.jar, :


  • org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration
  • org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
  • org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration
  • org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration等,等等。

我希望您现在了解使用各种自动配置类以及@Conditional功能如何使用Spring Boot自动配置


All Articles