一、前言
1.1 背景
在不改變原有代碼的基礎上,對方法進行功能性的增強;
1.2 簡介
代理模式是一種結構型模式,為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
組成:
- 抽象主體(Subject):通過接口或抽象類聲明真實主體實現的業務方法
- 代理(Proxy):實現抽象主體,是真實主體的代理,通過真實主體的業務邏輯方法來實現抽象方法,并可以附加自己的操作
- 真實主體(RealSubject):實現抽象主體,定義真實主體所要實現的業務邏輯,供代理主體調用
優點:
- 職責清晰
- 中介的作用、保護目標對象的作用
- 高擴展性
二、三種實現
- 靜態代理
- JDK動態代理
- Cglib動態代理
此處以代駕舉例:抽象主體(Driver),代理(ProxyDriver),真實主體(RealDriver)
2.1 靜態代理
缺點:
- 代理類過多:代理對象需要和目標對象實現相同的接口或父類
- 難以維護:一旦接口中增加了方法后,目標對象和代理對象都需要維護
package com.qiangesoft.design.structural.proxy;/*** 靜態代理*/
public class StaticProxy {public static void main(String[] args) {RealDriver realDriver = new RealDriver();Driver driver = new ProxyDriver(realDriver);driver.drive("奧迪A6L");}
}/*** 駕駛員接口*/
interface Driver {void drive(String car);
}/*** 真實駕駛員*/
class RealDriver implements Driver {@Overridepublic void drive(String car) {System.out.println("【" + car + "】被開回家了");}
}/*** 代理駕駛員*/
class ProxyDriver implements Driver {private RealDriver realDriver;public ProxyDriver(RealDriver realDriver) {this.realDriver = realDriver;}@Overridepublic void drive(String car) {System.out.println("代駕取鑰匙");realDriver.drive(car);System.out.println("代駕還鑰匙");}
}
2.2 JDK動態代理
特點:
- 利用JDK的API動態的在內存中構建代理對象(需指定目標對象實現接口的類型、代理對象)
- 目標對象一定要實現接口,否則不能用動態代理
package com.qiangesoft.design.structural.proxy.jdk;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** jdk動態代理*/
public class JdkDynamicProxy {public static void main(String[] args) {RealDriver realDriver = new RealDriver();ProxyDriver proxyDriver = new ProxyDriver(realDriver);Driver proxy = (Driver) proxyDriver.getProxyInstance();// 使用代理對象來調用抽象方法proxy.drive("奧迪A6L");}
}/*** 駕駛員接口*/
interface Driver {void drive(String car);
}/*** 真實駕駛員*/
class RealDriver implements Driver {@Overridepublic void drive(String car) {System.out.println("【" + car + "】被開回家了");}
}/*** 代理駕駛員*/
class ProxyDriver implements InvocationHandler {private Object realDriver;public ProxyDriver(RealDriver realDriver) {this.realDriver = realDriver;}public Object getProxyInstance() {// 使用Proxy類的靜態方法newProxyInstance來動態地創建一個代理對象,傳入真實主題對象的類加載器、接口和處理器return Proxy.newProxyInstance(realDriver.getClass().getClassLoader(), realDriver.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代駕取鑰匙");Object result = method.invoke(realDriver, args);System.out.println("代駕還鑰匙");return result;}
}
2.3 Cglib動態代理
特點:
- 通過生成目標對象的子類實現代理
- 需要引入cglib的jar包
- 目標對象的類不能是final(類不能被繼承)
- 目標對象的方法如果有final(方法不能被重寫)/static(非對象的方法)修飾,不會被執行。
引入依賴
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
代碼實現
package com.qiangesoft.design.structural.proxy.cglib;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** cglib動態代理*/
public class CglibDynamicProxy {public static void main(String[] args) {RealDriver realDriver = new RealDriver();ProxyDriver proxyDriver = new ProxyDriver(realDriver);RealDriver proxy = (RealDriver) proxyDriver.getProxyInstance();proxy.drive("奧迪A6L");}
}/*** 駕駛員接口*/
interface Driver {void drive(String car);
}/*** 真實駕駛員*/
class RealDriver implements Driver {@Overridepublic void drive(String car) {System.out.println("【" + car + "】被開回家了");}
}/*** 代理駕駛員*/
class ProxyDriver implements MethodInterceptor {private Object realDriver;public ProxyDriver(Object realDriver) {this.realDriver = realDriver;}public Object getProxyInstance() {// 1.工具類Enhancer enhancer = new Enhancer();// 2.設置父類enhancer.setSuperclass(realDriver.getClass());// 3.設置回調函數enhancer.setCallback(this);// 4.創建子類(代理對象)return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("代駕取鑰匙");Object result = method.invoke(realDriver, args);System.out.println("代駕還鑰匙");return result;}
}
三、總結
- 如果加入容器的目標對象有實現接口,用JDK代理
- 如果目標對象沒有實現接口,用Cglib代理
- 如果目標對象實現了接口,且強制使用cglib代理,則會使用cglib代理。