Spring 中@Autowired,@Resource,@Inject 注解实现原理

打印 上一主题 下一主题

主题 1775|帖子 1775|积分 5325

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
使用案例

前置条件: 现在有一个 Vehicle 接口,它有两个实现类 Bus 和 Car ,现在还有一个类 VehicleService 需要注入一个 Vehicle 类型的 Bean:
  1. public interface Vehicle {}
  2. @Component
  3. public class Car implements Vehicle {}
  4. @Component
  5. public class Bus implements Vehicle {}
复制代码
使用 @Autowired 注解注入 Bean
@Autowired 注解可以和 @Qualifier 注解一起使用,在有多个符合条件的 Bean 的情况下限制注入特定名称的 Bean:
  1. @Component
  2. public class VehicleService {
  3.     @Autowired
  4.     @Qualifier("car") //假设这里是想要注入Bean名称为car的这个Bean
  5.     private Vehicle vehicle;
  6. }
复制代码
使用 @Inject 注解注入 Bean
@Inject 注解可以和 @Qualifier或者 @Named 注解一起使用,在有多个符合条件的 Bean 的情况下限制注入特定名称的 Bean:
  1. @Component
  2. public class VehicleService {
  3.     @Inject
  4.     @Qualifier("car") //假设这里是想要注入Bean名称为car的这个Bean
  5.     private Vehicle vehicle;
  6.     @Inject
  7.     @Named("bus") //假设这里是想要注入Bean名称为bus的这个Bean
  8.     private Vehicle anotherVehicle;
  9. }
复制代码
使用 @Resource 注解注入 Bean:
  1. @Component
  2. public class VehicleService {
  3.     @Resource(name = "car")
  4.     private Vehicle vehicle;
  5. }
复制代码
虽然以上三种使用方法都能够实现注入 Bean 的需求,但是它们在底层实现上有什么区别呢?
注崩溃系

在 Java EE 和 Spring 体系中定义了几套注解:
JSR 250:定义了 @PostConstruct,@PreDestroy,@Resource 注解,其中 @Resource 注解默认是按照名称进行注入
JSR 330:定义了 @Inject,@Qualifier, @Named 注解,其中 @Inject 注解默认是按照类型进行注入,可以搭配 @Qualifier 或者@Named 注解实现按照名称注入。
Spring:定义了 @Autowired,@Qualifier注解,其中 @Autowired 注解默认是按照类型进行注入,可以搭配 @Qualifier 注解实现按照名称注入。
当前 JSR 250 定义的注解属于 jakarta.annotation-api,而 JSR 330 定义的注解属于 jakarta.inject-api。
实现原理

InstantiationAwareBeanPostProcessor 方法调用触发的位置:

Spring 中提供了 InstantiationAwareBeanPostProcessor 接口,它有一个 postProcessProperties() 负责实现对 Bean 的属性进行处理。
Spring 中提供了实现类 CommonAnnotationBeanPostProcessor 负责处理 @Resource 注解;提供了实现类 AutowiredAnnotationBeanPostProcessor 负责处理 @Autowired 注解和 @Inject 注解。
InstantiationAwareBeanPostProcessor的 postProcessProperties() 方法是在 AbstractAutowireCapableBeanFactory 中的 doCreateBean() 创建 Bean 的方法中触发调用的,在这个方法中的主要实现逻辑是实例化 Bean -> 添补 Bean 属性 -> 初始化 Bean。 代码如下:
  1. protected Object doCreateBean(String beanName, RootBeanDefinition mbd,
  2.         @Nullable Object[] args) throws BeanCreationException {
  3.         BeanWrapper instanceWrapper = null;
  4.         if (mbd.isSingleton()) {
  5.                 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  6.         }
  7.         if (instanceWrapper == null) {
  8.         //实例化Bean对象
  9.                 instanceWrapper = createBeanInstance(beanName, mbd, args);
  10.         }
  11.         Object bean = instanceWrapper.getWrappedInstance();
  12.         boolean earlySingletonExposure = (mbd.isSingleton()
  13.                 && this.allowCircularReferences
  14.                 && isSingletonCurrentlyInCreation(beanName));
  15.         if (earlySingletonExposure) {
  16.                 addSingletonFactory(beanName,
  17.                         () -> getEarlyBeanReference(beanName, mbd, bean));
  18.         }
  19.         Object exposedObject = bean;
  20.         try {
  21.         //填充Bean属性
  22.                 populateBean(beanName, mbd, instanceWrapper);
  23.                 //初始化Bean
  24.                 exposedObject = initializeBean(beanName, exposedObject, mbd);
  25.         }
  26. }
复制代码
在添补 Bean 属性的方法 populateBean() 中实现了对 postProcessProperties() 方法的调用,在该方法实现对注解修饰的需要注入的字段进行赋值,即自动注入。 代码如下:
  1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {  
  2.     //省略部分代码
  3.     PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);  
  4.     if (hasInstantiationAwareBeanPostProcessors()) {  
  5.        if (pvs == null) {  
  6.           pvs = mbd.getPropertyValues();  
  7.        }  
  8.        //这里获取所有InstantiationAwareBeanPostProcessor接口的实现类
  9.        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {  
  10.                   //调用postProcessProperties()方法
  11.           PropertyValues pvsToUse = bp.postProcessProperties(pvs,
  12.                   bw.getWrappedInstance(), beanName);  
  13.           if (pvsToUse == null) {  
  14.              return;  
  15.           }  
  16.           pvs = pvsToUse;  
  17.        }  
  18.     }  
  19. }
复制代码
InstantiationAwareBeanPostProcessor 注册的时机:

既然 InstantiationAwareBeanPostProcessor 是负责处理 Bean 的属性的自动注入的,那么它一定是在业务 Bean 创建之前就已经完成初始化了,这样在业务 Bean 创建的时间才气调用它的实例方法。它的初始化是在 Spring 上下文的基类 AbstractApplicationContext 的 refresh() 方法中完成的。代码如下:
  1. public void refresh() throws BeansException, IllegalStateException {
  2.     //省略其它代码
  3.         //这里注册了InstantiationAwareBeanPostProcessor
  4.         registerBeanPostProcessors(beanFactory);
  5.        
  6.     //省略其它代码
  7.    
  8.         //这里创建所有的单例Bean
  9.         finishBeanFactoryInitialization(beanFactory);
  10.    
  11.         finishRefresh();
  12. }
复制代码
而在 registerBeanPostProcessors() 方法中又调用了 PostProcessorRegistrationDelegate 的 registerBeanPostProcessors() 方法来完成注册的。代码如下:
  1. protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
  2.     PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
  3. }
复制代码
在PostProcessorRegistrationDelegate 的 registerBeanPostProcessors() 方法真正实现注册逻辑。代码如下:
  1. public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory,
  2.     AbstractApplicationContext applicationContext) {
  3.     //这里获取到所有实现了BeanPostProcessor接口的Bean名称
  4.     //InstantiationAwareBeanPostProcessor接口继承了BeanPostProcessor接口
  5.     String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
  6.     //遍历Bean名称调用BeanFactory.getBean()方法触发BeanPostProcessor Bean的创建
  7.     //然后根据是否实现了PriorityOrdered接口、Ordered接口和其它分为三大类
  8.     //分别将这三大类的BeanPostProcessor实例进行注册
  9.     List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  10.     List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
  11.     List<String> orderedPostProcessorNames = new ArrayList<>();
  12.     List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  13.     for (String ppName : postProcessorNames) {
  14.         if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  15.             //这里调用BeanFactory.getBean()方法触发BeanPostProcessor Bean的创建
  16.             BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  17.             priorityOrderedPostProcessors.add(pp);
  18.             if (pp instanceof MergedBeanDefinitionPostProcessor) {
  19.                 internalPostProcessors.add(pp);
  20.             }
  21.         }
  22.         else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
  23.             orderedPostProcessorNames.add(ppName);
  24.         }
  25.         else {
  26.             nonOrderedPostProcessorNames.add(ppName);
  27.         }
  28.     }
  29.     //首先注册实现了PriorityOrdered接口的BeanPostProcessor
  30.     sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  31.     registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
  32.     //然后触发实现了Ordered接口的BeanPostProcessor Bean的创建并注册
  33.     List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  34.     for (String ppName : orderedPostProcessorNames) {
  35.         BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  36.         orderedPostProcessors.add(pp);
  37.         if (pp instanceof MergedBeanDefinitionPostProcessor) {
  38.             internalPostProcessors.add(pp);
  39.         }
  40.     }
  41.     sortPostProcessors(orderedPostProcessors, beanFactory);
  42.     registerBeanPostProcessors(beanFactory, orderedPostProcessors);
  43.     //最后触发其它BeanPostProcessor Bean的创建并注册
  44.     List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  45.     for (String ppName : nonOrderedPostProcessorNames) {
  46.         BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  47.         nonOrderedPostProcessors.add(pp);
  48.         if (pp instanceof MergedBeanDefinitionPostProcessor) {
  49.             internalPostProcessors.add(pp);
  50.         }
  51.     }
  52.     registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
  53.     sortPostProcessors(internalPostProcessors, beanFactory);
  54.     registerBeanPostProcessors(beanFactory, internalPostProcessors);
  55. }
复制代码
CommonAnnotationBeanPostProcessor 实现逻辑(以修饰字段为例)

首先在 CommonAnnotationBeanPostProcessor 的静态初始化块中初始化了它要处理的注解。代码如下:
  1. static {
  2.     //这里是为了适配不同版本@Resource注解在不同的包路径下
  3.     jakartaResourceType = loadAnnotationType("jakarta.annotation.Resource");
  4.     if (jakartaResourceType != null) {
  5.         resourceAnnotationTypes.add(jakartaResourceType);
  6.     }
  7.     //这里是为了适配不同版本@Resource注解在不同的包路径下
  8.     javaxResourceType = loadAnnotationType("javax.annotation.Resource");
  9.     if (javaxResourceType != null) {
  10.         resourceAnnotationTypes.add(javaxResourceType);
  11.     }
  12. }
复制代码
在它的 postProcessProperties() 方法中主要实现逻辑为找到 @Resource 注解修饰的字段 -> 通过反射给字段赋值。代码如下:
  1. public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
  2.     //找@Resource注解修饰的字段
  3.     InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
  4.     try {
  5.         //给字段赋值
  6.         metadata.inject(bean, beanName, pvs);
  7.     }
  8.     catch (Throwable ex) {
  9.         throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
  10.     }
  11.     return pvs;
  12. }
复制代码
找 @Resource 注解修饰的字段是在 findResourceMetadata() 方法中实现的,在该方法中又调用了 buildResourceMetadata() 来进行实际的查找,在这个方法中通过反射的方式遍历字段看它是否有 @Resource 注解修饰,如果是的话把它包装为一个 ResourceElement 对象放到列表中。末了基于列表构造一个 InjectionMetadata 对象返回。代码如下:
  1. private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
  2.     String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
  3.     InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
  4.     if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  5.         synchronized (this.injectionMetadataCache) {
  6.             metadata = this.injectionMetadataCache.get(cacheKey);
  7.             if (InjectionMetadata.needsRefresh(metadata, clazz)) {
  8.                 if (metadata != null) {
  9.                     metadata.clear(pvs);
  10.                 }
  11.                 //这里调用buildResourceMetadata()方法
  12.                 metadata = buildResourceMetadata(clazz);
  13.                 this.injectionMetadataCache.put(cacheKey, metadata);
  14.             }
  15.         }
  16.     }
  17.     return metadata;
  18. }
  19. private InjectionMetadata buildResourceMetadata(Class<?> clazz) {
  20.     List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  21.     Class<?> targetClass = clazz;
  22.     //省略部分代码
  23.    
  24.     do {
  25.         final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  26.         //这里就会遍历每个字段看字段是否有@Resource注解修饰有的话就加入到列表中
  27.         ReflectionUtils.doWithLocalFields(targetClass, field -> {
  28.            //省略部分代码
  29.             
  30.            if (jakartaResourceType != null && field.isAnnotationPresent(jakartaResourceType)) {
  31.                 if (Modifier.isStatic(field.getModifiers())) {
  32.                     throw new IllegalStateException("@Resource annotation is not supported on static fields");
  33.                 }
  34.                 if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
  35.                     currElements.add(new ResourceElement(field, field, null));
  36.                 }
  37.             }
  38.             else if (javaxResourceType != null && field.isAnnotationPresent(javaxResourceType)) {
  39.                 if (Modifier.isStatic(field.getModifiers())) {
  40.                     throw new IllegalStateException("@Resource annotation is not supported on static fields");
  41.                 }
  42.                 if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
  43.                     currElements.add(new LegacyResourceElement(field, field, null));
  44.                 }
  45.             }
  46.         });
  47.         elements.addAll(0, currElements);
  48.         targetClass = targetClass.getSuperclass();
  49.     }
  50.     while (targetClass != null && targetClass != Object.class);
  51.     return InjectionMetadata.forElements(elements, clazz);
  52. }
复制代码
实际触发赋值的操作是在 InjectionMetadata 的 inject() 方法中实现的,在它的方法中又会循环调用 InjectedElement 的 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.     if (!elementsToIterate.isEmpty()) {
  6.         for (InjectedElement element : elementsToIterate) {
  7.             element.inject(target, beanName, pvs);
  8.         }
  9.     }
  10. }
复制代码
在 InjectedElement  的 inject() 方法中通过反射的方式将找到的 Bean 赋值给字段。代码如下:
  1. protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
  2.         throws Throwable {
  3.     if (!shouldInject(pvs)) {
  4.         return;
  5.     }
  6.     if (this.isField) {
  7.         Field field = (Field) this.member;
  8.         ReflectionUtils.makeAccessible(field);
  9.         //这里通过反射的方式设置值,设置的值就是根据Bean名称获取到的Bean
  10.         field.set(target, getResourceToInject(target, requestingBeanName));
  11.     } else {
  12.         //省略其它代码
  13.     }
  14. }
复制代码
在 ResourceElement 的 getResourceToInject() 方法中实现了查找逻辑:如果 BeanFactory 中包含这个 Bean 名称对应的 Bean 则直接根据名称查找,否则会根据类型进行匹配,这个就是常说的 @Resource 注解默认是按照名称进行匹配的,名称匹配不到的情况下再按照类型进行匹配。代码如下:
  1. protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
  2.     throws NoSuchBeanDefinitionException {
  3.     //省略代码
  4.     // Regular resource autowiring
  5.     if (this.resourceFactory == null) {
  6.         throw new NoSuchBeanDefinitionException(element.lookupType,
  7.                 "No resource factory configured - specify the 'resourceFactory' property");
  8.     }
  9.     return autowireResource(this.resourceFactory, element, requestingBeanName);
  10. }
  11. protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
  12.         throws NoSuchBeanDefinitionException {
  13.     Object resource;
  14.     Set<String> autowiredBeanNames;
  15.     String name = element.name;
  16.     if (factory instanceof AutowireCapableBeanFactory autowireCapableBeanFactory) {
  17.         //如果根据Bean名称找不到Bean且允许按照类型匹配的情况下走第一个分支
  18.         if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
  19.             autowiredBeanNames = new LinkedHashSet<>();
  20.             resource = autowireCapableBeanFactory.resolveDependency(
  21.                     element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
  22.             if (resource == null) {
  23.                 throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
  24.             }
  25.         } else { //如果根据名称找得到Bean则直接根据名称获取Bean
  26.             resource = autowireCapableBeanFactory.resolveBeanByName(name, element.getDependencyDescriptor());
  27.             autowiredBeanNames = Collections.singleton(name);
  28.         }
  29.     } else {
  30.         //省略代码
  31.     }
  32.     //省略代码
  33.     return resource;
  34. }
复制代码
按照类型匹配的逻辑是在 DefaultListableBeanFactory 的 doResolveDependency() 方法中实现的,在该方法中会根据类型找到所有是当前类型的 Bean,然后构造一个 Map,key 是 Bean 的名称,value 是对应的 Bean 对象,如果找到的 Bean 个数大于 1 则会选择一个最符合条件的返回(选择的依据背面会讲到),如果等于 1 则直接返回这个 Bean。代码如下:
  1. public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
  2.         @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  3.         InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
  4.         try {
  5.                 //省略代码
  6.                
  7.                 //这里根据类型找到所有的Bean,然后Bean的名称作为key,Bean作为Value
  8.                 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
  9.                 if (matchingBeans.isEmpty()) {
  10.                         // Step 4c (fallback): custom Collection / Map declarations for collecting multiple beans
  11.                         multipleBeans = resolveMultipleBeansFallback(descriptor, beanName, autowiredBeanNames, typeConverter);
  12.                         if (multipleBeans != null) {
  13.                                 return multipleBeans;
  14.                         }
  15.                         // Raise exception if nothing found for required injection point
  16.                         if (isRequired(descriptor)) {
  17.                                 raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
  18.                         }
  19.                         return null;
  20.                 }
  21.                 String autowiredBeanName;
  22.                 Object instanceCandidate;
  23.                 //如果根据类型找到多个Bean则需要选择一个合适的Bean返回
  24.                 if (matchingBeans.size() > 1) {
  25.                         autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
  26.                         if (autowiredBeanName == null) {
  27.                                 if (isRequired(descriptor) || !indicatesArrayCollectionOrMap(type)) {
  28.                                         // Raise exception if no clear match found for required injection point
  29.                                         return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
  30.                                 }
  31.                                 else {
  32.                                         // In case of an optional Collection/Map, silently ignore a non-unique case:
  33.                                         // possibly it was meant to be an empty collection of multiple regular beans
  34.                                         // (before 4.3 in particular when we didn't even look for collection beans).
  35.                                         return null;
  36.                                 }
  37.                         }
  38.                         instanceCandidate = matchingBeans.get(autowiredBeanName);
  39.                 } else {
  40.                         //如果只有一个Bean则直接返回这个Bean
  41.                         Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
  42.                         autowiredBeanName = entry.getKey();
  43.                         instanceCandidate = entry.getValue();
  44.                 }
  45.                 // Step 6: validate single result
  46.                 if (autowiredBeanNames != null) {
  47.                         autowiredBeanNames.add(autowiredBeanName);
  48.                 }
  49.                 if (instanceCandidate instanceof Class) {
  50.                         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  51.                 }
  52.                 return resolveInstance(instanceCandidate, descriptor, type, autowiredBeanName);
  53.         }
  54.         finally {
  55.                 ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
  56.         }
  57. }
复制代码
AutowiredAnnotationBeanPostProcessor 实现逻辑(以修饰字段为例)

首先在构造函数中初始化了需要处理的注解包罗 @Autowired 和 @Inject 注解。代码如下:
[code]public AutowiredAnnotationBeanPostProcessor() {    //添加要处理@Autowired注解    this.autowiredAnnotationTypes.add(Autowired.class);    this.autowiredAnnotationTypes.add(Value.class);    ClassLoader classLoader = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();    try {        //这里是为了适配不同版本@Inject注解在不同的包路径下        this.autowiredAnnotationTypes.add((Class
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

民工心事

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表