20. Spring源码篇之@Lookup详解

简介

Lookup注解可能平时开发中大家接触的少,但是又确实挺有用的,比如我们一个单例Bean注入了一个原型Bean,原型Bean的效果其实是会失效的,因为单例Bean一开始就实例化好了,后面也不会再变化,但我们可能需要的就是原型Bean呀,那么怎么解决呢,如果注入的是一个动态的就好了,于是我们马上就会想到代理对象,spring中@Lookup就可以帮助我们实现该效果,原理就是生成了一个代理对象

Lookup还有一个功能就是可以放在抽象类上,如果一个类是一个抽象类,一般是不能生成Bean对象的,但是里面有方法加了@Lookup就可以

Lookup放在抽象类上

@Component("abstractLookup")
public abstract class AbstractLookup {// @Lookuppublic abstract void test();
}public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.scan("com.shura.lookup");context.refresh();System.out.println(context.getBean("abstractLookup"));
}启动报错Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'abstractLookup' available

以上定义了一个抽象类,加了Component注解,通过扫描注册Bean,但是报错了,报错原因是spring并没有这么一个Bean

我们修改一下在test方法上面加上一个@Lookup注解

再次运行

输出
com.shura.lookup.AbstractLookup$$EnhancerBySpringCGLIB$$fb5a4910@25618e91

从输出的结果可以看出,其实也是一个CGLIB的代理对象

如何判断的?

在扫描出Bean生成BeanDefinition之前会做判断,代码如下 ClassPathScanningCandidateComponentProvider#isCandidateComponent

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata = beanDefinition.getMetadata();/*** 是否是独立的类,普通的类,静态的匿名内部类是独立的类,匿名内部类不是独立的类* 接口或者抽象类不能作为Bean* 但是如果是抽象类却有Lookup注解也是是一个Bean*/return (metadata.isIndependent() && (metadata.isConcrete() ||(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

注入原型Bean

上面讲到了,单例Bean注入原型Bean,是没有原型Bean的效果的,代码如下

@Component(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class A {@Autowiredprivate B b;public void test() {System.out.println(b);}
}@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class B {}

启动类

public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.scan("com.shura.lookup");context.refresh();A a = context.getBean(A.class);a.test();a.test();
}执行
com.shura.lookup.B@2aafb23c
com.shura.lookup.B@2aafb23c

从上面结果看出确实没有原型的效果

修改为Lookup注解方式

@Component(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class A {public void test() {System.out.println(b());}@Lookuppublic B b() {return null;}
}

再次执行启动类

com.shura.lookup.B@17d99928
com.shura.lookup.B@3834d63f

从结果来看大家也了解了其作用。

源码解析

效果有了,就来看下源码如何做到的,Lookup解析的源码在 AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors
中,这个方法每个Bean实例化都会执行,这个类的详细介绍将在下一篇文章介绍,本文先介绍其中Lookup部分

if (!this.lookupMethodsChecked.contains(beanName)) {// 这步判断没什么用,忽略if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {try {Class<?> targetClass = beanClass; // beanClass传入进来的do {// 遍历targetClass中的method,查看是否写了@Lookup方法ReflectionUtils.doWithLocalMethods(targetClass, method -> {Lookup lookup = method.getAnnotation(Lookup.class);if (lookup != null) {Assert.state(this.beanFactory != null, "No BeanFactory available");// 将当前method封装成LookupOverride// lookup.value()表示设置的beanNameLookupOverride override = new LookupOverride(method, lookup.value());RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName);// 设置到BeanDefinition的methodOverrides中mbd.getMethodOverrides().addOverride(override);}});// 父类有lookup也检查一下targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);}}// 表示该类已经检查过了lookup下次就不用检查了this.lookupMethodsChecked.add(beanName);
}

lookup找出来之后,就看实例化是怎么处理的了

下面是实例化的源码逻辑 SimpleInstantiationStrategy#instantiate

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// 判断BeanDefinition是否存在@Lookup的方法if (!bd.hasMethodOverrides()) {// 没有@LookupConstructor<?> constructorToUse;synchronized (bd.constructorArgumentLock) {constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;if (constructorToUse == null) {final Class<?> clazz = bd.getBeanClass();constructorToUse = clazz.getDeclaredConstructor();// 保存bd.resolvedConstructorOrFactoryMethod = constructorToUse;}}// 通过 Constructor#newInstance构造出来return BeanUtils.instantiateClass(constructorToUse);}else {// 如果存在@Lookup,生成一个代理对象return instantiateWithMethodInjection(bd, beanName, owner);}
}

上面看出如果是有@Lookup注解,生成的是一个代理对象,那么代理对象里面又是怎么处理的CglibSubclassingInstantiationStrategy.LookupOverrideMethodInterceptor#intercept

public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at allif (StringUtils.hasText(lo.getBeanName())) {// owner就是BeanFactoryObject bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :this.owner.getBean(lo.getBeanName()));return (bean.equals(null) ? null : bean);}else {ResolvableType genericReturnType = ResolvableType.forMethodReturnType(method);return (argsToUse != null ? this.owner.getBeanProvider(genericReturnType).getObject(argsToUse) :this.owner.getBeanProvider(genericReturnType).getObject());}
}

主要看关键逻辑是通过BeanFactory.getBean()获取的原型Bean,那么自然每次的Bean对象都是不一样的

总结

以上就是@Lookup注解的源码分析了,下一篇介绍推断构造方法扩展点


欢迎关注,学习不迷路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/155259.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类型体系与基本数据类型(题目)

目录 1. 本章讨论了标签的用法。在我们经常使用的标准模板库&#xff08;STL&#xff09;中也存在标签的概念。STL将迭代器进行了划分&#xff0c;为不同的迭代器赋予了不同的标签&#xff08;如双向迭代器、随机访问迭代器等&#xff09;。在网络上搜索一下相关的概念&#x…

Android Serializable / Parcelable

Serializable 序列化,将对象转为二进制序列 Parcelable 不是序列化,属于进程间通信,不需要IO/操作,没有拷贝内存的操作, Object -> ShareMemory -> Object 不需要IO,使用内存共享等方式 Kotlin inline fun 内联函数 TCP协议将数据包拆分,进行发送,保证网络数据的可…

基于纹理特征的kmeas聚类的图像分割方案

Gabor滤波器简介 在图像处理中&#xff0c;以Dennis Gabor命名的Gabor滤波器是一种用于纹理分析的线性滤波器&#xff0c;本质上是指在分析点或分析区域周围的局部区域内&#xff0c;分析图像中是否存在特定方向的特定频率内容。Gabor滤波器的频率和方向表示被许多当代视觉科学…

二十一、数组(3)

本章概要 Arrays的setAll方法增量生成 Arrays的setAll方法 在Java 8中&#xff0c; 在RaggedArray.java 中引入并在 ArrayOfGenerics.java.Array.setAll() 中重用。它使用一个生成器并生成不同的值&#xff0c;可以选择基于数组的索引元素&#xff08;通过访问当前索引&…

00、计算机视觉入门与调优简介

写在前面 每天更新1篇文章&#xff0c;共更新100篇以上 相关代码会放在gitee上 中间会按进度和反馈安排视频讲解 预计2023-11-11开始推送文章&#xff0c;持续3个月左右 专栏简介 本专栏带你从头开始入门计算机视觉。 内容会比之前写的文章更专业更全面&#xff0c;并且你…

Android项目更新依赖和打包步骤和问题汇总

目录 1、Android 项目打包&#xff0c;32位包升级到64位包问题一&#xff1a;ERROR: Conflicting configuration : armeabi-v7a,x86-64,arm64-v8a,x86 in ndk abiFilters cannot be present when splits abi filters are set : x86,armeabi-v7a,arm64-v8a 2、Android项目依赖升…

docker部署excalidraw画图工具(银角大王课堂使用的画图软件搭建)

环境搭建 安装docker&#xff08;不过多说&#xff0c;可以参照他人文档&#xff09; 安装docker-compose 运行以下命令以下载 Docker Compose 的当前稳定版本&#xff1a; sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose…

pytest-base-url插件之配置可选的项目系统URL

前言 ①当我们的自动化代码完成之后&#xff0c;通常期望可以在不同的环境进行测试&#xff0c;此时可以将项目系统的URL单独拿出来&#xff0c;并且可以通过pytest.ini配置文件和支持pytest命令行方式执行。 ② pytest-base-url 是一个简单的pytest插件&#xff0c;它通过命…

【数据结构】HashMap 和 HashSet

目录 1.哈希表概念 2冲突 2.1概念 2.2 冲突-避免 2.3冲突-避免-哈希函数设计 2.4 冲突-避免-负载因子调节 ​编辑 2.5 冲突-解决-开散列/哈希桶 2.5冲突严重时的解决办法 3.实现 4.性能分析 5.与Java集合类的关系 1.哈希表概念 在顺序结构中&#xff0c;元素关键码和存…

【vue+eltable】修改表格滚动条样式

<style lang"scss" scoped> ::v-deep .el-table__body-wrapper::-webkit-scrollbar {width: 10px; /*纵向滚动条的宽度*/height: 10px; /*横向滚动条的高度*/ } /*定义滚动条轨道 内阴影圆角*/ ::v-deep .el-table__body-wrapper::-webkit-scrollbar-track {bo…

Java 多线程之 volatile(可见性/重排序)

文章目录 一、概述二、使用方法三、测试程序3.1 验证可见性的示例3.2 验证指令重排序的示例 一、概述 在Java中&#xff0c;volatile 关键字用于修饰变量&#xff0c;其作用是确保多个线程之间对该变量的可见性和禁止指令重排序优化。 当一个变量被声明为volatile时&#xff0…

高德地图点击搜索触发输入提示

减少调用次数&#xff0c;不用每输入一次调用一次&#xff0c;输入完后再触发搜索 效果图&#xff1a; ![Alt](https://img-home.csdnimg.cn/images/20220524100510.png dom结构 <div class"seach"><van-searchshow-actionv-model"addressVal"…

【使用vscode在线web搭建开发环境--code-server搭建】

官方版本下载 https://github.com/coder/code-server/releases?q4.0.0&expandedtrue使用大于版本3.8.0,因为旧版本有插件市场不能访问的情况版本太高需要更新环境依赖 拉取安装包 []# wget "https://github.com/coder/code-server/releases/download/v4.0.0/code-…

探访九牧绿色黑灯工厂,找寻“科技卫浴 世界九牧”的答案

文 | 螳螂观察 作者 | 余一 你所想象中的工厂是怎么样的&#xff1f;灯火通明、人声鼎沸、人来人往&#xff1f;如果告诉你一座工厂既没有灯&#xff0c;也没有人&#xff0c;但却还在持续生产&#xff0c;你会不会觉得这是不可思议的事&#xff1f; 如果不是亲眼见证&#…

Simulink 自动代码生成:手写代码替换生成代码Code Replacement Tool使用

目录 前言 代码替换库操作步骤 代码生成验证 总结 前言 在实际工程开发过程中&#xff0c;Simulink生成的代码都是构建的算法实现的&#xff0c;纯软件实现&#xff0c;生成的代码大多也是直接在CPU上运行的。实际还有一些MCU集成了像Cordic&#xff0c;协处理器等。有些代…

WinEdt 11.1编辑器的尝鲜体验

WinEdt 11.1编辑器的尝鲜体验 2023年5月19日&#xff0c;WinEdt 11.1版本发布了&#xff0c;相比WinEdt 10.3, 最新版更加漂亮&#xff0c;更加友好&#xff0c;更加好用了&#xff01; 最大的改变是WinEdt 11.1 有了自带的WinEdtPDF阅读器&#xff0c;所以不再需要下载第三方…

ros2工作空间

我们先不管ros2工作空间是什么样子的&#xff0c;如果是我自己来搞一个工作空间&#xff0c;我一定是这样安排 一个文件夹用来放自己存放的文件&#xff0c;。。。。。。。。。。对应src文件夹 一个文件夹用来放编译后的文件&#xff0c;。。。。。。。。。。。对应intall文件…

模型微调技术

Parameter Efficient Fine Tuning (PEFT)和Low Rank Adaptation (LoRA)是2种非常重要的模型微调方法。这两种方法只微调模型的一小部分&#xff08;额外的&#xff09;参数&#xff0c;同时冻结预训练模型的大部分参数&#xff0c;从而大大降低了计算和存储成本。尤其是LoRA&am…

day61 layui和分页原理

昨日内容回顾 choices参数的使用 一般用在什么场景&#xff1a;当被存储的字段数据可能被列举完毕的时候一般会使用choices参数 性别 学历 来源 工作经验等 一般情况下不在数据表中直接存储中文&#xff0c;存数字、存字母来做映射 # 怎么使用 gender_choices ((1, 男),(2…

Weakly Supervised Visual Question Answer Generation

目录 一、论文速读 1. 1 论文概要总结 相关工作 主要贡献 论文主要方法 实验数据 未来研究方向 二、论文精度 2.1 论文试图解决什么问题&#xff1f; 2.2 论文中提到的解决方案之关键是什么&#xff1f; 2.3 用于定量评估的数据集是什么&#xff1f;代码有没有开源&a…