談談你對Spring AOP理解【面試】

前言

面試中經常會被問到Spring AOP的原理,相信大家都是條件反射的想到JDK動態代理和CGLib動態代理,本文將介紹這兩個代理的區別及實現一個Spring AOP實例。

1、爲什麼會有AOP

AOP的全稱是Aspect Oriented Programming,翻譯成中文是面向切面編程。當我們在開發日誌、權限驗證、事務等功能時,如果不使用AOP,只能在每個對象裏引用公共行爲,這樣做不便於維護,而且有大量重複代碼,AOP的出現就是解決這些問題。

2、Spring AOP的原理

Spring Aop中使用的代理有兩種方式,一種是Jdk的動態代理,另一種是基於CGLIB實現的代理,當我們的bean對象實現了某一接口後,Spring默認將採用Jdk動態代理,當我們的bean對象沒有實現接口時,默認將採用CGLIB代理,Spring也支持我們在bean對象實現了接口時也強制的使用CGLIB代理。Spring的Bean容器在初始化bean對象的時候就會判斷對應的bean是否需要進行切面編程,即是否需要對其進行代理,如果需要,則初始化的時候就會把它初始化爲一個代理對象。

3、Jdk動態代理

  • JDK動態代理基於攔截器和反射來實現。
  • JDK動態代理是不需要第三方庫支持的,只需要JDK環境就可以進行代理。

使用的條件:

  1. 必須實現InvocationHandler接口;
  2. 使用Proxy.newProxyInstance產生代理對象;
  3. 被代理的對象必須要實現接口;

4、CGLib動態代理

  • CGLib包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。
  • CGLib是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,並覆蓋其中方法實現增強。

使用條件

  1. CGLib是針對類實現代理,採用的是繼承,類或方法不要聲明成final;
  2. 必須實現MethodInterceptor接口;
  3. 需要使用CGLib包;

5、spring aop 實踐

  1. 編寫測試類
//user接口
public interface UserDao {
    //登錄
    void login();
}
//user實現類,測試jdk代理
public class MyUserDao implements UserDao {
    @Override
    public void login() {
        System.out.println("接口用戶正常登錄");
    }
}
//無接口實現,測試cglib
public class CglibDao {

    public void cglibLogin(){
        System.out.println("cglib用戶正常登錄");
    }
}
//自定義切面類
public class MyAspect {

    public void begin() {
        System.out.println("[前置通知]  開啓事務..");
    }
    public void commit() {
        System.out.println("[後置通知] 提交事務..");
    }
    public void after() {
        System.out.println("[返回後通知]");
    }
    public void afterThrowing() {
        System.out.println("[異常通知]");
    }
    public void arroud(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[環繞前:]");
        pjp.proceed(); // 執行目標方法
        System.out.println("[環繞後:]");
    }
}
//測試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class AopTest {
    @Resource
    UserDao userDao;
    @Resource
    CglibDao cglibDao;
    @Test
    public void testUser(){
        userDao.login();
    }
    @Test
    public void testCglib(){
        cglibDao.cglibLogin();
    }
}

配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- dao實例加入容器 -->
    <bean id="myUserDao" class="aop.MyUserDao"></bean>

    <!-- dao實例加入容器 -->
    <bean id="cglibDao" class="aop.CglibDao"></bean>

    <!-- 實例化切面類 -->
    <bean id="myAspect" class="aop.MyAspect"></bean>

    <!-- Aop相關配置 -->
    <aop:config>
        <!-- 切入點表達式定義 -->
        <aop:pointcut expression="execution(* aop.*Dao.*(..))" id="transactionPointcut"/>
        <!-- 切面配置 -->
        <aop:aspect ref="myAspect">
            <!-- 【環繞通知】 -->
            <aop:around method="arroud" pointcut-ref="transactionPointcut"/>
            <!-- 【前置通知】 在目標方法之前執行 -->
            <aop:before method="begin" pointcut-ref="transactionPointcut" />
            <!-- 【後置通知】 -->
            <aop:after method="commit" pointcut-ref="transactionPointcut"/>
            <!-- 【返回後通知】 -->
            <aop:after-returning method="after" pointcut-ref="transactionPointcut"/>
            <!-- 異常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

輸出結果:
在這裏插入圖片描述
在這裏插入圖片描述

結束語

本篇詳細的介紹了Jdk動態代理和Cglib代理,以及手寫了一個spring aop的實例,熟悉spring aop的配置。

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