spring-boot-starter自定义

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的装载 @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
spring-boot-starter自定义_第1张图片
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<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

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

@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"; ... }

你可能感兴趣的:(springboot,spring,java,spring,boot)