• Spring Boot 自动装配原理


    前言

    在 SpringBoot 中,我们需要使用某个依赖,直接添加一个 starter 即可。

    为什么添加了一个 starter 就能生效?这其中就离不来 SpringBoot 的自动装配。

    自动装配是 Starter 的基础,也是整个 Spring Boot 的核心,那什么是自动装配呢?简单来说,就是自动将 Bean 装配到 IOC 容器中这么一个操作。

    自动装配是如何实现的?

    先看一下 SpringBoot 的核心注解@SpringBootApplication

    @Target({ElementType.TYPE}) //接口、类、枚举、注解
    @Retention(RetentionPolicy.RUNTIME) //这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
    @Documented // 说明该注解将被包含在javadoc中
    @Inherited // 说明子类可以继承父类中的该注解
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
     		...
    }
    

    大概可以把 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

    • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制

    • @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类

    • @ComponentScan: 扫描被 @Component (@Service,@Controller) 注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。

    其中,@EnableAutoConfiguration 是实现自动装配的重要注解,我们以这个注解入手。

    @EnableAutoConfiguration

    @EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage //作用:将main包下的所欲组件注册到容器中
    @Import({AutoConfigurationImportSelector.class}) //加载自动装配类 xxxAutoconfiguration
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }
    

    AutoConfigurationImportSelector

    AutoConfigurationImportSelector 类的继承体系如下:

    public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
    }
    
    public interface DeferredImportSelector extends ImportSelector {
    
    }
    
    public interface ImportSelector {
        String[] selectImports(AnnotationMetadata var1);
    }
    

    可以看出,AutoConfigurationImportSelector 类继承自 DeferredImportSelector,DeferredImportSelector 继承自 ImportSelector ,并实现了 ImportSelector 接口,也就实现了这个接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中

    selectImports 方法

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

    其中,getAutoConfigurationEntry 方法则是自动装配的逻辑,继续点进去。

       protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            } else {
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.getConfigurationClassFilter().filter(configurations);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
            }
        }
    

    其中 getCandidateConfigurations 继续点进去

        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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 继续点进去:

     ...
     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");
          ...
    

    能看到,是在加载 "META-INF/spring.factories" 路径下的配置类。

    总结:到这里基本清楚了,springboot 的自动装配就是通过自定义实现 ImportSelector 接口,从而导致项目启动时会自动将所有项目 META-INF/spring.factories 路径下的配置类注入到 spring 容器中,从而实现了自动装配。

    参考资源

    1.https://www.cnblogs.com/fcb-it/p/12905525.html

    2.https://zhuanlan.zhihu.com/p/345895748

    每天学习一点点,每天进步一点点。

  • 相关阅读:
    hibernate反向工程 (eclipse和myeclipse)【转】
    让你明白response.sendRedirect()与request.getRequestDispatcher().forward()区别
    Struts Tags
    在Eclipse中配置tomcat
    The Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path:
    如何将maven项目导入myeclipse中
    Hibernate配置文件详解
    网站怎么布局能解决不同浏览器对CSS解析的差异,使用css reset
    SqlServer:此数据库处于单用户模式,导致数据库无法删除的处理
    Myeclipse最全快捷键
  • 原文地址:https://www.cnblogs.com/youcoding/p/15074650.html
Copyright © 2020-2023  润新知