SpringBoot自动装配原理解析

 

        要理解自动装配原理,首先要理解BeanDefinition的作用,在BeanDefinition里定义了一些属性,其实就是将Bean的定义信息存储到这个BeanDefinition相应的属性中,后面对Bean的操作就直接对BeanDefinition进行,例如拿到这个BeanDefinition后,可以根据里面的类名、构造函数、构造函数参数,使用反射进行对象创建,BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如 ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。

我们都知道@SpringBootApplication注解里面封装了@ComponentScan注解,这个注解默认情况下扫描main函数所在的包路径,把带有@Component, @Configuration, @Service, @Responsitory的类都加载到Spring 容器中,加载过程是怎么样的呢,在@SpringBootApplication注解上面有@EnableAutoConfiguration注解,在这个注解里面又一个很重要的注解,那就是@Import(AutoConfigurationImportSelector.Class), 这个注解的作用是加载META-INF/spring.factories文件里的内容,spring.factories文件里存储的内容是<key,value> 的键值对,key就是注解或者说配置类(XXXAutoConfiguration,比如@Import这个接口)的全路径,value就是需要自动配置的配置类(上面加上@Configuration的类),拿spring官方的spring-boot-autoconfigure下的spring.properties文件来看一下(部分配置):

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\    # key,下面的都是value
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\

在AutoConfigurationImportSelector类里面有一个getCandidateConfigurations方法,源码如下:

/**
     * Return the auto-configuration class names that should be considered. By default
     * this method will load candidates using {@link SpringFactoriesLoader} with
     * {@link #getSpringFactoriesLoaderFactoryClass()}.
     * @param metadata the source metadata
     * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
     * attributes}
     * @return a list of candidate configurations
     */
    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;
    }
 

我们进入loadFactoryNames方法查看:

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                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 factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

看到标记为橘黄色的部分了吧,它会去找META-INF/spring.factories文件,加载进来,解析文件的内容,根据键值对对应关系,循环遍历spring.factories,把每个键值对解析为一个.property文件,然后再放到一个beanDefinitionMap里面,这个map的key是beanName,value是beanDefinition对象,有了这个beanDefinitionMap,spring会遍历这个map,对每个bean进行实例化(懒加载的除外),这样就得到了autoConfiguration里面每个bean的实例,可以对其进行属性的配置

 

 

posted on 2019-11-30 19:18  成功没有捷径  阅读(381)  评论(0编辑  收藏  举报