源码分析github地址原文链接
了解springboot的都知道启动类上经常又个@SpringBootApplication
注解,这个注解点进去看下是包括一个自动配置很关键的注解,那就是@EnableAutoConfiguration
注解。我们看下源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以从源码看出关键功能是@import注解导入自动配置功能类AutoConfigurationImportSelector
类,主要方法getCandidateConfigurations()
使用了SpringFactoriesLoader.loadFactoryNames()
方法加载META-INF/spring.factories
的文件(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;
}
starter常用的条件注解
@ConditionalOnBean 当容器里有指定的Bean的条件下。
@ConditionalOnClass 当类路径下有指定的类的条件下。
@ConditionalOnProperty 指定的属性是否有指定的值。
自定义starter
模拟公司获取密码的组件写一个demo-starter
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yyl</groupId>
<artifactId>password-spring-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
服务类
package com.yyl.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
/**
* author:yangyuanliang Date:2019-10-15 Time:13:32
**/
public class PasswordService {
private String key;
@Autowired
//模拟的第三方系统service
private ThirdPartyService thirdPartyService;
public String getSystemPassword(String objectKey,String originalPassord){
if(StringUtils.isEmpty(objectKey)){
return originalPassord;
}
//从第三方系统获取密码
String password= thirdPartyService.getPassword(objectKey);
//返回密码
return password!=null?password:originalPassord;
}
}
public class ThirdPartyService {
public String getPassword(String objectKey){
//返回一个32位随机数
return UUID.randomUUID().toString();
}
}
属性配置类
package com.yyl.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* author:yangyuanliang Date:2019-10-15 Time:13:34
**/
@ConfigurationProperties(prefix = "spring.starter.password")
public class BaseServiceProperties {
private String serviceName;
private String serviceVersion;
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getServiceVersion() {
return serviceVersion;
}
public void setServiceVersion(String serviceVersion) {
this.serviceVersion = serviceVersion;
}
}
配置使用类
public class BaseStarterService {
public void addServiceName(BaseServiceProperties baseServiceProperties){
System.out.println("serviceName:"+baseServiceProperties.getServiceName()+"----"+"serviceVersion"+baseServiceProperties.getServiceVersion());
}
}
自动配置类
@Configuration
//自动加载配置文件属性值
@EnableConfigurationProperties(BaseServiceProperties.class)
@Import(BeanConfiguration.class)
//判断当前环境是否为windows
@Conditional(MacCondition.class)
//判断属性spring.project.ThirdPartySystemService.isPassword是否等于true
@ConditionalOnProperty(prefix = "spring.project.ThirdPartySystemService",value = "enablePassword", havingValue = "true",matchIfMissing = true)
public class AutoConfigurationPassoword {
@Autowired
private BaseServiceProperties baseServiceProperties;
@Autowired
private BaseStarterService baseWindowsService;
//加载第三方系统service
@Bean("thirdPartySystemService")
public ThirdPartyService thirdPartySystemService(){
baseWindowsService.addServiceName(baseServiceProperties);
return new ThirdPartyService();
}
@Bean
//判断IOC容器中是否存在ThirdPartySystemService类,存在则创建PasswordService bean
@ConditionalOnClass(ThirdPartyService.class)
public PasswordService passwordService(){
baseWindowsService.addServiceName(baseServiceProperties);
return new PasswordService();
}
}
条件类
package com.yyl.condition;
import com.yyl.service.BaseStarterService;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* author:yangyuanliang Date:2019-10-15 Time:13:37
**/
public class MacCondition implements Condition {
private final static String WINDOWS="Windows";
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//获取当前环境变量
Environment environment=conditionContext.getEnvironment();
//获取bean注册器
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取环境变量中操作系统
String property = environment.getProperty("os.name");
//判断操作系统是否为windows
if(!property.contains(WINDOWS)){
//判断是否存在baseWindowsSevice类,不存在则进行bean注册
boolean isWindowsSevice = registry.containsBeanDefinition("baseStarterService");
if(!isWindowsSevice){
//指定Bean定义信息;(Bean的类型,Bean的一系列信息)
RootBeanDefinition beanDefinition = new RootBeanDefinition(BaseStarterService.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("baseStarterService", beanDefinition);
BaseStarterService windowsSevice = (BaseStarterService)beanFactory.getBean("baseStarterService");
}
return true;
}
return false;
}
}
注册配置
在resource下新建META-INF文件夹新建spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yyl.autoconf.AutoConfigurationPassoword
完成并测试
<dependencies>
<dependency>
<groupId>com.yyl</groupId>
<artifactId>password-spring-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
application.properties属性
spring.starter.password.serviceName=aaa
spring.starter.password.serviceVersion=1
spring.project.ThirdPartyService.enablePassword=true
总结
- SpringBoot启动时会自动搜索包含spring.factories文件的JAR包;
- 根据spring.factories文件加载自动配置类AutoConfiguration;
- 通过AutoConfiguration类,加载满足条件(@ConditionalOnXxx)的bean到Spring IOC容器中;
- 使用者可以直接使用自动加载到IOC的bean。