Springboot自动装配原理

Springboot是可以自动装配的,是怎么进行实现的呢,Springboot启动类为啥都放在根目录下呢,大概有几种注入Bean的方式呢

  1. springboot启动类为啥放根目录下,因为默认扫描当前包下的类,可以把对应的Bean注入spring容器,如果挪到指定包下,需要单独配置需要扫描哪些包

SpringBootApplication注解

java 复制代码
@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{}

可以看到SpringBootApplication注解是个复合注解,先关注下EnableAutoConfiguration 这个注解,springboot通过这个注解实现了把类注入到spring容器,然后交给spring管理的bean就可以控制创建,依赖注入等

java 复制代码
@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 {};
}

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
// 实现了ImportBeanDefinitionRegistrar,通过这个注入到了BeanDefinition
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }

}

AutoConfigurationPackage� 注解就负责扫描包下面的类,把对应注解上的包名解析之后给了BeanDefinnitionn,后面创建的对象的时候进行创建

springboot-starter的原理

springboot的starter非常方便,引入一个starter,对应的bean就交给spring管理了,这个都是怎么进心整合,怎么注入spring容器的呢 分析下@Import({AutoConfigurationImportSelector.class}) 可以看到在引入这个Selecter

挑一下AutoConfigurationImportSelector关键方法

java 复制代码
	@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 (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 看这里怎么
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		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 static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
	// 关键方法,在load对应的spring.factories
	// 返回的map对应的配置的指定的接口对应的一到多个实现类
    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

            try {
                // 查找指定位置的内容
                Enumeration 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()) {
                        Entry<?, ?> entry = (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);
            }
        }
    }

总结

springboot的自动装配有@EnableAutoConfuration注解来实现的,注解为一个复合注解,里面存在@AutoConfurationPackage 这里是在扫描指定的包下的,不指定就是启动类所在的目录下的,里面也是在@Import 一个实现了BeanDefinitionRegistrar 的类来实现把对应的包交给其进行后续扫描创建BeanDefinition,这里在扫描自己写的代码

然后还有一个是在@Import(AutoConfigurationImportSelector.class) 这里的这个就是在处理整合框架,引入包的问题,springboot的扩展是通过springboot自己定义的spi实现的,在resources 目录下的spring,factories 里面记录对应接口的实现类,来让springboot去扫描,符合一定条件的,就进行了加载,这里就是主要加载的引入的包的下面类交给spring管理,跟上面的分工合作,一个处理自己的,一个处理外来的,都交给spring管理,这样bean就都进行装载了

相关推荐
新手小袁_J13 分钟前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅13 分钟前
C#关键字volatile
java·redis·c#
Monly2114 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
Ttang2316 分钟前
Tomcat原理(6)——tomcat完整实现
java·tomcat
goTsHgo17 分钟前
在 Spring Boot 的 MVC 框架中 路径匹配的实现 详解
spring boot·后端·mvc
钱多多_qdd27 分钟前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring
waicsdn_haha29 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
飞的肖37 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
Q_192849990639 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
Code_流苏42 分钟前
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
java·ide·vscode·搭建·java开发环境