动态代理
静态代理不适用的场景
JDK动态代理
- 在运行时动态生成一个新类ProxyClass作为代理类,其实现了interfaces接口,有一个InvocationHandler成员变量,体现了“动态”;
- 创建代理类对象返回;
public interface TestInterface{
void method1();
}
public class TestInterfaceImpl implements TestInterface{
void method1() {
System.out.printfln("method1 invoke");
}
}
调用Proxy.newProxyInstance(TestInterface.class.getClassLoader(),new Class[]{TestInterface.class},handler)生成的代理类大致如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//public final的,继承Proxy,实现你传入的接口
public final class TestProxy extends Proxy
implements TestInterface
{
//private static 的Method属性,对应所有方法
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
//唯一的构造方法,需要一个InvocationHandler接口传入
public TestProxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//代理的方法,回调传入的InvocationHandler的invoke方法
public final void method1()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{//每一个属性所代表的Method都是与上面加入代理方法列表时与固定类绑定的,这是class文件中的格式,方法要与固定的类绑定
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("TestInterface").getMethod("method1", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
从上面的代码我们可以看到,代理类实现的method1方法回调了InvocationHandler的invoce,也印证了之前说的对代理类方法的调用都会委派给对应的InvocationHandler。
Each proxy instance has an associated invocation handler.
* When a method is invoked on a proxy instance, the method
* invocation is encoded and dispatched to the {@code invoke}
* method of its invocation handler.
实现InvocationHandler接口,来自定义具体的业务逻辑。InvocationHandler就一个方法
invoke(Object proxy, Method method, Object[] args)
其中proxy是代理类实例,class TimestampLogInvocationHandler implements InvocationHandler {
private Object target;//被代理对象
TimestampLogInvocationHandler(Object target) {
this.target = target;
}
@Override
Object invoke(Object proxy, Method method, Object[] args) {
logger.info("当前时间:"+new Date());
return method.invoke(target,args);
}
}
//测试一下:
class MainTest {
public static void main(String[] args) {
TestInterface interface = new TestInterfaceImpl();//被代理对象
TimestampInvocationHandler tih = new TimestampInvocationHandler(interface);//调用处理器
//代理类实例
TestInterface myProxy = (TestInterface)Proxy.newProxyInstance(TestInterface.class.getClassLoader(),new Class[]{TestInterface.class},tih);
//调用代理类方法
myProxy.method1();
}
}
将会输出:
当前时间:1020021212
method1
可以看到调用都被委派到了InvocationHandler上。
之后若有其他类也需要打印时间戳,可复用此InvocationHandler创建自己的代理类实例,无需再次开发。以上就是JDK的动态代理,下面看下Cglib动态代理。
Cgllib动态代理
void setSuperclass(java.lang.Class superclass)//设置被代理的类型
void setCallback(Callback callback) //设置回调接口
Object create()//创建代理实例,该实例的类型为superclass的子类
在setCallback中定义了回调接口,调用被代理的方法都会委派到回调接口。最通用的回调接口是MethodInterceptor,它与InvocationHandler类似只有一个方法:/**
* All generated proxied methods call this method instead of the original method.
* The original method may either be invoked by normal reflection using the Method object,
* or by using the MethodProxy (faster).
* @param obj "this", the enhanced object
* @param method intercepted Method
* @param args argument array; primitive types are wrapped
* @param proxy used to invoke super (non-intercepted method); may be called
* as many times as needed
* @throws Throwable any exception may be thrown; if so, super method will not be invoked
* @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
* @see MethodProxy
*/
public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable
object是代理实例,method是被拦截的方法,可以通过proxy.invokeSuper(obj, args)调用object的父类的相同方法。class Cglib{//目标类
public void cglib(){
System.out.println("CGLIB");
}
}
class HelloProxy implements MethodInterceptor{//回调方法
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throwsThrowable {
System.out.println("Hello");
Object object = proxy.invokeSuper(obj, args);
System.out.println("Powerful!");
return object;
}
}
public class TestMain {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Cglib.class);
enhancer.setCallback(new HelloProxy());
Cglib cglibProxy = (Cglib)enhancer.create();
cglibProxy.cglib();
}
}
输出内容:
Hello
CGLIB
Powerful!
你可能会说,标准的代理模式中,代理类不是要持有被代理类的引用吗,乍看起来CGLib动态代理没有目标类的引用,其实仔细想一下,CGLib生成的代理类是继承自目标类的,在创建子类对象之前一定要先创建父类对象,所以在代理对象中一定会有目标对象。