Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
2022/2/6 17:15:10
本文主要是介绍Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
实验环境:spring-framework-5.0.2、jdk8、gradle4.3.1
- Spring源码-IOC部分-容器简介【1】
- Spring源码-IOC部分-容器初始化过程【2】
- Spring源码-IOC部分-Xml Bean解析注册过程【3】
- Spring源码-IOC部分-自定义IOC容器及Bean解析注册【4】
- Spring源码-IOC部分-Bean实例化过程【5】
- Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】
- Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
上文 Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】 讲到,Spring是通过一级缓存singletonObjects、二级缓存earlySingletonObjects、三级缓存singletonFactories构成了三层缓存的模式。
如果去掉二级缓存earlySingletonObjects,只使用一级缓存singletonObjects和三级缓singletonFactories的话,对于普通的bean没有影响,但对于AOP代理的bean会导致重复创建bean实例,违法了单例原则。
下面我将用一个实际例子来证明一下:AOP代理的bean没有了二级缓存earlySingletonObjects的确违反了单例原则。
1、定义一个注解
package beans; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyAopAnnotation { String value(); }
2、定义AOP,把切面切到@MyAopAnnotation上,这样的话@MyAopAnnotation标在谁上面,谁就是代理对象,比较灵活
package beans; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; @Aspect @Component @EnableAspectJAutoProxy public class MyAop { @Pointcut("@annotation(beans.MyAopAnnotation)") public void pointCat() { } @Before("pointCat()") public void before(JoinPoint joinPoint) { System.out.println("执行AOP before方法"); } }
3、定义bean,我把@MyAopAnnotation配置在TestBean方面上,也就是说TestBean需要创建AOP代理。另外TestBean、User、User2之间相互注入
TestBean
package beans; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TestBean { @Autowired private User user; @Autowired private User2 user2; @Autowired private TestBean testBean; public User getUser() { return user; } public User2 getUser2() { return user2; } public TestBean getTestBean() { return testBean; } @MyAopAnnotation("") public void hello() { System.out.println("TestBean 执行 hello 方法 "); } }
User
package beans; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class User { @Autowired private User2 user2; @Autowired private TestBean testBean; public TestBean getTestBean() { return testBean; } public User2 getUser2() { return user2; } }
User2
package beans; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; @Component @DependsOn({"user"}) public class User2 { @Autowired private User user; @Autowired private TestBean testBean; public User getUser() { return user; } public TestBean getTestBean() { return testBean; } }
4、运行代码
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.scan("beans"); context.refresh(); TestBean testBean = (TestBean) context.getBean("testBean"); testBean.hello(); User user = context.getBean(User.class); User2 user2 = context.getBean(User2.class); System.out.println("user == user2.getUser() : " + (user == user2.getUser())); System.out.println("testBean == user.getTestBean() : " + (testBean == user.getTestBean())); System.out.println("testBean == user2.getTestBean() : " + (testBean == user2.getTestBean())); System.out.println("user.getTestBean() == user2.getTestBean() : " + (user.getTestBean() == user2.getTestBean())); context.close(); }
运行结果如下:可以看到AOP生效了,另外各个bean注入的属性是同一个bean,由此证明无论是代理bean(TestBean)还是普通bean(User、User2)都是单例的。
那么,我修改一下getSingleton方法,把earlySingletonObjects屏蔽掉,也就是说bean在没有成为完整对象之前会一直从singletonFactory.getObject()获取早期bean实例。上文已经说过了,singletonFactory.getObject()是个工厂方法,每调用一次就会执行getEarlyBeanReference方法,而getEarlyBeanReference方法针对AOP代理的bean会创建一个新的代理对象,普通的bean直接返回。
修改后的getSingleton
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1.【从一级缓存里面获取】从单例对象缓存中获取beanName对应的单例对象 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 3.加锁进行操作 synchronized (this.singletonObjects) { // 4.【从二级缓存里面获取】从早期单例对象缓存中获取单例对象 // 之所称成为早期单例对象,是因为earlySingletonObjects里的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作 // TODO 屏蔽掉earlySingletonObjects //singletonObject = this.earlySingletonObjects.get(beanName); // 5.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用 if (singletonObject == null && allowEarlyReference) { // 6.【从三级缓存里面获取】从单例工厂缓存中获取beanName的单例工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 7.如果存在单例对象工厂,则通过工厂创建一个单例对象 singletonObject = singletonFactory.getObject(); // TODO 屏蔽掉earlySingletonObjects // 8.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中 //this.earlySingletonObjects.put(beanName, singletonObject); // 9.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了, // 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂 //this.singletonFactories.remove(beanName); } } } } return singletonObject; }
我们再运行一下,发现user和user2.getUser()是同一个对象,因为user只是一个普通的bean,无论获取多少次都是它自己。而testBean呢,发现各自不同了,就是因为它是代理对象,没有了二级缓存earlySingletonObjects,直接从三级缓存singletonFactories里面拿,每拿一次就返回一个新的代理对象,所以此时testBean已经不是单例了。
这篇关于Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)