@Resource注入

  • 本文源码基于spring-framework-5.3.10。
  • 源码位置:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(PropertyValues, Object, String)
  • 除了指定BeanName的Bean在注入的时候会进行ByType,我们在使用中的大部分时候都是使用ByName进行注入,所以我们认为@Resource是通过ByName的方式注入
  • @Resource注解属于jdk的注解:javax.annotation.Resource
  • @Resource在寻找注入点的时候,静态的方法直接抛异常,参数不是一个直接抛异常!

@Resource源码流程

  • 寻找注入点:字段、方法
  • 进行注入:没有写BeanName,并且默认的BeanName在Bean工厂找不到,先ByType后ByName。其他的大部分情况直接根据名称去Bean工厂找!

源码分析

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

@Resource的寻找注入点

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.// 得到要注入的BeanName,未加载直接得到BeanName,实例化了的话去加载当前Bean的clazz的名称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);}// 真正的通过class文件去扫描metadata = buildResourceMetadata(clazz);// 添加到缓存中this.injectionMetadataCache.put(cacheKey, metadata);}}}// 返回得到的字段要注入的点,或者方法参数要注入的点return metadata;
}/*** 寻找注入点*/
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {// 要注入的返回值对应的类型是java.开头的类。或者上面的注解有org.springframework.core.Ordered这个注解及其子类的时候。去创建一个空的注入点的类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 -> {......前面非@Resource相关的代码暂时忽略// 字段上存在@Resource注解else if (field.isAnnotationPresent(Resource.class)) {// 静态的字段,直接抛异常!if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}// 跳过@PostConstruct和@PreDestroy这俩个标记的注解。if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new ResourceElement(field, field, null));}}});// 方法注入点处理ReflectionUtils.doWithLocalMethods(targetClass, method -> {......前面非@Resource相关的代码暂时忽略// 方法上存在@Resource注解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();// 方法参数不为1,抛异常if (paramTypes.length != 1) {throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}// 唯一的一个参数不存在@PostConstruct和@PreDestroy这俩个标记的注解。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 resource = ae.getAnnotation(Resource.class);// 得到@Resource自己配置的名称String resourceName = resource.name();// 得到@Resource自己配置的类型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();}// null转空串this.name = (resourceName != null ? resourceName : "");this.lookupType = resourceType;// resource的lookup属性处理String lookupValue = resource.lookup();this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());Lazy lazy = ae.getAnnotation(Lazy.class);this.lazyLookup = (lazy != null && lazy.value());
}

字段、方法的注入

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);}}
}/*** 对每个注入点进行注入*/
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();}}
}

寻找需要注入的值

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {// 懒加载返回一个空的代理对象,否则调用getResourcereturn (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :getResource(this, requestingBeanName));
}protected Object getResource(LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {// 直接调用通过名称调用getBeanif (StringUtils.hasLength(element.mappedName)) {return this.jndiFactory.getBean(element.mappedName, element.lookupType);}// 直接调用通过名称调用getBeanif (this.alwaysUseJndiLookup) {return this.jndiFactory.getBean(element.name, element.lookupType);}// 直接调用通过名称调用getBeanif (this.resourceFactory == null) {throw new NoSuchBeanDefinitionException(element.lookupType,"No resource factory configured - specify the 'resourceFactory' property");}// 根据LookupElement从BeanFactory找到适合的bean对象return autowireResource(this.resourceFactory, element, requestingBeanName);
}/*** 根据类型或者名称去获取要设置的bean*/
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {Object resource;Set<String> autowiredBeanNames;String name = element.name;// 他是一个自动装配的BeanFactoryif (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)) {// 这里与@Autowired类型,先根据类型去筛选Bean、再根据name去筛选autowiredBeanNames = new LinkedHashSet<>();resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}}else {// 内部直接通过名称调用getBeanresource = beanFactory.resolveBeanByName(name, descriptor);// 设置一下注入了哪个BeanautowiredBeanNames = Collections.singleton(name);}}else {// 大部分情况直接通过名称调用getBeanresource = factory.getBean(name, element.lookupType);// 设置一下注入了哪个BeanautowiredBeanNames = Collections.singleton(name);}// 处理Bean的依赖关系吗,当前Bean被那些Bean依赖,依赖了那些Beanif (factory instanceof ConfigurableBeanFactory) {ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;for (String autowiredBeanName : autowiredBeanNames) {if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);}}}return resource;
}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!

【spring】依赖注入之@Resource注入相关推荐

  1. 最全的 Spring 依赖注入方式,你都会了吗?

    欢迎关注方志朋的博客,回复"666"获面试宝典 前言 Spring 正如其名字,给开发者带来了春天,Spring 是为解决企业级应用开发的复杂性而设计的一款框架,其设计理念就是:简 ...

  2. arg是什么函数_java后端开发三年!你还不了解Spring 依赖注入,凭什么给你涨薪...

    前言 前两天和一个同学吃饭的时候同学跟我说了一件事,说他公司有个做了两年的人向他提出要涨薪资,他就顺口问了一个问题关于spring依赖注入的,那个要求涨薪的同学居然被问懵了...事后回家想了想这一块确 ...

  3. Spring依赖注入:注解注入总结

    更多11 spring 依赖注入 注解 java 注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.C ...

  4. Spring依赖注入和控制反转

    文章目录 1.依赖注入 1.1.依赖注入和控制反转的概念 1.2.依赖注入的实现方式 1.3.控制反转的具体实现 1.4.依赖注入的具体实现 1.5.依赖注入和控制反转总结 1.依赖注入 1.1.依赖 ...

  5. spring依赖注入_Spring3:类型安全依赖项注入

    spring依赖注入 在从Spring跳到类型安全依赖注入之前,我想讨论一下我们之前所做的方式. 我们一直在借助Spring的Autowired注释按类型使用依赖项注入. 像这样的东西会注入Sprin ...

  6. spring依赖注入_Spring依赖注入技术的发展

    spring依赖注入 回顾Spring框架的历史,您会发现在每个发行版中实现依赖注入的方法越来越多. 如果您使用该框架已经超过一个月,那么在这篇回顾性文章中可能不会发现任何有趣的东西. 除了Scala ...

  7. Spring依赖注入技术的发展

    回顾Spring框架的历史,您会发现实现依赖注入的方式在每个发行版中都在增加. 如果您使用该框架已经超过一个月,那么在这篇回顾性文章中可能不会发现任何有趣的东西. 除了Scala中的最后一个示例,没有 ...

  8. spring依赖注入的基本方法及其原理

    Spring的注入有三种基本方法: 1.使用构造器注入 constructor injection.2.使用属性setter方法注入 setter injection. 3.使用Field注入(用于注 ...

  9. Spring依赖注入IOC(给字段赋值)和Spring测试

    含义: IOC是一种思想,它的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI(Dependency Injection,依赖注入)来实现的:Spring中对象的属性字 ...

最新文章

  1. mysql int和bigdecimal,mysql的 int 类型,刨析返回类型为BigDicemal 类型的奇怪现象
  2. JS开发中常用的小技巧
  3. webstorm两个文件比对_webstorm/phpstorm配置连接ftp快速进行文件比较(上传下载/同步)操作...
  4. git 远程分支创建与推送
  5. 阿里云智能物联网解决方案宣讲会,帮助天津东丽临空经济区数字腾飞
  6. MySQL主从复制虽好,能完美解决数据库单点问题吗?
  7. [NOI2018]冒泡排序
  8. 如何使用window.btoa base64对接口数组进行加密处理
  9. SPH(光滑粒子流体动力学)流体模拟实现七:屏幕空间流体渲染(SSF)
  10. opencv之waitKey()与waitKeyEx()的区别
  11. 大数据分析关键的5个思维
  12. 学术必备 | 论文写作中注意这些细节,能显著提升成稿质量
  13. 教你高效管理CrossOver容器
  14. 信息安全工程师 学习笔记 完结
  15. 游戏策划入门教程(1)工具篇
  16. cesium雷达图_Cesium专栏-气象雷达动图(附源码下载)
  17. 【图像处理】换脸技术哪家强?Python来挑战,一键换脸,毫无破绽~
  18. mysql MERGE 错误(differently defined or of non-MyISAM type)
  19. 腾讯云服务器按量付费如何转为包年包月?
  20. 第七十四章 Caché 函数大全 $WREVERSE 函数

热门文章

  1. Extracted SQL state class '99' from value '99999'
  2. ps1修改ubuntu终端(terminal)字体颜色
  3. 离线安装nginx(包括离线安装gcc-g++环境,免费下载gcc离线安装包)
  4. ap pdf to html 注册码,AP PDF to IMAGE Batch Converter(PDF转JPG工具)V4.2 免费版
  5. 推荐两部昨天刚看完的韩剧
  6. [办公软件学习教程] 如何使用Excel高亮查找出来的单元格
  7. docker安装php开发环境
  8. word 2016利用表格编排公式及编号
  9. 深入理解虚拟机实战:修改class文件实现System标准输出重定向
  10. python 格式化时间含中文报错