Spring @Import注解源码解析

简介

Spring 3.0之前,创建Bean可以通过xml配置文件与扫描特定包下面的类来将类注入到Spring IOC容器内。而在Spring 3.0之后提供了JavaConfig的方式,也就是将IOC容器里Bean的元信息以java代码的方式进行描述。我们可以通过@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述

@Import注解提供了@Bean注解的功能,同时还有xml配置文件里标签组织多个分散的xml文件的功能,当然在这里是组织多个分散的@Configuration

先看一下@Import注解的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
??? /**
????? * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
????? * or regular component classes to import.
????? */
    ??????? Class<?>[] value();
}

从源码里可以看出@Import可以配合 Configuration ,ImportSelector, ImportBeanDefinitionRegistrar 来使用,下面的or表示也可以把Import当成普通的Bean使用
@Import只允许放到类上面,不能放到方法上。下面我们来看具体的使用方式
??? ?

普通使用方法

这种方式可以直接把类加入到Spring IOC容器

@Configuration
@Import(value={UserServiceImpl.class})
public class Config {
?
}

但是这种方式有一些问题,那就是只能使用类的无参构造方法来创建bean,对于有参数的构造方法就无能为力了

结合ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar接口的源码如下:

public interface ImportBeanDefinitionRegistrar {
??? public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
??? ?
}

可以看到这个接口唯一的方法是有两个参数的

  1. AnnotationMetadata:通过这个参数可以拿到类的元数据信息
  2. BeanDefinitionRegistry:通过这个参数可以操作IOC容器

我们可以使用一个类来实现这个接口

public class UserServiceBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
?
??? public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
??????? BeanDefinitionBuilder userService = BeanDefinitionBuilder.rootBeanDefinition(UserServiceImpl.class);
??????? registry.registerBeanDefinition("userService", userService.getBeanDefinition());
??? }
?
}

可以看到我们在这个方法里面做一些特殊操作什么的都是可以的,相比较于普通的方式可是灵活了很多

接着我们在@Import注解引入的地方只需要修改为引入UserServiceBeanDefinitionRegistrar就可以了

@Configuration
@Import(value={UserServiceBeanDefinitionRegistrar.class})
public class Config {
?
}

??? ?

结合ImportSelector接口

相比较与实现ImportBeanDefinitionRegistrar接口之后直接操作Bean容器来说,使用ImportSelector会更加优雅一些,只需要返回需要注入类的全限定名就可以了

ImportSelector接口的源码如下:

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

public class UserServiceImportSelect implements ImportSelector{
?
??? public String[] selectImports(AnnotationMetadata importingClassMetadata) {
?????? ?
?????? return new String[]{UserServiceImpl.class.getName()};
??? }
?
}

@Configuration()
@Import(value={UserServiceImportSelect.class})
public class Config {
?
}

相比较三种方式来说可以看到最后这种才是最优雅的方式

源码解析

首先我们就以第三种比较优雅的方式出发,使用Call Hierarchy看一下ImportSelector接口的selectImports方法调用链关系:

1

看过之前Spring源码解析文章的同学都知道,refresh方法是用来初始化容器上下文的。跟着这个调用链走下来到中间有一个类是ConfigurationClassPostProcessor,根据类名我们就可以猜到这个类应该是处理配置类(也就是标注@Configuration)的。那么从这开始看吧

??? ?
??? ?


public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
??? List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
??? String[] candidateNames = registry.getBeanDefinitionNames();

??? for (String beanName : candidateNames) {
??????? BeanDefinition beanDef = registry.getBeanDefinition(beanName);

??????? if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
??????????????? ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
??????????? if (logger.isDebugEnabled()) {
??????????????? logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
??????????? }
??????? }
       //查看是否是配置类
??????? else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {

??????????? configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
??????? }
??? }
    //如果没有配置类就直接返回
??? if (configCandidates.isEmpty()) {
??????? return;
??? }

    //对这些配置类根据Order排序
??? Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
??????? @Override
??????? public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
??????????? int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
??????????? int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
??????????? return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
??????? }
??? });

??? SingletonBeanRegistry sbr = null;
??? if (registry instanceof SingletonBeanRegistry) {
??????? sbr = (SingletonBeanRegistry) registry;
??????? if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
??????????? BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
??????????? this.componentScanBeanNameGenerator = generator;
??????????? this.importBeanNameGenerator = generator;
??????? }
??? }
???????????? //创建配置类的解析类
??? ConfigurationClassParser parser = new ConfigurationClassParser(
??????????? this.metadataReaderFactory, this.problemReporter, this.environment,
??????????? this.resourceLoader, this.componentScanBeanNameGenerator, registry);

??? Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
??? Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
??? do {
ConfigurationClassParser的parse方法进行解析,重点哈
??????? parser.parse(candidates);
??????? parser.validate();

??????? Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
??????? configClasses.removeAll(alreadyParsed);

??????? // Read the model and create bean definitions based on its content
??????? if (this.reader == null) {
??????????? this.reader = new ConfigurationClassBeanDefinitionReader(
??????????????????? registry, this.sourceExtractor, this.resourceLoader, this.environment,
??????????????????? this.importBeanNameGenerator, parser.getImportRegistry());
??????? }
??????? this.reader.loadBeanDefinitions(configClasses);
??????? alreadyParsed.addAll(configClasses);

??????? candidates.clear();
??????? if (registry.getBeanDefinitionCount() > candidateNames.length) {
??????????? String[] newCandidateNames = registry.getBeanDefinitionNames();
??????????? Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
??????????? Set<String> alreadyParsedClasses = new HashSet<String>();
??????????? for (ConfigurationClass configurationClass : alreadyParsed) {
??????????????? alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
??????????? }
??????????? for (String candidateName : newCandidateNames) {
??????????????? if (!oldCandidateNames.contains(candidateName)) {
??????????????????? BeanDefinition bd = registry.getBeanDefinition(candidateName);
??????????????????? if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
??????????????????????????? !alreadyParsedClasses.contains(bd.getBeanClassName())) {
??????????????????????? candidates.add(new BeanDefinitionHolder(bd, candidateName));
??????????????????? }
??????????????? }
??????????? }
??????????? candidateNames = newCandidateNames;
??????? }
??? }
??? while (!candidates.isEmpty());

??? if (sbr != null) {
??????? if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
??????????? sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
??????? }
??? }

??? if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
??????? ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
??? }
}
??? ?

现在该进入ConfigurationClassParser类了

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

    for (BeanDefinitionHolder holder : configCandidates) {
    ??? BeanDefinition bd = holder.getBeanDefinition();
    ??? try {
    ??????? if (bd instanceof AnnotatedBeanDefinition) {
    ??????????? parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
    ??????? }
    ??????? else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
    ??????????? parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
    ??????? }
    ??????? else {
    ??????????? parse(bd.getBeanClassName(), holder.getBeanName());
    ??????? }
    ??? }
    ??? catch (BeanDefinitionStoreException ex) {
    ??????? throw ex;
    ??? }
    ??? catch (Throwable ex) {
    ??????? throw new BeanDefinitionStoreException(
    ??????????????? "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
    ??? }
    }

    processDeferredImportSelectors();
}
//下面三种方法用于处理不同的BeanDefinition 类型,但最终都是使用的processConfigurationClass方法
protected final void parse(String className, String beanName) throws IOException {
??? MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
??? processConfigurationClass(new ConfigurationClass(reader, beanName));
}

protected final void parse(Class<?> clazz, String beanName) throws IOException {
??? processConfigurationClass(new ConfigurationClass(clazz, beanName));
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
??? processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

可以看到配置类可能会是三种形式的存在,这三种形式的Bean在操作上有着部分不一样,但是大部分又是一样,所以Spring用这种模式来处理。不得不感叹人家设计的真好

接着往下看


protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
??? if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
??????? return;
??? }

??? ConfigurationClass existingClass = this.configurationClasses.get(configClass);
????//在这里处理Configuration重复import
    //如果同一个配置类被处理两次,两次都属于被import的则合并导入类,返回。如果配置类不是被导入的,则移除旧使用新的配置类
??? if (existingClass != null) {
??????? if (configClass.isImported()) {
??????????? if (existingClass.isImported()) {
??????????????? existingClass.mergeImportedBy(configClass);
??????????? }
??????????? return;
??????? }
??????? else {
??????????? this.configurationClasses.remove(configClass);
??????????? for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
??????????????? if (configClass.equals(it.next())) {
??????????????????? it.remove();
??????????????? }
??????????? }
??????? }
??? }

??? SourceClass sourceClass = asSourceClass(configClass);
??? do {
      //接着往下看吧
??????? sourceClass = doProcessConfigurationClass(configClass, sourceClass);
??? }
??? while (sourceClass != null);

??? this.configurationClasses.put(configClass, configClass);
}

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
??????? throws IOException {

??? // 处理递归类
??? processMemberClasses(configClass, sourceClass);

??? // 处理@PropertySource注解
??? for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
??????????? sourceClass.getMetadata(), PropertySources.class,
??????????? org.springframework.context.annotation.PropertySource.class)) {
??????? if (this.environment instanceof ConfigurableEnvironment) {
??????????? processPropertySource(propertySource);
??????? }
??????? else {
??????????? logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
??????????????????? "]. Reason: Environment must implement ConfigurableEnvironment");
??????? }
??? }

??? // 处理 @ComponentScan 注解
??? Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
??????????? sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
??? if (!componentScans.isEmpty() &&
??????????? !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
??????? for (AnnotationAttributes componentScan : componentScans) {
??????????? // The config class is annotated with @ComponentScan -> perform the scan immediately
??????????? Set<BeanDefinitionHolder> scannedBeanDefinitions =
??????????????????? this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
??????????? // Check the set of scanned definitions for any further config classes and parse recursively if needed
??????????? for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
??????????????? BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
??????????????? if (bdCand == null) {
??????????????????? bdCand = holder.getBeanDefinition();
??????????????? }
??????????????? if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
??????????????????? parse(bdCand.getBeanClassName(), holder.getBeanName());
??????????????? }
??????????? }
??????? }
??? }
????//处理Import注解,这个是咱们的菜
??? processImports(configClass, sourceClass, getImports(sourceClass), true);

??? // 处理@ImportResource 注解
??? if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
??????? AnnotationAttributes importResource =
??????????????? AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
??????? String[] resources = importResource.getStringArray("locations");
??????? Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
??????? for (String resource : resources) {
??????????? String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
??????????? configClass.addImportedResource(resolvedResource, readerClass);
??????? }
??? }

??? //处理包含@Bean注解的方法
??? Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
??? for (MethodMetadata methodMetadata : beanMethods) {
??????? configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
??? }

??? // 处理普通方法
??? processInterfaces(configClass, sourceClass);

???
??? if (sourceClass.getMetadata().hasSuperClass()) {
??????? String superclass = sourceClass.getMetadata().getSuperClassName();
??????? if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
??????????? this.knownSuperclasses.put(superclass, configClass);
??????????? // Superclass found, return its annotation metadata and recurse
??????????? return sourceClass.getSuperClass();
??????? }
??? }

??? return null;
}
??? ?

看到这里好像突然发现了新大陆呀,原来我们经常见的@Bean@ImportResource@Import@ComponentScan@PropertySource都是在这里处理的呀

咱们的重点还是放在@Import上,对其他几个注解感兴趣的同学可以自行研究一下

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)) {
??????????????????? 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 {
??????????????????????? String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
??????????????????????? Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
??????????????????????? processImports(configClass, currentSourceClass, importSourceClasses, false);
??????????????????? }
??????????????? }
                //如果实现了ImportBeanDefinitionRegistrar接口
??????????????? else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
??????????????????? 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 {
                     //将import当成Configuration来使用就是我们的第一种应用的方式
??????????????????? 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();
??????? }
??? }
}

原文地址:https://www.cnblogs.com/zhixiang-org-cn/p/11450972.html

时间: 2024-11-05 15:53:15

Spring @Import注解源码解析的相关文章

异步任务spring @Async注解源码解析

1.引子 开启异步任务使用方法: 1).方法上加@Async注解 2).启动类或者配置类上@EnableAsync 2.源码解析 虽然spring5已经出来了,但是我们还是使用的spring4,本文就根据spring-context-4.3.14.RELEASE.jar来分析源码. 2.1.@Async org.springframework.scheduling.annotation.Async 源码注释翻译: 1 /** 2 * Annotation that marks a method

Feign 系列(05)Spring Cloud OpenFeign 源码解析

Feign 系列(05)Spring Cloud OpenFeign 源码解析 [TOC] Spring Cloud 系列目录(https://www.cnblogs.com/binarylei/p/11563952.html#feign) 在 上一篇 文章中我们分析 Feign 参数解析的整个流程,Feign 原生已经支持 Feign.JAX-RS 1/2 声明式规范,本文着重关注 Spring Cloud 是如果整合 OpenFeign 的,使之支持 Spring MVC? 1. Sprin

Spring Security 解析(七) —— Spring Security Oauth2 源码解析

Spring Security 解析(七) -- Spring Security Oauth2 源码解析 ??在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .Spring Security Oauth2 等权限.认证相关的内容.原理及设计学习并整理一遍.本系列文章就是在学习的过程中加强印象和理解所撰写的,如有侵权请告知. 项目环境: JDK1.8 Spring boot 2.x Spring Security

Spring Boot 启动源码解析系列六:执行启动方法一

1234567891011121314151617181920212223242526272829303132333435363738394041424344 public ConfigurableApplicationContext (String... args) { StopWatch stopWatch = new StopWatch(); // 开始执行,记录开始时间 stopWatch.start(); ConfigurableApplicationContext context =

Spring一小部分源码解析(持续)

如何查看源码 Spring源码下载https://github.com/spring-projects/spring-framework/tags?after=v3.1.0.RC1 eclipse关联源码 自己百度吧 源代码结构组织 Build-spring-framework是整个Spring源代码的构建目录,里面是项目的构建脚本,如果要自己动手构建Spring,可以进入这个目录使用ANT进行构建. l  org.springframework.context是IoC容器的源代码目录 l  o

spring cloud ribbon源码解析(一)

我们知道spring cloud中restTemplate可以通过服务名调接口,加入@loadBalanced标签就实现了负载均衡的功能,那么spring cloud内部是如何实现的呢? 通过@loadBalanced我们进入标签 注释解释这个标签是标记为restTemplate,作为loadBalancerClient,接着去看loadBalancerClient loadBalancerClient通过继承serviceInstanceChooser,主要包含以下几个抽象方法: 1.choo

MapperScannerConfigurer源码解析

声明:源码基于mybatis-spring 1.3.2 前文 首先在阅读本文前需要明白整合后的使用方式以及熟悉MyBatis本身的工作原理,再者如果对于本文相关知识点不熟悉的可以参考下述文章. MyBatis与Spring整合 SqlSessionTemplate源码解析 Spring包扫描机制详解 前言 一般在项目中使用MyBatis时,都会和Spring整合一起使用,通过注入一个Mapper接口来操纵数据库.其中的原理就是使用了MyBatis-Spring的MapperScannerConf

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge

Spring源码解析和配置文件加载

Spring类的继承结构图: Spring运用了大量的模板方法模式和策略模式,所以各位看源码的时候,务必留意,每一个继承的层次都有不同的作用,然后将相同的地方抽取出来,依赖抽象将不同的处理按照不同的策略去处理. 步骤A. 读取 Resource 文件形成 Document 模型  类图: XmlBeanFactory -> XmlBeanDefinitionReader Spring 使用 XmlBeanDefinitionReader 来读取并解析 xml 文件,XmlBeanDefiniti