• 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可以配置多个,所以这里根据分隔符进行分割处理,形成包路径数组。默认分割符为:",; 	
    "
    	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的实现:

    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. 排除解析的过滤器

    该两种过滤器可以在<context:component-scane />中配置。在配置类型过滤器阶段将负责解析注册这两种过滤器。

    // 包含的过滤器
    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处理器负责,在上节中已经介绍在处理时,AutowiredAnnotationBeanPostProcessor被作为公共组件注册至BeanFactory中。这里将详细介绍其实现。

    首先看下其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两大组件。它们负责完成了本文的提到的注解式配置。

  • 相关阅读:
    解决Uploadify 3.2上传控件加载导致的GET 404 Not Found问题
    Intellij idea的Dependencies波浪线
    Web.xml配置详解之context-param
    The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path(Myeclipse添加Server Library)
    html5 video mp4播放不了问题
    切片优化小拾
    解决video标签的兼容性
    css module.css demo
    Gnet 响应式官网开发总结
    前端小总结
  • 原文地址:https://www.cnblogs.com/lxyit/p/10210581.html
Copyright © 2020-2023  润新知