前言
对于使用过 Spring 的朋友, 应该都使用过 AOP, 那么今天我们来对 AOP 的原理: 动态代理 来一探究竟.
静态代理
在动态代理之前, 我们先来看看静态代理, 看下其实现方式及其优缺点.
静态代理的原理是委托类和代理类都实现同一个接口, 代理类中会调用委托类的方法, 同时代理类中可以加一些自己的逻辑.
公共接口
代码语言:javascript复制public interface IService {
public void sayHello();
}
委托类
代码语言:javascript复制public class RealService implements IService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
代理类
代码语言:javascript复制public class ProxyService implements IService {
private IService iService;
public ProxyService(IService iService) {
this.iService = iService;
}
@Override
public void sayHello() {
System.out.println("before...");
iService.sayHello();
System.out.println("after...");
}
}
测试
代码语言:javascript复制public class Main {
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = new ProxyService(realService);
proxyService.sayHello();
}
}
输出:
代码语言:javascript复制before...
hello
after...
可以看到委托类和代理类都实现了同一个接口, 然后代理类在初始化时, 传入委托类对象, 然后在代理类自己的
sayHello()
方法中, 即调用了委托类的sayHello()
方法, 还加了自己的逻辑, 输出了before
和after
. 但这种方式有着明显的缺点:
- 必须要继承一个接口
- 必须要手工创建对应的代理类
- 硬编码, 当接口需要改动或代理类较多时不宜维护.
动态代理
JDK 动态代理
公共接口
代码语言:javascript复制public interface IService {
public void sayHello();
}
委托类
代码语言:javascript复制public class RealService implements IService {
@Override
public void sayHello() {
System.out.println("hello");
}
}
代理类
代码语言:javascript复制import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class SimpleInvocationHandler implements InvocationHandler {
private Object realObject;
public SimpleInvocationHandler(Object realObject) {
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before " method.getName());
Object result = method.invoke(realObject, args);
System.out.println("leaving " method.getName());
return result;
}
}
测试
代码语言:javascript复制import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
// 保存生成的代理类的字节码文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
IService realService = new RealService();
IService proxyService = (IService) Proxy.newProxyInstance(
IService.class.getClassLoader(),
new Class<?>[]{IService.class},
new SimpleInvocationHandler(realService));
proxyService.sayHello();
}
}
代码看起来好像更复杂了一些, 我们可以看到 IService
和 RealService
的定义不变, 但创建代理类的方式变化了, 它使用 java.lang.reflect.Proxy
的静态方法 newProxyInstance
来创建代理类.
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
它有三个参数:
loader
表示类加载器.interfaces
表示代理类要实现的接口列表, 元素的类型只能是接口.h
就是上面我们定义的SimpleInvocationHandler
它实现了InvocationHandler
接口, 并复写了invoke
方法, 对代理接口的所有方法调用都会转到此方法上.
newProxyInstance
的返回值是 Object
, 可以强制转化为 interfaces
数组中的任意接口类型, 但不能转化为某个普通类型, 如 RealService
. 即使它代理的实际对象是 RealService
.
接着我们在来看看 SimpleInvocationHandler
, 它实现了 InvocationHandler
接口, 它通过构造方法传入被代理对象 realObject
. 复写的 invoke
方法有三个参数:
proxy
表示代理对象本身, 需要注意, 它不是被代理的对象.method
表示正在被调用的方法args
表示方法的参数
需要注意这句代码:
代码语言:javascript复制Object result = method.invoke(realObject, args);
``
千万不能把 proxy 当成 method.invoke 的第一个参数. 这样会造成死循环, 因为这样表示代理类代理了它自身.
#### 原理解析
刚才的测试类中, 我们有一段代码, 它用来保存生成后的代理类的 class 文件:
```java
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")
我们来反编译看下, 它为我们动态生成的代理类:
代码语言:javascript复制package com.sun.proxy;
import im.zhaojun.jdk_proxy.IService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements IService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sayHello() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("im.zhaojun.jdk_proxy.IService").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到, 其实也就是生成了一个类, 实现了我们传入的接口, 它所有方法都是调用的 SimpleInvocationHandler
的 invoke 方法.
相比于静态代理, 这里的动态代理看起来麻烦了不少, 但它却更加通用. 我们不用为每个被代理的类都创建一个静态代理类, 而是当代理类要做的功能不变时, 只需要有这一个代理类即可. 说起来可能有些不好理解, 看代码吧:
代码语言:javascript复制public class GeneralProxyDemo {
static interface IServiceA {
public void sayHello();
}
static class ServiceAImpl implements IServiceA {
@Override
public void sayHello() {
System.out.println("hello");
}
}
static interface IServiceB {
public void fly();
}
static class ServiceBImpl implements IServiceB {
@Override
public void fly() {
System.out.println("flying");
}
}
static class SimpleInvocationHandler implements InvocationHandler {
private Object realObj;
public SimpleInvocationHandler(Object realObj) {
this.realObj = realObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("entering " realObj.getClass().getSimpleName() "::" method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving " realObj.getClass().getSimpleName() "::" method.getName());
return result;
}
}
@SuppressWarnings("unchecked")
private static <T> T getProxy(Class<T> intf, T realObj) {
return (T) Proxy.newProxyInstance(intf.getClassLoader(), new Class<?>[] { intf },
new SimpleInvocationHandler(realObj));
}
public static void main(String[] args) throws Exception {
IServiceA a = new ServiceAImpl();
IServiceA aProxy = getProxy(IServiceA.class, a);
aProxy.sayHello();
IServiceB b = new ServiceBImpl();
IServiceB bProxy = getProxy(IServiceB.class, b);
bProxy.fly();
}
}
在这里有两个接口 ServiceA
和 ServiceB
, 他们对应的实现类为 ServiceAImpl
和 ServiceBImpl
. 虽然他们的接口和实现类完全不同, 但通过动态代理. 他们都可以使用 SimpleInvocationHandler
中 invoke
中的代理逻辑.
CGLIB 动态代理
上面讲到的 JDK 动态代理, 有一定的局限性, 那就是只能为接口创建代理, 返回的对象也只能是接口类型的, 如果一个类没有接口, 或者想代理非接口中定义的方法, JDK 动态代理就无法实现了. 这里就要用到 CGLIB
动态代理了.
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class SimpleCGLibDemo {
/**
* 被代理类.
*/
static class RealService {
public void sayHello() {
System.out.println("hello");
}
}
/**
* 方法拦截器.
*/
static class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("entering " method.getName());
Object result = proxy.invokeSuper(object, args);
System.out.println("leaving " method.getName());
return result;
}
}
private static <T> T getProxy(Class<T> cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls); // 设置被代理类
enhancer.setCallback(new SimpleInterceptor()); // 设置方法拦截器
return (T) enhancer.create();
}
public static void main(String[] args) {
RealService proxyService = getProxy(RealService.class);
proxyService.sayHello();
}
}
RealService
表示被代理的类, 它没有实现任何接口. getProxy()
方法为一个类生成代理对象, 这个代理对象可以转换为被代理类的类型, 它使用了 cglib
的 Enhancer
类, Enhancer
类的 setSuperclass
设置被代理的类, setCallback
设置被代理类的方法被调用时的处理类, Enhancer
支持多种类型, 这里使用的类实现了 MethodInterceptor
接口, 它与 JDK 动态代理中的 InvocationHandler
有点类似, 方法名称变成了intercept, 多了一个MethodProxy类型的参数.
与前面的 InvocationHandler
不同,SimpleInterceptor
中没有被代理的对象,它通过 MethodProxy
的 invokeSuper
方法调用被代理类的方法:
Object result = proxy.invokeSuper(object, args);
注意,它不能这样调用被代理类的方法:
代码语言:javascript复制Object result = method.invoke(object, args);
object 是代理对象,不能自己代理自己,会造成死循环。
基本的使用就这些, 先消化下, 自己动手实现, 后续我会更新一些 JDK 更细节上的内容.