SpringBoot自动装配源码

自动装配:

实际上就是如何将Bean自动化装载到IOC容器中管理,Springboot 的自动装配时通过SPI 的方式来实现的

SPI:SpringBoot 定义的一套接口规范,这套规范规定:Springboot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories文件,将文件中配置的类型信息加载到 Spring 容器,并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 Springboot 定义的标准,就能将自己的功能装置进 Springboot。

Springboot 通过@EnableAutoConfiguration开启自动装配,我们点进去发现一个@Import(AutoConfigurationImportSelector.class) 查看他的Diagrams发现它继承于ImportSelector 在Spring源码中见到过,那我们就找selectImports() 方法

在这里插入图片描述

AutoConfigurationImportSelector → selectImports()

这里就是自动装配的核心代码了getAutoConfigurationEntry

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
				return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        
        // 通过 SpringFactoriesLoader.loadSpringFactories() 
        // 加载META-INF/spring.factories中的配置
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        
        // 去除重复项
        configurations = this.removeDuplicates(configurations);
        
        // 应用exclusion属性,排除掉的引入
        // 开发过程中某些服务不需要的配置信息可以在注解后加上(exclude = xxxAutoConfiguration.class)来排除加载
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        
        // 检查候选配置类上的注解@ConditionalOnClass
        // 如果要求的类不存在,则这个候选类会被过滤不被加载
        configurations = this.getConfigurationClassFilter().filter(configurations);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

通过this.getCandidateConfigurations()发现是SpringFactoriesLoader.loadSpringFactories() 加载META-INF/spring.factories中的配置来实现的

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        Map<String, List<String>> result = new HashMap();

        try {
            Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

this.getConfigurationClassFilter().filter(configurations) 这里是如何过滤的我们继续深入看一下在内部类ConfigurationClassFilter的实现

在这里插入图片描述

ConfigurationClassFilter.java中autoConfigurationMetadata 属性在构造函数中进行了赋值,查看loadMetadata() 发现是根据PATH = "META-INF/spring-autoconfigure-metadata.properties" 文件中的信息进行判断的

protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

	private AutoConfigurationMetadataLoader() {
	}

	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
		return loadMetadata(classLoader, PATH);
	}

	static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
		try {
			Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
					: ClassLoader.getSystemResources(path);
			Properties properties = new Properties();
			while (urls.hasMoreElements()) {
				properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
			}
			return loadMetadata(properties);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
		}
	}

我们拿出META-INF/spring-autoconfigure-metadata.properties 中一部分数据

org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration=
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.core.RabbitMessagingTemplate

分析发现如果项目中不存在org.springframework.amqp.rabbit.core.RabbitMessagingTemplate 这个类,那么在容器启动时就不加载。也就是说我们用的时候直接引入这个starter,启动的时候就会加载这个starter里的配置,不引入就不加载。META-INF/spring.factories 里的自动加载信息是为了方便我们后期引入不需要额外再进行配置。

总结:

Srpingboot使用@EnableAutoConfiguration 注解开启自动装配,通过SpringFactoriesLoader.*loadFactoryNames()*加载META-INF/spring.factories中的自动配置类实现自动装配,通过@ConditionalOn...按需加载的配置类过滤掉未引入用不到的项

@ConditionalOn 条件注解:

@ConditionalOnBean:当容器里有指定 Bean 的条件下

@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下

@ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean

@ConditionalOnClass:当类路径下有指定类的条件下

@ConditionalOnMissingClass:当类路径下没有指定类的条件下

@ConditionalOnProperty:指定的属性是否有指定的值

@ConditionalOnResource:类路径是否有指定的值

@ConditionalOnExpression:基于 SpEL 表达式作为判断条件

@ConditionalOnJava:基于 Java 版本作为判断条件

@ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置

@ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下

@ConditionalOnWebApplication:当前项目是 Web 项 目的条件下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值