ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring源码-IOC部分-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】

2022-02-06 16:04:49  阅读:199  来源: 互联网

标签:Spring testBean bean 源码 单例 import TestBean IOC public


实验环境:spring-framework-5.0.2、jdk8、gradle4.3.1

 

上文 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,testBean,bean,源码,单例,import,TestBean,IOC,public
来源: https://www.cnblogs.com/wwzyy/p/15865728.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有