天天看點

代理模式的應用

靜态代理

很多小夥伴去大城市打拼。來大城市第一件事就是租房,免不了和中介打交道,因為很多房東很忙,你根本找不到他。從這個場景中就可以抽象出來代理模式

ISubject:被通路者資源的抽象

SubjectImpl:被通路者具體實作類(房東)

SubjectProxy:被通路者的代理實作類(中介)

UML圖如下

代理模式的應用

舉個例子來了解一下這個設計模式

老闆讓記錄一下使用者服務的響應時間,用代理模式來實作這個功能。

public interface IUserService {
    public void request();
}      
public class UserServiceImpl implements IUserService {
    @Override
    public void request() {
        System.out.println("this is userService");
    }
}      
public class UserServiceProxy implements IUserService {

    private IUserService userService;

    public UserServiceProxy(IUserService userService) {
        this.userService = userService;
    }

    @Override
    public void request() {
        long startTime = System.currentTimeMillis();
        userService.request();
        System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
    }

    public static void main(String[] args) {
        IUserService userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
        // this is userService
        // reques cost :0
        userServiceProxy.request();
    }
}      

代理模式不隻有讓代理類和原始類實作同一個接口,然後将原始類注入到代理類中這一種寫法。如果原始類沒有定義接口,并且原始類并不是我們維護的,我們此時就可以用繼承的方式類實作代理模式,讓代理類繼承原始類,然後擴充功能。

public class UserService {

    public void request() {
        System.out.println("this is userService");
    }
}      
public class UserServiceProxy extends UserService {

    @Override
    public void request() {
        long startTime = System.currentTimeMillis();
        super.request();
        System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
    }

    public static void main(String[] args) {
        UserService userService = new UserServiceProxy();
        // this is userService
        // reques cost :1
        userService.request();
    }
}      

一切看起來都非常的美好,老闆又發話了,把産品服務的響應時間也記錄一下吧。又得寫如下3個類

IProductService 
ProductServiceImpl 
ProductServiceProxy      

UserServiceProxy和ProductServiceProxy這兩個代理類的邏輯都差不多,卻還得寫2次。其實這個還好,如果老闆說,把現有系統的幾十個服務的響應時間都記錄一下吧,你是不是要瘋了?這得寫多少代理類啊?這就得用到我們後續提到的動态代理了。

總結一下,代理模式的實作方式有兩種

  1. 代理類和原始類實作同一個接口,原始類注入到代理類
  2. 代理類繼承原始類

動态代理

黑暗總是暫時的,終究會迎來黎明,在JDK1.3之後引入了一種稱之為動态代理(Dynamic Proxy)的機制。使用該機制,我們可以為指定的接口在系統運作期間動态地生成代理對象,進而幫助我們走出最初使用靜态代理實作AOP的窘境

動态代理的實作有兩種方式

  1. 利用JDK實作動态代理,即調用Proxy.newProxyInstance()方法
  2. 使用CGLIB來實作動态代理

使用JDK實作動态代理,原始類必須實作某個接口,因為它是基于實作同一個接口的方式來實作的。而用CGLIB來實作動态代理,原始類有無實作接口都可以,因為它是基于繼承的方式實作的

JDK動态代理

動态代理的實作主要由一個類和一個接口組成,即java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。

讓我們用動态代理來改造一下上面記錄系統響應時間的功能。雖然要為IUserService和IProductService兩種服務提供代理對象,但因為代理對象中要添加的橫切邏輯是一樣的。是以我們隻需要實作一個InvocationHandler就可以了。代碼如下

public class RequestCostInvocationHandler implements InvocationHandler {

    private Object target;

    public RequestCostInvocationHandler(Object target) {
        this.target = target;
    }

    /** 被代理對象的任何方法被執行時,都會先進入這個方法 */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("request")) {
            long startTime = System.currentTimeMillis();
            // 執行目标對象的方法
            method.invoke(target, args);
            System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
        }
        return null;
    }

    public static void main(String[] args) {

        // 3個參數解釋如下
        // classloader,生成代理類
        // 代理類應該實作的接口
        // 實作InvocationHandler的切面類
        IUserService userService = (IUserService) Proxy.newProxyInstance(
        IUserService.class.getClassLoader(),
        new Class[]{IUserService.class},
        new RequestCostInvocationHandler(new UserServiceImpl()));

        IProductService productService = (IProductService) Proxy.newProxyInstance(
        IProductService.class.getClassLoader(), 
        new Class[]{IProductService.class},
        new RequestCostInvocationHandler(new ProductServiceImpl()));

        // this is userService
        // reques cost :0
        userService.request();

        // this is productService
        // reques cost :0
        productService.request();
    }

}      

生成動态代理也很簡單,調用Proxy.newProxyInstance()方法即可

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)      

三個參數如下:

loader:類加載器

interfaces: 代理類應該實作的接口

h:實作InvocationHandler接口的類,在裡面增加代理邏輯

這個方法以及3個參數在面試中偶爾會被問到,動态代理在各大架構中用的确實很多了

UML圖如下。Spring AOP就是用動态代理來實作的

代理模式的應用

CGLIB動态代理

CGLIB基于位元組碼技術為我們生成子類,不用我們自己去生成。用法如下

public class UserService {

    public void request() {
        System.out.println("welcome sir");
    }
}      
public class RequestCtrlCallback implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object object =  methodProxy.invokeSuper(o, objects);
        System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
        return object;
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new RequestCtrlCallback());

        UserService proxy = (UserService)enhancer.create();
        // welcome sir
        // reques cost :25
        proxy.request();
    }
}      

代理模式和裝飾者模式的差別

參考部落格