Spring boot starter 组件有两种方式 @EnableXX 和 autoconfigure 通过spring的spi加载
对于官方组件,是基于condition条件来决定对于类是否要自动装配,对于第三方组件,是采用spi机制来实现扩展
命名规则
官方命名规则 spring-boot-starter-xxx 参考 https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-starters第三方 xxx-spring-boot-starter
Spring boot starter 组件有两种方式
@EnableXX 开关启动模式EnableAutoConfiguration 通过spring的spi加载启动
Spring boot 的注解驱动
Spring 3.x 无配置化方式实现bean的状态 java config 注解 Spring 4.x @Conditional 条件装配Spring 5.x Indexed Spring的动态Bean的装载 @importimportSelector:DeferredimportSelectorRegistator:importBeanDefinintionRegistrar
SpringFactoriesLoader(SPI)
spring.factories
SPI 机制
Service provider interface
满足以下条件
需要在classpath目录下创建一个 meta-INF/services在该目录下创建一个 扩展点的全路径名.
文件中填写这个扩展点的实现文件编码格式UTF-8 ServiceLoader去进行加载
引入的依赖
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-autoconfigure
@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
@Configuration 配置类 通过@Bean 注入bean 这个案例就是用了这个机制importSelector 实现类 实现selectimports方法,返回需要注册的bean的类名字符串,ioc通过反射方式注入importBeanDefinitionRegistrar实现类,子类方法中拿到ioc容器registry,手动将注册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 loggingFilterFilterRegistrationBean(SmartLoggingProperties properties){
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(loggingFilter());
registrationBean.setName("loggingFilter");
registrationBean.addUrlPatterns("
Class>[] exclude() default {};
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 configurations = getCandidateConfigurations(annotationmetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 通过 spring.autoconfigure.exclude 和注解中的exclude属性排除
Set exclusions = getExclusions(annotationmetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationimportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected ListgetCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) { List 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 {
public static final String FACTORIES_RESOURCE_LOCATION = "meta-INF/spring.factories";
...
}



