Spring的@Configuration使用cglib代理的效果和我自己写的简单实现

下面的代码,照着复制就能跑起来

今天看了下Spring的@Configuration,即java类配置bean,(这个spring3的新功能,虽然现在已经spring5了,但是这种配置bean的方式也是比较火的)
做了如下测试,发现一个现象,先说这个现象,后面用自己的理解再简单实现一下。

先说现象:

在java配置类中加@Configuration,下面的声明bean的方法,就只会被调一次,也就是初始化的时候,哪怕是下面的方法直接互相引用,返回的new的对象的构造方法也只会调一次
而如果不加@Configuration,那么下面的方法如果有相互调用,那么返回的new的对象的构造方法就会被调多次

下面是测试代码:

@Configuration
@ComponentScan("com.zs.cglib")
//这个类作为配置类
public class CglibConfig {@Beanpublic TestDomain testDomain(){return new TestDomain();}@Beanpublic TestDomainTwo testDomainTwo(){//这个方法会预先调用上一个方法testDomain();return new TestDomainTwo();}
}@Component("testDomain")
public class TestDomain {public TestDomain() {//构造参数打印,证明被调过System.out.println("new TestDomain-------------");}
}@Component("testDomainTwo")
public class TestDomainTwo {public TestDomainTwo() {//构造参数打印,证明被调过System.out.println("new TestDomainTwo-------------");}
}public class StartMain {//启动测试public static void main(String[] args) {AnnotationConfigApplicationContext anno = new AnnotationConfigApplicationContext(CglibConfig.class);System.out.println(anno.getBean(TestDomain.class));System.out.println(anno.getBean(TestDomainTwo.class));}
}

可以发现,如果CglibConfig加上@Configuration,就会打印出:
new TestDomain-------------
new TestDomainTwo-------------
如果把@Configuration去掉,就会打印出:
new TestDomain-------------
new TestDomain-------------
new TestDomainTwo-------------
也就是说,加上@Configuration,new出TestDomain实例只执行了一次,也就是说testDomainTwo()中调用的testDomain(),并没有new出新的TestDomain实例。
而把@Configuration去掉,TestDomain实例就会被new两次,也就是testDomainTwo()中调用的testDomain()也有new出TestDomain实例。

这是为什么呢?当然,肯定和@Configuration有关。

一般情况,我们把带有@Configuration的类叫做全注解配置类,也叫Full配置类;
我们把不带@Configuration的类叫Lite配置类;

源码解释:

追了下源码,一直找到org.springframework.context.annotation.ConfigurationClassPostProcessor#enhanceConfigurationClasses
突然看到enhancer,这不是cglib的东西么,根据这个线索再追,就知道了,加上@Configuration,其实是用了Cglib代理了
所以方法,已经被增强了,那肯定还有其他逻辑,
再找到org.springframework.context.annotation.ConfigurationClassEnhancer#newEnhancer
这就是cglib了,那就找callback,再找intercept方法,org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#intercept
这个方法里面增强了原方法,其实就是用map实现的,下面来个简单的模仿

根据cglib实现简单的效果

亲自写了个简单的cglib实现,还原了@Configuration的这种现象
测试代码如下:

CglibConfig这个类,把注解都去掉,咱们自己实现这个只调一次的功能
public class CglibConfig {public TestDomain testDomain(){return new TestDomain();}public TestDomainTwo testDomainTwo(){testDomain();return new TestDomainTwo();}
}
TestDomain和TestDomainTwo两个类不变
新增一个callback,实现以下逻辑。这都是cglib的知识点,不动可以查下cglib简单实现:
public class MyCallBack implements MethodInterceptor {//这个map就记录了方法每次调用的痕迹,并把调用后的结果保存起来,不是第一次调用的话,就直接将结果返回就行了private static Map<String,Object> map = new HashMap<>();@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//获取方法名字String name = method.getName();if(isFirst(name)){//如果方法是第一次调用Object invoke = methodProxy.invokeSuper(o, objects);//调用完将结果保存在map中map.put(name,invoke);return invoke;}else{//第二次,第三次调用,就走这里,直接返回map中的结果return map.get(name);}}private boolean isFirst(String name) {//判断是不是第一次调用,其实就是看这个name在map中是不是已经注册了Object invoke = map.get(name);if(invoke == null){return true;}return false;}
}
再写个cglib的util,以便main方法调用
public class CglibUtil {public static Object getBean(){//看到这个,应该就要想到cglibEnhancer enhancer = new Enhancer();enhancer.setSuperclass(CglibConfig.class);enhancer.setCallback(new MyCallBack());CglibConfig proxy= (CglibConfig)  enhancer.create();return proxy;}
}
//主方法测试
public class StartMain {public static void main(String[] args) {CglibConfig bean = (CglibConfig) CglibUtil.getBean();bean.testDomain();bean.testDomainTwo();}
}

执行的结果是:
new TestDomain-------------
new TestDomainTwo-------------

总结

利用cglib代理增强,
如果这个方法第一次调用,就把调用的方法名和返回的结果保存在map中,
后面再有调用,就直接返回结果了,不会真正再去执行了

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

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

相关文章

Xcode中StaticLibrary和Framework的共同点和区别

一、共同点&#xff1a;两者其实都是静态库。二、区别1.承载的内容范畴&#xff1a;(1)StaticLibrary的产出物只是一个.a文件&#xff0c;为二进制执行文件。分享给别人的时候&#xff0c;头文件、静态资源文件需要另外提供。(2)Framework为一站式分享方案&#xff0c;其实是一…

[分布式] ------ 全局唯一id生成之雪花算法(Twitter_Snowflake)

雪花算法&#xff08;Twitter_Snowflake&#xff09; 我们知道&#xff0c;分布式全局唯一id的生成&#xff0c;一般是以下几种&#xff1a; 基于雪花算法生成基于数据库基于redis基于zookeeper 本文说下雪花算法&#xff0c;后面附源码以及测试代码。 如下图&#xff1a; …

非对称加解密交互故事

1.鲍勃有两把钥匙&#xff0c;一把是公钥&#xff0c;另一把是私钥。 2.鲍勃把公钥送给他的朋友们—-帕蒂、道格、苏珊—-每人一把。 3.苏珊要给鲍勃写一封保密的信。她写完后用鲍勃的公钥加密&#xff0c;就可以达到保密的效果 4.鲍勃收信后&#xff0c;用私钥解密&#xff0…

2019年规划

2019年规划&#xff0c;到2019-12-31检查&#xff1a; 1.至少读5本与工作无关的书&#xff08;平均两个月1本&#xff09;&#xff0c;每本都产出读后感 1.1 《重构 改善代码既有设计》&#xff0c;2月19日~3月5日&#xff0c;刚好两周读完&#xff0c;产出两篇总结&#xff0…

RSA私钥文件(PEM-PKCS#8)解析

***此文仅针对没有执行加密的PKCS#8私钥文件***一、实例解析PKCS#8格式使用的是ASN.1结构&#xff0c;首先我们对一个没有执行加密的PKCS#8格式私钥文件进行一下解析&#xff0c;方便我们下面分析其结构。命令&#xff1a;openssl asn1parse -i -in privatekey.pem结果如下&…

分布式和集群的区别

分布式和集群的区别 分布式是多个不同功能的机器共同完成一件事情 集群是多个相同功能的机器完成的是相同的事情 分布式是为了分担压力 集群是为了稳定性和高可用 举个例子&#xff1a; 一个厨师开了一个饭店&#xff0c;他要负责做饭和卖饭&#xff0c;他的压力就比较大&am…

Sqlite3中replace语句用法详解

在本例中使用如下数据库表&#xff1a; &#xff08;图 1&#xff09; 该表的表名为student&#xff0c; 存储学生信息。 所有字段的数据类型都是TEXT 。 其中id和name作为复合主键。 email字段加上了唯一约束。建表语句如下&#xff1a; CREATE TABLE IF NOT EXISTS student …

[分布式一致性协议] ------ raft协议的解释与理解

前言 在分布式系统中&#xff0c;为了保证容错性&#xff0c;一般会维护多个副本集群&#xff0c;提高系统的高可用&#xff0c;但与之带来的问题就是多个副本的一致性&#xff08;consensus&#xff09;问题。 我们认为&#xff0c;对于一个具有一致性的的集群中&#xff0c;…

iOS应用图片命名规则

一、界面图片命名规则&#xff1a;MyImage.png 一般图片命名MyImage2x.png 高清图片命名MyImage~iphone.png iPhone 和 iPod touch版一般图片命名MyImage2x~iphone.png iPhone 和 iPod touch版高清图片命名MyImage~ipad.png …

用户自定义排序的几种实现思路

场景 每个用户&#xff0c;有多个分组 每个分组在页面展示&#xff0c;而且是有顺序的&#xff0c;这个顺序是由用户决定 以下是关于多种情况下的库表设计思路&#xff1a; 情景一&#xff1a;如果每改一次&#xff0c;就要实时修改库&#xff0c;而且用户可以任意修改顺序&…

重构,体现一个工程师的基本素养和底蕴

重构小记&#xff08;重构&#xff0c;改善既有代码的设计读后总结&#xff09; 我们要时时刻刻保持一颗项目要重构的心。 在非技术出身的领导看来&#xff0c;能用的代码就是好代码&#xff0c;只关注功能。 在工程师看来&#xff0c;代码不仅要好用&#xff0c;更要好看&…

应用内购买(IAP)各类型在服务端的验证规则

一、非消耗品(比如单本杂志购买,苹果服务器支持恢复) 1.先验证服务器有没有购买记录&#xff0c;如果有&#xff0c;则不处理&#xff0c;此次操作成功 &#xff1b;2.如果服务器没有购买记录&#xff0c;则到苹果服务器验证(1)首先到正式验证地址验证收据&#xff0c;如果返回…

@Transactional事务生效条件与样例

Transactional事务生效条件 Transactional注释的方法&#xff0c;不能是private修饰 Transactional注释的方法&#xff0c;必须是有接口的方法实现&#xff08;通用的Spring面向接口编程的套路&#xff09; Transactional注释的方法&#xff0c;必须要通过接口的方式调用&…

利用.dSYM和.app文件准确定位Crash位置

当发布到iPhone上的应用程序Crash之后&#xff0c;iPhone会自动生成一个Crash Log&#xff08;*.crash&#xff09;&#xff0c;这个文件包含了一些有用的调试信息&#xff0c;但对于堆栈&#xff0c;它只记录的函数地址&#xff0c;而无法显示函数名。函数名保存在一个叫dSYM的…

使用maven的profile区分本地环境和线上环境

使用maven的profile区分本地环境和线上环境 多环境开发&#xff0c;使用maven-profile&#xff0c;就可以在打包的时候通过参数的调整&#xff0c;最终打的包也不同。 以区分本地数据库和线上数据库为例 比如测试环境&#xff0c;用的是本地测试数据库&#xff1b;生产环境用…

查看函数库.a函数符号信息

一、概述 nm命令可以列出一个函数库文件中的符号表。它对于静态的函数库和共享的函数库都起作用。对于一个给定的函数库&#xff0c;nm命令可以列出函数库中定义的所有符号&#xff0c;包括每个符号的值和类型。还可以给出在原程序中这个函数&#xff08;符号&#xff09;是在多…

重构,体现一个工程师的基本素养和底蕴(细节篇)

重构小记&#xff08;重构&#xff0c;改善既有代码的设计读后总结&#xff09; 方法级别 提炼函数&#xff1a; 将一个大方法&#xff0c;拆成多个小方法&#xff0c;难点在于小方法的命名。 假如有早上上学的一个大方法&#xff0c; 那么就应该在里面有起床&#xff0c;穿衣…

MVPVM模式介绍

一、概述MVPVM即&#xff1a;Model-View-Presenter-ViewModel。此模式是MVVM和MVP模式的结合体。但是交互模式发生了比较大的变化。MVVM参考本博客文章&#xff1a;iOS-MVVM-模式介绍MVP参考本博客文章&#xff1a;MVP模式介绍 二、原理&#xff1a;Presenter同时持有View、Mod…

[线程池] ------ 形象的描述线程池,用一个特好记的例子来记忆

线程池 为了减少线程频繁的创建和销毁过程&#xff0c;引入池的概念。 将一些线程先创建好放在线程池中&#xff0c;每次来任务就用池中的线程执行&#xff0c;空闲时池中线程就等待&#xff0c;但不销毁。 原始线程池的创建&#xff1a; ThreadPoolExecutor executor1 new …

分组密码的工作模式

一、理论基础1.概述密码学中&#xff0c;块密码的工作模式允许使用同一个块密码密钥对多于一块的数据进行加密&#xff0c;并保证其安全性。块密码自身只能加密长度等于密码块长度的单块数据&#xff0c;若要加密变长数据&#xff0c;则数据必须先被划分为一些单独的密码块。通…