您现在的位置是:首页 >技术交流 >1.Springboot自动装配原理拆解浅析网站首页技术交流
1.Springboot自动装配原理拆解浅析
1.springboot自动装配原理在于启动类上的注解@SpringBootApplication
@SpringBootApplication
public class JinyiUserProviderApplication {
public static void main(String[] args) throws UnknownHostException {
SpringApplication.run(JinyiUserProviderApplication.class, args);
}
}
2.@SpringBootApplication拆解分析
@SpringBootApplication是一个组合注解,主要由下面的注解复合而成。
@SpringBootConfiguration 标注在某个类上,表示这是一个Spring Boot的配置类;
@EnableAutoConfiguration 开启自动配置功能
@ComponentScan 扫描包范围,spring底层会自动扫描当前配置类所有在的包及子包
@Target(ElementType.TYPE) //设置当前注解可以标记在哪
@Retention(RetentionPolicy.RUNTIME) //当注解标注的类编译以什么方式保留
@Documented // java doc 会生成注解信息
@Inherited //是否会被继承
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
@AliasFor(annotation = Configuration.class)
boolean proxyBeanMethods() default true;
}
3.@EnableAutoConfiguration拆解分析
@EnableAutoConfiguration 是springboot自动装配的核心注解,内部使用了@Import注解导入了AutoConfigurationImportSelector类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
4.AutoConfigurationImportSelector类拆解
AutoConfigurationImportSelector类内部实现了DeferredImportSelector接口。DeferredImportSelector 接口继承了ImportSelector 接口。由AutoConfigurationImportSelector类实现ImportSelector 接口中的selectImports方法。
selectImports方法大致流程:
1.获取标记了注解的属性数据
2.获取待加载的候选bean配置信息
3.利用LinkedHashSet set集合不能重复的特性 去除重复的待加载的候选bean配置信息
4.获取exclude配置的排除配置信息
5.待加载的候选bean配置信息中移除配置了排除的后选bean配置信息
6.获取过滤链过滤满足Condition条件的候选bean配置信息
7.根据层层条件之后,发布满足自动配置的候选bean的Import事件
8.已经找到了JavaConfig自动配置类的全限定名对应的class, 后续就交给BeanDefinition BeanFactory。然后将所有自动配置类加载到Spring容器中
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//ImportSelector接口定义的方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//获取满足自动配置条件的Entry
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//获取标记了注解的属性数据
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取待加载的候选bean配置信息
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//LinkedHashSet 利用set集合不能重复的特性 去除重复的待加载的候选bean配置信息
configurations = removeDuplicates(configurations);
//获取exclude配置的排除配置信息
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//待加载的候选bean配置信息中移除配置的排除的后选bean配置信息
configurations.removeAll(exclusions);
//获取过滤链过滤满足Condition条件的候选bean配置信息
configurations = getConfigurationClassFilter().filter(configurations);
//根据层层条件之后,发布满足自动配置的候选bean的Import事件
//已经找到了JavaConfig自动配置类的全限定名对应的class, 后续就交给BeanDefinition BeanFactory。然后将所有自动配置类加载到Spring容器中
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
//获取标记了注解的属性数据
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
+ " annotated with " + ClassUtils.getShortName(name) + "?");
return attributes;
}
//获取待加载的候选bean信息
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;
}
}
核心SpringFactoriesLoader 类
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
//核心的2次方法
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
//最核心的方法来了
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//重点:加载"META-INF/spring.factories"里的配置信息
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
}
其他继承类说明:
DeferredImportSelector 接口继承了ImportSelector 接口
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
interface Group {
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
Iterable<Entry> selectImports();
class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
public Entry(AnnotationMetadata metadata, String importClassName) {
this.metadata = metadata;
this.importClassName = importClassName;
}
public AnnotationMetadata getMetadata() {
return this.metadata;
}
public String getImportClassName() {
return this.importClassName;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
Entry entry = (Entry) other;
return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
}
@Override
public int hashCode() {
return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
}
@Override
public String toString() {
return this.importClassName;
}
}
}
}
ImportSelector 接口内部提供了核心的selectImports方法,由AutoConfigurationImportSelector 类实现。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
额外说明:
Bean注入Spring容器的几种方式。
1。@Configuration + @Bean
@Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个bean,将其加入到Spring容器中。
如下,启动类中顺道加载RestTemplate
@SpringBootApplication
public class JinyiUserProviderApplication {
public static void main(String[] args) throws UnknownHostException {
SpringApplication.run(JinyiUserProviderApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2。@Componet + @ComponentScan
@Componet放在类名上面,然后@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有@Componet注解的bean,然后加至容器中。最常规的方法
3。@Import注解导入 第三方jar的引入方式
@Import直接导入类
@Import + ImportSelector 优点:一次能引入多个,可以不在本项目的Spring扫描范围内。 springboot加载使用的就是这种
@Import + DeferredImportSelector 延迟导入,内部有一个Group属性,可以保证局部有序加载。
@Import + ImportBeanDefinitionRegistrar 直接引入BeanDefinitionRegistrar bean定义注册器
还有其他的使用FactoryBean接口等。。
总结
@EnableAutoConfiguration自动装配的原理总结一下核心过程
•通过@lmport(AutoConfigurationimportSelector实现配置类的导入,但是这里并不是传统意
义上的单个配置类装配。
•AutoConfigurationimportselector 类实现了 Importselector 接口,重写了方法 selectlmports,
它用于实现选择性批量配置类的装配。
•通过 Spring 提供的 SpringFactoriesLoader 机制,扫描classpath 路径下的 META-INF/spring.factories,
读取需要实现自动装配的配置类。
•通过@Conditional等条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。