SpringBoot自动配置原理学习
介绍
构建Springboot项目时我们会创建一个启动类
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
1. 在之前使用Spring框架时,我们一般会创建web.xml和spring-context.xml等文件配置组件扫描、调度器、视图解析器等。
2. 而在SpringBoot中则简单了很多,这里就有自动配置发挥作用。如默认用的内嵌式容器是 Tomcat ,端口默认设置为 8080
正式进入流程解析
首先看@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) }) @ConfigurationPropertiesScan public @interface SpringBootApplication { }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
@EnableAutoConfiguration的作用是开启自动配置
核心是@EnableAutoConfiguration注解
这就是Spring Boot自动配置实现的核心入口,重头戏是@Import注解,这个注解导入了AutoConfigurationImportSelector类,利用AutoConfigurationImportSelector(自动配置导入选择器)将所有符合自动装配条件的bean注入到IOC容器中
查看AutoConfigurationImportSelector类代码,重头戏是selectImports方法
selectImports何时被执行
Springboot应用启动过程中使用ConfigurationClassParser分析配置类时,发现注解中存在@Import(ImportSelector)的情况。
就会创建一个相应的ImportSelector对象, 并调用其方法 public String[] selectImports(AnnotationMetadata annotationMetadata)
selectImports()方法流程
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { // 省略。。 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { // 1 判断是否开启自动配置,为false直接返回空字符数组
if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }
// 2 loadMetadata()去加载Spring预先定义的自动配置的依赖信息,下面会具体说明
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader);
// 3 该方法返回的就是配置项信息,下面会具体说明 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } // 省略。。。 }
2. loadMetadata()去加载Spring预先定义的自动配置的依赖信息
1 static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { 2 try { 3 Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) 4 : ClassLoader.getSystemResources(path); 5 Properties properties = new Properties(); 6 while (urls.hasMoreElements()) { 7 properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); 8 } 9 return loadMetadata(properties); 10 } 11 catch (IOException ex) { 12 throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); 13 } 14 }
path的地址是
protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
里面的自动配置文件(部分)
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.batch.core.launch.JobLauncher org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
2.1 根据path获取文件的URL对象
2.2 遍历URL节点,将spring-autoconfigure-metadata.properties中的依赖信息加载到properties对象中
2.3 调用loadMetadata()方法把properties存储到PropertiesAutoConfigurationMetadata类中
3. 在这里面会调用一个方法getAutoConfigurationEntry(),该方法返回的就是配置项信息,进入这个方法
1 protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, 2 AnnotationMetadata annotationMetadata) { // 3.1
3 if (!isEnabled(annotationMetadata)) { 4 return EMPTY_ENTRY; 5 } // 3.2
6 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 3.3
7 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 3.4
8 configurations = removeDuplicates(configurations); // 3.5
9 Set<String> exclusions = getExclusions(annotationMetadata, attributes); 10 checkExcludedClasses(configurations, exclusions); 11 configurations.removeAll(exclusions); 12 configurations = filter(configurations, autoConfigurationMetadata); // 3.6
13 fireAutoConfigurationImportEvents(configurations, exclusions); // 3.7
14 return new AutoConfigurationEntry(configurations, exclusions); 15 }
3.1 判断是否开启自动配置
3.2 getAttributes()方法获取@SpringBootApplication(主要是为了获取@EnableAutoConfiguation)注解上的属性
3.3 加载META-INF/spring.factories文件, 这个文件配置了具有哪些自动配置类,文件内容如下 ,具体代码如下
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; }
3.3.1 getSpringFactoriesLoaderFactoryClass方法返回EnableAutoConfiguration.class
3.3.2 SpringFactoriesLoader.loadFactoryNames会加载META-INF/spring.factories中EnableAutoConfiguration的值中定义的jar包,并将其封装到一个List中返回
3.4 removeDuplicates() 用LinkedHashSet去重,在包装成ArrayList
3.5 getExclusions()、checkExcludedClasses()、removeAll()、filter() 过滤注解中要排除的自动配置
3.6 fireAutoConfigurationImportEvents() 将自动配置导入监听
3.7 返回配置的AutoConfigurationEntry包装类
自动配置原理(简化版)
通过 @EnableAutoConfiguration
核心注解初始化,并扫描 ClassPath 目录中自动配置类。根据项目中需要添加的默认配置,如springMVC,就按一定规则获取默认配置并自动初始化所需要的 Bean。
1. 核心是@EnableAutoConfiguration
2. 里面会通过@Import注解导入(AutoConfigurationImportSelector.class)类
3. 调用importSelect方法,进行自动配置
3.1 判断是否开启了自动配置
3.2 getAutoConfigurationEntry方法会使用
SpringFactoriesLoader.loadFactoryNames方法加载classpath中spring.factories文件中EnableAutoConfiguration的配置类
3.3 经过去重,排除后会把这些配置导入监听
重点关注
@EnableAutoConfiguration
@Import
AutoConfigurationImportSelector类
selectImports方法
SpringFactoriesLoader.loadFactoryNames() 核心方法读取 ClassPath 目录下面的 META-INF/spring.factories 文件。
spring.factories 文件中存放springBoot自动配置类, 如Servlet、jpa
【示例】(JDBC)的DataSourceAutoConfiguration配置
在spring.factories中org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguratio
作用是:SpringBoot 自动配置DataSource
// 表示这是一个配置类 @Configuration(proxyBeanMethods = false) // 判断当前项目classpath有没有这两个类 DataSource EmbeddedDatabaseType @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // 将配置文件中对应的值和 DataSourceProperties 绑定起来,并把DataSourceProperties的属性值放到ioc容器中 @EnableConfigurationProperties(DataSourceProperties.class) // 导入数据源注册类、数据源初始化类 @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { // 省略。。 }
查看DataSourceProperties属性,提供的这些属性,就可以获取到在application.yml中提供的值来设置数据源了
@ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean { private ClassLoader classLoader; /** * Name of the datasource. Default to "testdb" when using an embedded database. */ private String name; /** * Whether to generate a random datasource name. */ private boolean generateUniqueName; /** * Fully qualified name of the connection pool implementation to use. By default, it * is auto-detected from the classpath. */ private Class<? extends DataSource> type; /** * Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. */ private String driverClassName; /** * JDBC URL of the database. */ private String url; /** * Login username of the database. */ private String username; /** * Login password of the database. */ private String password; /** * JNDI location of the datasource. Class, url, username & password are ignored when * set. */ private String jndiName; 。。。省略 }
总结
Spring Boot 内部提供了很多自动化配置的类, 配置类会更加classpath下是否又相关依赖类,来判断是否要配置字节,所以当我们在pom中引入了相关依赖,就可以自动配置了
====== 【多学一点,for Better】======