SpringBoot自动装载

本文介绍了SpringBoot的自动装载机制,重点讲解了ImportSelector接口的作用,模拟实现自动装配的过程,并详细阐述了SpringBoot自动装载的原理,包括@SpringBootApplication注解、@EnableAutoConfiguration和AutoConfigurationImportSelector类的逻辑。
ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

SpringBoot中的自动装载

(1)ImportSelector接口

ImportSelector接口是Spring导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)中起到了决定性的作用。当在@Configuration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把ImportSelector实现类中返回的Class名称都定义为bean。

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

DeferredImportSelector接口继承ImportSelector,他和ImportSelector的区别在于装载bean的时机上,DeferredImportSelector需要等所有的@Configuration都执行完毕后才会进行装载

public interface DeferredImportSelector 
					extends ImportSelector {
 //...省略
}

(2)模拟SpringBoot自动装配的实现

接下来我们写一个小例子,看下ImportSelector接口的用法

通过模拟实现ImportSelector接口来还原SpringBoot自动装配的原理(SpringBoot的自动装配原理就是通过封装ImportSelector接口等一些列操作来实现的)

1)定义Bean对象

这个类模拟的是我们要自动专配在SpringBoot容器中的Bean实例

public class User {
 private String username;
 private Integer age;
 //省略..
}

2)定义配置类Configuration

//定义一个configuration 
//注意这里并没有使用spring注解
//spring扫描的时候并不会装载该类
public class UserConfiguration {
 @Bean
 public User getUser() {
 return new User("张三",18);
 }
}

3 ) 定义ImportSelector接口的实现类

ImportSelector 接口中的selectImports方法会将加载的类当作一个配置类来初始化,就是相当于@Configuration注解的功能。

public class UserImportSelector implements ImportSelector {
 @Override
 public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //获取配置类名称
 return new String[]{UserConfiguration.class.getName()};
 }
}

4) 定义EnableXXX注解

在SpringBoot在自动装配正式通过注解@EnableAutoCofiguration注解实现的,这里通过自定义EnableXXX注解模拟SpringBoot中的@EnableAutoConfiguration注解实现自动装配。

@SpringBoot源码:在这里插入图片描述

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(UserImportSelector.class)
public @interface EnableUserBean {
}

5 ) 测试

/**
* 通过在类上声明@EnableUserBean,会自动的加载所有对象
*/
@EnableUserBean
public class TestApplication {
 public static void main(String[] args) {
 //获取sprinboot注解驱动的容器对象
 AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext(TestApplication.class);
 //加载通过实现ImportSelector接口模拟SpringBoot自动装载功能,将声明的User对象的实例装配到SpringBoot的容器中
 //获取SpringBoot容器中的User的实例对象
 User user = applicationContext.getBean(User.class);
 System.out.println(user);
 }
}

由此可见,User对象并没有使用Spring的对象创建注解声明(@Controller,@Service,@Repostiroty),而是使用编程的方式动态的载入bean。

这个接口在哪里调用呢?我们可以来看一下ConfigurationClassParser这个类的processImports方法

private void processImports(ConfigurationClass configClass, SourceClass
currentSourceClass,
            Collection<SourceClass> importCandidates, boolean
checkForCircularImports) {
        if (importCandidates.isEmpty()) {
            return;
       }
        if (checkForCircularImports && isChainedImportOnStack(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, 
this.importStack));
       }
        else {
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) 
{ //对ImportSelector的处理
                    if (candidate.isAssignable(ImportSelector.class)) {
                        // Candidate class is an ImportSelector -> delegate to 
it to determine imports
                        Class<?> candidateClass = candidate.loadClass();
                        ImportSelector selector =
BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                selector, this.environment, this.resourceLoader, 
this.registry);
                        if (this.deferredImportSelectors != null && selector
instanceof DeferredImportSelector) { //如果为延迟导入处理
则加入集合当中
                            this.deferredImportSelectors.add(
                                    new
DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                       }
                        else { //根据ImportSelector方法
的返回值来进行递归操作
                            String[] importClassNames =
selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses =
asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, 
importSourceClasses, false);
                       }
                   }
                    else if
(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                        // Candidate class is an ImportBeanDefinitionRegistrar -
>
                        // delegate to it to register additional bean 
definitions
                        Class<?> candidateClass = candidate.loadClass();
                        ImportBeanDefinitionRegistrar registrar =
                                BeanUtils.instantiateClass(candidateClass, 
ImportBeanDefinitionRegistrar.class);
                        ParserStrategyUtils.invokeAwareMethods(
                                registrar, this.environment, 
this.resourceLoader, this.registry);
                        configClass.addImportBeanDefinitionRegistrar(registrar, 
currentSourceClass.getMetadata());
                   }
                    else { // 如果当前的类既不是
ImportSelector也不是ImportBeanDefinitionRegistar就进行@Configuration的解析处理
                        // Candidate class not an ImportSelector or 
ImportBeanDefinitionRegistrar ->
                        // process it as an @Configuration class
                        this.importStack.registerImport(
                                currentSourceClass.getMetadata(), 
candidate.getMetadata().getClassName());
                       
processConfigurationClass(candidate.asConfigClass(configClass));
                   }
               }
           }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
           }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to process import candidates for configuration 
class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
           }
            finally {
                this.importStack.pop();
           }
       }
   }

(3)springBoot自动装载原理解析

SpringBoot开箱即用的特点,很大程度上归功于ImportSelector接口。接下来我们看下springBoot是如何在spring的基础上做扩展的。
在SpringBoot中最重要的一个注解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 {
 //...
}

在SpringBootApplication注解中声明了一个 @EnableAutoConfiguration注解

@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 {};
}

在EnableAutoConfiguration接口中通过@Import注解引入了SpringBoot定义的AutoConfigurationImportSelector类,这个类内容比较多,我们只需看下最主要的逻辑代码即可

public class AutoConfigurationImportSelector
 implements DeferredImportSelector, BeanClassLoaderAware, 
ResourceLoaderAware,
 BeanFactoryAware, EnvironmentAware, Ordered {
 @Override
 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 if (!isEnabled(annotationMetadata)) {
 return NO_IMPORTS;
 }
 AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader
 .loadMetadata(this.beanClassLoader);
 //主要逻辑在getAutoConfigurationEntry这个方法
 AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(
 autoConfigurationMetadata, annotationMetadata);
 return
StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
 }
 protected AutoConfigurationEntry getAutoConfigurationEntry(
 AutoConfigurationMetadata autoConfigurationMetadata,
 AnnotationMetadata annotationMetadata) {
 if (!isEnabled(annotationMetadata)) {
 return EMPTY_ENTRY;
 }
 AnnotationAttributes attributes = getAttributes(annotationMetadata);
 //通过getCandidateConfigurations方法获取所有需要加载的bean
 List<String> configurations =
getCandidateConfigurations(annotationMetadata,
 attributes);
 //去重处理
 configurations = removeDuplicates(configurations);
 //获取不需要加载的bean,这里我们可以通过spring.autoconfigure.exclude人为配置
 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
 checkExcludedClasses(configurations, exclusions);
 configurations.removeAll(exclusions);
 configurations = filter(configurations, autoConfigurationMetadata);
 //发送事件,通知所有的AutoConfigurationImportListener进行监听
 fireAutoConfigurationImportEvents(configurations, exclusions);
 return new AutoConfigurationEntry(configurations, exclusions);
 }
 //这里是获取bean渠道的地方,重点看SpringFactoriesLoader#loadFactoryNames
 protected List<String> getCandidateConfigurations(AnnotationMetadata
metadata,
 AnnotationAttributes attributes) {
 //这里的getSpringFactoriesLoaderFactoryClass()最终返回
EnableAutoConfiguration.class
 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
 Assert.notEmpty(configurations,
 "No auto configuration classes found in METAINF/spring.factories. If you "
 + "are using a custom packaging, make sure that file is 
correct.");
 return configurations;
 }
}

从上面的逻辑可以看出,最终获取bean的渠道在SpringFactoriesLoader.loadFactoryNames

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "METAINF/spring.factories";
    private static final Log logger =
LogFactory.getLog(SpringFactoriesLoader.class);
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache =
new ConcurrentReferenceHashMap();
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable
ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        //通过factoryClassName获取相应的bean全称
 //上面传入的factoryClass是EnableAutoConfiguration.class
        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 {
                //获取工程中所有META-INF/spring.factories文件,将其中的键值组合成Map
                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);
           }
       }
   }
    private static <T> T instantiateFactory(String instanceClassName, Class<T>
factoryClass, ClassLoader classLoader) {
        try {
            Class<?> instanceClass = ClassUtils.forName(instanceClassName, 
classLoader);
            if (!factoryClass.isAssignableFrom(instanceClass)) {
                throw new IllegalArgumentException("Class [" + instanceClassName
+ "] is not assignable to [" + factoryClass.getName() + "]");
           } else {
                return ReflectionUtils.accessibleConstructor(instanceClass, new
Class[0]).newInstance();
           }
       } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory 
class: " + factoryClass.getName(), var4);
       }
   }
}

每个jar都可以定义自己的META-INF/spring.factories ,jar被加载的同时 spring.factories里面定义的bean就可以自动被加载。

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值