pringBoot作为一款全新的框架,有着诸多的优点,正受到越来越多的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>
而这些属性值我们同样可以配置在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类里面的默认属性值,实现个性化配置。