通过完善的Mongo和MySQL数据库访问类实现示例深入了解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;
...
}
, spring.datasource.*, DataSourceProperties.
spring.datasource.url=jdbc:mysql:
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自动配置。