動態代理
靜態代理不適用的場景
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生成的代理類是繼承自目標類的,在創建子類對象之前一定要先創建父類對象,所以在代理對象中一定會有目標對象。