前言
前文中主要介绍了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由几个地方需要重点关注:
- 默认的过滤器
- 配置默认的BeanDefinitionDefaults
- 配置BeanName生成器
- 配置类型过滤器
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主要完成两项逻辑:
- 负责扫描目标包路径下的注解配置,并将其解析为BeanDefinition;
- 将解析出的BeanDefinition注册到BeanFactory中;
其中为了实现包路径下的Bean配置的扩展性、动态配置解析,Scanner中通过组合两种类型过滤器:
- 需要解析的过滤器
- 排除解析的过滤器
该两种过滤器可以在<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方法的实现。但是原理就是:
- 首先匹配自身类的metadataReader,即将metadataReader中的注解与AnnotationTypeFilter中的注解进行比较,如果metadataReader中的注解包含了AnnotationTypeFilter的,则认为是匹配;
- 否则再获取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的过程基本完成,这里再总结下:
- 循环遍历所有的包路径
- 将每个包路径组转成满足解析器的搜索路径格式
- 根据搜索路径加载所有资源
- 遍历所有资源,将资源转化成MetadataReader
- 根据MetadataReader和TypeFilter进行匹配,筛选出符合的MetadataReader
- 根据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系注解的大部分工作就已经完成了。但是还剩下最后的注册公共的组件步骤,这些公共组件主要用处理特定的注解:
- 注册处理@Configuration注解的处理器ConfigurationClassPostProcessor,其中有包含处理@Import,@ Configuration,@Bean等逻辑;
- 注册处理@Autowired和Value注解的处理器AutowiredAnnotationBeanPostProcessor,其中主要处理Bean的注解式自动装配逻辑;
- 注册处理@Required注解的处理器RequiredAnnotationBeanPostProcessor;
- 注册处理@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两大组件。它们负责完成了本文的提到的注解式配置。