1 动态代理和静态代理
1.1 静态代理
代理模式最主要的就是有一个公共接口(Subject),一个具体的类(RealSubject),一个代理类(Proxy),代理类持有具体类的实例。当Client调用时接口时,实际是Proxy代为执行具体类的实例方法(request)。
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。
静态代理在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
1.2 动态代理
相比于静态代理来说,动态代理更加灵活。不需要针对每个目标类都单独创建一个代理类,并且也不需要实现接口直接代理实现类。动态代理类的字节码在程序运行时,运用反射机制动态创建而成。
动态代理在日志、监控、事务中,主流框架中都有着广泛的应用。动态代理一般有两种实现方式:
- JDK动态代理:利用接口实现代理
- CGLIB动态代理:利用继承的方式实现代理
2 JDK动态代理类
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
调用处理器
这样对目标类中的所有方法的调用都会变为对invoke的调用,可以在invoke方法中添加统一的处理逻辑
创建一个动态代理对象步骤主要有两步:
2.1 创建InvocationHandler调用处理器
每个目标类(被代理类)都有一个与之关联的 InvocationHandler 实现类(调用处理器);如果目标类的方法被调用,那么代理便会转发给 InvocationHandler 实现类(调用处理器)的invoke方法。
public interface InvocationHandler {
Object invoke(Object proxy, Method var2, Object[] args) throws Throwable;
}
在此接口中只有一个 invoker 方法。该方法里有三个参数:
- proxy 代理对象
- method 目标类的方法
- args 目标类方法的参数
public class InvocationHandlerImpl implements InvocationHandler {
// 目标类(被代理类)
private Object target;
public TargetInvoker(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行前...");
Object result = method.invoke(target, args);
System.out.println("执行后");
return result;
}
}
2.2 创建代理对象
代理对象由Proxy 类进行创建,代理类继承了Proxy类。
public class Proxy implements Serializable {
// 静态方法,返回的是代理类,代理类是Proxy的子类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
Objects.requireNonNull(h);
Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
}
newProxyInstance方法中的三个参数
- loader 代理类的类加载器
- interfaces 代理类要实现的接口(需要被代理的目标类方法)
- h 目标类所关联的调用处理器(InvocationHandler)
2.3 实现示例
代理的接口
public interface Hello {
public void sayHello();
}
代理的目标类
public class HelloService implements Hello {
public void sayHello() {
System.out.println("Hello World...");
}
}
调用处理器实现类
public class HelloInvocationHandler implements InvocationHandler {
private Object target;
public HelloInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before hello ... ");
Object result = method.invoke(this.target, args);
System.out.println("after hello... ");
return result;
}
}
代理类
@Test
public void proxyHello() throws Exception {
//1. 创建被代理的目标对象
HelloService helloService= new HelloService();
//2. 创建调用处理器
HelloInvocationHandler handler = new HelloInvocationHandler(new HelloService());
//3. 获取对应的 ClassLoader
ClassLoader classLoader = helloService.getClass().getClassLoader();
//4. 获取所有接口的interface
Class[] interfaces = helloService.getClass().getInterfaces();
//5. 创建代理类
Hello hello = (Hello) proxy.proxyInstance(classLoader, interfaces, handler);
//5. 调用方法
hello.sayHello();
}
2.4 原理
Proxy#newProxyInstance
Proxy类的newProxyInstance方法创建了一个动态代理对象。主要的代码逻辑如下:
// 1. 获取代理类
Class<?> cl = getProxyClass0(loader, intfs);
...
// 2. 获取代理类的构造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
...
// 3. InvocationHandler 对象入参,反射调用构造方法生成动态代理对象
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{h});
getProxyClass0方法
上面步骤中最为核心的是 获取代理类的getProxyClass0方法。getProxyClass0的主要逻辑如下:
- 从一个 WeakCache 中去获取代理类,如果缓存中存在代理类则从缓存直接获取;
- 如果不存在,则通过代理类工厂创建代理类。
代理类的字节码生成
代理类的产生就是整个动态代理的关键。其步骤如下:
- 首先通过指定的类加载器去验证目标接口是否可被其加载
- 通过接口所在包等条件决定代理类所在包及代理类的全限定名称,代理类名称是包名+$Proxy+id
- 通过 ProxyGenerator.generateProxyClass() 生成字节码数组,然后调用 native 方法 defineClass0() 将其动态生成的代理类字节码加载到内存中
生产的proxy文件
我们可以通过下面的方法将动态生成的类输出成 class 文件:
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", HelloService.class.getInterfaces());
String path = "C:/dynamic//proxy/HelloProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("write success...");
} catch (Exception e) {
System.out.println("write fail...");
}
使用反编译工具对这个class文件进行反编译, 可以查看代理类的源码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Hello
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
* 生成代理类的构造方法,方法参数为InvocationHandler
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//静态块
static
{
try
{
//sayHello通过反射得到的名字m3
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.proxy.Hello").getMethod("sayHello", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*这里调用代理对象的sayHello方法,直接就调用了InvocationHandler中的invoke方法
*/
public final void sayHello()
throws
{
try
{
// m3 即为sayHello
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//...
}
从源码可以看出,通过 static 代码块将被代理类中每一个方法封装为 Method 对象;代理类对象构造时传入了InvocationHandler,执行同名方法时,通过InvocationHandler的invoke(方法,完成动态代理。