Spring源码系列 — 注解原理

前言

前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展。本篇文章承接该内容,详解Spring中如何利用BeanDefinitionParser的特性实现注解配置的解析。本文主要从以下几个方面介绍Spring中的注解配置解析原理:

  • @Component系注解配置的作用原理
  • @Autowired注解配置的作用原理

无论注解配置还是XML配置,只是外在配置形式的变化,但是Spring的核心仍然是相同的:

@Component系注解配置的作用原理

在介绍@Component原理前,先总结下@Component系的注解:

  • @Service
  • @Repository
  • @Configuration

以上这些都均属于@Component系注解,均被Component修饰。其实Spring应用者需要明白无论是XML配置,还是以上的这些注解配置,它们只是配置形式上的变化,但是内在表述这种配置的BeanDefinition模型仍然是统一的。形式上的变化,只会导致适配该形式的解析会发生变化。下文将从源码的角度先分析Component系注解配置的解析。

涉及到Bean配置的解析自然不能脱离前文中讲述到的BeanDefinitionParser(如果还没有阅读过前文的读者,这里送一个传送门Spring源码系列 — BeanDefinition扩展点)。

同时大家也应该知道触发@Component注解生效,需要配置XML:<component-scan/>。

从前文的ContextNameSpaceHandler中可以看到component-scan该配置由ComponentScanBeanDefinitionParser负责解析。该BeanDefinitionPaser是开始处理@Component的入口,负责将被@Component系注解修饰的配置解析为BeanDefinition。

同样ComponentScanBeanDefinitionParser实现了parse方法:

// element为XML配置元素,parserContext解析上下文
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 获取该元素的base-package的属性
    // spring应用者,应该不陌生<context:component-scan base-package="..."/>这样的配置
    // 获取被@Componet系注解配置的类所在的包路径
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    // 利用环境组件解析路径,处理其中的占位。关于这部分内容在前文中已经介绍
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    // 因为base-package可以配置多个,所以这里根据分隔符进行分割处理,形成包路径数组。默认分割符为:",; \t\n"
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    // Actually scan for bean definitions and register them.
    // 创建Bean定义的扫描器组件
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // 扫描basePackages下被@Componet系注解修饰的Bean,形成BeanDefinition
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    // 注册公共的组件
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

以上的逻辑已经非常清晰明了,对于使用环境组件处理basePackages中的占位部分,如果读者不了解,这里有传送门Spring源码系列 — Envoriment组件。 继续学习configureScanner中如果创建Scanner。主要分为两部分:

  • 创建Scanner
  • 配置Scanner
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
    // 解析componet-scan中配置的use-default-filters属性
    boolean useDefaultFilters = true;
    if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
        useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
    }
    // 创建Bean定义Scanner
    // Delegate bean definition registration to scanner class.
    ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
    // 配置默认的BeanDefinitionDefaults,该对象持有默认的Bean定义属性
    scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
    // 配置自动注入候选者模式
    scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
    // 配置资源模式
    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
        scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
    }
    try {
        // 配置BeanName生成器
        parseBeanNameGenerator(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
    try {
        // 配置作用域
        parseScope(element, scanner);
    }
    catch (Exception ex) {
        parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
    }
    // 配置类型过滤器
    parseTypeFilters(element, scanner, parserContext);
    return scanner;
}

关于以上配置Scanner由几个地方需要重点关注:

  1. 默认的过滤器
  2. 配置默认的BeanDefinitionDefaults
  3. 配置BeanName生成器
  4. 配置类型过滤器

BeanDefinitionDefaults是持有BeanDefinition属性的默认配置,其中有是否惰性初始化、自动装配模式、初始化/销毁方法等。这些Bean定义属性将会作为默认值配置到将要被解析的BeanDefinition中。
BeanName生成器将会为没有配置Bean名字的BeanDefinition生成Bean名字。
类型过滤器非常重要,使用base-packages配置了包路径,这些路径下的类需要被过滤。如使用@Component注解修饰的类将会被过滤,然后解析为BeanDefinition,其他的类将不会被处理。
其中有变量useDefaultFilters标志是否使用默认的过滤器,我们继续看下createScanner的实现:

扫描二维码关注公众号,回复: 4751549 查看本文章
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
    // 直接new创键Scanner
    return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
            readerContext.getEnvironment(), readerContext.getResourceLoader());
}

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
        Environment environment, ResourceLoader resourceLoader) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    // 赋值BeanDefinitionRegistry
    this.registry = registry;
    // 判断是否使用默认的过滤器
    if (useDefaultFilters) {
        // 注册默认的过滤器
        registerDefaultFilters();
    }
    // 设置环境组件
    setEnvironment(environment);
    // 设置资源加载器
    setResourceLoader(resourceLoader);
}

以上需要注意的是resourceLoader,前文在介绍BeanDefinition解析时,提到XmlReaderContext是用于持有Spring解析XML配置时的各种组件信息,其中包括资源加载器。这里将资源加载器传给Scanner,Scanner通过组合ResourceLoader从而具有了加载资源的能力。

同时需要注意useDefaultFilters,它用于控制是否注册默认的类型过滤器,继续看下默认的过滤器是什么:

protected void registerDefaultFilters() {
    // includeFilters中添加关于Component注解的过滤器
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 添加ManagedBean注解的过滤器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
        // 添加Named注解的过滤器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

从以上默认的过滤器可以看出,被@Component、@ManagedBean、@Named注解修饰的类都将被注册为Bean。

创建和配置Scanner的逻辑虽然不复杂,当时调用链路还是有点绕弯。这里整理下:

Scanner主要完成两项逻辑:

  1. 负责扫描目标包路径下的注解配置,并将其解析为BeanDefinition;
  2. 将解析出的BeanDefinition注册到BeanFactory中;

其中为了实现包路径下的Bean配置的扩展性、动态配置解析,Scanner中通过组合两种类型过滤器:

  1. 需要解析的过滤器
  2. 排除解析的过滤器

该两种过滤器可以在

// 包含的过滤器
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();

// 排除的过滤器
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();

在了解Scanner的创建和配置后,再来看其如何加载资源,检测并解析BeanDefinition和注册。

其中doScan中完成以上的三步逻辑:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    // 创建将被解析的BeanDefinition容器
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    // 循环遍历扫描多个包路径
    for (String basePackage : basePackages) {
        // 在basePackage下寻找BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 循环遍历已寻找到的BeanDefinition
        for (BeanDefinition candidate : candidates) {
            // 解析该candidate的作用域
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成BeanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 如果是AbstractBeanDefinition,则后置处理BeanDefinition
            // 主要将BeanDefinitionDefaults的属性覆盖到BeanDefiniton中
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 处理该BeanDefinition的一些公共的注解信息
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 检查该Bean定义是否已经存在,或者与已存在的Bean定义是否兼容
            if (checkCandidate(beanName, candidate)) {
                // 注册Bean定义至BeanFactory中
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

首先看前两步骤,加载资源然后检测解析Bean定义,这个过程是由findCandidateComponents完成:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    // 创建Bean定义容器
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    try {
        // 基于给定的包路径组装成检索的路径:
        // 1. 将包路径分隔符替换成文件系统分隔符
        // 2. 加上资源解析模式前缀"classpath*:"
        // 3. 加上资源模式后缀"**/*.class",用于表示路径层次和要检测的文件类型为.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 使用resourceloader获取给定的检索路径上的所有匹配的资源
        // 关于这部分逻辑在介绍BeanDefinition一文中已经详细介绍,这里不再赘述
        Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        // 遍历每个资源
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            // 如果资源是可读
            if (resource.isReadable()) {
                try {
                    // 这里使用工厂模式获取该资源的元数据阅读器(MetadataReader)
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    // 根据MetadataReader判断是否为匹配的候选者Component
                    if (isCandidateComponent(metadataReader)) {
                        // 如果是,则构造ScannedGenericBeanDefinition类型的Bean定义
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        // 设置Bean定义的resourc和source属性
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        // 决策该class是否为满足条件的候选者
                        // 1. 需要满足是非抽象类;2. 需要满足是顶级的无依赖类(即非嵌套的内部类)
                        if (isCandidateComponent(sbd)) {
                            // 如果是候选者,则加入bean定义容器中
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            // 资源不可读,则忽略跳过该资源
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

在细说这段逻辑之前,先来了解一些基础的组件:MetadataReader、MetadataReaderFactory、ClassMetadata、AnnotationMetadata、ScannedGenericBeanDefinition、TypeFilter。

MetadataReader是Spring封装用于访问class文件的门面工具,通过它可以获取到class的元信息。主要是通过ASM库的ClassReader实现。从接口定义上分析:

public interface MetadataReader {
    // 获取class类的元信息
    ClassMetadata getClassMetadata();
    // 获取该类被修饰的注解的元信息
    AnnotationMetadata getAnnotationMetadata();
}

MetadataReaderFactory也是Spring封装用于创建指定类的MetadataReader门面。即这里使用了工厂模式。通过其接口抽象可以可以体会到其工厂的设计理念:

public interface MetadataReaderFactory {
    // 根据类名获取指定的MetadataReader
    MetadataReader getMetadataReader(String className) throws IOException;
    // 根据指定的resource获取对应的MetadataReader
    MetadataReader getMetadataReader(Resource resource) throws IOException;
}

ClassMetadata是该类的元信息的抽象,其中提供大量的接口用于描述该类的特征。如:

// 获取该类类名
String getClassName();
// 是否为接口
boolean isInterface();
boolean isAnnotation();
boolean isAbstract();
// 是否为实现类
boolean isConcrete();
// 是否是final
boolean isFinal();
boolean isIndependent();
boolean hasEnclosingClass();
String getEnclosingClassName();

AnnotationMetadata是修饰该类的注解信息的抽象,它用于描述修饰该类注解的元信息。包含了修饰的注解所有信息,包括注解本身的属性。

// 获取所有的注解类型
Set<String> getAnnotationTypes();
// 根据名称获取注解属性
Map<String, Object> getAnnotationAttributes(String annotationName);

Spring通过提供这套组件,可以非常便捷的访问类信息。它底层的原理是依赖ASM字节码库实现。下图描述了大致原理:

Tips:
Spring中封装的MetadataReader和MetadataReaderFactory,笔者认为本身定位也是作为工具使用,所以日常应用中开发中,完全可以使用其提供的能力。当然是Spring应用才是最好这样使用。

Note:
从以上的介绍中,不可忽略的是这里访问class信息,并未涉及到类加载器系统将该类加载至JVM中形成class对象。这个需要重点关注:该类尚未被加载。只是被ASM加载了而已!

同时可以通过以下的例子更深入的了解其特性和API:

String className = AnnotatedTargetClass.class.getName();
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
ClassMetadata classMetadata = metadataReader.getClassMetadata();
System.out.println("isConcrete:" + classMetadata.isConcrete());
System.out.println("isAbstract:" + classMetadata.isAbstract());
System.out.println("isFinal:" + classMetadata.isFinal());
System.out.println("isIndependent:" + classMetadata.isIndependent());
System.out.println("isInterface:" + classMetadata.isInterface());
System.out.println("isAnnotation:" + classMetadata.isAnnotation());

AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
System.out.println("all annotations type:" + String.join(",",
        annotationMetadata.getAnnotationTypes()));
Map<String, Object>  annotationAttrs = annotationMetadata
        .getAnnotationAttributes(XmlRootElement.class.getName());
Set<Map.Entry<String, Object>> entries = annotationAttrs.entrySet();
for (Map.Entry<String, Object> entry : entries) {
    System.out.println("attribute name:" + entry.getKey() + ", attribute name:" + entry.getValue());
}

在介绍完访问类的元信息后,继续看下ScannedGenericBeanDefinition和TypeFilter。

首先ScannedGenericBeanDefinition是一种BeanDefinition类型,它是扩展GenericBeanDefinition实现,同时基于ASM增加了对AnnotatedBeanDefinition注解暴露接口的支持,即实现了AnnotatedBeanDefinition接口。提供获取AnnotationMetadata和MethodMetadata的信息。通过注解形式配置的Bean定义需要转化为该类型的BeanDefinition。因为它可以提供获取注解信息。

TypeFilter是类型过滤器,通过抽象接口:

boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
        throws IOException;

匹配过滤指定的metadataReader。metadataReader本身代表class的访问器,通过TypeFilter筛选出需要的metadataReader,然后将其转化为BeanDefinition。

在了解一些基础组件后,便可以深入分析Spring如何筛选出被@Componet系注解修饰的类。

上述代码在遍历每个Resource中,利用isCandidateComponent(metadataReader)筛选指定的metadataReader:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    // 首先根据排除过滤器进行匹配,然后排除掉metadataReader
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return false;
        }
    }
    // 然后根据包含过滤器筛选出满足的metadataReader
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

前文在介绍excludeFilters和includeFilters正是被这里使用。在默认情况下(componet-scan中未做任何filter的配置时),只有includeFilters中包含@Component的AnnotationTypeFilter,这个由前文中介绍的registerDefaultFilters将其注册。

由于篇幅原因,这里不在详述match方法的实现。但是原理就是:

  1. 首先匹配自身类的metadataReader,即将metadataReader中的注解与AnnotationTypeFilter中的注解进行比较,如果metadataReader中的注解包含了AnnotationTypeFilter的,则认为是匹配;
  2. 否则再获取metadataReader的父类的metadataReader,再如上的方式比较;

可以看下其比较方式:

// 获取所有的注解元信息
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// 如果注解中包含AnnotationTypeFilter指定的注解
return metadata.hasAnnotation(this.annotationType.getName()) ||
        (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));

通过以上的方式可以成功的找到@Component系注解的所有metadataReader,然后利用其构造ScannedGenericBeanDefinition。然后再看下如何筛选BeanDefinition的isCandidateComponent:

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    // 获取BeanDefintion中的注解元信息
    AnnotationMetadata metadata = beanDefinition.getMetadata();
    // 抉择该类是否为实现和无依赖类,或者该类是抽象类但是有Lookup注解修饰的方法
    return (metadata.isIndependent() && (metadata.isConcrete() ||
            (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

到这里,关于资源的加载、@Component系注解配置检测、解析为BeanDefition的过程基本完成,这里再总结下:

  1. 循环遍历所有的包路径
  2. 将每个包路径组转成满足解析器的搜索路径格式
  3. 根据搜索路径加载所有资源
  4. 遍历所有资源,将资源转化成MetadataReader
  5. 根据MetadataReader和TypeFilter进行匹配,筛选出符合的MetadataReader
  6. 根据MetadataReader创建ScannedGenericBeanDefinition

再继续生成BeanName的逻辑:

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    // 如果是注解型BeanDefinition
    if (definition instanceof AnnotatedBeanDefinition) {
        // 从注解配置中解析BeanName
        String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
        // 如果注解中指定了BeanName,则返回注解中配置的BeanName
        if (StringUtils.hasText(beanName)) {
            // Explicit bean name found.
            return beanName;
        }
    }
    // 如果注解中没有配置,则根据Bean的ShortClassName(即简单的class名字,非全限定名)生成
    // 生成时,className的首字母小写
    // Fallback: generate a unique default bean name.
    return buildDefaultBeanName(definition, registry);
}

然后再继续看处理公共注解的逻辑AnnotationConfigUtils.processCommonDefinitionAnnotations

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    // 处理公共注解
    processCommonDefinitionAnnotations(abd, abd.getMetadata());
}
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    // 处理惰性初始化注解,将BeanDefinition设置成Lazy
    if (metadata.isAnnotated(Lazy.class.getName())) {
        abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value"));
    }

    else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) {
        abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value"));
    }
    // 处理Primary注解,将BeanDefinition设置为首选的主要候选者
    if (metadata.isAnnotated(Primary.class.getName())) {
        abd.setPrimary(true);
    }
    // 解析DependsOn注解
    if (metadata.isAnnotated(DependsOn.class.getName())) {
        abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value"));
    }
    // 处理Role和Description注解
    if (abd instanceof AbstractBeanDefinition) {
        AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
        if (metadata.isAnnotated(Role.class.getName())) {
            absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue());
        }
        if (metadata.isAnnotated(Description.class.getName())) {
            absBd.setDescription(attributesFor(metadata, Description.class).getString("value"));
        }
    }
}

以上公共的注解处理逻辑中关于惰性初始化的尤其需要注意。

对BeanDefinition解析完成后,接下来就是将其注册到BeanFactory中。但是在注册之前需要进行检查该BeanDefinition是否已经存在,防止重复注册。注册BeanDefinition的逻辑相信大家应该不陌生了,就是使用BeanDefinition注册器将BeanDefinition注册至BeanFactory。

完成了BeanDefinition的注册,关于@Component系注解的大部分工作就已经完成了。但是还剩下最后的注册公共的组件步骤,这些公共组件主要用处理特定的注解:

  1. 注册处理@Configuration注解的处理器ConfigurationClassPostProcessor,其中有包含处理@Import,@ Configuration,@Bean等逻辑;
  2. 注册处理@Autowired和Value注解的处理器AutowiredAnnotationBeanPostProcessor,其中主要处理Bean的注解式自动装配逻辑;
  3. 注册处理@Required注解的处理器RequiredAnnotationBeanPostProcessor;
  4. 注册处理@PreDestroy,@PostConstruct,@Resource(JSR-250的注解)这些java自身的注解处理器CommonAnnotationBeanPostProcessor
protected void registerComponents(
        XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
    // 定义复合组件定义对象
    Object source = readerContext.extractSource(element);
    CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
    // 将扫描出的BeanDefinition添加到符合组件定义中
    for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
        compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
    }
    // 从xml中获取是否注册公共注解处理器的配置属性
    // Register annotation config processors, if necessary.
    boolean annotationConfig = true;
    if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
        annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
    }
     // 根据配置判断是否注册一些公共的处理注解配置处理器
    if (annotationConfig) {
        // 注册公共的注解处理器
        Set<BeanDefinitionHolder> processorDefinitions =
                AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
        // 将公共的注解处理器加入符合组件定义中
        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
            compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
        }
    }
    // 发起组件注册事件
    readerContext.fireComponentRegistered(compositeDef);
}

以上主要是获取配置,判断是否注册公共的注解配置处理器,其中注册的逻辑由AnnotationConfigUtils.registerAnnotationConfigProcessors工具方法实现。

其中关于ConfigurationClassPostProcessor的注解处理器还是非常重要,但是由于篇幅原因这里不再详述。后续可能会不上一篇单独介绍他。

到这里,关于@Component系注解的作用原理就已经介绍完成,后续就是Bean的实例化的逻辑,关于该内容请参考之前的文章,这里不再赘述。

接下来就开始介绍以上注解处理器中的另一个至关重要的角色:AutowiredAnnotationBeanPostProcessor

@Autowired注解配置的作用原理

关于@Autowired的作用相信读者应该都知道:即注解式自动装配。类似XML配置一样,配置对象之间的依赖关系,使用也是非常简单。但是关于该注解的内部实现原理,很多使用者都知之甚少,本节将对其原理进行深入介绍。

先分析@Autowired注解完成的工作,以及这些工作应该在Spring的哪些阶段中完成相应的逻辑。

@Autowired作用在方法或者成员域上,将依赖的Bean注入。比如A依赖B,A类中有B类性的成员,在B成员域上加上@Autowired,Spring将会管理这种依赖,将B的实例注入A的实例中(前提A和B都需要被注册为Bean)。这个工作需要完成两件事:

  • 解析@Autowired配置
  • 寻找依赖的Bean,将依赖的Bean注入

其中解析@Autowired配置属于解析BeanDefinition阶段,寻找依赖的Bean,将依赖的Bean注入属于在装配阶段。Spring中在解析@Autowired配置,虽然是解析BeanDefinition,但是是在实例化阶段之后解析,并非在解析BeanDefinition阶段。

关于以上两部分逻辑由Spring中的AutowiredAnnotationBeanPostProcessor处理器负责,在上节中已经介绍在处理

首先看下其javadocs的部分描述:

{@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
that autowires annotated fields, setter methods and arbitrary config methods.
Such members to be injected are detected through a Java 5 annotation: by default,
Spring's {@link Autowired @Autowired} and {@link Value @Value} annotations. Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation, if available, as a direct alternative to Spring's own {@code @Autowired}.

从docs中可以看出,其是BeanPostProcessor的实现,用于处理在域和setter方法上的@ Autowired注解和@Value注解。同时还支持jsr-330的注解@Inject。

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {}

该类通过同时还实现了MergedBeanDefinitionPostProcessor接口,该接口是对BeanPostProcessor的更进一步抽象,提供对合并BeanDeifnition的回调处理。其实现该接口,实现对合并BeanDeifnition解析,最终完成对@Autowired和@Value的解析。

通过继承实现InstantiationAwareBeanPostProcessorAdapter抽象类,以实现InstantiationAwareBeanPostProcessor接口,从而完成对该方法postProcessPropertyValues的实现,在这里完成对依赖的Bean的寻找和注入装配工作。InstantiationAwareBeanPostProcessor也是对BeanPostProcessor的更进一步抽象,提供对Bean实例化之前和之后的后置处理回调,也提供了对装配阶段的后置处理回调postProcessPropertyValues。

MergedBeanDefinitionPostProcessor在Bean实例化之后,被触发。InstantiationAwareBeanPostProcessor对装配阶段的后置处理回调,是在装配之前触发,对于详情可以参考我的另一篇文章Spring源码系列 — Bean生命周期

首先看AutowiredAnnotationBeanPostProcessor的结构

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

    // 自动注入的注解类型,能够处理的自动注入注解的类型都存在该集合中
    private final Set<Class<? extends Annotation>> autowiredAnnotationTypes =
            new LinkedHashSet<Class<? extends Annotation>>(4);

    // 硬编码,注解参数required
    private String requiredParameterName = "required";

    private boolean requiredParameterValue = true;

    // 实现beanFactoryAware接口,准入BeanFactory
    private ConfigurableListableBeanFactory beanFactory;

    // 自动注入元信息集合,存储被自动注入注解修饰的信息
    private final Map<String, InjectionMetadata> injectionMetadataCache =
            new ConcurrentHashMap<String, InjectionMetadata>(256);
}

其中autowiredAnnotationTypes集合存储能够被处理的注解信息:

public AutowiredAnnotationBeanPostProcessor() {
    // 增加对@Autowired注解的支持
    this.autowiredAnnotationTypes.add(Autowired.class);
    // 增加对@Vaule的注解的支持
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        // 增加对Inject注解的支持
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

然后便是解析@Autowired和@Value配置,将其抽象成InjectionMetadata对象,存入其集合中。InjectionMetadata结构如下:

public class InjectionMetadata {
    // 目标类型
    private final Class<?> targetClass;
    // 被注入元素的集合
    private final Collection<InjectedElement> injectedElements;
    // 被检查元素的集合
    private volatile Set<InjectedElement> checkedElements;
}

该实例中抽象有InjectedElement,它是用来描述单个被注入的信息,如:

@Autowired
private A a;

将被解析成InjectedElement用于描述:

public abstract static class InjectedElement {
    // 被注解的成员,Field还是Method
    protected final Member member;
    // 是否为Field被注入
    protected final boolean isField;
    // 属性描述,javabeans中的接口
    protected final PropertyDescriptor pd;
}

其中有两个实现被作为AutowiredAnnotationBeanPostProcessor其内部类,AutowiredFieldElement和AutowiredMethodElement。前者代表被注入的域信息,后者代表被注入的方法信息。

关于数据结构的示意图如下:

自动注入的注解@Autowired、@Value等最终将会解析成如上的结构,被injectionMetadataCache成员持有。所以AutowiredAnnotationBeanPostProcessor中将会有所有Bean的注解式自动配置元信息。关于具体实现看以下分析:

// 后置处理合并的BeanDefinition,解析@Autowired和@Value等注解配置
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 如果beanType不为空,则查找解析@Autowired注解配置,将抽象成InjectionMetadata,用其描述注入元信息
    if (beanType != null) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }
}

这里的核心逻辑便是findAutowiringMetadata,根据beanType寻找其自动装配的元配置信息:

// 因为解析class,查找解析注解配置信息是耗性能且这些配置信息比较固定,所以这里使用缓存,将解析的注解配置信息缓存在
// injectionMetadataCache中
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // 获取缓存的key,如果存在beanName,则将beanName作为缓存key;如果不存在,则将类名作为缓存key
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // 从缓存中获取注解配置元信息
    // Quick check on the concurrent map first, with minimal locking.
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    // 如果没有缓存或者类型不匹配,则重新获取
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        // 线程同步
        synchronized (this.injectionMetadataCache) {
            // 使用双重检查,再次获取
            metadata = this.injectionMetadataCache.get(cacheKey);
            // 如果没有或者类型不匹配,则重新获取
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                try {
                    // 构建自动注入的配置信息
                    metadata = buildAutowiringMetadata(clazz);
                    // 将其缓存
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
                catch (NoClassDefFoundError err) {
                    throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
                            "] for autowiring metadata: could not find class that it depends on", err);
                }
            }
        }
    }
    return metadata;
}

这个查找逻辑非常简单,就是从缓存中获取,如果没有则重新构建自动注入的配置信息。但是这里的设计思想却是非常经典。

Notes:
缓存的设计思想在日常应用的开发中被大量使用,一般模式都遵循先从缓存获取,如果缓存没有,则从数据源获取,再填充缓存。

对于配置信息的具体解析构建则在buildAutowiringMetadata中实现:

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    // 创建注入配置元素集合
    LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
    // 将当前需要解析的类作为目标类,即将解析
    Class<?> targetClass = clazz;
    // 循环遍历解析当前类以及所有的父类
    do {
        // 创建解析类所对应的自动注入配置的集合
        final LinkedList<InjectionMetadata.InjectedElement> currElements =
                new LinkedList<InjectionMetadata.InjectedElement>();
        // 解析自动注入的域配置
        ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                AnnotationAttributes ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static fields: " + field);
                        }
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            }
        });
        // 解析自动注入的方法的配置
        ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static methods: " + method);
                        }
                        return;
                    }
                    if (method.getParameterTypes().length == 0) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation should only be used on methods with parameters: " +
                                    method);
                        }
                    }
                    boolean required = determineRequiredStatus(ann);
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            }
        });
        // 加入总个集合中
        elements.addAll(0, currElements);
        // 获取父类,再次解析父类
        targetClass = targetClass.getSuperclass();
    }
    // 当类为空,或者是Object最顶层类时跳出循环
    while (targetClass != null && targetClass != Object.class);
    // 根据自动注入配置集合,构建自动注入配置对象
    return new InjectionMetadata(clazz, elements);
}

以上的逻辑也是非常简单,就利用反射库提供的API获取class的所有Field和Method,然后解析其被标注的注解是否匹配自动注入注解类型,如果匹配则将其解析为AutowiredFieldElement和AutowiredMethodElement。

可以看其使用反射API的具体细节:

public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
    // 获取class的所有域,循环遍历处理其上注解
    for (Field field : getDeclaredFields(clazz)) {
        try {
            fc.doWith(field);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
        }
    }
}

public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
    // 获取class中所有的方法,循环遍历处理其上注解
    Method[] methods = getDeclaredMethods(clazz);
    for (Method method : methods) {
        try {
            mc.doWith(method);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
        }
    }
}

当然这里使用了回调的方式非常巧妙,使其更具有扩展性。再以具体的寻找域上的注解的实例详解:

private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
    // 该ao上的注解不为空
    if (ao.getAnnotations().length > 0) {  // autowiring annotations have to be local
        // 遍历自动处理注解类型
        for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
            // 获取该ao上的自动处理注解(@Autowired, @Value, @Injected)
            AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
            // 如果注解属性不为空,则返回
            if (attributes != null) {
                return attributes;
            }
        }
    }
    return null;
}

到这里,读者应该明白Spring中如何解析自动注入的注解式配置了。接下来便再从装配的角度分析其原理:

@Override
public PropertyValues postProcessPropertyValues(
        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    // 寻找自动装配元配置,这个逻辑前面已经介绍
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        // 进行注入处理
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

以上逻辑非常简单,根据自动装入元配置进行注入:

public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
    // 获取需要注入的元素InjectedElement的集合
    Collection<InjectedElement> elementsToIterate =
            (this.checkedElements != null ? this.checkedElements : this.injectedElements);
    // 进行迭代注入
    if (!elementsToIterate.isEmpty()) {
        for (InjectedElement element : elementsToIterate) {
            if (logger.isDebugEnabled()) {
                logger.debug("Processing injected element of bean '" + beanName + "': " + element);
            }
            element.inject(target, beanName, pvs);
        }
    }
}

关于使用InjectedElement注入的逻辑由其实现类实现,这里主要分析下AutowiredFieldElement根据域注入的实现方式:

@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    // 是域方式注入,将Member接口强转成Field类型
    Field field = (Field) this.member;
    Object value;
    // 如果参数有被缓存,则从缓存中解析
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    // 否则重新解析依赖
    else {
        // 将field重新包装成依赖描述
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        // 设置包含这个依赖的bean的class
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
        // 获取转换器
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            // 获取依赖的值,这里使用了BeanFactory提供方法,这个方法之前的Bean创建中已经介绍,这里不再赘述
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        // 同步,然后缓存value值,并修改缓存表示为true
        synchronized (this) {
            if (!this.cached) {
                if (value != null || this.required) {
                    this.cachedFieldValue = desc;
                    registerDependentBeans(beanName, autowiredBeanNames);
                    if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        if (beanFactory.containsBean(autowiredBeanName) &&
                                beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                            this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                    desc, autowiredBeanName, field.getType());
                        }
                    }
                }
                else {
                    this.cachedFieldValue = null;
                }
                this.cached = true;
            }
        }
    }
    // 使用反射方式将值注入
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

其中BeanFactory的resolveDependency解析依赖中使用到了QualifierAnnotationAutowireCandidateResolver解析器,该解析器实现了解析Field中的@Value注解获取值,如果value值不为空则当做依赖返回。关于这些细节由于篇幅原因这里不再详述。

总结

本文主要针对Spring中的注解式配置@Component系和@Autowired的作用原理从源码实现的角度进行深层次的剖析。对于这两项基本功能,Spring都是以插件的形式提供扩展的,主要基于Spring上下文内核提供的运行解析BeanDefinitionParser能力和Bean的实例化装配能力。
其中需要重点掌握的是ComponentScanBeanDefinitionParser和AutowiredAnnotationBeanPostProcessor两大组件。它们负责完成了本文的提到的注解式配置。

猜你喜欢

转载自www.cnblogs.com/lxyit/p/10210581.html