Como a configuração automática de inicialização da Spring funciona

Imersão profunda no mundo da anotação Spring Boot @Conditional com exemplos bem desenvolvidos de implementações de classes de acesso a bancos de dados Mongo e MySQL .


No meu post “ Por que o Spring Boot? "A criação de um aplicativo Spring Boot foi revisada a partir da qual você mal consegue entender o que está acontecendo nos bastidores. Talvez você queira entender a mágica da auto-configuração do Spring Boot.


Antes disso, você deve aprender sobre a anotação @Conditional sobre o Spring , da qual depende toda a mágica de configuração automática do Spring Boot.


Explorando recursos @Conditional


Ao desenvolver aplicativos com base no Spring, pode ser necessário registrar condicionalmente os componentes do bean.


Por exemplo, você pode registrar um bean DataSource que aponte para o banco de dados DEV quando o aplicativo iniciar localmente e aponte para outro banco de dados PRODUCTION ao trabalhar em um ambiente de produção.


Você pode transferir as configurações de conexão do banco de dados para o arquivo de propriedades e usar um arquivo que corresponda ao ambiente, mas é necessário alterar a configuração sempre que precisar especificar um ambiente diferente e criar um aplicativo.


Spring 3.1 Profiles (). . , , .


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

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

, System Property -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.JacksonAutoConfigurationetc, etc.

Espero que agora você entenda como a configuração automática de inicialização do Spring funciona usando várias classes de configuração automática , além dos recursos @Conditional .


All Articles