Spring boot starter 组件有两种方式 @EnableXX 和 autoconfigure 通过spring的spi加载
对于官方组件,是基于condition条件来决定对于类是否要自动装配,对于第三方组件,是采用spi机制来实现扩展
命名规则
Spring boot starter 组件有两种方式
Spring boot 的注解驱动
- Spring 3.x 无配置化方式实现bean的状态 java config 注解 Spring 4.x @Conditional 条件装配
- Spring 5.x Indexed Spring的动态Bean的装载 @Import
- ImportSelector:DeferredImportSelector
- Registator:ImportBeanDefinintionRegistrar
SpringFactoriesLoader(SPI)
spring.factories
SPI 机制
Service provider interface
满足以下条件
- 需要在classpath目录下创建一个 META-INF/services
- 在该目录下创建一个 扩展点的全路径名.
- 文件中填写这个扩展点的实现
- 文件编码格式UTF-8
- ServiceLoader去进行加载
引入的依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-autoconfigureartifactId>
dependency>
dependencies>
@EnableXXX 开关的自动配置
案例: smart-logging-spring-boot-starter
EnableSmartLogClient开关配置
EnableSmartLogClient 开关
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({SmartLoggingConfig.class})
public @interface EnableSmartLogClient {
}
Enablexxx开关 主要用了spring的 import的特性,import可以通过下面三种方式项目spring的ioc容器中注入bean
SmartLoggingConfig
// 真正starter组件需要事情的类
public class LoggingFilter extends OncePerRequestFilter {
private static Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request,response);
logger.info("request-url:"+request.getRequestURI());
}
}
// 配置类
@Configuration
@EnableConfigurationProperties(SmartLoggingProperties.class)
public class SmartLoggingConfig {
@Bean
public LoggingFilter loggingFilter(){
return new LoggingFilter();
}
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilterFilterRegistrationBean(SmartLoggingProperties properties){
FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(loggingFilter());
registrationBean.setName("loggingFilter");
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(properties.getOrder());
return registrationBean;
}
}
SmartLoggingProperties
@ConfigurationProperties("smart.logging")
public class SmartLoggingProperties {
private String name;
private int order;
... getter settter
}
SmartLoggingProperties 这个属性类不是必须的,软件工程中的开闭原则,推荐使用,可以让使用者通过外部化配置更改组件内部一些行为
spring-boot-configuration-processor
这个引入主要为了生成MATA-INF/spring-configuration-metadata.json文件
会让idea在applcation.properties写配置如smart.logging时自动提示,跳转到源码
EnableAutoConfiguration模式
EnableAutoConfiguration模式利用的是spring的spi扩展点功能(spring.factories)
redisson-spring-boot-starter
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.3.1.RELEASEversion>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>3.13.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<version>2.3.1.RELEASEversion>
dependency>
RedissonAutoConfiguration
@ConditionalOnClass(Redisson.class) //条件装配
@EnableConfigurationProperties(RedissonProperties.class)
@Configuration
public class RedissonAutoConfiguration {
@Bean
RedissonClient redissonClient(RedissonProperties redissonProperties){
Config config=new Config();
String prefix="redis://";
if(redissonProperties.isSsl()){
prefix="rediss://";
}
config.useSingleServer().
setAddress(prefix+redissonProperties.getHost()+":"+redissonProperties.getPort()).
setConnectTimeout(redissonProperties.getTimeout());
return Redisson.create(config);
}
}
RedissonProperties
@ConfigurationProperties(prefix = "card.redisson")
public class RedissonProperties {
private String host="localhost";
private int port=6379;
private int timeout; //超时时间
private boolean ssl;
...GET SET
}
META-INF/spring.factories
在resources中添加META-INF/spring.factories 文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.card.redisson.RedissonAutoConfiguration
Spring SPI源码解析
@SpringBootApplication
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
AutoConfigurationImportSelector
AutoConfigurationImportSelector#selectImport关键代码
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 从spring.factories配置文件中获取配置类
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 这一步 获取配置候选 的类名
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 通过 spring.autoconfigure.exclude 和注解中的exclude属性排除
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
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;
}
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
...
}