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


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

    上文 Spring源码-Spring是如何解决Bean循环依赖的【6】 讲到,Spring是通过一级缓存(singletonObjects)、二级缓存(earlySingletonObjects)、三级缓存(singletonFactories)构成了三层缓存的模式。

    如果去掉二级缓存(earlySingletonObjects),只使用一级缓存(singletonObjects)和三级缓(singletonFactories)的话,对于普通的bean没有影响,但对于AOP代理的bean会导致重复创建bean实例,违法了单例原则。

    下面我将用一个实际例子来证明一下:去掉二级缓存(earlySingletonObjects) ,AOP代理的bean违反了单例原则。

    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)) {
    			// 2.加锁进行操作
    			synchronized (this.singletonObjects) {
    
    				// 3.【从二级缓存里面获取】从早期单例对象缓存中获取单例对象
    				// 之所称成为早期单例对象,是因为earlySingletonObjects里的对象的都是通过提前曝光的ObjectFactory创建出来的,还未进行属性填充等操作
    				// TODO 屏蔽掉earlySingletonObjects
    				//singletonObject = this.earlySingletonObjects.get(beanName);
    
    				// 4.如果在早期单例对象缓存中也没有,并且允许创建早期单例对象引用
    				if (singletonObject == null && allowEarlyReference) {
    
    					// 5.【从三级缓存里面获取】从单例工厂缓存中获取beanName的单例工厂
    					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    					if (singletonFactory != null) {
    
    						// 6.如果存在单例对象工厂,则通过工厂创建一个单例对象
    						singletonObject = singletonFactory.getObject();
    
    						// TODO 屏蔽掉earlySingletonObjects
    						// 7.将通过单例对象工厂创建的单例对象,放到早期单例对象缓存中
    						//this.earlySingletonObjects.put(beanName, singletonObject);
    						// 8.移除该beanName对应的单例对象工厂,因为该单例工厂已经创建了一个实例对象,并且放到earlySingletonObjects缓存了,
    						// 因此,后续获取beanName的单例对象,可以通过earlySingletonObjects缓存拿到,不需要在用到该单例工厂
    						//this.singletonFactories.remove(beanName);
    					}
    				}
    			}
    		}
    
    		return singletonObject;
    	}

    我们再运行一下,发现user和user2.getUser()是同一个对象,因为user只是一个普通的bean,无论获取多少次都是它自己。而testBean呢,发现各自不同了,就是因为它是代理对象,没有了二级缓存earlySingletonObjects,直接从三级缓存singletonFactories里面拿,每拿一次就返回一个新的代理对象,所以此时testBean已经不是单例了。

  • 相关阅读:
    输入框input只能输入数字和小数点
    ES6判断数组是否存在重复元素
    AutoPostBack的含义
    首次的boe with wss
    我的msn能在w2k3上使用了哈哈
    挑战excel
    wss的webpart的3种开发方式
    我来操作dts olap
    My dbconn of ASP
    sharepoint学习理解过程
  • 原文地址:https://www.cnblogs.com/wwzyy/p/15865728.html
Copyright © 2020-2023  润新知