定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
UML
角色
- Target: 被代理类的接口。
- TargetImpl: 被代理类的实现。
- TargetProxy: Target实例的代理类,控制客户端访问Target对象。
示例
- 静态代理的实现
/**
* desc : 目标接口
* Created by tiantian on 2018/10/7
*/
public interface Target {
void action();
}
/**
* desc : 目标实现类
* Created by tiantian on 2018/10/7
*/
public class TargetImpl implements Target{
@Override
public void action() {
System.out.println("target do something");
}
}
/**
* desc : 目标代理类
* Created by tiantian on 2018/10/7
*/
public class TargetProxy {
private Target target;
public TargetProxy(Target target) {
this.target = target;
}
public void action() {
System.out.println("befor target action");
target.action();
System.out.println("after target action");
}
}
/**
* desc : 静态代理测试
* Created by tiantian on 2018/10/7
*/
public class Test {
public static void main(String[] args) {
TargetProxy proxy = new TargetProxy(new TargetImpl());
proxy.action();
}
// output:
// befor target action
// target do something
// after target action
}
jdk动态代理
静态代理的方式实现简单,也确实达到了代理的目的,但仍存存在瑕疵。假如需要代理的类非常多将不得不创建相同类型的代理类,维护成本增加了。为了改善这种缺陷,jdk实现了动态代理的方式。通过jdk代理技术实现的代理类能代理很多不同的类。实现动态代理必须实现InvocationHandler接口,该接口方法invoke至关重要,代理类的控制访问正体现在这个方法调用时。
/**
* desc : jdk动态代理
* Created by tiantian on 2018/10/7
*/
public class MyHandle implements InvocationHandler {
private Target target;
public MyHandle(Target target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("befor invoke method.");
method.invoke(target, args);
System.out.println("after invoke method.");
return null;
}
}
测试类我们打印出来代理类想要达成的效果,并且生成了代理类的class文件,用于分析动态代理的原理。
/**
* desc : jdk动态代理测试
* Created by tiantian on 2018/10/7
*/
public class JdkProxyTest {
public static void main(String[] args) {
Target target = new TargetImpl();
MyHandle myHandle = new MyHandle(target);
Class[] targetInterfaceClass = {Target.class};
Target proxy = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(), targetInterfaceClass , myHandle);
// 此处代理类的方法调用可以看出代理类要么实现目标类的接口,要么继 承目标类的父类,如此才能具有多态效果。这样印证JDK动态代理只能代理接口的实现类
proxy.action();
Class<? extends Target> proxyClass = proxy.getClass();
System.out.println(proxyClass.getName());
String proxyClassName = proxyClass.getName();
byte[] classBytes = ProxyGenerator.generateProxyClass(proxyClassName, targetInterfaceClass);
String filePath = "/Users/enn/" + proxyClassName + ".class";
System.out.println(filePath);
try {
OutputStream out = new FileOutputStream(new File(filePath));
try {
out.write(classBytes);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// output:
// befor invoke method.
// target do something
// after invoke method.
}
动态生成的代理类反编译后的如下。通过反编译后的源码我们发现动态生成的代理类都继承自Proxy类并且实现了需要被代理的接口,这正好也解释了jdk动态代理的缺陷:只能为实现了接口的类生成代理类,而没实现接口或者继承自某类的类则不行。原因是java被设计为单根继承,如果需要被代理的目标类继承自一个类则动态代理生成的类无法再继承目标类的父类,也就无法代理父类中的方法。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import design.patten.proxy.Target;
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 Target {
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
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 String toString() throws {
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() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void action() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("design.patten.proxy.Target").getMethod("action");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
cglib代理
cglib(Code Generator)是通过底层ASM字节码生成框架动态生成代理类的技术。ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
cglib生成代理类要定义一个实现自MethodInterceptor接口的拦截器。该拦截器作为回调对象传入Enhancer对象中用于生成代理类。
/**
* desc : 方法拦截器
* Created by tiantian on 2018/10/7
*/
public class MyMethodIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("befor method invoke. method = " + method);
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("after method invoke. method = " + method);
return object;
}
}
/**
* desc : spring cglib测试
* Created by tiantian on 2018/10/7
*/
public class CglibTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetImpl.class);
enhancer.setCallback(new MyMethodIntercepter());
Target tarProxy = (Target) enhancer.create();
tarProxy.action();
}
// output:
// befor method invoke. method = public void design.patten.proxy.TargetImpl.action()
// target do something
// after method invoke. method = public void design.patten.proxy.TargetImpl.action()
}
jdk和cglib动态代理实现的区别
1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类(目标类)的子类,且不能处理被final关键字修饰的方法;
3、jdk动态代理采用反射机制生成代理类调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;