飞道的博客

Spring源码分析@Autowired、@Resource注解的区别

267人阅读  评论(0)

       关于Spring中@Autowired、@Resource和@Inject注解的区别请看:@Autowired、@Resource和@Inject注解的区别(最详细),本片文章将会带领你进行源码分析@Autowired、@Resource注解的不同。

       在上面所说的那篇博客中,我们知道:

          Spring对于@Autowired、@Resource注解使用不同的后置处理器进行处理

          @Autowired、@Resource之间的处理方式不同,@Autowired是根据类型,@Resource是根据名称

       在进行源码分析之前,你需要了解以下Bean的生命周期:Spring中bean的生命周期(最详细)其中在第五次调用bean的后置处理器时,完成属性的依赖注入,第五次调用bean的后置处理器的步骤:拿到Spring容器中所有的实现了BeanPostProcessor接口的类,然后判断其是否为InstantiationAwareBeanPostProcessor接口的实现类,如果是调用postProcessProperties方法,完成属性赋值。

       在@Autowired、@Resource和@Inject注解的区别(最详细)文章中,已经知道了Spring使用AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor类分别处理@Autowired注解和@Resource注解,他们都实现了InstantiationAwareBeanPostProcessor类,所以标注了@Autowired、@Resource会在第六次调用bean的后置处理器的时候完成属性注入。

                                

 

     从上面两张类的关系图可以看到其都间接实现了InstantiationAwareBeanPostProcessor类,下面我们就一起分析一下源码:

代码块1.AbstractAutowireCapableBeanFactory#populateBean方法


  
  1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  2. // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
  3. // state of the bean before properties are set. This can be used, for example,
  4. // to support styles of field injection.
  5. boolean continueWithPropertyPopulation = true;
  6. //1.第五次后置处理器,对应着我之前博客里面写的bean生命周期,首先判断这个bean是否是合成的,这个绝大多数是不是合成的,然后判断是否有InstantiationAwareBeanPostProcessors的接口
  7. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  8. //2.拿到所有的BeanPostProcessors处置处理器
  9. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  10. //3.逐一判断这个后置处理器是否是InstantiationAwareBeanPostProcessor类型的,因为BeanPostProcessor接口是为了统一进行管理bean后置处理器的
  11. //BeanPostProcessor还有子接口,用于实现不同的作用,可以参考bean后置处理器那篇博客
  12. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  13. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  14. if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
  15. continueWithPropertyPopulation = false;
  16. break;
  17. }
  18. }
  19. }
  20. }
  21. //4.如果为true,则说明在第五次调用后置处理的时候返回为false,这样就不会进行属性注入了
  22. //所以当你想bean中不进行属性注入,可以实现InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法
  23. if (!continueWithPropertyPopulation) {
  24. return;
  25. }
  26. PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
  27. // 5.解析自动装配模式为AUTOWIRE_BY_NAME和AUTOWIRE_BY_TYPE(现在几乎不用,现在默认是AUTOWIRE_NO)
  28. /**
  29. * <bean id="fruit" class="com.joonwhee.open.demo.simple.Fruit" autowire="byName">
  30. * <property name="color" value="Red"/>
  31. * </bean>
  32. * id值跟Fruit里的属性名一致
  33. * <bean id="apple" class="com.joonwhee.open.demo.simple.Apple"/>
  34. * public class Fruit {
  35. * private Apple apple;//apple 会根据名称完成自动注入
  36. * private String color;
  37. * }
  38. * */
  39. if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
  40. MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
  41. // Add property values based on autowire by name if applicable.
  42. if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
  43. autowireByName(beanName, mbd, bw, newPvs);
  44. }
  45. // Add property values based on autowire by type if applicable.
  46. if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
  47. autowireByType(beanName, mbd, bw, newPvs);
  48. }
  49. pvs = newPvs;
  50. }
  51. //6.检查是否有InstantiationAwareBeanPostProcessors接口的类
  52. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  53. boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
  54. if (hasInstAwareBpps || needsDepCheck) {
  55. if (pvs == null) {
  56. pvs = mbd.getPropertyValues();
  57. }
  58. PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  59. if (hasInstAwareBpps) {
  60. //7.执行第六次后置处理器,完成属性赋值
  61. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  62. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  63. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  64. //在Spring5.1之后,使用的是postProcessProperties方法完成属性注入
  65. PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
  66. if (pvsToUse == null) {
  67. if (filteredPds == null) {
  68. filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  69. }
  70. pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  71. if (pvsToUse == null) {
  72. return;
  73. }
  74. }
  75. pvs = pvsToUse;
  76. }
  77. }
  78. }
  79. //8.依赖检查,对应depends-on属性
  80. if (needsDepCheck) {
  81. checkDependencies(beanName, mbd, filteredPds, pvs);
  82. }
  83. }
  84. //9.将所有PropertyValues中的属性填充到bean中
  85. if (pvs != null) {
  86. applyPropertyValues(beanName, mbd, bw, pvs);
  87. }
  88. }

       AbstractAutowireCapableBeanFactory#populateBean方法是在Spring启动时,完成非懒加载单实例bean注册到容器时会执行的方法,可以对着Spring中bean的生命周期(最详细)来进行分析,populateBean方法会执行bean生命周期的第五、第六次后置处理器,第五次就不做分析了,关键进行分析第六次完成属性注入的生命周期,在populateBean方法中的第7步,会拿到所有的BeanPostProcessors方法,然后判断是否是InstantiationAwareBeanPostProcessor类型的,如果是则执行postProcessPropertyValues方法完成属性注入,因为AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor类都实现了InstantiationAwareBeanPostProcessor接口,所以会在第7步进行调用各自的方法,我们先看一下getBeanPostProcessors方法,看代码块2。

      然后看一下AutowiredAnnotationBeanPostProcessor的postProcessProperties方法,看代码块3.

      CommonAnnotationBeanPostProcessor的postProcessProperties方法,看代码块8.

代码块2.AbstractBeanFactory#getBeanPostProcessors方法


  
  1. /**
  2. * Return the list of BeanPostProcessors that will get applied
  3. * to beans created with this factory.
  4. */
  5. //就是简单的返回容器中所有的beanPostProcessors
  6. public List<BeanPostProcessor> getBeanPostProcessors() {
  7. return this.beanPostProcessors;
  8. }

代码块3. AutowiredAnnotationBeanPostProcessor#postProcessProperties方法


  
  1. @Override
  2. public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  3. //1.获取这个类中所有标注了@Autowired的属性和方法,并把它们封装在InjectionMetadata对象中
  4. InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
  5. try {
  6. //2.完成bean的属性注入
  7. metadata.inject(bean, beanName, pvs);
  8. }
  9. catch (BeanCreationException ex) {
  10. throw ex;
  11. }
  12. return pvs;
  13. }

       第1步,解析出这个类中所有的标注了@Autowired的属性和方法,看代码块4,第2步,完成对bean属性的注入,看代码块6.

代码块4.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata方法


  
  1. private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
  2. // Fall back to class name as cache key, for backwards compatibility with custom callers.
  3. //1.得到bean的名称
  4. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
  5. // Quick check on the concurrent map first, with minimal locking.
  6. //2.先从缓存中获取已经解析过的类,在完成对一个类的解析之后,会进行缓存,第二次就不用在去解析了
  7. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
  8. //3.判断是否刷新
  9. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  10. //4.加锁
  11. synchronized ( this.injectionMetadataCache) {
  12. //6.再次从缓存中获取解析之后的类
  13. metadata = this.injectionMetadataCache.get(cacheKey);
  14. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  15. if (metadata != null) {
  16. metadata.clear(pvs);
  17. }
  18. //7.完成对类的解析,获取所有的标注了@Autowired的属性和方法
  19. metadata = buildAutowiringMetadata(clazz);
  20. //8.放入到缓存中
  21. this.injectionMetadataCache.put(cacheKey, metadata);
  22. }
  23. }
  24. }
  25. return metadata;
  26. }

      在第7步中会完成对类的解析,解析出所有标注了@Autowired的属性和方法,看代码块5.

代码块5.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata方法


  
  1. private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
  2. List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  3. Class<?> targetClass = clazz;
  4. do {
  5. final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  6. //1.解析类中属性标注@Autowired注解的情况
  7. ReflectionUtils.doWithLocalFields(targetClass, field -> {
  8. AnnotationAttributes ann = findAutowiredAnnotation(field);
  9. if (ann != null) {
  10. //2.如果这个属性是static修饰的,即使被@Autowired标注也不会进行属性的自动注入,直接返回
  11. //这里使用的是lamda表达式,虽然有返回,但是不是退出buildAutowiringMetadata方法,不懂的自行百度
  12. if (Modifier.isStatic(field.getModifiers())) {
  13. return;
  14. }
  15. //3.判断@Autowired注解里面的required字段
  16. boolean required = determineRequiredStatus(ann);
  17. //4.封装成AutowiredFieldElement对象,添加到currElements集合中
  18. currElements.add( new AutowiredFieldElement(field, required));
  19. }
  20. });
  21. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  22. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  23. if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  24. return;
  25. }
  26. AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
  27. if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  28. if (Modifier.isStatic(method.getModifiers())) {
  29. //5.如果这个方法是static修饰的,即使被@Autowired标注也不会进行属性的自动注入,直接返回
  30. return;
  31. }
  32. boolean required = determineRequiredStatus(ann);
  33. //6.判断@Autowired注解里面的required字段
  34. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  35. currElements.add( new AutowiredMethodElement(method, required, pd));
  36. }
  37. });
  38. elements.addAll( 0, currElements);
  39. //7.解析父类,这个可以看出Spring想的真周到
  40. targetClass = targetClass.getSuperclass();
  41. }
  42. while (targetClass != null && targetClass != Object.class);
  43. return new InjectionMetadata(clazz, elements);
  44. }

      可以看到如果属性和方法被static修饰的话,是不会完成属性的自动注入的,在第7步中,还会递归解析当前类的父类,这一点我感觉Spring做的真周到,你想到的没想到的,Spring都会帮你做。

代码块6.InjectionMetadata#inject方法


  
  1. public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  2. Collection<InjectedElement> checkedElements = this.checkedElements;
  3. Collection<InjectedElement> elementsToIterate =
  4. (checkedElements != null ? checkedElements : this.injectedElements);
  5. //1.拿到类中(包括父类)标注了@Autowired注解的属性和方法
  6. if (!elementsToIterate.isEmpty()) {
  7. for (InjectedElement element : elementsToIterate) {
  8. //2.遍历,然后对每一个完成属性注入,这个是分属性和方法的,他们都实现了InjectedElement类,重写了inject方法
  9. //这里已属性注入为例
  10. element.inject(target, beanName, pvs);
  11. }
  12. }
  13. }

     第2步遍历类中所有的标注了@Autowired属性和方法,具体看代码块7

代码块7.AutowiredFieldElement#inject方法


  
  1. @Override
  2. protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  3. //1.记住这里是调用InjectedElement的方法,标注了@Autowired注解的属性和方法都会被封装成InjectedElement类,
  4. //所以这里使用this.member就是获取封装在InjectedElement的属性
  5. Field field = (Field) this.member;
  6. Object value;
  7. //2.是否已有解析之后的缓存,如果有,则直接从缓存里面取
  8. if ( this.cached) {
  9. value = resolvedCachedArgument(beanName, this.cachedFieldValue);
  10. }
  11. else {
  12. //3.将属性和@Autowired注解里面的@Autowired字段封装成DependencyDescriptor对象
  13. DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
  14. desc.setContainingClass(bean.getClass());
  15. Set<String> autowiredBeanNames = new LinkedHashSet<>( 1);
  16. TypeConverter typeConverter = beanFactory.getTypeConverter();
  17. try {
  18. //4.从容器中获取所依赖的bean,由于该方法过于复杂,我只看懂了一点,所以这个方法就不行解析,我大致说一下过程
  19. //首先会判断需要注入属性的类型是否是Array、Collection、Map类型的,如果是,则查找所有属性的泛型类型进行注入
  20. //例如Map<String,Student> map,然后Spring判断是Map类型,将就回去查找Value所对应的类型是Student,
  21. //然后就会查找所有的Student类型,以bean的id为key,以Student的实例为Value注入到这个map中
  22. //如果不是Array、Collection、Map类型的,就是去查找所有的需要注入属性的类型,如果只有一个,则直接注入,
  23. //如发现多个就会查找一个最优,会先通过@Primary注解查找最优,如果找不到会通过@Priority注解查找最优,如果该找不到
  24. //就是用基本策略,使用属性的名称进行逐个匹配查找到的bean的id,如果属性的名称和bean的id相同即为最优,如果还找打不到
  25. //那么就不要意思了,直接报错了
  26. value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
  27. }
  28. catch (BeansException ex) {
  29. throw new UnsatisfiedDependencyException( null, beanName, new InjectionPoint(field), ex);
  30. }
  31. //5.放到缓存中,下次不用进行解析了
  32. synchronized ( this) {
  33. if (! this.cached) {
  34. if (value != null || this.required) {
  35. this.cachedFieldValue = desc;
  36. registerDependentBeans(beanName, autowiredBeanNames);
  37. //步骤如果autowiredBeanNames大于1,说明是Array、Collection、Map类型
  38. if (autowiredBeanNames.size() == 1) {
  39. String autowiredBeanName = autowiredBeanNames.iterator().next();
  40. //6.如果是1个,也需要判断是不是Array、Collection、Map类型
  41. if (beanFactory.containsBean(autowiredBeanName) &&
  42. beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
  43. this.cachedFieldValue = new ShortcutDependencyDescriptor(
  44. desc, autowiredBeanName, field.getType());
  45. }
  46. }
  47. }
  48. else {
  49. this.cachedFieldValue = null;
  50. }
  51. this.cached = true;
  52. }
  53. }
  54. }
  55. if (value != null) {
  56. ReflectionUtils.makeAccessible(field);
  57. field.set(bean, value);
  58. }
  59. }
  60. }

        其中第4步是最重点,也是最难得看懂的,如果想要了解这个方法,可以参考大神写的博客:Spring IoC:createBean 详解(上),这篇文章从代码块9开始讲的就是这个方法,反正我是看懵逼了,有兴趣的可以看看,到这里关于@Autowired源码就介绍完了。

      下面我们来看一下CommonAnnotationBeanPostProcessor的postProcessProperties方法。

代码块8.CommonAnnotationBeanPostProcessor#postProcessProperties方法


  
  1. @Override
  2. public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  3. //1.获取这个类中所有标注了@Resource的属性和方法,并把它们封装在InjectionMetadata对象中
  4. InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
  5. try {
  6. //2.完成bean的属性注入
  7. metadata.inject(bean, beanName, pvs);
  8. }
  9. catch (Throwable ex) {
  10. throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
  11. }
  12. return pvs;
  13. }

       CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor的postProcessProperties方法基本一样。

      第1步,解析出所有标注了@Resource的属性和方法,并把它们封装在InjectionMetadata对象中,具体看代码块9。

      第2步,完成bean的属性注入,具体看代码块13.

代码块9.CommonAnnotationBeanPostProcessor#findResourceMetadata方法


  
  1. private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
  2. // Fall back to class name as cache key, for backwards compatibility with custom callers.
  3. //1.得到bean的名称
  4. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
  5. // Quick check on the concurrent map first, with minimal locking.
  6. //2.先从缓存中获取已经解析过的类,在完成对一个类的解析之后,会进行缓存,第二次就不用在去解析了
  7. InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
  8. //3.判断是否刷新
  9. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  10. //4.加锁
  11. synchronized ( this.injectionMetadataCache) {
  12. //5.再次从缓存中获取解析之后的类
  13. metadata = this.injectionMetadataCache.get(cacheKey);
  14. if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  15. if (metadata != null) {
  16. metadata.clear(pvs);
  17. }
  18. //6.完成对类的解析,获取所有的标注了@Resource的属性和方法
  19. metadata = buildResourceMetadata(clazz);
  20. //7.放入到缓存中
  21. this.injectionMetadataCache.put(cacheKey, metadata);
  22. }
  23. }
  24. }
  25. return metadata;
  26. }

      这个方法和AutowiredAnnotationBeanPostProcessor的一样就不多介绍,关键看一下第6步,进行解析@Resource注解,解析的时候就不一样了,具体看代码块10。

代码块10.CommonAnnotationBeanPostProcessor#buildResourceMetadata方法


  
  1. private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
  2. List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  3. Class<?> targetClass = clazz;
  4. do {
  5. final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  6. //1.解析标注了@Resource注解的属性,
  7. ReflectionUtils.doWithLocalFields(targetClass, field -> {
  8. //2.判断是否标注了@WebServiceRef注解,这个没看
  9. if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
  10. if (Modifier.isStatic(field.getModifiers())) {
  11. throw new IllegalStateException( "@WebServiceRef annotation is not supported on static fields");
  12. }
  13. currElements.add( new WebServiceRefElement(field, field, null));
  14. }
  15. //3.判断是否标注了@EJB注解,这个没看
  16. else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
  17. if (Modifier.isStatic(field.getModifiers())) {
  18. throw new IllegalStateException( "@EJB annotation is not supported on static fields");
  19. }
  20. currElements.add( new EjbRefElement(field, field, null));
  21. }
  22. //4.判断是否标注了@Resource注解,
  23. else if (field.isAnnotationPresent(Resource.class)) {
  24. //5.@Resource注解如果标注在static修饰的属性上,直接报错,不知道上面你是否记得@Autowired是怎么处理static修饰的属性的
  25. //忘记的翻看上文代码
  26. if (Modifier.isStatic(field.getModifiers())) {
  27. throw new IllegalStateException( "@Resource annotation is not supported on static fields");
  28. }
  29. //6.判断是否是需要被忽略的类型
  30. if (! this.ignoredResourceTypes.contains(field.getType().getName())) {
  31. currElements.add( new ResourceElement(field, field, null));
  32. }
  33. }
  34. });
  35. //7.解析标注了@Resource注解的方法
  36. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  37. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  38. if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  39. return;
  40. }
  41. if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  42. if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
  43. if (Modifier.isStatic(method.getModifiers())) {
  44. throw new IllegalStateException( "@WebServiceRef annotation is not supported on static methods");
  45. }
  46. if (method.getParameterCount() != 1) {
  47. throw new IllegalStateException( "@WebServiceRef annotation requires a single-arg method: " + method);
  48. }
  49. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  50. currElements.add( new WebServiceRefElement(method, bridgedMethod, pd));
  51. }
  52. else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
  53. if (Modifier.isStatic(method.getModifiers())) {
  54. throw new IllegalStateException( "@EJB annotation is not supported on static methods");
  55. }
  56. if (method.getParameterCount() != 1) {
  57. throw new IllegalStateException( "@EJB annotation requires a single-arg method: " + method);
  58. }
  59. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  60. currElements.add( new EjbRefElement(method, bridgedMethod, pd));
  61. }
  62. //8.判断方法上是否标注了@Resource注解
  63. else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
  64. //9.@Resource注解如果标注在static修饰的方法上,直接报错,
  65. if (Modifier.isStatic(method.getModifiers())) {
  66. throw new IllegalStateException( "@Resource annotation is not supported on static methods");
  67. }
  68. //10.获取其@Resource标注方法的形参列表
  69. Class<?>[] paramTypes = method.getParameterTypes();
  70. //11.如果不是1个就报错,这Spring对于@Resource要求的真实苛刻啊,两个为啥不行呢,我是不知道的
  71. //有知道的可以告诉我一声,我猜是鼓励大家用SPring自家的注解
  72. if (paramTypes.length != 1) {
  73. throw new IllegalStateException( "@Resource annotation requires a single-arg method: " + method);
  74. }
  75. if (! this.ignoredResourceTypes.contains(paramTypes[ 0].getName())) {
  76. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  77. currElements.add( new ResourceElement(method, bridgedMethod, pd));
  78. }
  79. }
  80. }
  81. });
  82. elements.addAll( 0, currElements);
  83. //12.递归解析父类的@Resource注解
  84. targetClass = targetClass.getSuperclass();
  85. }
  86. while (targetClass != null && targetClass != Object.class);
  87. return new InjectionMetadata(clazz, elements);
  88. }

        到这里我们已经知道了:

       一:使用@Resource时,如果是static则直接报错,使用@Autowired则不会。

       二:使用@Resource标注在方法上面时,方法的参数只能有一个,没有或多个参数则直接报错,使用@Autowired则没有限制。

       在第6步,会判断是否是被忽略的类型,如果不是,则添加到集合中,我们看一下ResourceElement类是如何实现的,看代码块11

代码块11.CommonAnnotationBeanPostProcessor类的内部类ResourceElement


  
  1. /**
  2. * Class representing injection information about an annotated field
  3. * or setter method, supporting the @Resource annotation.
  4. */
  5. private class ResourceElement extends LookupElement {
  6. private final boolean lazyLookup;
  7. public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
  8. super(member, pd);
  9. //1.从属性中面获取@Resource注解
  10. Resource resource = ae.getAnnotation(Resource.class);
  11. //2.获取@Resource注解上面name属性的值
  12. String resourceName = resource.name();
  13. //3.获取@Resource注解上面type属性的值
  14. Class<?> resourceType = resource.type();
  15. //4.如果@Resource注解name属性值为空,则isDefaultName为ture,就会使用默认的名称
  16. this.isDefaultName = !StringUtils.hasLength(resourceName);
  17. if ( this.isDefaultName) {
  18. //5.如果使用了默认名称的话,就会使用标注了@Resource注解属性的名称
  19. resourceName = this.member.getName();
  20. //6.如果标注了@Resource注解的是方法,那么就是判断是否是以set开头的
  21. if ( this.member instanceof Method && resourceName.startsWith( "set") && resourceName.length() > 3) {
  22. //7.如果是以set开头长度大于3则将set去除,并把首字母变成小写
  23. resourceName = Introspector.decapitalize(resourceName.substring( 3));
  24. }
  25. }
  26. else if (embeddedValueResolver != null) {
  27. resourceName = embeddedValueResolver.resolveStringValue(resourceName);
  28. }
  29. //8.检查在@Resource注解中设置的属性和和标注了@Resource注解的属性类型是否是一致的
  30. if (Object.class != resourceType) {
  31. checkResourceType(resourceType);
  32. }
  33. else {
  34. // No resource type specified... check field/method.
  35. //9.没用设置的的话,使用@Resource注解的属性类型
  36. resourceType = getResourceType();
  37. }
  38. this.name = (resourceName != null ? resourceName : "");
  39. this.lookupType = resourceType;
  40. String lookupValue = resource.lookup();
  41. this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
  42. //10.判断是否为懒加载的
  43. Lazy lazy = ae.getAnnotation(Lazy.class);
  44. this.lazyLookup = (lazy != null && lazy.value());
  45. }
  46. @Override
  47. protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
  48. return ( this.lazyLookup ? buildLazyResourceProxy( this, requestingBeanName) :
  49. getResource( this, requestingBeanName));
  50. }
  51. }

        这个类主要是用来解析@Resource注解的,因为@Resource注解有很多属性,具体看代码块12

代码块12.@Resource注解


  
  1. @Target({TYPE, FIELD, METHOD})
  2. @Retention(RUNTIME)
  3. public @interface Resource {
  4. String name() default "";
  5. String lookup() default "";
  6. Class<?> type() default java.lang.Object.class;
  7. enum AuthenticationType {
  8. CONTAINER,
  9. APPLICATION
  10. }
  11. AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
  12. boolean shareable() default true;
  13. String mappedName() default "";
  14. String description() default "";
  15. }

     代码块12是为了让你更好的理解代码块11,这两个要结合着看。

代码块13.InjectionMetadata#inject方法


  
  1. public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  2. Collection<InjectedElement> checkedElements = this.checkedElements;
  3. Collection<InjectedElement> elementsToIterate =
  4. (checkedElements != null ? checkedElements : this.injectedElements);
  5. //1.判断类中标注了@Resource注解的集合是否为空
  6. if (!elementsToIterate.isEmpty()) {
  7. for (InjectedElement element : elementsToIterate) {
  8. //2.不为空,则进行处理
  9. element.inject(target, beanName, pvs);
  10. }
  11. }
  12. }

     第2步会遍历类中所有加了@Resource注解的属性和方法,然后对每个完成依赖注入,具体看代码14

代码块14.InjectionMetadata类的内部类InjectedElement#inject方法


  
  1. /**
  2. * Either this or {@link #getResourceToInject} needs to be overridden.
  3. */
  4. protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
  5. throws Throwable {
  6. //1.判断是属性还是方法
  7. if ( this.isField) {
  8. Field field = (Field) this.member;
  9. ReflectionUtils.makeAccessible(field);
  10. //2.getResourceToInject方法会依赖项的查找
  11. field.set(target, getResourceToInject(target, requestingBeanName));
  12. }
  13. else {
  14. if (checkPropertySkipping(pvs)) {
  15. return;
  16. }
  17. try {
  18. Method method = (Method) this.member;
  19. ReflectionUtils.makeAccessible(method);
  20. method.invoke(target, getResourceToInject(target, requestingBeanName));
  21. }
  22. catch (InvocationTargetException ex) {
  23. throw ex.getTargetException();
  24. }
  25. }
  26. }

       重点关注属性的依赖注入,对于方法的和属性的差不多,这里不做过多介绍,在第2步会查找并注入依赖项,具体看代码块15.

代码块15.CommonAnnotationBeanPostProcessor类的内部类ResourceElement#getResourceToInject方法


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

         判断是否为懒加载,如果是则进行懒加载,懒加载会返回一个代理的类用来替代,只有在使用到这个属性的时候才回去加载,如果不是懒加载,则调用getResource方法获取依赖项,具体看代码块16.

代码块16.CommonAnnotationBeanPostProcessor#getResource方法


  
  1. protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
  2. throws NoSuchBeanDefinitionException {
  3. //这个mappedName没有研究
  4. if (StringUtils.hasLength(element.mappedName)) {
  5. return this.jndiFactory.getBean(element.mappedName, element.lookupType);
  6. }
  7. if ( this.alwaysUseJndiLookup) {
  8. return this.jndiFactory.getBean(element.name, element.lookupType);
  9. }
  10. //1.完成依赖注入
  11. return autowireResource( this.resourceFactory, element, requestingBeanName);
  12. }

     在第1步中会完成依赖注入,看代码块17

代码块17.CommonAnnotationBeanPostProcessor#autowireResource方法


  
  1. protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
  2. throws NoSuchBeanDefinitionException {
  3. Object resource;
  4. Set<String> autowiredBeanNames;
  5. //1.获取名称,如果在注解内部指定了name属性,则name就为执行的name
  6. //如果没有指定,则会获取标注了@Resource注解中属性的名称
  7. String name = element.name;
  8. if (factory instanceof AutowireCapableBeanFactory) {
  9. AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
  10. DependencyDescriptor descriptor = element.getDependencyDescriptor();
  11. //2.fallbackToDefaultTypeMatch:判断是否回退到默认类型匹配
  12. //isDefaultName:代码块11中进行了说明,如果@Resource注解name属性有值,则isDefaultName为false,如果@Resource注解name属性没有值,则为true,
  13. //isDefaultName为true,element.name是标注了@Resource注解中属性的名称
  14. //查看工厂中是否包含该名称的bean
  15. if ( this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
  16. autowiredBeanNames = new LinkedHashSet<>();
  17. //3.resolveDependency方法在代码块7中介绍了,如果走这个,是和@Autowired注解一样
  18. resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
  19. if (resource == null) {
  20. throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
  21. }
  22. }
  23. else {
  24. //4.根据名称去查找bean
  25. resource = beanFactory.resolveBeanByName(name, descriptor);
  26. autowiredBeanNames = Collections.singleton(name);
  27. }
  28. }
  29. else {
  30. resource = factory.getBean(name, element.lookupType);
  31. autowiredBeanNames = Collections.singleton(name);
  32. }
  33. if (factory instanceof ConfigurableBeanFactory) {
  34. ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
  35. for (String autowiredBeanName : autowiredBeanNames) {
  36. if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
  37. beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
  38. }
  39. }
  40. }
  41. return resource;
  42. }

          在第二步中,比较绕,我们举例说明:


  
  1. @Resource
  2. private String student;
  3. //因为此时@Resource注解中name属性为空,
  4. //所以:element.isDefaultName = true
  5. //element.name = student
  6. @Resource(name = "student1")
  7. private String student2;
  8. //因为此时@Resource注解中name属性有值,
  9. //所以:element.isDefaultName = false
  10. //element.name = student1,即@Resource内部指定的

       根据第二步我们知道,如果@Resource想要像@Autowired一样,使用类型进行匹配,需要满足一下条件,是都需要满足的

       1.@Resource注解中name属性没有进行设置

       2.第一条满足之后,会查看标注了@Resource属性的名称在容器在容器中不存在,什么意思?比如


  
  1. @Resource
  2. private String student2;

       id为student2的bean在容器中不存在。

       只有上面两条同时存在,则会和@Autowired走一样的逻辑。

总结:

       这里在总结一下@Autowired、@Resource注解的区别:

       @Autowired注解:

       1.Spring本身替换的注解(org.springframework.beans.factory.annotation.Autowired),需要导入Spring相应的jar包才能使用

       2.可以标注的位置:构造器、方法、方法参数、变量域和注解上面

       3.在Spring容器解析@Autowired注解时,使用的后置处理器为AutowiredAnnotationBeanPostProcessor

       4. @Autowired注解有一个required属性,当指定required属性为false时,意味着在容器中找相应类型的bean,如果找不到则忽略,而不报错(这一条是两个注解所没有的功能)

       5. 默认优先按照类型去容器中找对应的组件,找到就赋值,如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找,如果组件id对象的bean不存在,而且required属性为true,就报错

       6.如果标注了@Autowired的是static静态的属性或方法,那么Spring会直接忽略,但不会报错。

      

       @Resource注解:

         1.JSR250规范提供的注解(javax.annotation.Resource),不需要导入格外的包,这个注解在JDK的rt.jar包中

         2.可以标注的位置:TYPE(表示可以标注在接口、类、枚举),FIELD(变量域)和METHOD(方法)上面。

         3.在Spring容器解析@Resource注解时,使用的后置处理器为CommonAnnotationBeanPostProcessor

         4. 默认是按照组件名称进行装配的,根据@Resource注解name属性的名称去容器中查找,如果name没有指定,则根据标注了@Resource的属性名称去判断容器中是否存在该名称的bean,如果不存在,则会走和@Autowired一样的逻辑,这个时候就会支持@Primary注解(有的博客说不支持,那么错误的,源码不会骗人)。

         5.如果标注了@Resource的是static静态的属性或方法,那么会直接报错

        6.标注了@Resource的是方法只能有一个参数,如果是没有或者是多个,那么Spring会直接报错,而@Autowired则没有限制。


转载:https://blog.csdn.net/qq_35634181/article/details/104802906
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场