网站备案 拍照网点,重庆新华网,做网站写代码好还是模板,正规的大宗商品交易平台在我以前的文章中#xff0c; 为什么选择SpringBoot#xff1f; 我们已经研究了如何创建SpringBoot应用程序。 但是您可能会也可能不会了解幕后发生的事情。 您可能想了解SpringBoot自动配置背后的魔力。 但是在此之前#xff0c;您应该了解Spring的Conditional功能#x… 在我以前的文章中 为什么选择SpringBoot 我们已经研究了如何创建SpringBoot应用程序。 但是您可能会也可能不会了解幕后发生的事情。 您可能想了解SpringBoot自动配置背后的魔力。 但是在此之前您应该了解Spring的Conditional功能所有SpringBoot的AutoConfiguration魔术赖以其基础。 探索Conditional的力量 在开发基于Spring的应用程序时我们可能会遇到有条件地注册bean的需求。 例如您可能想在本地运行应用程序时注册一个指向DEV数据库的DataSource bean而在生产环境中运行时则指向一个不同的PRODUCTION数据库。 您可以将数据库连接参数外部化为属性文件并使用适合于环境的文件。 但是每当需要指向其他环境并构建应用程序时都需要更改配置。 为了解决这个问题Spring 3.1引入了Profiles的概念。 您可以注册多个相同类型的bean并将它们与一个或多个概要文件关联。 当您运行该应用程序时您可以激活所需的概要文件并且仅与激活的概要文件关联的bean将被注册。 Configuration
public class AppConfig
{BeanProfile(DEV)public DataSource devDataSource() {...}BeanProfile(PROD)public DataSource prodDataSource() {...}
} 然后您可以使用系统属性-Dspring.profiles.active DEV指定活动配置文件 这种方法适用于简单的情况例如基于激活的配置文件启用或禁用bean注册。 但是如果您要基于某些条件逻辑来注册bean那么Profiles方法本身是不够的。 为了为有条件地注册Spring Bean提供更大的灵活性Spring 4引入了Conditional的概念。 通过使用Conditional方法您可以根据任意条件有条件地注册bean。 例如在以下情况下您可能要注册一个bean 一个特定的类存在于类路径中 某种类型的Spring bean尚未在ApplicationContext中注册 特定文件存在于某个位置 在配置文件中配置特定的属性值 存在/不存在特定的系统属性 这些仅是几个示例您可以具有所需的任何条件。 让我们看一下Spring的Conditional的工作原理。 假设我们有一个UserDAO接口其中包含从数据存储中获取数据的方法。 我们UserDAO的接口即JdbcUserDAO两种工具进行对话的MySQL数据库和MongoUserDAO进行对话的MongoDB的 。 我们可能只想基于系统属性dbType启用JdbcUserDAO和MongoUserDAO之一。 如果使用java -jar myapp.jar -DdbType MySQL启动应用程序则我们要启用JdbcUserDAO 否则如果使用java -jar myapp.jar -DdbType MONGO启动应用程序 则要启用MongoUserDAO 。 假设我们有UserDAO接口和JdbcUserDAO MongoUserDAO实现如下 public interface UserDAO
{ListString getAllUserNames();
}public class JdbcUserDAO implements UserDAO
{Overridepublic ListString getAllUserNames(){System.out.println(**** Getting usernames from RDBMS *****);return Arrays.asList(Siva,Prasad,Reddy);}
}public class MongoUserDAO implements UserDAO
{Overridepublic ListString getAllUserNames(){System.out.println(**** Getting usernames from MongoDB *****);return Arrays.asList(Bond,James,Bond);}
} 我们可以实现条件MySQLDatabaseTypeCondition来检查系统属性dbType是否为“ MYSQL” 如下所示 public class MySQLDatabaseTypeCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){String enabledDBType System.getProperty(dbType);return (enabledDBType ! null enabledDBType.equalsIgnoreCase(MYSQL));}
} 我们可以实现条件MongoDBDatabaseTypeCondition来检查系统属性dbType是否为“ MONGODB ”如下所示 public class MongoDBDatabaseTypeCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){String enabledDBType System.getProperty(dbType);return (enabledDBType ! null enabledDBType.equalsIgnoreCase(MONGODB));}
} 现在我们可以使用Conditional有条件地配置JdbcUserDAO和MongoUserDAO Bean如下所示 Configuration
public class AppConfig
{BeanConditional(MySQLDatabaseTypeCondition.class)public UserDAO jdbcUserDAO(){return new JdbcUserDAO();}BeanConditional(MongoDBDatabaseTypeCondition.class)public UserDAO mongoUserDAO(){return new MongoUserDAO();}
} 如果我们像java -jar myapp.jar -DdbType MYSQL那样运行应用程序则仅JdbcUserDAO bean将被注册。 但是如果您-DdbType MONGODB则仅MongoUserDAO Bean将被注册。 现在我们已经看到了如何基于系统属性有条件地注册bean。 假设我们希望注册MongoUserDAO豆只有当MongoDB的 Java驱动程序类“com.mongodb.Server”可在类路径中如果不是我们想注册JdbcUserDAO豆。 为此我们可以创建条件来检查MongoDB驱动程序类“ com.mongodb.Server”的存在与否如下所示 public class MongoDriverPresentsCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata){try {Class.forName(com.mongodb.Server);return true;} catch (ClassNotFoundException e) {return false;}}
}public class MongoDriverNotPresentsCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){try {Class.forName(com.mongodb.Server);return false;} catch (ClassNotFoundException e) {return true;}}
} 我们刚刚看到了如何根据类路径中是否存在类来有条件地注册bean。 如果仅在尚未注册其他类型为UserDAO的 Spring Bean的情况下才想注册MongoUserDAO Bean该怎么办 我们可以创建一个条件来检查是否存在某种特定类型的现有bean如下所示 public class UserDAOBeanNotPresentsCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){UserDAO userDAO conditionContext.getBeanFactory().getBean(UserDAO.class);return (userDAO null);}
} 如果仅在属性占位符配置文件中设置属性app.dbType MONGO时才想注册MongoUserDAO bean该怎么办 我们可以如下实现该条件 public class MongoDbTypePropertyCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata){String dbType conditionContext.getEnvironment().getProperty(app.dbType);return MONGO.equalsIgnoreCase(dbType);}
} 我们刚刚看到了如何实现各种类型的条件。 但是还有使用注释来实现条件的更优雅的方法。 除了为MYSQL和MongoDB创建Condition实现之外我们还可以创建一个DatabaseType注释如下所示 Target({ ElementType.TYPE, ElementType.METHOD })
Retention(RetentionPolicy.RUNTIME)
Conditional(DatabaseTypeCondition.class)
public interface DatabaseType
{String value();
} 然后我们可以实现DatabaseTypeCondition以使用DatabaseType值来确定是启用还是禁用bean注册如下所示 public class DatabaseTypeCondition implements Condition
{Overridepublic boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata){MapString, 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));}
} 现在我们可以在bean定义上使用DatabaseType批注如下所示 Configuration
ComponentScan
public class AppConfig
{DatabaseType(MYSQL)public UserDAO jdbcUserDAO(){return new JdbcUserDAO();}BeanDatabaseType(MONGO)public UserDAO mongoUserDAO(){return new MongoUserDAO();}
} 在这里我们从DatabaseType批注中获取元数据并对照System Property dbType值进行检查以确定是启用还是禁用Bean注册。 我们已经看到了很多示例以了解如何使用Conditional批注有条件地注册bean。 SpringBoot广泛使用Conditional功能根据各种条件有条件地注册bean。 您可以在spring-boot-autoconfigure- {version} .jar的 org.springframework.boot.autoconfigure包中找到SpringBoot使用的各种Condition实现。 现在我们了解了SpringBoot如何使用Conditional功能有条件地检查是否注册Bean。 但是究竟是什么触发了自动配置机制呢 这就是我们将在下一部分中讨论的内容。 SpringBoot自动配置 SpringBoot自动配置魔术的关键是EnableAutoConfiguration批注。 通常我们使用SpringBootApplication注释应用程序入口点类或者如果要自定义默认值则可以使用以下注释 Configuration
EnableAutoConfiguration
ComponentScan
public class Application
{} EnableAutoConfiguration批注通过扫描类路径组件并注册与各种条件匹配的bean来启用Spring ApplicationContext的自动配置。 SpringBoot在spring-boot-autoconfigure- {version} .jar中提供了各种AutoConfiguration类这些类负责注册各种组件。 通常 AutoConfiguration类使用Configuration注释以将其标记为Spring配置类并使用EnableConfigurationProperties注释以绑定定制属性和一个或多个条件Bean注册方法。 例如考虑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 {}ConfigurationConditionalOnMissingBean(DataSourceInitializer.class)protected static class DataSourceInitializerConfiguration {Beanpublic DataSourceInitializer dataSourceInitializer() {return new DataSourceInitializer();}}Conditional(DataSourceAutoConfiguration.NonEmbeddedDataSourceCondition.class)ConditionalOnMissingBean({ DataSource.class, XADataSource.class })protected static class NonEmbeddedConfiguration {Autowiredprivate DataSourceProperties properties;BeanConfigurationProperties(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();}}......ConfigurationConditionalOnProperty(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 {Beanpublic Object dataSourceMBean(DataSource dataSource) {........}}......
} 此处 DataSourceAutoConfiguration带有ConditionalOnClass{DataSource.classEmbeddedDatabaseType.class}注释这意味着仅当在类路径上有DataSource.class和EmbeddedDatabaseType.class类可用时才会考虑在DataSourceAutoConfiguration中对bean进行自动配置。 该类还带有EnableConfigurationPropertiesDataSourceProperties.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.urljdbc:mysql://localhost:3306/test
spring.datasource.usernameroot
spring.datasource.passwordsecret
spring.datasource.driver-class-namecom.mysql.jdbc.Driver 您还可以看到一些内部类和bean定义方法这些内部类和bean定义方法用SpringBoot的条件注释例如 ConditionalOnMissingBean ConditionalOnClass和ConditionalOnProperty等进行注释。 仅当这些条件匹配时这些Bean定义才会在ApplicationContext中注册。 您还可以在spring-boot-autoconfigure- {version} .jar中探索许多其他AutoConfiguration类例如 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等。 我希望您现在通过使用各种AutoConfiration类以及Conditional功能来了解SpringBoot自动配置的工作方式。 翻译自: https://www.javacodegeeks.com/2016/03/springboot-autoconfiguration-magic-works.html