一、Resource注解解析过程

@Resource和@Autowired处理的逻辑基本相同,都是先查找注入点,然后再根据注入点进行属性注入,所不同的是@Resource的解析是由CommonAnnotationBeanPostProcessor来完成的,@Autowired解析由AutowiredAnnotationBeanPostProcessor完成,下面介绍@Resource解析的全过程

1.1 寻找注入点

寻找注入点的时机与@Autowired一致,都是在实例化后,调用所有实现了MergedBeanDefinitionPostProcessor接口的postProcessMergedBeanDefinition()方法,同样Resource注解的处理类CommonAnnotationBeanPostProcessor也实现了MergedBeanDefinitionPostProcessor接口,所以会调用该类的postProcessMergedBeanDefinition()方法来寻找注入点

方法的第一行代码会去调用InitDestroyAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()来缓存和检查初始化方法和销毁方法

第二行代码去查找@Resource注解的元数据

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);
}

根据beanName先去缓存中获取,如果缓存中没有或者InjectionMetadata中的目标class与当前beanType不符,则调用buildResourceMetadata来生成注解元数据

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;
}

循环遍历当前类和父类的属性和方法,判断是否有@Resource注解,如果有,则生成ResourceElement对象,ResourceElement继承自InjectionMetadata.InjectedElement,将ResourceElement对象对象放入到InjectedElement对象结合中,然后生成InjectionMetadata对象返回

对于静态属性和静态方法,@Autowired不会进行处理,但@Resource则直接抛出异常

private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();ReflectionUtils.doWithLocalFields(targetClass, field -> {……else if (field.isAnnotationPresent(Resource.class)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new ResourceElement(field, field, null));}}});ReflectionUtils.doWithLocalMethods(targetClass, method -> {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}……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();if (paramTypes.length != 1) {throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}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);
}

ResourceElement的构造方法如下,@Resource注解可以通过name和type两个方法指定注入Bean的名字和类型,如果没有指定name,则使用默认的名称,使用参数名或set方法去掉set前缀当作name

如果指定了type属性,检查属性的类型或方法的第一个参数类型是否与指定的类型匹配,如果不匹配将抛出异常

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,那么则用field的name,或setXxx()中的xxxthis.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));}}// 使用@Resource时指定了具体的name,进行占位符填充else if (embeddedValueResolver != null) {resourceName = embeddedValueResolver.resolveStringValue(resourceName);}// @Resource除开可以指定bean,还可以指定type,type默认为Objectif (Object.class != resourceType) {// 如果指定了type,则验证一下和field的类型或set方法的第一个参数类型,是否和所指定的resourceType匹配checkResourceType(resourceType);}else {// No resource type specified... check field/method.resourceType = getResourceType();}……
}

1.2 属性注入

属性注入是在属性填充的时候过程中完成,调用InstantiationAwareBeanPostProcessor实现类的postProcessProperties(),@Resource注解的属性注入是在CommonAnnotationBeanPostProcessor的postProcessProperties()中完成

  • 开始注入

    获取已经缓存的注入点,然后调用InjectedElement的inject(),这个地方不同于@Autowired,Autowired注解的属性注入分别会调用AutowiredFieldElement和AutowiredMethodElement的inject()

    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<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {// 遍历每个注入点进行依赖注入for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}
    }
    
  • 获取注入值

    InjectedElement的inject()源码如下,直接调用getResourceToInject()获取依赖值进行注入

    protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)throws Throwable {if (this.isField) {Field field = (Field) this.member;ReflectionUtils.makeAccessible(field);field.set(target, getResourceToInject(target, requestingBeanName));}else {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();}}
    }
    

    如果属性或方法标注了@Lazy注解,则生成一个代理对象,否则调用getResource()获取注入对象

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

    getResource()源码如下,调用核心的autowireResource()获取bean对象

    protected Object getResource(LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {……// 根据LookupElement从BeanFactory找到适合的bean对象return autowireResource(this.resourceFactory, element, requestingBeanName);
    }
    

    如果当前的beanFactory属于AutowireCapableBeanFactory,如果根据名称找不到bean对象时,还可以调用resolveDependency()根据type获取或生成bean对象

    @Resource注解查找bean对象时,先判断是否使用默认名,如果用默认名,则先根据默认名判断BeanFactory是否有,如果有的话直接调用getBean()获取bean对象,如果没有,再调用resolveDependency()根据类型去查找或生成bean对象,resolveDependency()是@Autowired解析的核心方法

    如果指定了名称,则直接调用getBean()方法去查找bean对象

    如果找到了bean对象,则将它的name缓存在autowiredBeanNames中,最后调用beanFactory.registerDependentBean()缓存beanName之间的依赖关系,当前注入的bean对象的被哪些bean依赖,缓存的是beanNaem,而不是bean对象

    protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {Object resource;Set<String> autowiredBeanNames;String name = element.name;if (factory instanceof AutowireCapableBeanFactory) {AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;DependencyDescriptor descriptor = element.getDependencyDescriptor();// 假设@Resource中没有指定name,并且field的name或setXxx()的xxx不存在对应的bean,那么则根据field类型或方法参数类型从BeanFactory去找if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {autowiredBeanNames = new LinkedHashSet<>();resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}}else {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;
    }
    

1.3 与@Autowired区别

1、性能上两者没有任何区别,@Resource是Jdk提供了注解,Spring对其进行了具体实现,在获取依赖注入值的过程中,@Autowired先根据类型来查找,然后进行Qualifier筛选等层层筛选,而@Resource先根据名城查找,如果使用的是默认名,并且beanFactory根据名称找不到的时候,才会调用与@Autowired一样的逻辑根据类型来找

2、如果@Resource指定了名称,则只会根据名称来查找bean对象,找不到直接抛异常

详解@Resource解析过程相关推荐

  1. 详解Java解析XML的四种方法

    http://developer.51cto.com  2009-03-31 13:12  cnlw1985  javaeye  我要评论(8) XML现在已经成为一种通用的数据交换格式,平台的无关性 ...

  2. 30.jvm.gc(GC之详解CMS收集过程和日志分析)

    30.jvm.gc(GC之详解CMS收集过程和日志分析) 30.1.话题引入 30.2.ParNew and CMS 30.3.日志 30.3.1.GC日志初体验 30.3.2.Minor GC 30 ...

  3. php xml 实例教程,php解析xml方法实例详解,解析xml实例详解_PHP教程

    php解析xml方法实例详解,解析xml实例详解 本文以实例形式详细讲述了php解析xml方法.分享给大家供大家参考.具体分析如下: books.xml文件如下: Harry Potter J K. ...

  4. Open3D 区域生长分割:详解Python实现过程

    Open3D 区域生长分割:详解Python实现过程 Open3D是一个专业的开源三维计算机视觉库,支持针对点云数据进行多种类型算法处理及可视化.其中包括基于区域生长分割的算法,可以对点云数据进行自动 ...

  5. Android Studio 安装详解及安装过程中出现的问题解决方案

    Android Studio 安装详解及安装过程中出现的问题解决方案 一,Android Studio安装包下载, 首先到官网下载,就是去Android Studio中文社区官网下载你的平台需要的安装 ...

  6. 计算机开机过程 南桥,详解电脑开机过程

    详解电脑开机过程 1.首先给ATX电源加电,加电后,ATX电源开始输出待机工作电压(SB5V),接着实时时钟开始工作,向CMOS电路和开机电路发送32.768KHZ的实时时钟信号. 2.接着按下电源开 ...

  7. Windows系统Git安装教程(详解Git安装过程)

    Windows系统Git安装教程(详解Git安装过程)   今天更换电脑系统,需要重新安装Git,正好做个记录,希望对第一次使用的博友能有所帮助! 获取Git安装程序   到Git官网下载,网站地址: ...

  8. dockerfile详解-dockerfile构建过程解析,dockerfile体系结构保留字解析,案例解析

    Dockerfile详解 1.定义:Dockerfile是用来构建Docker镜像的构建文件,是一系列命令和参数构成的脚本 2.步骤: --手动编写一个dockerfile文件,必须要要符合规范 -- ...

  9. 万字干货!详解JavaScript执行过程

    作者 | FinGet       责编 | 欧阳姝黎 JS代码的执行,主要分为两个个阶段:编译阶段.执行阶段.本文所有内容基于V8引擎. 前言 v8引擎 v8引擎工作原理: V8由许多子模块构成,其 ...

  10. python之33个关键字详解_Python解析、提取url关键字的实例详解

    解析url用的类库: python2版本: from urlparse import urlparse import urllib python3版本: from urllib.parse impor ...

最新文章

  1. dogdoggo搜索引擎_Giphy:专搜GIF动态图的搜索引擎
  2. Luntik and Subsequences 思维
  3. springMVC配置文件spring-servlet.xml中mvc:annotation-driven /的意义
  4. shadows a parameter
  5. 加加减减的奥秘——从数学到魔术的思考(二)
  6. 创建索引的方法有两种
  7. python url拼接_python学习笔记——urllib篇
  8. 小米5如何支持ATT网络运营商
  9. java数据同步处理_[java数据库同步解决方案]系统间通讯实现数据信息实时同步解决方案...
  10. Cocos2d-x一张小背景重复贴图充满整个屏幕
  11. 为前端而生的编辑器Brackets及配置推荐
  12. date 日期时间命令
  13. 为了让你在“口袋奇兵”聊遍全球,java面试代码题
  14. 锋迷商城spring-vue项目流程和笔记
  15. 定积分,二重积分,三重积分
  16. 2022年全球及中国工业五金垫圈行业研究
  17. MSSQL Server 2008 - express 版 安装 企业管理器Management Studio
  18. 更换SSD系统盘-PE重装系统
  19. 移动端 h5和原生交互的方式
  20. 自己的微信竟然延迟接收消息了,这是什么情况?

热门文章

  1. Domain Adaptation理论分析
  2. SiteSucker Mac版(整站下载工具)中文版
  3. 设计师工作经验_得到我作为设计师的第一份工作
  4. 160x128JAva_GitHub - hanang128/X-SpringBoot: X-SpringBoot是一个轻量级的Java快速开发平台,能快速开发项目并交付【接私活利器】...
  5. sidetone 、回声抑制
  6. word中如何将所有同一级标题统一格式
  7. 简约网站维护单页html源码
  8. 大话GraphQL新手上车
  9. 关于java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)的问题解决办法。(相对路径出错)
  10. word在另外计算机格式不对,为什么同一个word文档在两台电脑上打开里面的格式不一样...