代理模式為物件提供一個代理以控制對這個物件的訪問。所謂代理,是指與代理元(即:被代理的物件)具有相同介面的類,客戶端必須通過代理與代理元進行互動。我們可以將代理理解成另一個物件的代表。
代理(proxy)模式本質上就是通過一個代理物件訪問目標物件,而不直接訪問目標物件;代理模式可以在不修改被代理物件的基礎上,通過擴充套件代理類,進行一些功能的附加與增強。這麼做的好處是,可以讓目標物件只關注業務邏輯,而非業務邏輯(比如日誌輸出等)可以在代理物件中實現,實現業務分離。
代理模式要點:
- 代理模式包含三個要素:1)介面/Interface; 2)代理元/Target:即被代理的物件; 3)代理類/Proxy:代理元的代表,對外提供訪問;
- 代理元與代理類都是介面的實現類;
- 客戶端只能訪問代理類,無法直接訪問代理元;
代理分為靜態代理和動態代理。
靜態代理
靜態代理就是在編譯時生成代理類。即:通過編碼手動建立代理類,並在代理類中呼叫代理元。
編碼實現
1.介面
package effectiveJava.proxy; public interface HelloService { void sayHello(); }
2,代理元
package effectiveJava.proxy; public class HelloServiceImpl implements HelloService{ @Override public void sayHello() { System.out.println("Hello Proxy."); } }
3,代理類
package effectiveJava.proxy.staticProxy; import effectiveJava.proxy.HelloService; public class LogProxy implements HelloService { private HelloService service; public LogProxy(HelloService service) { this.service = service; } @Override public void sayHello() { System.out.println("Static Proxy : Before Hello...."); service.sayHello(); System.out.println("Static Proxy : After Hello...."); } }
4,測試類
package effectiveJava.proxy.staticProxy; import effectiveJava.proxy.HelloServiceImpl; public class LogProxyDemo { public static void main(String[] args) { LogProxy logProxy = new LogProxy(new HelloServiceImpl()); logProxy.sayHello(); } }
5,測試結果
Static Proxy : Before Hello....
Hello Proxy.
Static Proxy : After Hello....
動態代理
動態代理地實現有兩種方式:1)基於JDK的動態代理;2)基於CGLIB的動態代理;
1)基於JDK的動態代理
API
public class Proxy implements java.io.Serializable { /** * 建立代理例項 * @param loader 代理元的類載入器 * @param interfaces 代理元的介面 * h 一個 InvocationHandler 物件 */ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h); }
/** * * 每個代理的例項都有一個與之關聯的 InvocationHandler 實現類, * 如果代理的方法被呼叫,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。 */ public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
編碼實現
1,介面、代理元(編碼與靜態代理一致,不再贅述)
2,代理類(必須實現InvocationHandler介面)
package effectiveJava.proxy.v0; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class HelloInvocation implements InvocationHandler { /** * 代理元 */ private Object target; public HelloInvocation(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; } }
3,測試類
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(); HelloInvocation helloInvocation = new HelloInvocation(helloService); HelloService impl = (HelloService)Proxy.newProxyInstance( helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), helloInvocation); impl.sayHello(); } }
4,測試結果
HelloInvocation : Before Hello....
Hello Proxy.
HelloInvocation : After Hello....
2)基於CGLIB的動態代理
Cglib是基於繼承的方式進行代理,代理類去繼承目標類,每次呼叫代理類的方法都會被方法攔截器攔截,在攔截器中才是呼叫目標類的該方法的邏輯。因此,基於CGLIB的動態代理不需要介面。
編碼實現
1,引入架包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
2,代理類(HelloServiceImpl.java,編碼與靜態代理一致)
3, 方法攔截器
package effectiveJava.proxy.cglib; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class LogInterceptor implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("Cglib : Before hello..."); //呼叫父委方法 Object result = methodProxy.invokeSuper(object, args); System.out.println("Cglib : After hello..."); return result; } }
4,測試類
package effectiveJava.proxy.cglib; import effectiveJava.proxy.HelloService; import effectiveJava.proxy.HelloServiceImpl; import net.sf.cglib.proxy.Enhancer; public class CglibDemo { public static void main(String[] args) { //建立Enhancer物件,類似於JDK動態代理的Proxy類 Enhancer enhancer = new Enhancer(); //設定父類 enhancer.setSuperclass(HelloServiceImpl.class); //設定回撥函式(攔截器) enhancer.setCallback(new LogInterceptor()); //建立代理類例項 HelloService service = (HelloService)enhancer.create(); service.sayHello(); } }
5,測試結果
Cglib : Before hello...
Hello Proxy.
Cglib : After hello...
總結:
- 靜態代理是在編譯時建立代理,動態代理是在執行時建立代理;
- JDK動態代理是基於介面的方式,CGLib動態代理是基於繼承;