实验环境: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之间相互注入
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 方法 "); } }
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; } }
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直接返回。
@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已经不是单例了。