zoukankan      html  css  js  c++  java
  • Springboot自动装配原理

    一、引言

    @SpringBootApplication
    public class SampleTomcatApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SampleTomcatApplication.class, args);
        }
    
    }

    通常情况下,我们使用springboot开发,入口都是这样的,那么入口类上的注解是怎么生效的?注解又包含了什么信息呢?

    二、入口类

        public ConfigurableApplicationContext run(String... args) {
            ```
    
            //完成启动类的加载
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                
            ```
        }
        
        private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            ···
            Set<Object> sources = getAllSources();
            //sources就是启动类,这里面把启动类注册成了一个beanDefinition
            load(context, sources.toArray(new Object[0]));
            listeners.contextLoaded(context);
        }
        
        protected void load(ApplicationContext context, Object[] sources) {
    
            //初始化了annotatedReader、扫描器等
            BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
            if (this.beanNameGenerator != null) {
                loader.setBeanNameGenerator(this.beanNameGenerator);
            }
            if (this.resourceLoader != null) {
                loader.setResourceLoader(this.resourceLoader);
            }
            if (this.environment != null) {
                loader.setEnvironment(this.environment);
            }
            //加载注册
            loader.load();
        }
        void load() {
            for (Object source : this.sources) {
                load(source);
            }
        }
        private void load(Object source) {
            Assert.notNull(source, "Source must not be null");
            if (source instanceof Class<?>) {
                load((Class<?>) source);
                return;
            }
            if (source instanceof Resource) {
                load((Resource) source);
                return;
            }
            if (source instanceof Package) {
                load((Package) source);
                return;
            }
            if (source instanceof CharSequence) {
                load((CharSequence) source);
                return;
            }
            throw new IllegalArgumentException("Invalid source type " + source.getClass());
        }
        private void load(Class<?> source) {
            if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
                // Any GroovyLoaders added in beans{} DSL can contribute beans here
                GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
                ((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
            }
            if (isEligible(source)) {
                //把source注册成一个beanDefinition
                this.annotatedReader.register(source);
            }
        }

    跟踪源码可以看到,传进来的入口类,是被先注册成一个BeanDefinition,后续就会经历bean的初始化生命周期,并且解析bean上注解。

    三、@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 {
    
        //排除类名数组
        @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 {};
        
        //自定义beanName生成器
        @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
        Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    
        //配置类是否被代理
        @AliasFor(annotation = Configuration.class)
        boolean proxyBeanMethods() default true;
    
    }

    @SpringBootConfiguration

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {
    
        @AliasFor(annotation = Configuration.class)
        boolean proxyBeanMethods() default true;
    
    }

    就是一个配置类注解,等同于@Configuration

    @EnableAutoConfiguration

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    //导入了一个ImportSelector实现类
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    
    }

    @AutoConfigurationPackage

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    //导入了一个ImportBeanDefinitionRegistrar实现类
    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
    
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    
    }
        static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    
            @Override
            public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                //注册AutoConfigurationPackages的beandefinition
                register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
            }
    
            @Override
            public Set<Object> determineImports(AnnotationMetadata metadata) {
                return Collections.singleton(new PackageImports(metadata));
            }
    
        }

    @Import(AutoConfigurationImportSelector.class)关键注解,自动装配的原理所在,导入的是ImportSelector的实现类,那么会执行他的selectImports方法,并把返回的全限定类名字符串数组注册成beanDefinition。

        @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);
            //加载配置文件META-INF/spring.factories中配置的key为EnableAutoConfiguration的beanNames
            List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
            //去重
            configurations = removeDuplicates(configurations);
            //根据exclude属性排除beanName
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            //过滤beanNames
            configurations = getConfigurationClassFilter().filter(configurations);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            //包装赋值给configurations
            return new AutoConfigurationEntry(configurations, exclusions);
        }

    但是实际上这地方并没有执行selectImports方法,因为AutoConfigurationImportSelector重写了process方法,如果我们自定义的Selector没有进行方法重写,那么会调用DefaultDeferredImportSelectorGroup类的process方法

            public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            //调用selector的selectImports方法
                for (String importClassName : selector.selectImports(metadata)) {
                    this.imports.add(new Entry(metadata, importClassName));
                }
            }

    但是AutoConfigurationImportSelector调用的是自己的内部类AutoConfigurationGroup的process方法,不过依然是调用了上面所说的getAutoConfigurationEntry方法,具体为什么这么做,还不清楚

            public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
                Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                        () -> String.format("Only %s implementations are supported, got %s",
                                AutoConfigurationImportSelector.class.getSimpleName(),
                                deferredImportSelector.getClass().getName()));
                //自动装配
                AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
                        .getAutoConfigurationEntry(annotationMetadata);
                this.autoConfigurationEntries.add(autoConfigurationEntry);
                for (String importClassName : autoConfigurationEntry.getConfigurations()) {
                    this.entries.putIfAbsent(importClassName, annotationMetadata);
                }
            }

    这时我们定义在spring.factories中的配置类,都会被加载成beanDefinition,后续还会对他们进行解析,其中有一个类DispatcherServletAutoConfiguration,这个配置类就是为了把DispatcherServlet放入到spring的IOC容器,使用@Bean注解的方式,这里就不详解了。

    四、扩展

      利用springboot的自动装配原理,我们可以在平时的工作中开发springboot应用的starter工具包,非常实用,而且springboot自己也是这么做的,我们经常用到的带有starter的jar包就是这么来的,具体怎么做,网上也有教程,这么就不做扩展了。

  • 相关阅读:
    路由
    更改HTTP头信息
    laravel 笔记
    laraven安装记录
    虚拟机Centos设置静态IP
    关于正向代理,反向代理,负载均衡的个人理解
    exce族函数详解
    【C】多线程编程笔记
    【转】Linux C 网络编程——TCP套接口编程
    MySQL 用户管理及权限管理
  • 原文地址:https://www.cnblogs.com/sglx/p/15671588.html
Copyright © 2011-2022 走看看