spring源码

您所在的位置:网站首页 autowire和resoure区别 spring源码

spring源码

2023-09-14 01:13| 来源: 网络整理| 查看: 265

上篇博客说了自动注入模型的使用,这篇笔记打算记录下自动注入模型中,autowireMode为0的情况,也就是我们经常用到的@Autowired、@Resource注解的原理

首先对于这两个注解,都是由后置处理器来完成解析的,解析的时机是一样的,在第六个后置处理器执行的时候;但是是由不同的后置处理器来完成解析的 @Autowired注解:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues

@Resource注解:

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues 前言

1、这两个注解,都是在第六个后置处理器执行的时候,进行解析的,但是在第三个处理器

org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

执行的时候,会先解析一下当前bean中有哪些属性要进行注入,在第六个后置处理器执行的时候,就无须进行解析,直接从map中获取要注入的属性即可

2、这两个注解虽然是由不同的后置处理器处理的,但是处理的思想、步骤大致都是一样的:

会先尝试获取bean中添加了该注解的方法(method)和属性(field)找到之后,会根据实现类的优先级、或者是name等从容器中或者beanDefinitionMap中找到对应的属性通过filed.set()完成属性的注入

3、这里有几个点要先行说明:

对于@Resource注解,会根据要注入属性的name,从单实例池中获取bean,完成属性注入对于@Autowired注解,我们通常说是现根据类型注入,如果找到多个或者没有找到,再根据name进行注入;这里要说明的是:spring在解析@Autowired注解的时候,并不是直接从单实例池中查找bean,而是先从beanDefinitionMap中,根据type查找,如果找到多个,再根据name从单实例池中查找,只有找到确定要注入哪个类,才会从单实例池中找到对应的bean,完成注入当然,和@Autowired注解配合使用的注解有好几个,在下面会一一解析 @Autowired

在这里插入图片描述 这是@Autowired注解解析的整个流程,其中,和@Autowired注解配合使用的有几下几个注解: @Qualifier @Primary @Priority

第一个注解使用在field上,在根据类型从beanDefinitionMap中找到bean之后,会判断哪个beanName是否是合格的;如果要注入的bean有多个实现类,会优先注入@Qualifier注解指定的实现类

第二个直接是在类上使用的,在未添加@Qualifier注解的场景下,如果有多个实现类,会判断哪个实现类上添加了@Primary注解,添加了,就注入该实现类

第三个注解也是加载类上,如果既没有添加@Qualifer、也没有添加@Priority注解,那么,会判断哪个实现类的优先级比较高,值越小,优先级越高

这是

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

的部分代码

/** * @Autowired注解源码 三 * 查找要注入的bean,按照type来查找,如果匹配到一个,就获取到这个实例,然后返回,返回之后,会调用field.set();注入进去 * 如果找到了多个,那就先判断哪个实现类有@primary注解,如果都没有注解,那就判断priority的优先级 * * 这个方法中,对Qualifier注解进行了处理;如果一个接口有多个实现类,但是没有加@Qualifier注解,那么就会返回多个 * * 如果@Qualifier指定的value在spring容器中没有,这里返回的map集合就是空 * */ Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { // 如果匹配到的要注入的bean是null,就报错 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1) { /** * 如果根据类型,匹配到有多个bean,那就判断@Primary注解和@Priority注解; * 所以:可以得出一个结论: * 当一个接口有多个实现类的时候,添加了@Qualifier注解,同时在一个类上添加了@Primary注解,那么会注入@Qualifier注解对应的bean * * 在下面的这个推断方法中: * 1.会先判断@Primary注解 * 2.再判断@Priority注解声明的优先级 * 3.如果两个注解都没有,那就根据controller中注入的service的name和matchingBeans中的beanName进行匹配 * 如果匹配上,就注入匹配上的beanName对应的beanClass * 这里需要注意的是:根据name查询的时候,会先根据type从beanDefinitionMap中查询到符合类型的beanDefinitionNames * 然后从beanDefinitionNames中依次匹配,是否和name一致 */ autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). return null; } } /** * 如果能找到一个,那就根据name获取到对应的instance实例,然后再field.set(); */ instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // We have exactly one match. Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { /** * 根据获取到的beanName和type从容器中查找。如果没有找到,就创建 * 这里才是真正的从单实例池中获取对象,如果对象没有,就创建 */ instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); }

在注释中,详细注释了哪行代码解析了哪个注解,我想着重看下 determineAutowireCandidate这个方法

/** * 如果根据type获取到有多个符合的bean,那就来这个方法中,推断出其中的一个 * @param candidates:推断出来的多个bean * @param descriptor:要注入的属性对应的类型,如果是接口,那这里就是接口的类型 * @return */ @Nullable protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) { Class requiredType = descriptor.getDependencyType(); /** * 判断哪个bean有@Primary注解,返回注解对应的beanName */ String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } /** * 如果都没有添加@Primary注解,那就判断bean是否有添加@Priority注解,注解值越小,优先级越高;返回优先级高的beanName */ String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { return priorityCandidate; } // Fallback /** * 这里的descriptor中保存的是注入的bean对应的名称(在controller注入service,这里就是controller中的service属性名) * 判断 从容器中拿出来的beanName是否和controller中的service属性名一致,如果一致,就注入一致的这个 * 这也就是在controller注入service的时候,如果service有多个实现类,那么,如果注入的service属性名和实现类的beanName一致,也可以注入成功的原因 * * class Controller{ * @Autowired * Service service01 * } * 如果service有多个实现类,其实注入的是service01,就是这里判断的 * * 这里我觉得,也是我们平常所说的,如果根据type找到多个,或者没有找到,就根据name来注入,这里就是根据name来注入的这一步 * candidates:是根据类型从beanDefinitionMap中获取到的符合类型的beanDefinitionNames * descriptor.getDependencyName():是@Autowired注解属性注入的属性name */ for (Map.Entry entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null; }

通过这两段代码,我觉得可以说明一个问题:

对于@Autowired注解,spring在处理的时候:会先解析@Qualifer注解,然后解析@Primary注解,然后再解析@Priority注解如果解析之后,匹配的bean还是有多个,那就根据name来查找,需要注意的是这里的查找都是基于从beanDefinitionMap中找到的符合注入条件的bean;只有在确认要注入的是哪个实现类之后,才会从单实例池中获取实例化之后的bean

这里有一个点再补充一下,我前面一直在说的这个点:所有的查找都是基于从beanDefinitionMap中查找到符合注入条件的bean:下面这段代码可以证实这个观点: 在这里插入图片描述

这里的代码,我就不再追进去了,因为这里的底层,可以看到是遍历beanDefinitionNames这个集合,依次根据beanDefinitionName获取到beanDefinition,然后判断beanDefinition是否是非抽象类,是否是非懒加载的、是否是单实例的,是否是type类型的等等,如果满足,就把当前beanName返回

@Resource

在这里插入图片描述 这是一个简单的流程图,图里面基本上就把@Resource注解的解析说明白了,我们看为什么说@Resource是根据name来注入属性的,只需要看图中的最后一个方法

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException { Object resource; Set autowiredBeanNames; String name = element.name; if (this.fallbackToDefaultTypeMatch && element.isDefaultName && factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) { autowiredBeanNames = new LinkedHashSet(); /** * 这里也可以佐证@Resource是根据name来进行注入的,这里代码追进去,可以看到,是也是调用的doGetBean() * 所以:无论是走这里,还是下面的getBean,都是从单实例池中尝试获取bean的 */ resource = ((AutowireCapableBeanFactory) factory).resolveDependency( element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null); if (resource == null) { throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); } } else { /** * 之所以说@Resource是按照name来注入的,就是这里的代码可以佐证这个观点 * 这里,直接根据name从单实例池中获取bean,这里是从单实例池中获取,如果存在就返回,不存在就初始化 * 这里的getBean()调用的就是org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean */ 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; }

所以:对于@Resource注解,就是根据在bean中设置的name,从单实例池中查找,如果找到就注入,没找到,就是null

总结

@Resource注解没什么可说的,就是根据name从单实例池中,获取能匹配上的beanDefinition

对于@Autowired注解

首先会根据type,从beanDefinitionMap中获取到所有符合该类型的beanDefinition对应的beanName然后判断对应的@Qualifier注解,优先注入该注解指定的bean,如果在该注解指定了bean,但是没有找到,那就返回null如果没有添加@Qualifer注解,但是找到了多个实现类,那就判断是否有添加@Primary注解,优先注入添加了该注解的bean;如果多个类添加了该注解,spring会报错,因为spring也不知道要注入哪个如果没有添加@Primary注解,就处理@Priority注解,该注解是声明优先级的,值越小,优先级越高如果这三个注解都没有添加,那就根据name,判断从beanDefinitionMap中查找的符合注入类型的所有beanName中,是否有和field对应的name一样的,有,就注入该bean,没有就根据@Autowired注解的required是true还是false,来决定是抛出异常还是返回null如果找到、确定了注入的bean,就会从单实例池中获取beanName对应的完成实例化的bean对象,然后注入

最后一点,需要说明的是:@Autowired注解和@Resource注解都是通过field.set()完成属性填充的



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3