AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能。
除此以外,aspectj 提供了两种另外的 AOP 底层实现:
-
第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
-
第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
AOP 实现之 ajc 编译器
定义一个目标类
public class MyService {
// 静态方法
public static void foo1() {
System.out.println("foo1()");
}
// 实例方法
public void foo2() {
System.out.println("foo2()");
this.foo3();
}
public void foo3() {
System.out.println("foo3()");
}
}
定义一个切面类
@Aspect
public class MyAspect {
@Before("execution(* com.spring.aspectj.MyService.*())")
public void before() {
System.out.println("before()");
}
}
在pom.xml引入 acj编译器插件
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>8</source>
<target>8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
测试
/**
* 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
* 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器
*/
public class AspectDemo {
public static void main(String[] args) {
MyService.foo1();
MyService myService = new MyService();
myService.foo2();
}
}
AOP 实现之 agent 类加载
/**
*
* 运行时需要在 VM options里加入:
* -javaagent:D:/development/tools/maven/repos/org/aspectj/aspectjweaver/1.9.19/aspectjweaver-1.9.19.jar
* 把其中D:/development/tools/maven/repos/ 改成你自己的 maven 仓库的起始路径
*/
public class AspectDemo {
public static void main(String[] args) {
MyService.foo1();
MyService myService = new MyService();
myService.foo2();
}
}
agent类加载增强方式不需要在pom.xml引入 acj编译器插件,但需要在 VM options 里添加一个参数 :
简单比较的话:
- aspectj 在编译和加载时,修改目标字节码,性能较高
- aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
- 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行
AOP 实现之 proxy
jdk 动态代理
基本使用
public class JdkProxyDemo {
interface Foo {
void foo();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
/**
* newProxyInstance的3个参数:
* ClassLoader loader: 类加载器,用来加载在运行期间动态生成的字节码
* Class<?>[] interfaces: 要代理的接口
* InvocationHandler h: 代理增强的逻辑
*
* InvocationHandler中invoke方法的3个参数:
* Object proxy: 代理类自己
* Method method: 要执行的目标方法
* Object[] args: 执行目标传入的参数
*/
public static void main(String[] param) {
// 目标对象
Target target = new Target();
// 代理对象
Foo proxy = (Foo) Proxy.newProxyInstance(
Target.class.getClassLoader(), new Class[]{Foo.class},
(p, method, args) -> {
System.out.println("proxy before...");
Object result = method.invoke(target, args);
System.out.println("proxy after...");
return result;
});
// 调用代理
proxy.foo();
}
}
jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系
模拟 jdk 动态代理
public class JdkProxyDemo2 {
interface Foo {
void foo();
int bar();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
public int bar() {
System.out.println("target bar");
return 100;
}
}
public static void main(String[] param) {
// 创建代理,这时传入 InvocationHandler
Foo proxy = new $Proxy0(new InvocationHandler() {
// 进入 InvocationHandler
// proxy: 代理类本身
// method: 要执行的目标方法
// args: 执行method 传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 功能增强
System.out.println("before...");
// 反射调用目标方法
return method.invoke(new Target(), args);
}
});
//调用代理方法
proxy.foo();
proxy.bar();
}
}
模拟代理实现
public class $Proxy0 extends Proxy implements JdkProxyDemo2.Foo {
public $Proxy0(InvocationHandler h) {
super(h);
}
// 进入代理方法
public void foo() {
try {
// 回调 InvocationHandler
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 {
Object result = h.invoke(this, bar, new Object[0]);
return (int) result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
static Method foo;
static Method bar;
static {
try {
foo = CglibProxyDemo2.Foo.class.getMethod("foo");
bar = CglibProxyDemo2.Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
用arthas工具查看jdk生成的代理对象
final class $Proxy0
extends Proxy
implements JdkProxyDemo1.Foo {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.spring.proxy.JdkProxyDemo1$Foo").getMethod("foo", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void foo() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException {
if (lookup.lookupClass() == Proxy.class && lookup.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
}
throw new IllegalAccessException(lookup.toString());
}
}
方法反射优化
// 运行时请添加 --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
for (int i = 1; i <= 17; i++) {
show(i, foo);
Thread.sleep(1000);
foo.invoke(null, i);
}
System.in.read();
}
// 方法反射调用时, 底层 MethodAccessor 的实现类
private static void show(int i, Method foo) throws Exception {
Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
getMethodAccessor.setAccessible(true);
Object invoke = getMethodAccessor.invoke(foo);
if (invoke == null) {
System.out.println(i + ":" + null);
return;
}
Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
delegate.setAccessible(true);
System.out.println(i + ":" + delegate.get(invoke));
}
public static void foo(int i) {
System.out.println(i + ":" + "foo");
}
}
打印结果
1:null
1:foo
2:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
2:foo
3:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
3:foo
4:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
4:foo
5:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
5:foo
6:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
6:foo
7:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
7:foo
8:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
8:foo
9:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
9:foo
10:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
10:foo
11:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
11:foo
12:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
12:foo
13:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
13:foo
14:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
14:foo
15:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
15:foo
16:jdk.internal.reflect.NativeMethodAccessorImpl@27fa135a
16:foo
17:jdk.internal.reflect.GeneratedMethodAccessor2@22d8cfe0
17:foo
前16次MethodAccessor接口的实现类是NativeMethodAccessorImpl,为反射调用,性能较低。第17次开始,为了把反射调用优化为正常调用,生了一个代理类来完成。
用arthas工具查看GeneratedMethodAccessor2的实现,
public class GeneratedMethodAccessor2
extends MethodAccessorImpl {
/*
* Loose catch block
*/
public Object invoke(Object object, Object[] objectArray) throws InvocationTargetException {
char c;
block9: {
if (objectArray.length != 1) {
throw new IllegalArgumentException();
}
Object object2 = objectArray[0];
if (object2 instanceof Byte) {
c = ((Byte)object2).byteValue();
break block9;
}
if (object2 instanceof Character) {
c = ((Character)object2).charValue();
break block9;
}
if (object2 instanceof Short) {
c = (char)((Short)object2).shortValue();
break block9;
}
if (object2 instanceof Integer) {
c = (char)((Integer)object2).intValue();
break block9;
}
throw new IllegalArgumentException();
}
try {
TestMethodInvoke.foo((int)c); // 非反射调用
return null;
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
catch (ClassCastException | NullPointerException runtimeException) {
throw new IllegalArgumentException(super.toString());
}
}
}
jdk的代理优化:前16次为反射调用,被调用第17次时,每次方法调用都会生成一个代理对象。
cglib 代理
基本使用
public class CglibProxyDemo {
static class Target {
public void foo() {
System.out.println("target foo");
}
public void bar() {
System.out.println("target bar");
}
}
/**
* MethodInterceptor中的intercept方法参数说明:
* Object obj: 代理类自己
* Method method: 要执行的目标方法
* Object[] args: 目标方法传入的参数
* MethodProxy proxy: 可以避免反射调用
*/
public static void main(String[] param) {
// 目标对象
Target target = new Target();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Target.class);
// 设置Callbacks,是一个数组,可以设置多个方法拦截器
enhancer.setCallbacks(new Callback[]{(MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("proxy before...");
Object result = method.invoke(target, args); // 用方法反射调用目标
// Object result = methodProxy.invoke(target, args); // methodProxy可以避免反射调用,内部没有反射 (spring用的这种方式)
// Object result = methodProxy.invokeSuper(p, args); // 内部没有反射,不需要目标对象实例,需要代理对象
System.out.println("proxy after...");
return result;
}, NoOp.INSTANCE});
// 设置方法拦截过滤器,不同的方法可以选择不同的方法拦截器
enhancer.setCallbackFilter(method -> {
if (method.getName().equals("foo")) {
return 0;
}
return 1;
});
// 创建代理对象
Target t = (Target) enhancer.create();
t.foo(); // foo被代理增强了
t.bar(); // bar方法不会被代理增强
}
}
cglib 不要求目标实现接口,它生成的代理类是目标的子类,重写目标方法,代理与目标之间是子父关系。final类和final方法不能被cglib增强。
模拟 cglib 代理
定义一个目标类
public class Target {
public void save() {
System.out.println("save()");
}
public void save(int i) {
System.out.println("save(int)");
}
public void save(long j) {
System.out.println("save(long)");
}
}
模拟 cjlib 代理实现
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 j) {
super.save(j);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@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 j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
cjlib代理实现原理和和 jdk 动态代理原理查不多
- 回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
- 调用目标时有所改进
- method.invoke 是反射调用,必须调用到足够次数才会进行优化
- methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
- methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
cglib 如何避免反射
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类:ProxyFastClass 配合代理对象一起使用, 避免反射;TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)。
TargetFastClass
public class TargetFastClass {
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});
}
}
TargetFastClass 记录了 Target 中方法与编号的对应关系:
-
save(long) 编号 2
-
save(int) 编号 1
-
save() 编号 0
-
首先根据方法名和参数个数、类型,用 switch 和 if 找到这些方法编号
-
然后再根据编号去调用目标方法,又用了一大堆 switch 和 if, 但避免了反射
ProxyFastClass
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 记录了 Proxy 中方法与编号的对应关系,不过 Proxy 额外提供了下面几个方法
-
saveSuper(long) 编号 2,不增强,仅是调用 super.save(long);
-
saveSuper(int) 编号 1,不增强,仅是调用 super.save(int);
-
saveSuper() 编号 0,不增强,仅是调用 super.save();
-
查找方式与 TargetFastClass 类似。
为什么有这么麻烦的一套东西呢?
-
避免反射, 提高性能,代价是一个代理类配两个 FastClass 类,代理类中还得增加仅调用 super 的一堆方法;
-
用编号处理方法对应关系比较省内存,另外,最初获得方法顺序是不确定的,这个过程没法固定死。