第一步:创建一个基础SpringBoot项目
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
第二步:创建一个测试类
package com.lvpt.circular;
/**
* 创建代理原理
* @author jgy
* @date 2022-09-14
**/
public class App01 {
public static void main(String[] args) {
//aspect(切面) = advice(通知) + pointcut(切点),切面类就是可用抽离出来的公共代码
//advisor = 更细粒度的切面,包含一个通知和切点
}
interface I1{
void foo();
void bar();
}
static class Target1 implements I1{
@Override
public void foo(){
System.out.println("target1 foo");
}
@Override
public void bar() {
System.out.println("target1 bar");
}
}
}
第三步:写一个测试方法,使用ProxyFactory实现增强
/**
* 代理工厂 proxyFactory测试1
**/
public void proxyFactoryTest1(){
//1、创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
//2、指定代理对象
proxyFactory.setTarget(new Target1());
//3、添加通知: MethodInterceptor: 方法拦截器,属于环绕通知; 底层还是调的addAdvisor(),
//用了Pointcut.TRUE: 即匹配所有方法: DefaultPointcutAdvisor(Pointcut.TRUE, new MethodInterceptor)
proxyFactory.addAdvice((MethodInterceptor) invocation -> {
try {
System.out.println("前置增强....");
//调用目标
return invocation.proceed();
}finally {
System.out.println("后置增强....");
}
});
//我们也可用改变代理方式,设置代理方式为JDK代理: 默认CGLib
proxyFactory.addInterface(I1.class);
//这是就需要写成接口类型
I1 proxy = (I1)proxyFactory.getProxy();
//强制指定用CGLib: true:CGLib; false:JDK
//proxyFactory.setProxyTargetClass(true);
//4、拿到代理对象: CGLib代理方式(默认)
//Target1 proxy =(Target1) proxyFactory.getProxy();
//5、调用增强后的方法
proxy.foo();
proxy.bar();
//打印类名,可用知道用的是CGLib代理
System.out.println("代理方式: " + proxy.getClass().getName());
}
测试结果:都得到增强
前置增强....
target1 foo
后置增强....
前置增强....
target1 bar
后置增强....
com.lvpt.circular.$Proxy0
第四步:再写一个测试方法,添加AspectJExpressionPointcut:切点表达式,只增强指定方法
/**
* 代理工厂: proxyFactory测试2
**/
public void proxyFactoryTest2(){
//1、创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
//2、指定代理对象
proxyFactory.setTarget(new Target1());
//3、获取AspectJ切点表达式
AspectJExpressionPointcut expressionPointcut = new AspectJExpressionPointcut();
//4、定义切点表达式
expressionPointcut.setExpression("execution(* foo())");
//5、创建通知: 使用默认通知DefaultPointcutAdvisor,
//参数为: 切点表达式 + 通知(这就是advisor,只有一个切面,也就是这两个参数)
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(expressionPointcut, (MethodInterceptor) invocation -> {
try {
System.out.println("前置增强....");
//调用目标
return invocation.proceed();
}finally {
System.out.println("后置增强....");
}
}));
//再次添加切面Advisor(可用多个)
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(expressionPointcut, (MethodInterceptor) invocation -> {
try {
System.out.println("前置增强22....");
//调用目标
return invocation.proceed();
}finally {
System.out.println("后置增强22....");
}
}));
//6、拿到代理对象
Target1 target1 =(Target1) proxyFactory.getProxy();
//7、调用增强后的方法: 只有foo得到增强
target1.foo();
target1.bar();
}
输出结果:发现,只增强了foo()方法
前置增强....
前置增强22....
target1 foo
后置增强22....
后置增强....
target1 bar
切面信息存储才代理对象的advisors属性中;当然,我们也也是基于注解的方式AOP编程;
标注@Aspect标识一个切面类,@Aroubd()、@Before(execution(…))、@After()等等表示通知,
注解里写切点表达式,定位切点;
保证对象只存在一份,即单例
解决不了循环依赖;
整理流程表述:
二级缓存中有Bug,就是创建A代理对象比较晚,导致拿了未增强的A对象去创建B,应该拿增强后的A去创建B;添加了一个工厂类,工厂类判断A需不需要增强,不需要就没啥影响,如果需要,就需要用到三级缓存,利用工厂创建代理后的Bean;
创建一个类,内部又内部类A和B,两个类中都都依赖了彼此,然后创建A、B两个类的Bean;
package com.lvpt.circular;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.PostConstruct;
/**
* set方法出现循环依赖(不具有代理)
* @author jgy
* @date 2022-09-14
* */
public class App02 {
/**
* A类
* */
static class A{
private static final Logger log = LoggerFactory.getLogger("A");
private B b;
public A() {
log.info("执行A的构造方法");
}
/** 注解@Autowired用在set方法上,和直接加再变量上一样可用实现注入 **/
@Autowired
public void setB(B b){
log.info("赋值B: setB({})",b);
this.b = b;
}
@PostConstruct
public void init(){
log.info("A初始化: init()");
}
}
/**
* B类
* */
static class B{
public static final Logger log = LoggerFactory.getLogger("B");
private A a;
/**
* 构造方法
* */
public B(){
log.info("执行B的构造方法");
}
@Autowired
public void setA(A a){
log.info("赋值A: setA({})",a);
this.a = a;
}
@PostConstruct
public void init(){
log.info("B初始化: init()");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
//注册Bean
context.registerBean("a" , A.class);
context.registerBean("b" , B.class);
//添加后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
//refresh过程中就会创建A和B的Bean
context.refresh();
}
}
18:07:23.015 [main] INFO A - 执行A的构造方法
18:07:23.061 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b'
18:07:23.062 [main] INFO B - 执行B的构造方法
18:07:23.066 [main] INFO B - 赋值A: setA(com.lvpt.circular.App02$A@64485a47)
18:07:23.067 [main] INFO B - B初始化: init()
18:07:23.067 [main] INFO A - 赋值B: setB(com.lvpt.circular.App02$B@7276c8cd)
18:07:23.067 [main] INFO A - A初始化: init()
创建A对象=>发现需要B对象=>创建B对象=>从三级缓存中获取半成品A=>完成B对象创建=>
回到A中继续为B赋值=>完成A对象的创建
运行结果来看,并没有报错,因为Spring为我们解决了循环依赖问题;
第一步:选取断点
第一个断点:AbstractBeanFactory类中的doGetBean()方法中:
创建流程的开始:别名处理、缓存中找对象等
然后为断点添加条件:右键断点
第二个断点:AbstractAutowireCapableBeanFactory类中的docreateBean()方法中,这里涉及到了Bean的实例创建、依赖注入和初始化等,getSingleton()方法处H
第二步:调试
A1类具有代理增强,需要用到三级缓存
package com.lvpt.circular;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.PostConstruct;
/**
* set方法出现循环依赖(具有代理)
* @author jgy
* @date 2022-09-14
* */
public class App03 {
/**
* A类
* */
static class A1{
private static final Logger log = LoggerFactory.getLogger("A1");
private B1 b1;
public A1() {
log.info("执行A的构造方法");
}
/** 注解@Autowired用在set方法上,和直接加再变量上一样可用实现注入 **/
@Autowired
public void setB(B1 b1){
log.info("赋值B: setB1({})",b1);
this.b1 = b1;
}
@PostConstruct
public void init(){
log.info("A1初始化: init()");
}
public void foo(){
System.out.println("foo");
}
}
/**
* B1类
* */
static class B1{
public static final Logger log = LoggerFactory.getLogger("B1");
private A1 a1;
/**
* 构造方法
* */
public B1(){
log.info("执行B的构造方法");
}
@Autowired
public void setA1(A1 a1){
log.info("赋值A1: setA1({})",a1.getClass());
this.a1 = a1;
}
@PostConstruct
public void init(){
log.info("B1初始化: init()");
}
}
/**
* 注解类: 表示这是一个切面类
* */
@Aspect
static class MyAspect{
@Before("execution(* foo())")
public void before(){
System.out.println("前置增强...");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
//注册Bean
context.registerBean("a1" , A1.class);
context.registerBean("b1" , B1.class);
context.registerBean(MyAspect.class);
//自动代理处理器
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
//添加后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
//refresh过程中就会创建A和B的Bean
context.refresh();
//增强后foo方法
context.getBean(A1.class).foo();
}
}
INFO A1 - 执行A的构造方法
20:38:30.042 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b1'
20:38:30.042 [main] INFO B1 - 执行B的构造方法
20:38:30.198 [main] INFO B1 - 赋值A1: setA1(class com.lvpt.circular.App03$A1$$EnhancerBySpringCGLIB$$66940c28)
20:38:30.199 [main] INFO B1 - B1初始化: init()
20:38:30.202 [main] INFO A1 - 赋值B: setB1(com.lvpt.circular.App03$B1@6f03482)
20:38:30.202 [main] INFO A1 - A1初始化: init()
20:38:30.202 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.lvpt.circular.App03$MyAspect'
前置增强...
foo
我们发现,A不是原始的对象,而是增强后的代理对象:
class com.lvpt.circular.App03$A1$$EnhancerBySpringCGLIB$$66940c28
Spring利用三级缓存解决set方式的注入Bean的循环依赖,如果在构造方法中出现了循环依赖,三级缓存就不适用了;最好的就是尽量避免构造方法方式的注入,从根源上解决问题;
解决思路:
1、创建A时,给A一个假的B对象,即B的代理对象;推辞真实B的获取,这样A就能正常创建了;那么B也能正常创建;
A创建依赖B,B创建依赖A;
package com.lvpt.circular;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.PostConstruct;
/**
* 构造方法出现循环依赖
* @author jgy
* */
public class App04 {
static class A2{
public static final Logger log = LoggerFactory.getLogger("A2");
private B2 b2;
public A2(B2 b2){
log.info("A2(B2 b2){}",b2.getClass());
this.b2 = b2;
}
@PostConstruct
public void init(){
log.info("A初始化");
}
}
static class B2{
public static final Logger log = LoggerFactory.getLogger("B2");
private A2 a2;
public B2(A2 a2){
log.info("B2(A2 a2){}",a2.getClass());
this.a2 = a2;
}
@PostConstruct
public void init(){
log.info("B初始化");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("a2" , A2.class);
context.registerBean("b2" , B2.class);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.refresh();
}
}
运行结果:直接报异常:
UnsatisfiedDependencyException异常:
Is there an unresolvable circular reference?:循环依赖
/**
* 加@lazy注解懒加载,解决构造方法依赖循环
* */
public A2(@Lazy B2 b2){
log.info("A2(B2 b2){}",b2.getClass());
this.b2 = b2;
}
运行结果:
21:53:42.608 [main] INFO A2 - A2(B2 b2)class com.lvpt.circular.App04$B2$$EnhancerBySpringCGLIB$$31b73ab2
21:53:42.617 [main] INFO A2 - A初始化
21:53:42.617 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b2'
21:53:42.623 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'b2' via constructor to bean named 'a2'
21:53:42.623 [main] INFO B2 - B2(A2 a2)class com.lvpt.circular.App04$A2
21:53:42.624 [main] INFO B2 - B初始化
创建A时并不是用到真实的B对象,而是B的代理对象:
class com.lvpt.circular.App04$B2$$EnhancerBySpringCGLIB$$31b73ab2
加了该注解,在DefaultListableBeanFactory中的resolveDependency()方法中就会创建一个代理对象;
static class A2{
public static final Logger log = LoggerFactory.getLogger("A2");
//用B2的工厂对象取代B2对象
private ObjectFactory<B2> b2;
public A2(ObjectFactory<B2> b2){
log.info("A2(B2 b2){}",b2.getClass());
this.b2 = b2;
}
@PostConstruct
public void init(){
log.info("A初始化");
}
}
//要用真正的B2对象时: 调用工厂的getObject()方法即可
System.out.println(context.getBean(A2.class).b2.getObject());
原理:利用工厂,延迟对象的创建,要用正真的对象时,就调用工厂的getObject方法即可;
这个是Java官方提供的解决方法,需引入依赖:
<dependency>
<groupId>javax.injectgroupId>
<artifactId>javax.injectartifactId>
<version>1version>
dependency>
static class A2{
public static final Logger log = LoggerFactory.getLogger("A2");
//方案3: inject: 注入; Provider: 提供者
private Provider<B2> b2;
public A2(Provider<B2> b2){
log.info("A2(B2 b2){}",b2.getClass());
this.b2 = b2;
}
@PostConstruct
public void init(){
log.info("A初始化");
}
}
原理还是和工厂一样,延迟对象的获取;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 解决循环依赖: @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
* @author jgy
*/
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class A3 {
public static final Logger log = LoggerFactory.getLogger(A3.class);
private B3 b3;
public A3(B3 b3){
this.b3 = b3;
}
@PostConstruct
public void init(){
log.debug("A初始化方法init()");
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class B3 {
public static final Logger log = LoggerFactory.getLogger(B3.class);
private A3 a3;
public B3(A3 a3){
this.a3 = a3;
}
@PostConstruct
public void init(){
log.debug("B初始化方法init()");
}
}
测试:
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
//注册Bean
context.registerBean("a1" , App03.A1.class);
context.registerBean("b1" , App03.B1.class);
//自动代理处理器
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
//添加后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
//refresh过程中就会创建A和B的Bean
context.refresh();
}