關於各類代理的基本信息,請查看代理模式詳解。本文主要講解基於JDK的動態代理的組件化封裝之路。html
首先,咱們看一下傳統的使用方式:java
1,接口git
package effectiveJava.proxy; public interface HelloService { void sayHello(); }
2,代理元github
package effectiveJava.proxy; public class HelloServiceImpl implements HelloService{ @Override public void sayHello() { System.out.println("Hello Proxy."); } }
3,代理類(必須實現InvocationHandler接口)ide
package effectiveJava.proxy.v0; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ProxyInvocation implements InvocationHandler { /** * 代理元 */ private Object target; public ProxyInvocation(Object target) { this.target = target; } /** * * @param proxy 代理類實例 * @param method 實際要調用的方法 * @param args 實際要調用方法的參數類型 * @return 結果值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("HelloInvocation : Before Hello...."); Object reslut = method.invoke(target, args); System.out.println("HelloInvocation : After Hello...."); return reslut; } }
4,測試類組件化
package effectiveJava.proxy.v0; import effectiveJava.proxy.HelloService; import effectiveJava.proxy.HelloServiceImpl; import java.lang.reflect.Proxy; /** * 經過Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)建立代理對象的實例 */ public class HelloInvocationDemo { public static void main(String[] args) { HelloServiceImpl helloService = new HelloServiceImpl(); ProxyInvocation helloInvocation = new ProxyInvocation(helloService); HelloService impl = (HelloService)Proxy.newProxyInstance( helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), helloInvocation); impl.sayHello(); } }
咱們發現,客戶端每次使用代理的時候,都必須經過Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)建立代理對象的實例,使用不太方便。咱們能夠將該方法封裝到代理類(ProxyInvocation)中,簡化客戶端的調用。代碼修改以下:測試
package effectiveJava.proxy.v1; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocation implements InvocationHandler { /** * 代理元 */ private Object target; public ProxyInvocation(Object target) { this.target = target; } /** * * @param proxy 代理類實例 * @param method 實際要調用的方法 * @param args 實際要調用方法的參數類型 * @return 結果值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before Hello...."); Object reslut = method.invoke(target, args); System.out.println("after Hello...."); return reslut; } public static Object getProxyObject(Object target) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new ProxyInvocation(target)); } }
4,測試類優化
package effectiveJava.proxy.v1; import effectiveJava.proxy.HelloService; import effectiveJava.proxy.HelloServiceImpl; public class HelloInvocationDemo { public static void main(String[] args) { HelloService impl = (HelloService)ProxyInvocation.getProxyObject(new HelloServiceImpl()); impl.sayHello(); } }
出於組建化考慮,咱們能夠把代理類封裝成一個組件,所以咱們須要將可能產生變化的邏輯剝離出來(3中黃色部分)。咱們能夠定義一個攔截器接口,用來封裝這些動態邏輯,而後用代理類(ProxyInvocation)調用這個攔截器的方法。而動態邏輯在攔截器接口的具體實現類中實現。修改後代碼代碼以下:this
3,攔截器接口spa
package effectiveJava.proxy.v2.jar; public interface Interceptor { void intercept(); }
4,代理類
package effectiveJava.proxy.v2.jar; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; public class ProxyInvocation implements InvocationHandler { private Object target; private List<Interceptor> beforeInterceptors; private List<Interceptor> afterInterceptors; public ProxyInvocation(Object target, List<Interceptor> beforeInterceptors, List<Interceptor> afterInterceptors) { this.target = target; this.beforeInterceptors = beforeInterceptors; this.afterInterceptors = afterInterceptors; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { for(Interceptor interceptor : beforeInterceptors) { interceptor.intercept(); } Object reslut = method.invoke(target, args); for(Interceptor interceptor : afterInterceptors) { interceptor.intercept(); } return reslut; } public static Object getProxyObject(Object target,List<Interceptor> beforeInterceptors,List<Interceptor> afterInterceptors) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new ProxyInvocation(target,beforeInterceptors,afterInterceptors)); } }
5,攔截器的實現類
package effectiveJava.proxy.v2; import effectiveJava.proxy.v2.jar.Interceptor; public class BeforeInterceptor implements Interceptor { @Override public void intercept() { System.out.println("BeforeInterceptor : Before Hello...."); } }
package effectiveJava.proxy.v2; import effectiveJava.proxy.v2.jar.Interceptor; public class AfterIntercept implements Interceptor { @Override public void intercept() { System.out.println("AfterIntercept : After Hello...."); } }
6,測試類
package effectiveJava.proxy.v2; import effectiveJava.proxy.HelloService; import effectiveJava.proxy.HelloServiceImpl; import effectiveJava.proxy.v2.jar.ProxyInvocation; import java.util.Arrays; public class HelloInvocationDemo { public static void main(String[] args) { HelloService impl = (HelloService) ProxyInvocation.getProxyObject( new HelloServiceImpl(), Arrays.asList(new BeforeInterceptor()), Arrays.asList(new AfterIntercept())); impl.sayHello(); } }
上述方法中,代理類中的invoke()須要分別便利前置通知和後置通知,比較繁瑣,須要再次優化。能夠經過建立Invocation類把攔截對象信息進行封裝,做爲攔截器攔截方法的參數,把攔截目標對象真正的執行方法放到Interceptor中完成。修改後代碼代碼以下:
3,攔截對象
package effectiveJava.proxy.v3.jar; import java.lang.reflect.Method; /** * 封裝攔截的目標,包含:類實例、方法、參數 * @author Winn */ public class Invocation { private Object target; private Method method; private Object[] args; public Invocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } public Object invoke() throws Throwable { return method.invoke(target, args); } }
4, 攔截器接口【代理對象的實例在還接口的方法中建立】
package effectiveJava.proxy.v3.jar; public interface Interceptor { Object intercept(Invocation invocation) throws Throwable;
/**
*使用JDK8中的接口默認方法
*/ default Object wrap(Object target) { return ProxyInvocationHandler.getProxyObject(target,this); } }
5,代理類
package effectiveJava.proxy.v3.jar; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { private Object target; private Interceptor interceptor; public ProxyInvocationHandler(Object target, Interceptor interceptor) { this.target = target; this.interceptor = interceptor; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Invocation invocation = new Invocation(target, method, args); return interceptor.intercept(invocation); } public static Object getProxyObject(Object target, Interceptor interceptor) { ProxyInvocationHandler handler = new ProxyInvocationHandler(target, interceptor); return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler); } }
6,攔截器的實現類(添加附加邏輯,並調用代理元)
package effectiveJava.proxy.v3; import effectiveJava.proxy.v3.jar.Interceptor; import effectiveJava.proxy.v3.jar.Invocation; public class LogIntercept implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("LogIntercept : After Hello...."); Object invoke = invocation.invoke(); System.out.println("LogIntercept : After Hello...."); return invoke; } }
7,測試類
package effectiveJava.proxy.v3; import effectiveJava.proxy.HelloService; import effectiveJava.proxy.HelloServiceImpl; public class HelloInvocationDemo { public static void main(String[] args) { HelloService target = new HelloServiceImpl(); //攔截器調用兩次,日誌輸出兩遍 target = (HelloService)new LogIntercept().wrap(target); target = (HelloService)new LogIntercept().wrap(target); target.sayHello(); } }
至此,將代理進行組件化封裝已經告一段落。攔截對象(3),攔截器接口(4),代理類(5)能夠封裝成一個jar包。而後根據業務,自定義須要的接口(1)、接口實現類(2),以及攔截器(6)。因爲咱們可能須要多個不一樣功能的攔截器,經過上述方式,咱們須要不停地在客戶端調用各個攔截器實例的wrap()方法,這樣不太美觀。咱們利用責任鏈模式的思想,再將攔截器封裝到一個集合裏,用這個集合容器去實現調用。修改後代碼代碼以下:
7,攔截器鏈
package effectiveJava.proxy.v4.jar; import java.util.ArrayList; import java.util.List; public class InterceptChain { private List<Interceptor> interceptorList = new ArrayList<>(); public void regist(Interceptor interceptor) { interceptorList.add(interceptor); } public void registAll(Interceptor ... interceptors) { for(Interceptor interceptor : interceptors) { regist(interceptor); } } public Object wrap(Object target) { for(Interceptor interceptor : interceptorList) { target = interceptor.wrap(target); } return target; } }
8,測試類
package effectiveJava.proxy.v4; import effectiveJava.proxy.HelloService; import effectiveJava.proxy.HelloServiceImpl; import effectiveJava.proxy.v4.jar.InterceptChain; public class HelloInvocationDemo { public static void main(String[] args) { InterceptChain chain = new InterceptChain(); chain.registAll(new LogIntercept(),new LogIntercept(),new LogIntercept()); HelloService target = new HelloServiceImpl(); target = (HelloService)chain.wrap(target); target.sayHello(); } }