目录
自动配置原理
我们跟随代码来看
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ServletComponentScan
@EnableScheduling
public class DaseApplication {
public static void main(String[] args) {
SpringApplication.run(DaseApplication.class, args);
}
}
这个入口类要使用@SpringBootApplication注解声明,它是SpringBoot的核心注解
@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,这么直白的名字,一看就知道它要开启自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
可以看到,在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能(@Enable*里面都是@import)而EnableAutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//1.扫描具有META-INF/spring.factories文件的jar包
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//2.去重
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
//3.删除需要排除的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
getCandidateConfigurations代码
//加载spring.factories实现
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;
}
可以看到通过 SpringFactoriesLoader.loadFactoryNames()把 spring-boot-autoconfigure.jar/META-INF/spring.factories中每一个xxxAutoConfiguration文件都加载到容器中。进入SpringFactoriesLoader.loadFactoryNames()可以看到
public abstract class SpringFactoriesLoader {
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));
任何一个springboot应用,都会引入spring-boot-autoconfigure,而spring.factories文件就在该包下面。spring.factories文件是Key=Value形式,多个Value时使用,隔开,该文件中定义了关于初始化,监听器等信息,而真正使自动配置生效的key是org.springframework.boot.autoconfigure.EnableAutoConfiguration
# 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,\
上面的EnableAutoConfiguration配置了多个类,这些都是Spring Boot中的自动配置相关类;在启动过程中会解析对应类配置信息。每个Configuation类都定义了相关bean的实例化配置。都说明了哪些bean可以被自动配置,什么条件下可以自动配置,并把这些bean实例化出来。如果我们自定义了一个starter的话,也要在该starter的jar包中提供 spring.factories文件,并且为其配置org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类。所有框架的自动配置流程基本都是一样的,判断是否引入框架,获取配置参数,根据配置参数初始化框架相应组件。下面的初始化数据源的部分源码:
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
private static final Log logger = LogFactory
.getLog(DataSourceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean
public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties,
ApplicationContext applicationContext) {
return new DataSourceInitializer(properties, applicationContext);
}
//略
}
@Configuration:这个配置就不用多做解释了,我们一直在使用
@EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。
以下为SpringBoot内置条件注解:
@ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件
@ConditionalOnClass:当SpringIoc容器内存在指定Class的条件
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnJndi:在JNDI存在时查找指定的位置
@ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件
@ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnSingleCandidate:当指定Bean在SpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件
以上注解都是元注解@Conditional演变而来的,根据不用的条件对应创建以上的具体条件注解。
自定义自动配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
1、新建服务类TestService
package com.bm.demostarter;
public class TestService {
private String myip;
private String myport;
public String getMyip() {
return myip;
}
public void setMyip(String myip) {
this.myip = myip;
}
public String getMyport() {
return myport;
}
public void setMyport(String myport) {
this.myport = myport;
}
public TestService(String myip, String myport) {
this.myip = myip;
this.myport = myport;
}
public void printInfo() {
System.out.println("打印:" + myip + " " + myport);
}
}
2、创建属性配置实体类TestServiceProperties
package com.bm.demostarter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix="test.config")
public class TestServiceProperties {
String ip;
String port;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}
3、新建一个自动配置类TestServiceAutoConfiguration
package com.bm.demostarter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(TestServiceProperties.class)
@ConditionalOnClass(TestService.class) //判断TestService这个类在类路径中是否存在
//test.config.enabled=true时才能启动,允许不存在该配置
@ConditionalOnProperty(name = "test.config.enabled", havingValue = "true", matchIfMissing = true)
public class TestServiceAutoConfiguration {
@Autowired
private TestServiceProperties testServiceProperties;
@Bean(name = "testService")
@ConditionalOnMissingBean(TestService.class)
//当容器中没有这个Bean时(BambooServer)就自动配置这个Bean,Bean的参数来自于BambooServerProperties
public TestService mistraService() {
TestService testService = new TestService(testServiceProperties.getIp(), testServiceProperties.getPort());
return testService;
}
}
4、自定义一个注解@EnableTestService
package com.bm.demostarter;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(TestServiceAutoConfiguration.class)
public @interface EnabledTestService {
}
5、resources - META-INF-spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bm.demostarter.TestServiceAutoConfiguration
打包生成jar
然后另一个项目要应用
<dependency>
<groupId>com.bm</groupId>
<artifactId>demostarter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
写配置信息
引入@EnabledTestService
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ServletComponentScan
@EnableScheduling
@EnabledTestService
public class DaseApplication {
/**
* Title: main
* Description: 程序启动入口
* @param args
* void
*/
public static void main(String[] args) {
SpringApplication.run(DaseApplication.class, args);
}
}
调用
@Autowired
private TestService testService;
@GetMapping("/ii")
public String ii()
{
testService.printInfo();
return "123123";
}
效果