AOP是一种编程范式,主要实现方式之一是代理,由代理结合通知和目标,提供增强功能
除此以外,aspectj 提供了两种另外的 AOP 底层实现:
- 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
- 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
- 作为对比,之前学习的代理是运行时生成新的字节码
简单比较的话:
- aspectj 在编译和加载时,修改目标字节码,性能较高
- aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
- 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行
1、AOP 实现之 ajc 编译器
先看aop的第一种实现ajc编译器代码增强,这是一种编译时的代码增强。
新建一个普通的maven项目 ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0“
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0 org.springframework.boot spring-boot-starter-parent 2.5.5 org.example advanced_aspectj_01 1.0-SNAPSHOT <java.version>8</java.version>
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.aspectj aspectjweaver org.aspectj aspectjrt org.springframework.boot spring-boot-maven-plugin org.codehaus.mojo aspectj-maven-plugin 1.14.0 1.8 8 8 true true ignore UTF-8 compile test-compile
- 编写业务类
```java
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);
public void foo() {
log.debug("foo()");
}
}
编写切面类
@Aspect // ⬅️ 注意此切面并未被 Spring 管理 public class MyAspect { private static final Logger log = LoggerFactory.getLogger(MyAspect.class); @Before("execution(* com.itheima.service.MyService.foo())") public void before() { log.debug("before()"); } }
测试类
public class A09 { private static final Logger log = LoggerFactory.getLogger(A09.class); public static void main(String[] args) { new MyService().foo(); } }
输出
01:04:53.493 [main] DEBUG org.demo.aspect.MyAdvise - before() 01:04:53.495 [main] DEBUG org.demo.service.MyService - foo()
总结:
- 可以看到没有引入任何跟spring框架相关的包,MyService类是通过直接new()的方式获得的,所以也就不存在使用了动态代理的说法了
- 打开编译后的MyService.class文件,双击以后idea会反编译该字节码文件,可以看到foo()方法体的开头加了一行代码,这就是增强的代码,这是ajc编译器在编译MyService类的时候为我们添加的代码,这是一种编译时的增强。
- aop 的原理并非代理一种, 编译器也能玩出花样
2、AOP 实现之 agent 类加载
现在来看aop的另外一种实现agent增强,这是一种类加载时的代码增强。
新建一个普通的maven工程,导入如下依赖 ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0“
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0 org.springframework.boot spring-boot-starter-parent 2.5.4 org.example advanced_aspectj_02 1.0-SNAPSHOT 8 8 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.aspectj aspectjweaver org.aspectj aspectjrt org.springframework.boot spring-boot-maven-plugin
- 需要增强的类MyService
```java
@Slf4j
public class MyService {
public void foo() {
log.debug("foo()");
bar();
}
public void bar() {
log.debug("bar()");
}
}
- 切面类MyAspect,编写execution表达式,对MyService类的foo()方法进行增强
```java
@Aspect // ⬅️ 注意此切面并未被 Spring 管理,本项目pom文件中根本没有引入spring的相关类
@Slf4j
public class MyAspect {
@Before(“execution( top.jacktgq.service.MyService.())”)
public void before() {
} }log.debug("before()");
- resources/META-INF下增加aop.xml
```java
<aspectj>
<aspects>
<aspect name="com.itheima.aop.MyAspect"/>
<weaver options="-verbose -showWeaveInfo">
<include within="com.itheima.service.MyService"/>
<include within="com.itheima.aop.MyAspect"/>
</weaver>
</aspects>
</aspectj>
测试代码
/* 注意几点 1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16 2. 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar 把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址 */ @SpringBootApplication public class A10 { private static final Logger log = LoggerFactory.getLogger(A10.class); public static void main(String[] args) { // ⬇️ MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码 log.info("service class: {}", service.getClass()); service.foo(); } }
运行测试代码,可以看到创建MyService对象并调用foo()方法会先执行切面类中的before()方法
总结:
- 类加载时可以通过 agent 修改 class 实现增强
3、AOP 实现之 proxy
1、proxy增强-jdk
- jdk代理只针对接口代理,代理对象和目标对象是兄弟关系,这里先给出接口和目标对象定义 ```java public interface Foo { void foo(); }
public class Target implements Foo { @Override public void foo() { System.out.println(“target foo”); } }
- 创建代理对象
```java
/**
* 1、代理对象类加载器
* 2、代理接口的Class
* public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h)
*/
/**
* public interface InvocationHandler {
*
* 1、代理对象
* 2、目标对象的方法
* 3、代理参数
* public Object invoke(Object proxy,Method method,Object[]args) throws Throwable;
*
* }
*/
public class JdkProxyDemo {
public static void main(String[] param) throws IOException {
Target target = new Target();
ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();
Foo proxy = (Foo) Proxy.newProxyInstance(classLoader, new Class[] {Foo.class}, (p, method, args) -> {
System.out.println("before");
// 使用反射调用目标对象的方法
Object result = method.invoke(target, args);
System.out.println("after");
// 返回目标方法的执行结果
return result;
});
System.out.println(proxy.getClass());
// 运行代理方法
proxy.foo();
}
}
总结:
- 代理对象和目标对象是兄弟关系,都实现了Foo接口,代理对象类型不能强转成目标对象类型,但是可以转成接口类型
- 目标类定义的时候可以加final修饰。
2、proxy增强-cglib
cglib针对目标类进行代理,这里给出目标类定义
public class Target { public void foo() { System.out.println("target foo"); } }
代理对象 ```java /**
- 目标对象
- public class Enhancer {
- public static Object create(Class type, Callback callback) {}
- } */
/**
- 代理对象
- 目标对象的方法
- 方法参数
- MethodInterceptor() {
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {}
- } */
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] param) {
Target target = new Target();
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("before...");
// 方式1:使用反射调用目标对象的方法
Object result = method.invoke(target, args);
// 方式2:内部没有用反射, 需要目标 (spring使用该方式)
Object result = methodProxy.invoke(target, args);
// 方式3:内部没有用反射, 需要代理
Object result = methodProxy.invokeSuper(p, args);
System.out.println("after...");
return result;
});
proxy.foo();
}
}
总结:
1. 代理对象和目标对象是父子关系,代理类继承于目标类;
1. 目标类定义的时候不能加final修饰,否则代理类就无法继承目标类了,会报java.lang.IllegalArgumentException: Cannot subclass final class top.jacktgq.proxy.cglib.Target异常;
1. 目标类方法定义的时候不能加final修饰,否则代理类继承目标类以后就不能重写目标类的方法了。
<a name="TWw5C"></a>
# 4、jdk 动态代理原理
<a name="foxVZ"></a>
## 1、模拟jdk动态代理
- 先定义一个Foo接口,里面有一个foo()方法,再定义一个Target类来实现这个接口,代码如下所示:
```java
public interface Foo {
void foo();
}
@Slf4j
public final class Target implements Foo {
public void foo() {
log.debug("target foo");
}
}
- 接下来对Target类中的foo()方法进行增强
- 首先想到的是,再定义一个类也同样地实现一下Foo接口,然后在foo()方法中编写增强代码,接着再new一个Target对象,调用它的foo()方法,代码如下所示: ```java public class $Proxy0 implements Foo { @Override public void foo() { // 1. 功能增强 System.out.println(“before…”); // 2. 调用目标 new Target().foo(); } }
// 测试运行 public class Main { public static void main(String[] args) { Foo proxy = new $Proxy0(); proxy.foo(); } }
- 上面的代码把功能增强的代码和调用目标的代码都固定在了代理类的内部,不太灵活。因此可以通过定义一个InvocationHandler接口的方式来将这部分代码解耦出来,代码如下:
```java
public interface InvocationHandler {
void invoke();
}
public interface Foo {
void foo();
}
@Slf4j
public final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
public class $Proxy0 implements Foo {
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void foo() {
h.invoke();
}
}
public class Main {
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public void invoke() {
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
new Target().foo();
}
});
proxy.foo();
}
}
- 第2个版本的代码虽然将功能增强的代码和调用目标的代码通过接口的方式独立出来了,但还是有问题,如果此时接口中新增了一个方法bar(),Target类和$Proxy0类中都要实现bar()方法,那么调用proxy的foo()和bar()方法都将间接调用目标对象的foo()方法,因为在InvocationHandler的invoke()方法中调用的是target.foo()方法,代码如下: ```java public interface InvocationHandler { void invoke(); }
public interface Foo { void foo(); void bar(); }
@Slf4j public final class Target implements Foo { public void foo() { System.out.println(“target foo”); }
@Override
public void bar() {
log.debug("target bar");
}
}
public class $Proxy0 implements Foo { private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void foo() {
h.invoke();
}
@Override
public void bar() {
h.invoke();
}
}
public class Main { public static void main(String[] args) { Foo proxy = new $Proxy0(new InvocationHandler() { @Override public void invoke() { // 1. 功能增强 System.out.println(“before…”); // 2. 调用目标 new Target().foo(); } }); proxy.foo(); proxy.bar(); } }
改进方法是,代理类中调用方法的时候,通过反射把接口中对应的方法Method对象作为参数传给InvocationHandler,代码如下:
```java
public interface InvocationHandler {
void invoke(Method method, Object[] args) throws Throwable;
}
public interface Foo {
void foo();
void bar();
}
public final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
@Override
public void bar() {
System.out.println("target bar");
}
}
public class $Proxy0 implements Foo {
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void foo() {
try {
Method foo = Foo.class.getDeclaredMethod("foo");
h.invoke(foo, new Object[0]);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void bar() {
try {
Method bar = Foo.class.getDeclaredMethod("bar");
h.invoke(bar, new Object[0]);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public void invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
method.invoke(new Target(), args);
}
});
proxy.foo();
proxy.bar();
}
}
- 第3个版本的代码其实已经离jdk动态代理生成的代码很相近了,为了更好地学习底层,更近一步,修改Foo接口的中bar()方法,使其具有int类型的返回值,因此InvocationHandler的invoke()方法也得有返回值,同时将代理对象本身作为第一个参数,具体代码如下: ```java public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
public interface Foo { void foo(); int bar(); }
public final class Target implements Foo { public void foo() { System.out.println(“target foo”); }
@Override
public int bar() {
System.out.println("target bar");
return 1;
}
}
public class $Proxy0 implements Foo {
// 将Method抽取出来,避免每次调用方法都获取一次
static Method foo;
static Method bar;
static {
try {
foo = Foo.class.getDeclaredMethod("foo");
bar = Foo.class.getDeclaredMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void foo() {
try {
h.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public int bar() {
try {
return (int) h.invoke(this, bar, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
public class Main { public static void main(String[] args) { Foo proxy = new $Proxy0(new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { // 1. 功能增强 System.out.println(“before…”); // 2. 调用目标 return method.invoke(new Target(), args); } }); proxy.foo(); System.out.println(“bar()方法返回值:” + proxy.bar()); } }
- 到这里跟jdk的动态代理只有些微差距了,只是jdk的动态代理会让代理类再继承一个Proxy类,将InvocationHandler接口的属性赋值操作都交予父类Proxy
```java
public $Proxy0(InvocationHandler h) {
super(h);
}
总结:
代理一点都不难,无非就是利用了多态、反射的知识
- 方法重写可以增强逻辑,只不过这【增强逻辑】千变万化,不能写死在代理内部
- 通过接口回调将【增强逻辑】置于代理类之外
- 配合接口方法反射(是多态调用),就可以再联动调用目标方法
- 限制:代理增强是借助多态来实现,因此成员变量、静态方法、final 方法均不能通过代理实现
5、cglib代理原理
1、模拟cglib代理
定义一个目标对象 ```java public class Target {
public void save() {
System.out.println("save()");
}
public void save(int i) {
System.out.println("save(int)");
}
public void save(long i) {
System.out.println("save(long)");
}
}
- 定义一个代理对象,继承自目标对象
```java
public class Proxy extends Target{
private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
@Override
public void save() {
super.save();
}
@Override
public void save(int i) {
super.save(i);
}
@Override
public void save(long i) {
super.save(i);
}
}
与jdk动态代理不同,cglib使用的是MethodInterceptor
public class Proxy extends Target{ private MethodInterceptor methodInterceptor; public void setMethodInterceptor(MethodInterceptor methodInterceptor) { this.methodInterceptor = methodInterceptor; } // 抽取方法定义 static Method save0; static Method save1; static Method save2; static { try { save0 = Target.class.getMethod("save"); save1 = Target.class.getMethod("save1", int.class); save2 = Target.class.getMethod("save2", long.class); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } @Override public void save() { try { methodInterceptor.intercept(this, save0, new Object[0], null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } super.save(); } @Override public void save(int i) { try { methodInterceptor.intercept(this, save1, new Object[] {i}, null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public void save(long i) { try { methodInterceptor.intercept(this, save2, new Object[] {i}, null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } }
测试类
public class A14 { public static void main(String[] params) { Target target = new Target(); Proxy proxy = new Proxy(); proxy.setMethodInterceptor((p, method, args, methodProxy) -> { System.out.println("before()"); return method.invoke(target, args); }); proxy.save(); proxy.save(1); proxy.save(2L); } }
由于
method.invoke
是反射调用,必须调用到足够次数才会进行优化,改成使用MethodProxy
的调用,下面是MethodProxy
的创建 ```java public class Proxy extends Target{private MethodInterceptor methodInterceptor;
public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}
static Method save0; static Method save1; static Method save2;
static MethodProxy save0Proxy; static MethodProxy save1Proxy; static MethodProxy save2Proxy;
static {
try { save0 = Target.class.getMethod("save"); save1 = Target.class.getMethod("save", int.class); save2 = Target.class.getMethod("save", long.class); // 目标对象,代理对象,方法描述,增强方法,原始父类方法 save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper"); save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper"); save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper"); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); }
}
/**
* 带原始功能的方法
*/
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long l) {
super.save(l);
}
/**
* 带增强功能的方法
*/
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[] {i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(long i) {
try {
methodInterceptor.intercept(this, save2, new Object[] {i}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
- 测试类
```java
public class A14 {
public static void main(String[] params) {
Target target = new Target();
Proxy proxy = new Proxy();
proxy.setMethodInterceptor((p, method, args, methodProxy) -> {
System.out.println("proxy before..." + methodProxy.getSignature());
// 调用目标方法(三种)
Object result = method.invoke(target, args); // 反射调用
Object result = methodProxy.invoke(target, args); // 非反射调用, 结合目标用
Object result = methodProxy.invokeSuper(p, args); // 非反射调用,结合代理用
System.out.println("proxy after..." + methodProxy.getSignature());
return result;
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
}
总结:
和 jdk 动态代理原理查不多
- 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
- 调用目标时有所改进,见下面代码片段
method.invoke
是反射调用,必须调用到足够次数才会进行优化methodProxy.invoke
是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)methodProxy.invokeSuper
也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
6、cglib避免反射调用
1、MethodProxy如何避免反射调用方法
当调用methodProxy.invoke
和 methodProxy.invokeSuper
时,会动态生成两个类
- TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)
- ProxyFastClass 配合代理对象一起使用, 避免反射
模拟FastClass内部实现,主要是
getIndex()
和invoke()
的实现 ```java public class TargetFastClass {// MethodProxy.create 调用时,会创建这部分方法签名 static Signature s0 = new Signature(“save”, “()V”); static Signature s1 = new Signature(“save”, “(I)V”); static Signature s2 = new Signature(“save”, “(J)V”);
/**
- 获取目标方法的编号 *
- Target
- save() 0
- save(int) 1
- save(long) 2
signature 包括方法名字、参数返回值 */ public int getIndex(Signature signature) { if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
} return -1; }
// 根据方法编号, 正常调用目标对象方法 public Object invoke(int index, Object target, Object[] args) { if (index == 0) {
((Target) target).save(); return null;
} else if (index == 1) {
((Target) target).save((int) args[0]); return null;
} else if (index == 2) {
((Target) target).save((long) args[0]); return null;
} else {
throw new RuntimeException("无此方法");
} }
public static void main(String[] args) { TargetFastClass fastClass = new TargetFastClass(); int index = fastClass.getIndex(new Signature(“save”, “(I)V”)); System.out.println(index); fastClass.invoke(index, new Target(), new Object[]{100}); } }
分析:
1. 可以看到这个类的作用其实就是根据你传进来的方法签名,找到要执行的原始方法,再使用目标对象调用而已
```java
public class ProxyFastClass {
static Signature s0 = new Signature("saveSuper", "()V");
static Signature s1 = new Signature("saveSuper", "(I)V");
static Signature s2 = new Signature("saveSuper", "(J)V");
/**
* 获取代理方法的编号
* Proxy
* saveSuper() 0
* saveSuper(int) 1
* saveSuper(long) 2
* signature 包括方法名字、参数返回值
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}
// 根据方法编号, 正常调用目标对象方法
public Object invoke(int index, Object proxy, Object[] args) {
if (index == 0) {
((Proxy) proxy).saveSuper();
return null;
} else if (index == 1) {
((Proxy) proxy).saveSuper((int) args[0]);
return null;
} else if (index == 2) {
((Proxy) proxy).saveSuper((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}
public static void main(String[] args) {
ProxyFastClass fastClass = new ProxyFastClass();
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
System.out.println(index);
fastClass.invoke(index, new Proxy(), new Object[0]);
}
}
分析:
- 相较于目标对象,ProxyFastClass是获取代理对象中的原始方法
- 为什么没有增强方法呢?因为增强方法的功能已经被调用者实现了,而且这里使用反射的就是原始方法,这一节的目的就是为了避免反射原始方法,曲线救国,改为正常(间接)调用,所以要获取到原始方法,改为对象调用
总结:
为什么有这么麻烦的一套东西呢?
- 避免反射, 提高性能, 代价是一个代理类配两个 FastClass 类, 代理类中还得增加仅调用 super 的一堆方法
- 用编号处理方法对应关系比较省内存, 另外, 最初获得方法顺序是不确定的, 这个过程没法固定死
2、与jdk对比
- jdk需要调用16次以上,才会不使用反射,改为正常调用
- jdk每次调用都会产生一个代理类,cglib只会给每个类产生两个代理类
7、Spring选择代理
Spring 中对切点、通知、切面的抽象如下
- 切点:接口 Pointcut,典型实现 AspectJExpressionPointcut
- 通知:典型接口为 MethodInterceptor 代表环绕通知
- 切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut
代理相关类图
- AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
- AopProxy 通过 getProxy 创建代理对象
- 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
- 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor
1、模拟Spring代理选择规则
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
public class A15 {
public static void main(String[] args) {
/**
* 两个切面概念
* aspect =
* 通知1(advice) + 切点1(pointcut)
* 通知2(advice) + 切点2(pointcut)
* 通知3(advice) + 切点3(pointcut)
* ...
* advisor = 更细粒度的切面,包含一个通知和切点
*/
// 1. 准备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 准备好通知
MethodInterceptor advice = invocation -> {
System.out.println("before...");
Object result = invocation.proceed(); // 调用目标
System.out.println("after...");
return result;
};
// 3. 准备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
// 4. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现
Target1 target1 = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target1);
factory.addAdvisor(advisor);
Target1 proxy = (Target1) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}
interface I1 {
void foo();
void bar();
}
static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}
public void bar() {
System.out.println("target1 bar");
}
}
static class Target2 {
public void foo() {
System.out.println("target2 foo");
}
public void bar() {
System.out.println("target2 bar");
}
}
}
输出
class org.demo.a15.A15$Target1$$EnhancerBySpringCGLIB$$ca3597ed
before...
target1 foo
after...
target1 bar
更换规则,proxyTargetClass = false 且没有实现接口
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
public class A15 {
public static void main(String[] args) {
/**
* 两个切面概念
* aspect =
* 通知1(advice) + 切点1(pointcut)
* 通知2(advice) + 切点2(pointcut)
* 通知3(advice) + 切点3(pointcut)
* ...
* advisor = 更细粒度的切面,包含一个通知和切点
*/
// 1. 准备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 准备好通知
MethodInterceptor advice = invocation -> {
System.out.println("before...");
Object result = invocation.proceed(); // 调用目标
System.out.println("after...");
return result;
};
// 3. 准备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
// 4. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现
Target2 target = new Target2();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setInterfaces(target.getClass().getInterfaces());
factory.setProxyTargetClass(false);
Target2 proxy = (Target2) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}
interface I1 {
void foo();
void bar();
}
static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}
public void bar() {
System.out.println("target1 bar");
}
}
static class Target2 {
public void foo() {
System.out.println("target2 foo");
}
public void bar() {
System.out.println("target2 bar");
}
}
}
输出
class org.demo.a15.A15$Target2$$EnhancerBySpringCGLIB$$ddd12090
before...
target2 foo
after...
target2 bar
分析:
- 底层切点实现使用了AspectJExpressionPointcut
- 底层通知实现使用了MethodInterceptor
- 底层的切面使用了DefaultPointcutAdvisor
- ProxyFactory 是用来创建代理的核心实现,更底层的是AopProxyFactory,AopProxyFactory根据如下几个规则,选择具体的实现
- proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
- proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
- proxyTargetClass = true, 总是使用 cglib 实现