SpringBoot作為一款全新的框架,有着諸多的優點,正受到越來越多的Java開發者的歡迎。
SpringBoot極大地簡化了Spring應用的初始創建和開發過程。其優點如下:
1、內嵌Tomcat,無需部署WAR文件;
2、創建獨立的Spring應用;
3、簡化Maven配置;
4、自動配置Spring;
5、提供生產就緒型功能,如指標,健康檢查和外部配置;
6、絕對沒有代碼生成並且對XML也沒有配置要求。
下面就來深入源碼分析下SpringBoot自動配置的原理(此文針對的SpringBoot版本為1.5.17.RELEASE)。
首先從主類SpringbootApplication入手:
@SpringBootApplication public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } }
類上的@SpringBootApplication注解定義如下:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication {
其中的@EnableAutoConfiguration注解是關鍵所在,其定義如下:
@SuppressWarnings("deprecation") @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
@Import注解可以讓普通的類導入到Spring的IOC容器中,由Spring進行管理。
EnableAutoConfigurationImportSelector類定義如下:
@Deprecated public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
由於該類已被標記為過時,我們來看下它的父類AutoConfigurationImportSelector:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
AutoConfigurationImportSelector類實現了多個接口,作為一個選擇器實現了對自動配置類的選擇導入功能,其類圖關系如下:
AutoConfigurationImportSelector類中有一個重要的selectImports方法,其定義如下:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } try { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);//1.1 AnnotationAttributes attributes = getAttributes(annotationMetadata);//1.2 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//1.3 configurations = removeDuplicates(configurations);//1.4 configurations = sort(configurations, autoConfigurationMetadata);//1.5 Set<String> exclusions = getExclusions(annotationMetadata, attributes);//1.6 checkExcludedClasses(configurations, exclusions);//1.7 configurations.removeAll(exclusions);//1.8 configurations = filter(configurations, autoConfigurationMetadata);//1.9 fireAutoConfigurationImportEvents(configurations, exclusions);//2.0 return configurations.toArray(new String[configurations.size()]);//2.1 } catch (IOException ex) { throw new IllegalStateException(ex); } }
1.1 加載元數據並返回AutoConfigurationMetadata對象;
1.2 獲取傳入的元數據對應的屬性值(AnnotationAttributes對象);
1.3 返回候選自動配置類名的list集合;
1.4 移除list中的重復自動配置類名;
1.5 通過讀取@AutoConfigureOrder、@link AutoConfigureBefore、@AutoConfigureAfter注解對自動配置類名進行優先級排序;
1.6 返回排除掉的自動配置類的set集合;
1.7 檢查候選配置類中是否存在排除項,然后返回給SpringFactoriesLoader加載的候選自動配置類;
1.8 移除候選配置類當中的的所有排除項(不需要自動配置的類);
1.9 利用在spring工廠注冊過的AutoConfigurationImportFilter過濾器對候選自動配置類進行過濾;
2.0 通過AutoConfigurationImportListener監聽器觸發自動配置導入事件;
2.1 返回經過上述步驟得到的自動配置類的數組。
該方法最終返回了能進行自動配置的類的數組。
我們着重看下紅色標注的getCandidateConfigurations方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
其中的getSpringFactoriesLoaderFactoryClass()方法正是返回EnableAutoConfiguration.class。
該方法里面調用了SpringFactoriesLoader.loadFactoryNames()方法掃描加載引入的jar包中包含META-INF/spring.factories文件中的自動配置類。
具體方法如下:
/** * The location to look for factories. * <p>Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
藍色加粗的程序表示會將掃描到的文件內容包裝成properties對象。
紅色標注的文件spring.factories可以在spring-boot-autoconfigure等springboot相關的jar包中找到:
該文件里面內容都是鍵值對形式的,其中spring-boot-autoconfigure這個jar中的org.springframework.boot.autoconfigure.EnableAutoConfiguration正是我們要找的自動配置的類,包括Aop、WebMvc、Jpa、redis、jdbc等自動配置類。
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration,\ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
當然這些自動配置類並不是都能自動配置進來的,里面會根據@Conditional等注解控制bean的創建行為。
只有滿足了條件才能自動注入相關的bean。
以RabbitAutoConfiguration為例,其定義如下:
@Configuration @ConditionalOnClass({ RabbitTemplate.class, Channel.class }) @EnableConfigurationProperties(RabbitProperties.class) @Import(RabbitAnnotationDrivenConfiguration.class) public class RabbitAutoConfiguration {
// Only available in rabbitmq-java-client 5.4.0 +
private static final boolean CAN_ENABLE_HOSTNAME_VERIFICATION = ReflectionUtils
.findMethod(com.rabbitmq.client.ConnectionFactory.class,
"enableHostnameVerification") != null;
@Bean
public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties config)
throws Exception {
RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
if (config.determineHost() != null) {
factory.setHost(config.determineHost());
.
.
.
}
@ConditionalOnClass表示有某個類的情況下才會加載bean,這里需要同時有RabbitTemplate和Channel類,當所有條件滿足的情況下,就能通過@Bean注解,讓方法產生一個bean對象,交由Spring容器來進行管理,這樣就能在SpringBoot項目啟動的時候實現自動配置。
如果上述2個類提示找不到,可以在pom.xml引入以下依賴解決:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
@EnableConfigurationProperties(RabbitProperties.class)能實現屬性類的自動配置,RabbitProperties類是配置Rabbitmq屬性用的,其定義如下:
@ConfigurationProperties(prefix = "spring.rabbitmq") public class RabbitProperties { /** * RabbitMQ host. */ private String host = "localhost"; /** * RabbitMQ port. */ private int port = 5672; /** * Login user to authenticate to the broker. */ private String username; /** * Login to authenticate against the broker. */ private String password;
而這些屬性值我們同樣可以配置在resources下的application.properties文件里面,而配置的這些屬性將會覆蓋RabbitProperties類里面配置的默認值。
#自定義rabbitmq屬性值 spring.rabbitmq.host=198.168.121.1 spring.rabbitmq.username=root spring.rabbitmq.password=123456
至此,SpringBoot自動配置的源碼分析告一段落,總結如下:
(1)SpringBoot在啟動的時候會掃描所有jar包里面的META-INF/spring.properties,找出自動配置的類信息並將這些信息包裝成properties對象。到時會從properties中獲取EnableAutoConfiguration.class類名對應的值,然后使用@Bean注解將這些bean添加到Spring容器中。
(2)給Spring容器添加自動配置組件時,會從properties類中獲取屬性值,我們可以配置自定義的屬性覆蓋對應xxxProperties.class類里面的默認屬性值,實現個性化配置。