springboot启动扩展原理

springboot启动扩展原理

1.@Enable**原理

Enable 注解是在 JavaConfig 框架上更进一 步的完善,用户在使用 spring 相关的框架时,避免配置大量的代码从而降低使用的难度 。每个Enable开头的注解都会有一个@Import的注解。

@Import({AsyncConfigurationSelector.class})

@Impart是为了导入配置类,@Import 注解可以配置三种不同的 class

  1. 基于普通 bean 或者带有 @Configuration 的 bean 进行注入
  2. 实现 ImportSelector 接口进行动态注入
  3. 实现 ImportBeanDefinitionRegistrar 接口进行动态注入

以下例子是EnableAsync 异步框架使用的注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AsyncConfigurationSelector.class})
public @interface EnableAsync {
    Class<? extends Annotation> annotation() default Annotation.class;

    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}

重点就是在@Import注解中的AsyncConfigurationSelector配置类中。

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

    public AsyncConfigurationSelector() {
    }

    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{ProxyAsyncConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
        default:
            return null;
        }
    }
}

这个的selectImports方法就是返回一个需要配置信息Config的类名集合,然后框架会对这个集合中的类进行注册调用,这样就实现了注册逻辑。

自己的实现

1.Enable类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({CommonAsyncSelect.class})
public @interface EnableCommonAsync {

}

这个注解代表你要使用一个模块,会启动这个模块所依赖的配置信息的调用

2.Import类

public class CommonAsyncSelect implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.meng.common.spring.starter.AsyncConfiguration"};
    }
}

这个类返回这个模块所需要的配置信息类在哪里,需要哪些配置信息类。

3.配置信息类

@Configuration
@ComponentScan("com.meng.common.spring.async")
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfiguration {

    @Autowired
    private AsyncProperties asyncProperties;

    @Bean
    public AsyncConfig asyncConfig() {
        AsyncConfig asyncConfig = new AsyncConfig();
        asyncConfig.setProjectName(asyncProperties.getProjectName());
        asyncConfig.setExecutor(asyncProperties.getExecutor());
        return asyncConfig;
    }
}

这个就是一个正常的配置信息类。他配置了这个模块所需要的所有配置信息逻辑。

总结:

Enable就是指定了你需要使用的模块,然后会去初始化这个模块所使用的配置信息。

2.Spring SPI

SPI 是一种服务发现机制。可以用它来做服务的扩展发现。简单来说,它是一种动态发现的机制。一般面向框架的扩展使用

在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后解析成properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包。

spring.factories文件是key-value键值对的方式,key使用这个SPI的类名,value是需要加载的配置文件的名字。

下面的配置的意思就是使用EnableAutoConfiguration的逻辑来加载AsyncConfiguration类。key可以重复。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.meng.common.spring.starter.AsyncConfiguration

这里的EnableAutoConfiguration其实就是使用了@Enable**原理。

springboot的启动注解@SpringBootApplication中就包含了@EnableAutoConfiguration

代表着EnableAutoConfiguration在启动时会运行

1.EnableAutoConfiguration

本质上来说,其实 EnableAutoConfiguration 会帮助 springboot 应用把所有符合@Configuration 配置都加载到当前 SpringBoot 创建的 IoC 容器,而这里面借助了 Spring 框架提供的一个工具类 SpringFactoriesLoader 的 支持。以及用到了 Spring 提供的条件注解 @Conditional,选择性的针对需要加载的 bean 进行条件 过滤

2.SpringFactoriesLoader

SpringFactoriesLoader 这个工具类的使用。它其实和 java 中的 SPI 机制的原理是一样的,不过它比 SPI 更好的点在于不会一次性加载所有的类,而是根据 key 进行加 载。 首 先 , SpringFactoriesLoader 的作用是从 classpath/META-INF/spring.factories 文件中,根据 key 来 加载对应的类到 spring IoC 容器中。

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的格式为:key=value1,value2,value3
// 从所有的jar包中找到META-INF/spring.factories文件
// 然后从文件中解析出key=factoryClass类名称的所有value值
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // 取得资源文件的URL
    Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    List<String> result = new ArrayList<String>();
    // 遍历所有的URL
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        // 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        // 组装数据,并返回
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }
    return result;
}

例子

  1. 需要启动时运行的配置信息类,因为这个都是其他组件中的配置信息,所有在本项目中的springboot扫描不会扫描到。
@Configuration
@ComponentScan("com.meng.common.spring.async")
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfiguration {

    @Autowired
    private AsyncProperties asyncProperties;

    @Bean
    @ConditionalOnMissingBean
    public AsyncConfig asyncConfig() {
        AsyncConfig asyncConfig = new AsyncConfig();
        asyncConfig.setProjectName(asyncProperties.getProjectName());
        asyncConfig.setExecutor(asyncProperties.getExecutor());
        return asyncConfig;
    }
}

2.spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.meng.common.spring.starter.AsyncConfiguration

这个配置会被SpringFactoriesLoader扫描到,当组件被maven添加到项目后,当项目启动时,配置信息就会被调用。对于开发人员来说,只要这个组件被导入,就可以直接使用,无需做任何配置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值