javascript
SpringBoot AutoConfiguration魔术如何工作?
在我以前的文章中, 為什么選擇SpringBoot? 我們已經研究了如何創建SpringBoot應用程序。 但是您可能會也可能不會了解幕后發生的事情。 您可能想了解SpringBoot自動配置背后的魔力。
但是在此之前,您應該了解Spring的@Conditional功能,所有SpringBoot的AutoConfiguration魔術賴以其基礎。
探索@Conditional的力量
在開發基于Spring的應用程序時,我們可能會遇到有條件地注冊bean的需求。
例如,您可能想在本地運行應用程序時注冊一個指向DEV數據庫的DataSource bean,而在生產環境中運行時則指向一個不同的PRODUCTION數據庫。
您可以將數據庫連接參數外部化為屬性文件,并使用適合于環境的文件。 但是,每當需要指向其他環境并構建應用程序時,都需要更改配置。
為了解決這個問題,Spring 3.1引入了Profiles的概念。 您可以注冊多個相同類型的bean,并將它們與一個或多個概要文件關聯。 當您運行該應用程序時,您可以激活所需的概要文件,并且僅與激活的概要文件關聯的bean將被注冊。
@Configuration public class AppConfig {@Bean@Profile("DEV")public DataSource devDataSource() {...}@Bean@Profile("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 {List<String> getAllUserNames(); }public class JdbcUserDAO implements UserDAO {@Overridepublic List<String> getAllUserNames(){System.out.println("**** Getting usernames from RDBMS *****");return Arrays.asList("Siva","Prasad","Reddy");} }public class MongoUserDAO implements UserDAO {@Overridepublic List<String> 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 {@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那樣運行應用程序,則僅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){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));} }現在,我們可以在bean定義上使用@DatabaseType批注,如下所示:
@Configuration @ComponentScan public class AppConfig {@DatabaseType("MYSQL")public UserDAO jdbcUserDAO(){return new JdbcUserDAO();}@Bean@DatabaseType("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 {}@Configuration@ConditionalOnMissingBean(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;@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 {@Beanpublic Object dataSourceMBean(DataSource dataSource) {........}}...... }此處, DataSourceAutoConfiguration帶有@ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class})注釋,這意味著僅當在類路徑上有DataSource.class和EmbeddedDatabaseType.class類可用時,才會考慮在DataSourceAutoConfiguration中對bean進行自動配置。
該類還帶有@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定義方法,這些內部類和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
總結
以上是生活随笔為你收集整理的SpringBoot AutoConfiguration魔术如何工作?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dns服务器的配置与管理
- 下一篇: 小娟张鹤伦歌词 关于张鹤伦演唱的小娟歌词