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包就是这么来的,具体怎么做,网上也有教程,这么就不做扩展了。

 

posted @ 2022-01-03 12:14  上官兰夏  阅读(22)  评论(0编辑  收藏  举报