【Spring AOP】AOP 底層實現原理 —— 動態代理類的建立(JDK、CGlib)、工廠如何加工原始物件

kyin8040發表於2020-09-27

AOP 程式設計

AOP 概念

POP (Producer Oriented Programing)

  • 程式導向(方法、函式)程式設計 —— C
  • 過程為基本單位的程式開發,通過過程間的彼此協同,相互呼叫,完成程式的構建。

OOP (Object Oritened Programing)

  • 物件導向程式設計 —— Java
  • 物件為基本單位的程式開發,通過物件間的彼此協同,相互呼叫,完成程式的構建。

AOP (Aspect Oriented Programing)

  • 面向切面程式設計 = Spring動態代理開發
  • 切面為基本單位的程式開發,通過切面間的彼此協同,相互呼叫,完成程式的構建。
  • 切面 = 切入點 + 額外功能

AOP 的概念:

  • 本質就是 Spring 的動態代理開發,通過代理類為原始類增加額外功能。
  • 好處:利於原始類的維護
  • 注意:AOP 程式設計不可能取代 OOP,AOP 是 OOP 程式設計的補充。

AOP 程式設計的開發步驟

  1. 原始物件
  2. 額外功能 (MethodInterceptor)
  3. 切入點
  4. 組裝切面 (額外功能+切入點)

詳情可參見之前的部落格:Spring 動態代理開發詳解

切面的名詞解釋

切面 = 切入點 + 額外功能
幾何學:面 = 點 + 相同的性質
在這裡插入圖片描述

AOP 的底層實現原理

核心問題:

  1. AOP 如何建立動態代理類?
    動態位元組碼技術
  2. Spring 工廠如何加工建立代理物件?
    通過原始物件的 id 值,獲得的是代理物件

動態代理類的建立

JDK 的動態代理(原理 + 編碼)

  • Proxy.newPorxyInstance 方法引數詳解
    在這裡插入圖片描述
    在這裡插入圖片描述
  • 編碼
public class TestJDKProxy {
    /**
     1. 借⽤類載入器  TestJDKProxy 或 UserServiceImpl 都可以
     2. JDK8.x 前必須加 final
     final UserService userService = new UserServiceImpl();
     */
    public static void main(String[] args) {
        // 1. 建立原始物件
        UserService userService = new UserServiceImpl();

        // 2. JDK 動態代理
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---- proxy log ----");
                // 原始方法執行
                Object ret = method.invoke(userService, args);
                return ret;
            }
        };
        UserService userServiceProxy = (UserService) Proxy.
                newProxyInstance(TestJDKProxy.class.getClassLoader(),
                                userService.getClass().getInterfaces(),
                                handler);
        userServiceProxy.login("zhenyu", "123456");

        userServiceProxy.register(new User());
    }
}

CGlib 的動態代理

CGlib 建立動態代理的原理:通過父子繼承關係建立代理物件,原始類作為父類,代理類作為子類,這樣既可以保證 2 者方法⼀致,同時在代理類中可以提供新的實現(額外功能+原始方法)。
在這裡插入圖片描述

  • CGlib 編碼
public class TestCglib {
    public static void main(String[] args) {
        // 1. 建立原始物件
        UserService userService = new UserService();

        /*
         2. 通過 cglib 方式建立動態代理物件
         對比 jdk 動態代理 ---> Proxy.newProxyInstance(classLoader, interface, invocationHandler);

         Enhancer.setClassLoader()
         Enhancer.setSuperClass()
         Enhancer.setCallBack() ---> MethodInterceptor(cglib)
         Enhancer.createProxy() ---> 建立代理物件
         */
        Enhancer enhancer = new Enhancer();

        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("--- cglib log ----");
                Object ret = method.invoke(userService, args); // 執行原始方法
                return ret;
            }
        };

        enhancer.setCallback(interceptor);
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("zhenyu", "123456");
        userServiceProxy.register(new User());
    }
}

總結

  1. JDK 動態代理
    Proxy.newProxyInstance:通過介面建立代理的實現類
  2. Cglib 動態代理
    Enhancer:通過繼承⽗類建立的代理類

Spring 工廠如何加工原始物件

  • 思路分析:主要通過 BeanPostProcessor 將原始物件加工為代理物件

在這裡插入圖片描述

  • 編碼
public class ProxyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("--- new log ---");
                Object ret = method.invoke(bean, args);
                return ret;
            }
        };
        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), handler);
    }
}
<bean id="userService" class="com.yusael.factory.UserServiceImpl"/>
<!--1. 實現 BeanPostProcessor 進行加工-->
<!--2. 配置檔案中對 BeanPostProcessor 進行配置-->
<bean id="proxyBeanPostProcessor" class="com.yusael.factory.ProxyBeanPostProcessor"/>

相關文章