标签:el表达式 nullable cto com pat opera condition 元注解 url
参考:
https://www.jianshu.com/p/2a446ce48803
https://blog.csdn.net/htf2620032/article/details/79298661
https://www.cnblogs.com/xichji/p/11811733.html
https://www.cnblogs.com/hello-shf/p/10864977.html
目录:
自动加载的原理
SpringBoot的自动化配置让我们的开发彻底远离了Spring繁琐的各种配置,让我们专注于开发,但是SpringBoot的自动化配置是怎么实现的呢?
SpringBoot最为重要的一个注解是@SpringBootApplication,它其实是一个组合元注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
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
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
从这个注解可以看出来,它包含了@EnableAutoConfiguration 这 个注解,这个注解就是自动化配置的原理核心所在:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@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 {};
}
我们发现,它使用了Spring 框架提供的@Import 注解注入了注册Bean 的配置类,再往下分析不妨先了解下这个 @Import 注解,再我们平时使用Sping 框架的Enable* 类 注解时,发现他们都有一个共同的特点,就是都有一个@Import注解,用来导入配置类,这些配置方式又分为三种:
很明显,@EnableAutoConfiguration 这个注解使用的是第二种情况,导入@Import(AutoConfigurationImportSelector.class) 类,借助于AutoConfigurationImportSelector, @EnableAutoConfiguration 可以帮助SpringBoot 应用将所有符合条件的@Configuration 配置都加载到当前SpringBoot 创建并使用IoC容器。
借助于Spring框架原有的一个工具类,SpringFactoriesLoader 的支持,@EnableAutoConfiguration 可以智能的自动配置功效才得以大功告成!
在AutoConfigurationImportSelector 类中,可看到通过SpringFactoriesLoader.loadFactoryNames() 把 spring-boot-autoconfigure.jar/META-INF/spring.factories 中每一个xxxAutoConfiguration文件都加载到容器中,spring.factories文件里每一个xxxAutoConfiguration文件一般都会有下面的条件注解:
@ConditionalOnClass : classpath中存在该类时起效
@ConditionalOnMissingClass : classpath中不存在该类时起效
@ConditionalOnBean : DI容器中存在该类型Bean时起效
@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
@ConditionalOnExpression : SpEL表达式结果为true时
@ConditionalOnProperty : 参数设置或者值一致时起效
@ConditionalOnResource : 指定的文件存在时起效
@ConditionalOnJndi : 指定的JNDI存在时起效
@ConditionalOnJava : 指定的Java版本存在时起效
@ConditionalOnWebApplication : Web应用环境下起效
@ConditionalOnNotWebApplication : 非Web应用环境下起效
SpringFactoriesLoader属于Spring框架私有的一种扩展方案(类似于Java的SPI方案java.util.ServiceLoader),其主要功能就是从指定的配置文件META-INF/spring-factories加载配置,spring-factories是一个典型的java properties文件,只不过Key和Value都是Java类型的完整类名,比如:
#-------starter自动装配---------
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.starter.config.MsgAutoConfiguration
对于@EnableAutoConfiguration来说,SpringFactoriesLoader的用途稍微不同一些,其本意是为了提供SPI扩展的场景,而在@EnableAutoConfiguration场景中,它更多提供了一种配置查找的功能支持,即根据@EnableAutoConfiguration的完整类名org.springframework.boot.autoconfig.EnableAutoConfiguration作为查找的Key,获得对应的一组@Configuration类。SpringFactoriesLoader是一个抽象类,类中定义的静态属性定义了其加载资源的路径public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories",此外还有三个静态方法:
loadFactories:加载指定的factoryClass并进行实例化。
loadFactoryNames:加载指定的factoryClass的名称集合。
instantiateFactory:对指定的factoryClass进行实例化。
在loadFactories方法中调用了loadFactoryNames以及instantiateFactory方法。
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "‘factoryType‘ must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
创建 自定义 starter 的步骤:
确保在Pom.xml文件中声明了使用该组件所需要的全部dependency
利用@ConfigurationProperties注解对外暴露恰当的properties
利用条件注解@ConditionalXXX编写XXXAutoConfigration类
把写好的的XXXAutoConfigration类加到META-INF/spring.factories文件的EnableAutoConfiguration配置中,这样在应用启动的时候就会自动加载XXXAutoConfiguration。
SpringBoot在进行SpringApplication对象实例化时会加载META-INF/spring.factories文件,将该配置文件中的配置载入到Spring容器。
首先我们在程序入口的位置点进SpringApplication这个类的run方法
我们可以看到run方法值执行了一个方法
我们继续跟进找到run方法
可以看到run方法只是创建了SpringApplication对象,并执行run方法。我们继续跟进
现在我们找到了initialize方法,这个方法主要的作用是初始化一些对象和实例,点进getSpringFactoriesInstances方法找到loadFactoryNames方法
loadFactoryNames方法的作用是把/spring.factories文件中的配置类转化为对象,我们可以看到SpringApplication对象实例化时就是在这里加载META-INF/spring.factories文件
找到META-INF/spring.factories文件,我们看到Spring容器初始化时会加载到的类
看到这里我们不妨想想关闭自动配置时,我们是怎么写的?以Redis为例,我们可以看到spring.factories文件中有Redis的配置
关闭自动配置时我们就是把spring.factories文件不需要自动配置的类过滤掉
由此可见,读取该配置文件来加载内容。
标签:el表达式 nullable cto com pat opera condition 元注解 url
原文地址:https://www.cnblogs.com/xuwc/p/13993838.html