Spring AOP 代理總結:靜態代理和動態代理

Spring AOP 代理總結:靜態代理和動態代理

原文地址:http://jachindo.top:8090/archives/springaop%E4%BB%A3%E7%90%86%E6%80%BB%E7%BB%93%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86%E5%92%8C%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86

參考:https://mp.weixin.qq.com/s/dLOn23waK4gv-Rp9dC0Kfg

0、作用

***將業務邏輯和系統處理的代碼(關閉連接、事務管理、操作日誌記錄)解耦。***


1、靜態代理

自己手動創建Proxy類,並將RealSubject類注入。

image-20200201222845606


2、動態代理 - JDK、Cglib

運行時增強:動態代理就是說AOP框架不會去修改字節碼,而是在內存中臨時爲方法生成一個AOP對象,這個AOP對象包含了目標對象的全部方法,並且在特定的切點做了增強處理,並回調原對象的方法。

動態代理主要包含:

JDK動態代理:利用反射接收被代理的類,從而生成代理類Proxy類。被代理的類必須實現一個接口

Cglib動態代理:生成被代理類的子類作爲代理類。(所以==被代理類不能被final修飾==)

spring aop會根據被代理類是否實現了某個接口來自動選擇動態代理方式


1)JDK動態代理

必須實現一個接口(必須有接口和實現類)

目標類接口:

package a_proxy.a_jdk;
public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}

實現類:

package a_proxy.a_jdk;
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("a_proxy.a_jdk addUser()");
    }

    @Override
    public void updateUser() {
        System.out.println("a_proxy.a_jdk updateUser()");

    }

    @Override
    public void deleteUser() {
        System.out.println("a_proxy.a_jdk deleteUser()");

    }
}

切面類:

package a_proxy.b_cglib;
public class MyAspect {
    public void before() {
        System.out.println("雞首");
    }

    public void after() {
        System.out.println("牛後");
    }
}

工廠類(生成代理類):

package a_proxy.a_jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyBeanFactory {

    public static UserService createService() {
        //1目標類
        UserService userService = new UserServiceImpl();
        //2切面類
        MyAspect myAspect = new MyAspect();
        /*
        *3代理類:將目標類(切入點)和切面類(通知)結合--->切面
        *
        * Proxy.newProxyInstance
        *   參數1:loader:類加載器,動態代理類運行時創建,任何類都需要類加載器將其加載到內存。
        *       一般情況:採用當前類.class.getClassLoader();
        *                目標類實例.getClass().getClassLoader();
        *   參數2:interfaces:代理類要實現的所有接口
        *       方式1:目標實例.getClass().getInterfaces() ,注意:只能獲得自己的接口,不能獲得父元素接口
        *       方式2:new Class[]{UserService.class}
        *   參數3:InvocationHandler:處理類,是一個接口,必須進行實現類,一般採用匿名內部方式.表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個 InvocationHandler 對象上
        *       提供invoke方法,代理類的每一個方法執行時,都將調用一次invoke
        *           參數1:Object proxy:代理對象
        *           參數2:Method method:代理對象當前執行的方法的描述對象(反射)
        *                   執行方法名:method.getName()
        *                   執行方法:method.invoke(對象,實際參數)
        *           參數3:Object[] args:方法的實際參數
        * */
        UserService proxyService = (UserService) Proxy.newProxyInstance(
                MyBeanFactory.class.getClassLoader(),
                userService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        //前執行
                        myAspect.before();

                        //執行目標類方法
                        Object obj = method.invoke(userService, args);

                        //後執行
                        myAspect.after();
                        return obj;
                    }
                }
        );

        return proxyService;
    }

}

Proxy.newProxyInstance內部通過反射創建代理對象(實現了某接口)。

每一個動態代理類都必須要實現 InvocationHandler 這個接口,並且每個代理類的實例都關聯到了一個 Handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由 InvocationHandler 這個接口的 invoke 方法來進行調用。

測試類:

package a_proxy.a_jdk;

import org.junit.Test;

public class TestJDK {
    @Test
    public void demo1() {
        UserService userService = MyBeanFactory.createService();
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

結果:

image-20190528111901900

注意:

通過 Proxy.newProxyInstance 創建的代理對象是在 jvm 運行時動態生成的一個對象,它並不是我們的 InvocationHandler 類型,也不是我們定義的那組接口的類型,而是在運行是動態生成的一個對象,並且命名方式都是這樣的形式,以$開頭,proxy 爲中,最後一個數字表示對象的標號,如com.sun.proxy.$Proxy0


2)Cglib動態代理(字節碼增強)

不需要接口,只有實現類

!!!在運行時創建目標類的子類(代理類),從而對目標類進行增強!!!

工廠類:

package a_proxy.b_cglib;
public class MyBeanFactory {

    public static UserServiceImpl createService() {
        //1目標類
        UserServiceImpl userService = new UserServiceImpl();
        //2切面類
        MyAspect myAspect = new MyAspect();

        /*
        * 3 代理類,採用cglib,底層創建目標的子類
        * */
        //3.1核心類
        Enhancer enhancer = new Enhancer();
        //3.2確定父類
        enhancer.setSuperclass(userService.getClass());
        //3.3設置回調 MethodInterceptor接口等效於jdk中的InvocationHandler
        /*
        * intercept()等效於jdk的invoke()
        *       參數1,2,3與invoke的一樣
        *       參數4:methodProxy方法的代理,通常不用
        * */
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //前
                myAspect.before();
                //執行目標類的方法
                Object obj = method.invoke(userService, objects);
                //執行代理類的父類,即目標類(目標類和代理類是父子關係)
                //methodProxy.invokeSuper(o, objects);

                //後
                myAspect.after();
                return obj;
            }
        });

        //3.4創建代理
        UserServiceImpl proxyService = (UserServiceImpl)enhancer.create();
        return proxyService;
    }

}

測試類:

package a_proxy.b_cglib;

import org.junit.Test;

public class TestCglib {
    @Test
    public void demo1() {
        UserServiceImpl userService = MyBeanFactory.createService();
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

結果與jdk動態代理相同

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章