@Autowired实现流程
作者:互联网
@Autowired实现流程
与xml配置方式相比,开启注解处理之后在加载BeanDefinition时会额外添加几个用于处理注解的组件,一个BeanDefinitionRegistryPostProcessor和若干个BeanPostProcessor,这些组件用于在bean的各个生命周期中对标注的注解做相应的处理。
大体流程与不使用注解的方式类似,使用注解的方式只是在某些阶段额外做了一些对于注解的处理。开启注解扫描其实就是在原有的基础上增加了一些功能。
初始化工厂,加载BeanDefinition
同样使用XmlBeanDefinitionReader
先将xml配置文件封装为Resource
,然后将Resource
转成流并使用DOM解析得到Document
,然后从根节点开始遍历所有节点。
当扫描到<context:component-scan base-package="bean"/>
标签时,会加载用来解析该节点所属namespace的NamespaceHandler
,namespace与NamespaceHandler
的对应关系在META-INF\spring.handlers
文件中,因此会加载ContextNamespaceHandler
用来处理\<context:component-scan/>
标签。
通过调用Class.forName()
实例化ContextNamespaceHandler
,并调用其init()
方法,在init()
方法中会注册该namespace下每个标签对应的BeanDefinitionParser
,保存在一个Map中。
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
然后找到与<context:component-scan\>
标签对应的BeanDefinitionPaser
,调用parse()
方法开始解析。
public BeanDefinition parse(Element element, ParserContext parserContext) {
//...
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//扫描并注册BeanDefinition
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//注册处理注解相关的其他组件
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
doScan()
方法会扫描并注册BeanDefinition
,流程如下:
首先获取标签的base-package属性的值,扫描该包下的所有*.class
文件并包装成Resource
,遍历每个Resource
,获取其元数据,判断该类上是否有@Component
注解(@Service
、@Repository
、@Controller
其实都被@Component
标注),并且判断是否有@Conditional
注解,如果不符合条件则跳过,符合条件则将该Resource
转换为BeanDefinition
注册到beanFactory的Map中。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
//遍历指定的每个包
for (String basePackage : basePackages) {
//扫描当前包下含有@Component的类并封装为BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
//处理每个BeanDefinition并判断是否需要注册到容器中
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//判断该BeanDefinition是否应该注入到容器中
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将BeanDefinition注册到容器中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
在findCandidateComponents()
方法中会调用isCandidateComponent()
方法根据Resource
的元数据判断当前类上是否含有@Component
注解,如果没有则跳过该类,如果有则判断当前类是否还标注了@Conditional
注解,如果标注了则会去处理@Conditional
注解判断当前类是否需要跳过。如果该类符合条件则将其封装为BeanDefinition
返回。对于每一个BeanDefinition
都要根据其类型做相应的处理,例如依据@Lazy
注解设置该BeanDefinition
的属性。对BeanDefinition
做完相应的处理之后会判断每一个BeanDefinition
是否应该被注入到容器中,判断条件是:如果容器中不存在beanName相同的BeanDefinition
则直接将该BeanDefinition
注入到容器中,否则不注册。
至此,doScan()
方法结束,也就是说@Component
所标记的类已经被封装为BeanDefinition
注册到容器中,接下来会调用registerComponents()
方法向容器中注入一些处理注解所需要的组件。
registerComponents()
方法首先会判断\<context:component-scan>
标签的annotation-config
的属性是否为true
(默认为true
,除非在xml中显示配置为false
),该属性表示将来是否需要处理已加载到容器中的bean中包含的注解。如果此属性配置为true,则会在注册完@Component
标记的类之后会注册一系列处理注解需要的组件,新注册的组件为:ConfigurationClassPostProcessor
、AutowiredAnnotationBeanPostProcessor
、EventListenerMethodProcessor
、DefaultEventListenerFactory
。
至此,<context:component-scan>
标签处理完毕,接着会处理xml中的其他标签,直到对应的BeanDefinition
全部注册到容器中。
实例化并有序调用BeanFactoryPostProcessor
大体上分为:分类->排序->调用。首先会实例化并调用BeanDefinitionRegistryPostProcessor
接口的实现类,在这里会调用之前注册的ConfigurationClassPostProcessor
,因为这个类实现了BeanDefinitionRegistryPostProcessor
接口(BeanFactoryPostProcessor
接口的扩展),可以在所有BeanDefinition
注册到容器之后做一些操作。这个类用于处理与配置相关的注解,比如@Configuration
、@PropertySources
、@ComponentScans
、@ImportResource
等注解。
BeanDefinitionRegistryPostProcessor
接口的实现类实例化并调用完成后,会实例化并调用仅实现了BeanFactoryPostProcessor
接口的类的对应方法。
实例化并有序注册BeanPostProcessor
大体上也分为:分类->排序->注册,这里会将之前添加的AutowiredAnnotationBeanPostProcessor
实例化并注册,这个BeanPostProcessor
主要用来处理@Autowired
注解。
实例化Bean
实例化bean之前会调用InstantiationAwareBeanPostProcessor
接口的postProcessBeforeInstantiation()
方法,然后从BeanDefinition
中拿到Class
并实例化对象,实例化之后会调用MergedBeanDefinitionPostProcessor
接口的postProcessMergedBeanDefinition()
方法。AutowiredAnnotationBeanPostProcessor
也实现了该接口,在这个方法中会扫描被实例化的这个bean的注解信息并包装成InjectionMetadata
,里面包含所有字段和方法上的注解信息,然后以beanName为key、封装的注解元数据为value缓存到injectionMetadataCache
中。
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
//...
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);
}
//构建自动注入元数据
//使用反射处理所有属性和方法,将注解信息包装为InjectionMetadata
metadata = buildAutowiringMetadata(clazz);
//将InjectionMetadata添加至Map缓存
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
所有的MergedBeanDefinitionPostProcessor
调用完成后,会将实例化后但没填充属性的bean保存到三级缓存中用来解决循环依赖,最后调用InstantiationAwareBeanPostProcessor
接口的postProcessAfterInstantiation()
方法。
接着在填充属性之前会调用所有InstantiationAwareBeanPostProcessor
接口实现类的postProcessProperties()
方法,在这里会调用AutowiredAnnotationBeanPostProcessor
的postProcessProperties()
方法。
在这个方法中,首先会依据beanName为key从之前构建的injectionMetadataCache
中找到该bean对应的注解元数据InjectionMetadata
,注解元数据中指明了哪些成员变量或方法包含了哪些注解,表示成员变量需要注入的依赖对象。拿到InjectionMetadata
后会处理每一个标有@Autowired
的成员变量(或方法)。这种被标注的成员变量被封装为InjectedElement
,然后为每一个element调用inject()
方法寻找并注入所需bean。
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
//缓存过则直接从缓存中拿
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
//...
try {
//解析需要注入的依赖值
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
//将获取的依赖缓存起来
//...
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
//使用反射直接为成员变量赋值,因此@Autowired注入不需要提供setXX()方法
field.set(bean, value);
}
}
inject()
方法中会调用beanFactory.resolveDependency()
来解析需要注入的依赖值,里面调用doResolveDependency()
方法,主要解析步骤如下:
首先会使用反射查看该变量是否有@Value
注解,如果有则获取注解中指定的值并返回。如果没有@Value
注解则说明该变量需要注入容器中的bean,那么就会调用findAutowireCandidates()
方法去容器中寻找所需类型的bean,如果容器中存在已经完全实例化过的bean则将bean实例返回,否则会从BeanDefinition
中找到Class
并返回。如果返回的是该bean的Class,则会在resolveCandidate()
方法中调用beanFactory.getBean()
方法来获取bean的实例(如果发生循环引用则会在三级缓存中得到bean的提前引用)。最后使用反射将获取到的bean注入到@Autowired
标注的变量里(这里可以看出@Autowired
不需要提供set(XX)
方法,而xml设置属性必须提供对应的set(XX)
方法,因为xml设置属性值是通过反射调用对应属性的setXX()
方法实现的)。
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
try {
//...
Class<?> type = descriptor.getDependencyType();
//尝试从@Value注解中获取到应被注入的值
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
//...
//解析得到的值,判断是否需要转换
//如果需要转换则转换成功后直接return
//...
}
//判断需要注入的成员变量是否为数组、Collection、Map,查找值并做相应的转换操作
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
//如果以上都不是则说明需要注入一个容器中的其他bean
//寻找符合条件的bean,返回的可能是bean实例,也可能是Class
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
//多个bean符合条件则判断应该使用哪一个
if (matchingBeans.size() > 1) {
//根据BeanDefinition的isPrimary属性判断应该使用哪个
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
//...
//根据符合条件的name从众多候选bean中得到符合条件的bean
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
//只找到唯一的一个符合条件的bean或Class
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
//如果前面得到的是符合条件的Class,则在这里调用resolveCandidate()方法
//里面调用了beanFactory.getBean(beanName)方法获取bean
//如果该bean未实例化则跳转去执行bean的实例化操作(getBean()方法都能做到)
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
//省略判null操作...
return result;
}
}
至此@Autowired
注解已经处理完毕,接着会继续走bean的生命周期流程,如果在xml中指定了bean成员变量的值,则在这里会将xml中指定的值赋给bean的成员变量(如果之前通过@Value
指定初始值则在这里会被xml中指定的值覆盖)。
关于xml和注解同时定义bean的优先级问题
-
如果xml里先定义了一个bean,之后在处理
<context:component-scan/>
标签时(即扫描注解指定的bean时)不能再注册同名的bean。 -
如果先通过注解注册了一个bean(即
<context:component-scan/>
标签位于<bean>
标签之上,所以先被解析),那么之后在处理<bean>
标签时,会先判断allowBeanDefinitionOverriding
标志是否为true
(默认为true
),如果允许覆盖则xml里定义的bean会覆盖注解注入的bean,如果不允许覆盖则抛异常。总结:如果容器允许覆盖
BeanDefinition
则xml中定义的bean会覆盖掉注解定义的bean,反之不成立。
标签:调用,Autowired,实现,流程,beanName,bean,实例,注解,BeanDefinition 来源: https://www.cnblogs.com/callme86/p/16557702.html