目前,java实现AOP的方式有三种:
- 代理
- 编译时修改class
- 类加载时修改class
代理是目前最流行的实现方式,我们主要介绍代理的实现方式。
编译时修改class
使用 aspectj-maven-plugin 1.14.0 插件在编译时修改class文件,实现AOP
注意:
目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
类加载时修改class
使用agent在类加载时修改class
运行时需要在 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 仓库起始地址
代理
代理又分为jdk动态代理和cglib代理
JDK代理
JDK代理是针对于接口代理的,所以目标类和代理类是兄弟关系。
public class JdkProxyDemo {
interface Foo {
void foo();
}
static class Target implements Foo {
@Override
public void foo() {
System.out.println("target foo");
}
}
//jdk 只能针对接口代理
public static void main(String[] args) {
//目标对象
Target target = new Target();
ClassLoader loader = JdkProxyDemo.class.getClassLoader(); //代理类通过ClassLoader来实现运行期间自动生成字节码
Foo instance = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (proxy, method, args1) -> {
System.out.println("before...");
//目标.方法(参数)
//方法.invoke(目标,参数)
Object result = method.invoke(target, args1);
System.out.println("after...");
return result; //让代理返回目标方法执行的结果
});
instance.foo();
}
}
我们来模拟一下jdk动态代理的实现:
// jdk代理类和目标类是兄弟关系,所以页需要实现接口
public class $Proxy0 implements test.Foo {
// 用来处理方法调用和返回结果
private test.InvocationHandler h;
public $Proxy0(test.InvocationHandler h) {
this.h = h;
}
@Override
public void foo() {
try {
// 这里调用用户实现的invoke
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);
}
}
// 需要得到Method是为了使得可以正确的调用目标类的方法,定义为静态属性是保证只加载一次,节省内存
static Method foo;
static Method bar;
static{
try {
foo = test.Foo.class.getMethod("foo");
bar = test.Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
public class test {
//定义一个接口,jdk动态代理是通过接口实现的
interface Foo {
void foo();
int bar();
}
//定义目标类
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
@Override
public int bar() {
System.out.println("target bar");
return 100;
}
}
// 用来处理代理实例上的方法调用并返回结果,这里也可以直接使用jdk的,实现一模一样
interface InvocationHandler {
Object invoke(Object proxy,Method method, Object[] args) throws Throwable;
}
public static void main(String[] param) {
//实现invoke
Foo proxy0 = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
System.out.println("before...");
return method.invoke(new Target(),args);
}
});
proxy0.foo();
proxy0.bar();
}
}
这是一个简单是实现,jdk的实现和这个也大差不差。
接下来我们看看jdk动态代理的源码,JDK动态代理有两大核心类,它们都在Java的反射包下(java.lang.reflect),分别为InvocationHandler接口和Proxy类。
-
InvocationHandler接口
public interface InvocationHandler {
/**
* Processes a method invocation on a proxy instance and returns
* the result. This method will be invoked on an invocation handler
* when a method is invoked on a proxy instance that it is
* associated with.
*
* @param proxy the proxy instance that the method was invoked on
*
* @param method the {@code Method} instance corresponding to
* the interface method invoked on the proxy instance. The declaring
* class of the {@code Method} object will be the interface that
* the method was declared in, which may be a superinterface of the
* proxy interface that the proxy class inherits the method through.
*
* @param args an array of objects containing the values of the
* arguments passed in the method invocation on the proxy instance,
* or {@code null} if interface method takes no arguments.
* Arguments of primitive types are wrapped in instances of the
* appropriate primitive wrapper class, such as
* {@code java.lang.Integer} or {@code java.lang.Boolean}.
*
* @return the value to return from the method invocation on the
* proxy instance. If the declared return type of the interface
* method is a primitive type, then the value returned by
* this method must be an instance of the corresponding primitive
* wrapper class; otherwise, it must be a type assignable to the
* declared return type. If the value returned by this method is
* {@code null} and the interface method's return type is
* primitive, then a {@code NullPointerException} will be
* thrown by the method invocation on the proxy instance. If the
* value returned by this method is otherwise not compatible with
* the interface method's declared return type as described above,
* a {@code ClassCastException} will be thrown by the method
* invocation on the proxy instance.
*
* @throws Throwable the exception to throw from the method
* invocation on the proxy instance. The exception's type must be
* assignable either to any of the exception types declared in the
* {@code throws} clause of the interface method or to the
* unchecked exception types {@code java.lang.RuntimeException}
* or {@code java.lang.Error}. If a checked exception is
* thrown by this method that is not assignable to any of the
* exception types declared in the {@code throws} clause of
* the interface method, then an
* {@link UndeclaredThrowableException} containing the
* exception that was thrown by this method will be thrown by the
* method invocation on the proxy instance.
*
* @see UndeclaredThrowableException
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
代理实例的调用处理器需要实现InvocationHandler接口,并且每个代理实例都有一个关联的调用处理器。当一个方法在代理实例上被调用时,这个方法调用将被编码并分派到其调用处理器的invoke方法上。
我们创建的每一个代理实例都要有一个关联的InvocationHandler,并且在调用代理实例的方法时,会被转到InvocationHandler的invoke方法上。
其有三个参数,分别为:
- proxy:是调用该方法的代理实例。
- method:是在代理实例上调用的接口方法对应的Method实例。
- args:一个Object数组,是在代理实例上的方法调用中传递的参数值。如果接口方法为无参,则该值为null。
其返回值为:调用代理实例上的方法的返回值。
-
Proxy类
Proxy提供了用于创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
代理类具有以下属性:
- 代理类的名称以 “$Proxy” 开头,后面跟着一个数字序号。
- 代理类继承了Proxy类。
- 代理类实现了创建时指定的接口(JDK动态代理是面向接口的)。
- 每个代理类都有一个公共构造函数,它接受一个参数,即接口InvocationHandler的实现,用于设置代理实例的调用处理器。
Proxy提供了两个静态方法,用于获取代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
....
}
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
...
}
那么代理类和其对象是怎么创建的呢?
我们从newProxyInstance()方法开始下手,它的源码里面有这么一段代码,很明显就是在这个方法创建的代理类
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
getProxyClass0的实现是这样的,可以看出代理接口最多不能超过65535,这段注释的意思是 如果由实现给定接口的给定加载程序定义的代理类存在,这将简单地返回缓存的副本;否则,它将通过ProxyClassFactory创建代理类
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
简单介绍一下WeakCache<K, P, V> 类,该类主要是为代理类进行缓存的。获取代理类时,会首先从缓存中获取,若没有会调用ProxyClassFactory类进行创建,创建好后会进行缓存。
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
进去apply方法找到ProxyClassFactory类,ProxyClassFactory是Proxy类的一个静态内部类,该类用于生成代理类。
看到这行代码,生成代理类就是这个方法无疑了
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
将这个字节数组显示出来就是最终的代理类了,然后在通过下面这个方法实现类加载,这个方法是一个本地方法
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
cglib代理
CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰。
和 jdk 动态代理原理查不多
-
回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
-
调用目标时有所改进,见下面代码片段
-
method.invoke 是反射调用,必须调用到足够次数才会进行优化
-
methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
-
methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
-
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的子类,所以Target不能为final
Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("before...");
// Object invoke = method.invoke(target, args); //用发射调用目标
// Object invoke = methodProxy.invoke(target, args); //内部没有用反射,需要目标,spring使用的方式
Object invoke = methodProxy.invokeSuper(p, args); //内部没有用反射,需要代理
System.out.println("after...");
return invoke;
});
proxy.foo();
}
}
现在我们需要研究的就是CGLIB是怎么避免反射调用的(反射调用耗费性能)
我们来进行模拟一下
先创建一个Target,做为目标类
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)");
}
}
代理类
public class Proxy extends Target {
// 相当于 jdk代理里面的InvocationHandler
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);
}
}
}
public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
// return method.invoke(target, args); // 反射调用
// FastClass
return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
// return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
}
});
proxy.save();
proxy.save(1);
proxy.save(2L);
}
在使用methodProxy的invoke和invokeSuper时生成两个FastClass类
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]);
}
}
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});
}
}
我们现在来梳理一下流程,CGLIB在生成代理类时候,为每个目标类的方法都封装了一个MethodProxy,代理类第一次被实例化或者第一次调用其方法时还会生成两个FastClass类,在使用methodProxy调用invoke或者invokeSuper方法时,methodProxy底层会调用对应的FastClass类的对应的方法。这样就避免了反射调用,来提高方法调用性能
这是CGLIB底层MethodProxy的invoke方法的实现
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}