热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

spring源码分析(五)@Autowire注入补充,@Resource源码分析

自动注入补充的点:1:AutowireMode之前博客中讲到@Autowire,@Value,@Inject自动注入的处理都是在后置处理器 AutowiredAnnotationB

自动注入补充的点:


1:AutowireMode

之前博客中讲到@Autowire,@Value,@Inject自动注入的处理都是在后置处理器 AutowiredAnnotationBeanPostProcessor#postProcessProperties中,这个后置处理器的调用是在 AbstractAutowireCapableBeanFactory#populateBean 中:

技术图片

 但是在这段逻辑上面有一段 AutowireMode的判断,会根据bean对应的BeanDefinition中的 AutowireMode属性判断是否要走byType, byName。但是这段逻辑很少走到,因为平常用的@Component,@Service ,@Bean生成的 BeanDefinition#getResolvedAutowireMode()结果都是AUTOWIRE_NO

技术图片

 

除了在xml定义bean的时候可以设置autowire属性:  

在使用@Bean定义bean的时候也可以定义这个属性,但是推荐已经过期了:

技术图片

 

 



 


2:@Lazy

注入点的属性加了@Lazy之后的处理逻辑,上篇中还没有提到,在DefaultListableBeanFactory#resolveDependency 中,

技术图片技术图片

属性注入的对象只是个代理对象,当调用对象的相应方法时,会调用getTarget()得到真实的对象,也还是调用doResolveDependency方法。

 

技术图片



 

3:bean自动注入自身对象

    加入定义了三个类型为People的bean, beanName分别是 people, people1,people2,  我在people的定义中注入自身。



@Component
public class People {
@Autowired
private People people;
private String userName="people";
public People(String userName) {
this.userName = userName;
}
public People() {
}
public String getFieldPeopleUserName(){
return people.getUserName();
}

 

config中定义了两个:


@Bean
@Scope(
"prototype")
public People people1()
{
People p1
= new People();
p1.setUserName(
"people1");
return p1;
}
@Bean
@Scope(
"prototype")
public People people2(){
People p1
= new People();
p1.setUserName(
"people2");
return p1;
}

 

去geBean("people");


AnnotationConfigApplicationContext applicatiOnContext= new AnnotationConfigApplicationContext(BeanConfig.class);
System.out.println("======================");
People son=(People)applicationContext.getBean("people");
System.out.println(son.getFieldPeopleUserName());
System.out.println("======================");

 

执行是会报错的:这里它只找到了两种people1,people2两个bean。   注意这里并没有把people找出来。

技术图片

 

 把people2的@Bean注释掉之后,再次执行是可以成功的。但是注意哦,这里属性注入的实例是beanName为people1的。

技术图片

 

把people1也注释掉,就只有people一个。属性注入也是可以成功。

技术图片

 

 

 为什么会出现上面两种情况呢?要从DefaultListableBeanFactory#findAutowireCandidates 的源码中得知。再找到候选者beanName之后会经过 isAutowireCandidate的筛选,但是筛选之前还有个判断,就是根据byType找到的beanName是不是自身。如果是自身的话result中就不会存这个beanName。isSelfReference这个方法。


// 对候选者bean进行过滤,首先候选者不是自己,然后候选者是支持自动注入给其它beand的
// 这里判断是不是候选者的时候不止会判断 autowire-candidate是否为true,还有一些其它判断:isAutowireCandidate(candidate, descriptor)
for (String candidate : candidateNames) {
// 根据byType找到的beanName判断 isSelfReference(beanName, candidate) 首先排除了候选者是自己
// isAutowireCandidate方法中会去判断候选者是否和descriptor匹配 并不是所有bean都是可以进行注入的
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 如果只有一个people,上面过滤后result为空。
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
(
!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
if (result.isEmpty() && !multiple) {
// Consider self references as a final pass...
// but in the case of a dependency collection, not the very same bean itself.
for (String candidate : candidateNames) {
// 如果上面的result都为空,判断是否是自身注入,如果是的就把自身的bean添加到result中
if (isSelfReference(beanName, candidate) &&
(
!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;

 

1)如果有people,people1,people2三个beanName的People。经过上面的逻辑之后,result还有两个people1,people2,然后返回给DefaultListableBeanFactory#doResolveDependency

    返回的这个matchingBeans有两个。在调用determineAutowireCandidate 要确定唯一  一个beanName的时候是没法确定的,因为属性名是people,所以会走到descriptor.resolveNotUnique  抛出错误。

技术图片

 

 2:当把people2注释掉之后,返回的result中就剩下people2一个beanName,所以注入的就是这个,不会再报错。

 

3:注释掉people1之后,就只有people自己了,注入自身people也是成功的,这是为啥呢?  候选者bean筛选的时候是去掉自身的bean了吗。是的,候选者bean筛选的时候是去掉自身的bean,这时候在DefaultListableBeanFactory#findAutowireCandidates方法筛选之后,result 为空,但是下面如果筛选结果为空,又有一些判断,其中就把所有的候选者beanName又过滤一遍,如果有自身的beanname就添加进了result中了,

所以返回的结果中,就只有自身的beanName了。

技术图片

 

 




 @Resource

注入的流程先作个总结再看源码:

1:如果@Resource中指定了name属性,则只会根据这个name属性值去找bean,找不到就报错。

2:如果没有指定这个name属性,那么先判断注入点的名字(属性名/set方法名)是不是存在bean,如果存在,则直接根据注入点名字获取bean注入。

      如果不存在,则会走@Autowire的注入逻辑,根据注入点类型去找bean。

我们上篇提到@Autowire自动注入的的源码是在后置处理器AutowiredAnnotationBeanPostProcessor 中的,但是@Resource的注解的解析注入是在 CommonAnnotationBeanPostProcessor中实现的。

CommonAnnotationBeanPostProcessor  继承了 InitDestroyAnnotationBeanPostProcessor,实现了InstantiationAwareBeanPostProcessor,所以也有postProcessMergedBeanDefinition ,postProcessProperties方法。

技术图片

 

 

 了解了@Autowire的源码,这个就很简单了,流程基本都是一样的。但是这个后置处理器并不是单独处理@Resource注解的,这是个公共的后置处理器,还会处理其它注解。


@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
// 找到注入点
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}


private InjectionMetadata findResourceMetadata(String beanName, final Class clazz, @Nullable PropertyValues pvs) {
// 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);
}
// 构造注入点
metadata = buildResourceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}


private InjectionMetadata buildResourceMetadata(final Class clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List elements = new ArrayList<>();
Class targetClass = clazz;
do {
// 注意,下面InjectedElement子类不再按属性和方法分了,而是按注解类型来分了
final List currElements = new ArrayList<>();
// 找到属性上面的注解
ReflectionUtils.doWithLocalFields(targetClass, field -> {
// 判断属性上是否有 javax.xml.ws.WebServiceRef 注解
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
//判断判断属性上是否有 javax.ejb.EJB
else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
// 判断属性上是否有 @Resource注解
else if (field.isAnnotationPresent(Resource.class)) {
// 不能注入静态属性
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
// 可以向ignoredResourceTypes添加不想被注入的类型,当ignoredResourceTypes包含属性的类型时,这个属性不作为注入点
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {

// 构造方法中会根据是否在注解上传入了name属性来给this.name 赋值

currElements.add(new ResourceElement(field, field, null));
}
}
});
// 判断方法上是否有相关注解
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
// 不能加在静态方法上
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class[] paramTypes = method.getParameterTypes();
// @Resource 加的方法上的参数必须只有一个
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
// ignoredResourceTypes 包含参数的类型的话 这个方法也是不作为注入点的
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
// 还是会向上找父类的
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}

 




public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
Resource resource
= ae.getAnnotation(Resource.class);
String resourceName
= resource.name();
Class
resourceType = resource.type();
// 这里判断,@Resource有没有设置name属性值,如果没有的话,把this.isDefaultName=true
// 默认的名字 this.name 就被赋值为了属性名resourceName = this.member.getName(); 得到属性名
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
resourceName
= this.member.getName();
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
resourceName
= Introspector.decapitalize(resourceName.substring(3));
}
}
else if (embeddedValueResolver != null) {
resourceName
= embeddedValueResolver.resolveStringValue(resourceName);
}
if (Object.class != resourceType) {
checkResourceType(resourceType);
}
else {
// No resource type specified... check field/method.
resourceType = getResourceType();
}
this.name = (resourceName != null ? resourceName : "");
this.lookupType = resourceType;
String lookupValue
= resource.lookup();
this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
Lazy lazy
= ae.getAnnotation(Lazy.class);
this.lazyLookup = (lazy != null && lazy.value());
}

 

注意:  @Resource不能加在静态属性和方法上,加上方法上,方法的参数必须只有一个,可以通过 this.ignoredResourceTypes 去掉不想被注入的类型。

上面找到注入点之后,下面就进行注入 ,  执行的方法:CommonAnnotationBeanPostProcessor#postProcessProperties 方法,


public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata
= findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}


public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection
checkedElements = this.checkedElements;
Collection
elementsToIterate =
(checkedElements
!= null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
//遍历每个能够注入的属性 进行注入
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace(
"Processing injected element of bean ‘" + beanName + "‘: " + element);
}
// element可能是Method 也可能是Field 会走不同的子类 AutowiredFieldElement AutowiredMethodElement

// 在@Resource逻辑里,那些注入的子类,并没有重写inject方法,还是走的 InjectedElement#inject
element.inject(target, beanName, pvs);
}
}
}

InjectedElement#inject

/**
* Either this or {@link #getResourceToInject} needs to be overridden.
*/
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
// 如果时属性则反射赋值
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
//getResourceToInject 需要被重写,就是ResourceElement
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
// 检查当前的属性是不是通过 by type by name注入的
if (checkPropertySkipping(pvs)) {
return;
}
try {
// 如果时方法,则通过方法赋值
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}

 

ResourceElement#getResourceElement


@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
getResource(
this, requestingBeanName));
}

 

看getResource


protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
if (StringUtils.hasLength(element.mappedName)) {
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
}
if (this.alwaysUseJndiLookup) {
return this.jndiFactory.getBean(element.name, element.lookupType);
}
if (this.resourceFactory == null) {
throw new NoSuchBeanDefinitionException(element.lookupType,
"No resource factory configured - specify the ‘resourceFactory‘ property");
}
return autowireResource(this.resourceFactory, element, requestingBeanName);
}

autowireResource,里面逻辑主要也是resolveDependency.这部分见@Autowire分析

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Object resource;
Set
autowiredBeanNames;
String name
= element.name;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory
= (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor
= element.getDependencyDescriptor();
// 如果isDefaultName 为true说明@Resource没有配置属性name属性,默认的是属性名。 !factory.containsBean(name) 且bean工厂中不含有这个bean
// 走这里之前 fallbackToDefaultTypeMatch 这个才是第一个开关 相当于看是否需要根据Type进行找bean
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames
= new LinkedHashSet<>();
// 走到 resolveDependency 这个是@Autowire的逻辑是一样的 先byType 在byName找bean
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
// 直接从beanFactory中获取bean
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames
= Collections.singleton(name);
}
}
else {
resource
= factory.getBean(name, element.lookupType);
autowiredBeanNames
= Collections.singleton(name);
}
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory
= (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
return resource;
}

 

spring源码分析(五)@Autowire注入补充,@Resource源码分析



推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
author-avatar
君与龙_501
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有