代理設計模式

bisnow_發表於2019-04-24

 

靜態代理        一個代理類       只能代理一個類

實現介面,傳入被代理類,方法攔截

 

動態代理

jdk動態代理(反射虛擬生成代理物件)  ,需要介面

jdk動態代理,必須是面向介面,目標業務類必須實現介面

什麼是動態代理

1.代理物件,不需要實現介面

2.代理物件的生成,是利用JDK的API,動態的在記憶體中構建代理物件(需要我們指定建立代理物件/目標物件實現的介面的型別)

3.動態代理也叫做:JDK代理,介面代理

JDK動態代理

1)原理:是根據類載入器和介面建立代理類(此代理類是介面的實現類,所以必須使用介面 面向介面生成代理,位於java.lang.reflect包下)

2)實現方式:

1. 通過實現InvocationHandler介面建立自己的呼叫處理器 IvocationHandler handler = new InvocationHandlerImpl(…);

2. 通過為Proxy類指定ClassLoader物件和一組interface建立動態代理類Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});

3. 通過反射機制獲取動態代理類的建構函式,其引數型別是呼叫處理器介面型別Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

4. 通過建構函式建立代理類例項,此時需將呼叫處理器物件作為引數被傳入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

// 每次生成動態代理類物件時,實現了InvocationHandler介面的呼叫處理器物件 
public class InvocationHandlerImpl implements InvocationHandler {
    private Object target;// 這其實業務實現類物件,用來呼叫具體的業務方法
    // 通過建構函式傳入目標物件
    public InvocationHandlerImpl(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("呼叫開始處理");
        result = method.invoke(target, args);
        System.out.println("呼叫結束處理");
        return result;
    }

    public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
            IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // 被代理物件
        IUserDao userDao = new UserDao();
        InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
        ClassLoader loader = userDao.getClass().getClassLoader();
        Class<?>[] interfaces = userDao.getClass().getInterfaces();
        // 主要裝載器、一組介面及呼叫處理動態代理例項
        IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
        newProxyInstance.save();
    }

}

 

cglib(基於asm位元組碼技術虛擬生成代理類)  只需要字類實現

 

CGLIB動態代理

原理:利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。 

什麼是CGLIB動態代理

使用cglib[Code Generation Library]實現動態代理,並不要求委託類必須實現介面,底層採用asm位元組碼生成框架生成代理類的位元組碼

CGLIB動態代理相關程式碼

public class CglibProxy implements MethodInterceptor {
    private Object targetObject;
    // 這裡的目標型別為Object,則可以接受任意一種引數作為被代理類,實現了動態代理
    public Object getInstance(Object target) {
        // 設定需要建立子類的類
        this.targetObject = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("開啟事物");
        Object result = proxy.invoke(targetObject, args);
        System.out.println("關閉事物");
        // 返回代理物件
        return result;
    }
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
        userDao.save();
    }
}

CGLIB動態代理與JDK動態區別

java動態代理是利用反射機制生成一個實現代理介面的匿名類,在呼叫具體方法前呼叫InvokeHandler來處理。

而cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。

Spring中。

1、如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP

2、如果目標物件實現了介面,可以強制使用CGLIB實現AOP

3、如果目標物件沒有實現了介面,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

JDK動態代理只能對實現了介面的類生成代理,而不能針對類 。
CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。
因為是繼承,所以該類或方法最好不要宣告成final ,final可以阻止繼承和多型。

 

相關文章