当前位置: 首页 >服务端 > Spring源码系列 — 注解原理

Spring源码系列 — 注解原理

前言

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

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

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

Spring源码系列 — 注解原理 _ JavaClub全栈架构师技术笔记

@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解析上下文@Overridepublic 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);	retu 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.setAutowireCandidatePattes(parserContext.getDelegate().getAutowireCandidatePattes());	// 配置资源模式	if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {		scanner.setResourcePatte(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);	retu 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	retu 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);			}		}	}	retu 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 = ResourcePatteResolver.CLASSPATH_ALL_URL_PREFIX +				resolveBasePackage(basePackage) + '/' + this.resourcePatte;		// 使用resourceloader获取给定的检索路径上的所有匹配的资源		// 关于这部分逻辑在介绍BeanDefinition一文中已经详细介绍,这里不再赘述		Resource[] resources = this.resourcePatteResolver.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);	}	retu 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();// 是否是finalboolean isFinal();boolean isIndependent();boolean hasEnclosingClass();String getEnclosingClassName();

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

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

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

Spring源码系列 — 注解原理 _ JavaClub全栈架构师技术笔记

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 {// 首先根据排除过滤器进行匹配,然后排除掉metadataReaderfor (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {retu false;}}// 然后根据包含过滤器筛选出满足的metadataReaderfor (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {retu isConditionMatch(metadataReader);}}retu 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指定的注解retu 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注解修饰的方法retu (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) {// 如果是注解型BeanDefinitionif (definition instanceof AnnotatedBeanDefinition) {// 从注解配置中解析BeanNameString beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);// 如果注解中指定了BeanName,则返回注解中配置的BeanNameif (StringUtils.hasText(beanName)) {// Explicit bean name found.retu beanName;}}// 如果注解中没有配置,则根据Bean的ShortClassName(即简单的class名字,非全限定名)生成// 生成时,className的首字母小写// Fallback: generate a unique default bean name.retu buildDefaultBeanName(definition, registry);}

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

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {// 处理公共注解processCommonDefinitionAnnotations(abd, abd.getMetadata());}static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {// 处理惰性初始化注解,将BeanDefinition设置成Lazyif (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 alteative 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 InstantiationAwareBeanPostProcessorAdapterimplements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {// 自动注入的注解类型,能够处理的自动注入注解的类型都存在该集合中private final Set<Class<? extends Annotation>> autowiredAnnotationTypes =new LinkedHashSet<Class<? extends Annotation>>(4);// 硬编码,注解参数requiredprivate String requiredParameterName = "required";private boolean requiredParameterValue = true;// 实现beanFactoryAware接口,准入BeanFactoryprivate 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,它是用来描述单个被注入的信息,如:

@Autowiredprivate A a;

将被解析成InjectedElement用于描述:

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

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

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

Spring源码系列 — 注解原理 _ JavaClub全栈架构师技术笔记

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

// 后置处理合并的BeanDefinition,解析@Autowired和@Value等注解配置@Overridepublic 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);}}}}retu 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() {@Overridepublic void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {AnnotationAttributes ann = findAutowiredAnnotation(field);if (ann != null) {if (Modifier.isStatic(field.getModifiers())) {if (logger.isWaEnabled()) {logger.wa("Autowired annotation is not supported on static fields: " + field);}retu;}boolean required = determineRequiredStatus(ann);currElements.add(new AutowiredFieldElement(field, required));}}});// 解析自动注入的方法的配置ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {@Overridepublic void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {retu;}AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (Modifier.isStatic(method.getModifiers())) {if (logger.isWaEnabled()) {logger.wa("Autowired annotation is not supported on static methods: " + method);}retu;}if (method.getParameterTypes().length == 0) {if (logger.isWaEnabled()) {logger.wa("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);// 根据自动注入配置集合,构建自动注入配置对象retu 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) {retu attributes;}}}retu null;}

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

@Overridepublic 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);}retu 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根据域注入的实现方式:

@Overrideprotected 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的classdesc.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值,并修改缓存表示为truesynchronized (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两大组件。它们负责完成了本文的提到的注解式配置。

作者:怀瑾握瑜XI
来源链接:https://www.cnblogs.com/lxyit/p/10210581.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaclub.cn/server/117212.html

标签:Spring注解
分享给朋友:

“Spring源码系列 — 注解原理” 的相关文章