萬字長文!深入底層帶你理解Spring之AOP,看完還不會真的算我輸!

什么是AOP

AOP (Aspect Oriented Programming)意為:面向切面編程,通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術(shù)。AOP是OOP的延續(xù),是軟件開發(fā)中的一個熱點,也是Spring框架中的一個重要內(nèi)容,是函數(shù)式編程的一種衍生范型。利用AOP可以對業(yè)務(wù)邏輯的各個部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。


個人整理了一些資料,有需要的朋友可以直接點擊領(lǐng)取。

Java基礎(chǔ)知識大全

22本Java架構(gòu)師核心書籍

從0到1Java學(xué)習(xí)路線和資料

1000+道2021年最新面試題

AOP在Spring中的作用

  • 提供聲明式事務(wù);允許用戶自定義切面

  • 橫切關(guān)注點:跨越應(yīng)用程序多個模塊的方法或功能。即是,與我們業(yè)務(wù)邏輯無關(guān)的,但是我們需要關(guān)注的部分,就是橫切關(guān)注點。如日志,安全,緩存,事務(wù)等等…

  • 切面(ASPECT)︰橫切關(guān)注點被模塊化的特殊對象。即,它是一個類。

  • 通知(Advice):切面必須要完成的工作。即,它是類中的一個方法。

  • 目標(biāo)(Target)︰被通知對象。

  • 代理(Proxy)︰向目標(biāo)對象應(yīng)用通知之后創(chuàng)建的對象。

  • 切入點(PointCut)︰切面通知執(zhí)行的“地點"的定義。

  • 連接點(JointPoint) :與切入點匹配的執(zhí)行點。


  • SpringAOP中,通過Advice定義橫切邏輯,Spring中支持5種類型的Advice:

即Aop在不改變原有代碼的情況下,去增加新的功能.


使用Spring實現(xiàn)AOP

需要的依賴

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

方式一:使用原生的Spring API接口

UserService.java

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("【Debug】執(zhí)行了add方法");
        System.out.println("增加了一個用戶");
    }

    public void delete() {
        System.out.println("【Debug】執(zhí)行了delete方法");
        System.out.println("刪除了一個用戶");
    }

    public void update() {
        System.out.println("【Debug】執(zhí)行了update方法");
        System.out.println("修改了一個用戶");
    }

    public void query() {
        System.out.println("【Debug】執(zhí)行了query方法");
        System.out.println("查詢了一個用戶");
    }
}

Log.java

public class Log implements MethodBeforeAdvice {
    /**
     *
     * @param method 要執(zhí)行目標(biāo)對象方法
     * @param args   參數(shù)
     * @param target 目標(biāo)對象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"類中的"+method.getName()+"方法執(zhí)行了");
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="userService" class="com.test.service.impl.UserServiceImpl"/>
    <bean id="log" class="com.test.log.Log"/>
    <bean id="afterLog" class="com.test.log.AfterLog"/>
    <!--方式一:使用原生的Spring API接口-->
    <!--配置aop需要導(dǎo)入約束-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.test.service.impl.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

AfterLog.java
這個地方必須是寫接口,因為代理的對象是接口;

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.getName()+"方法執(zhí)行了,值為"+returnValue);
    }
}

方式二:自定義來實現(xiàn)AOP

UserService.java

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("【Debug】執(zhí)行了add方法");
        System.out.println("增加了一個用戶");
    }

    public void delete() {
        System.out.println("【Debug】執(zhí)行了delete方法");
        System.out.println("刪除了一個用戶");
    }

    public void update() {
        System.out.println("【Debug】執(zhí)行了update方法");
        System.out.println("修改了一個用戶");
    }

    public void query() {
        System.out.println("【Debug】執(zhí)行了query方法");
        System.out.println("查詢了一個用戶");
    }
}

DiyPointcut.java

public class DiyPointcut {
    public void before() {
        System.out.println("方法前執(zhí)行!");
    }
    public void after() {
        System.out.println("方法后執(zhí)行!");
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="diy" class="com.test.diy.DiyPointcut"/>
    <bean id="userService" class="com.test.service.impl.UserServiceImpl"/>
    <!--方式二:使用自定義實現(xiàn)AOP-->
    <aop:config>
        <aop:aspect ref="diy">
            <aop:pointcut id="diyPointcut" expression="execution(* com.test.service.impl.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="diyPointcut"/>
            <aop:after method="after" pointcut-ref="diyPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

AfterLog.java
這個地方必須是寫接口,因為代理的對象是接口;

public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method.getName()+"方法執(zhí)行了,值為"+returnValue);
    }
}

注解實現(xiàn)AOP

UserService.java

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

UserServiceImpl.java

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("【Debug】執(zhí)行了add方法");
        System.out.println("增加了一個用戶");
    }

    public void delete() {
        System.out.println("【Debug】執(zhí)行了delete方法");
        System.out.println("刪除了一個用戶");
    }

    public void update() {
        System.out.println("【Debug】執(zhí)行了update方法");
        System.out.println("修改了一個用戶");
    }

    public void query() {
        System.out.println("【Debug】執(zhí)行了query方法");
        System.out.println("查詢了一個用戶");
    }
}

AnnotationPointcut.java

@Aspect
public class AnnotationPointcut {
    @Before("execution(* com.test.service.impl.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("方法前執(zhí)行!");
    }
    @After("execution(* com.test.service.impl.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("方法后執(zhí)行!");
    }
    @Around("execution(* com.test.service.impl.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("環(huán)繞前");
        System.out.println(jp.getSignature());
        Object proceed = jp.proceed();
        System.out.println(proceed);
        System.out.println("環(huán)繞后");
    }
}

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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--方式三:使用注解實現(xiàn)AOP-->
    <bean id="annotationPointcut" class="com.test.annotation.AnnotationPointcut"/>
    <!--開啟aop注解支持, 動態(tài)代理:jdk(默認(rèn)) proxy-target-class="false" ,cgib proxy-target-class="true"-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
    <bean id="userService" class="com.test.service.impl.UserServiceImpl"/>
</beans>

說明:
開啟aop注解支持

<aop:aspectj-autoproxy/>

動態(tài)代理:jdk(默認(rèn)) proxy-target-class=“false” ,cgib proxy-target-class=“true”
測試:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("aplicationContext.xml");
        //動態(tài)代理的是接口,需要注意!
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

①Spring4.0
    正常情況:環(huán)繞前置-------->@Before-------->目標(biāo)方法執(zhí)行-------->環(huán)繞返回-------->環(huán)繞最終-------->@After-------->@AfterReturning
    異常情況:環(huán)繞前置-------->@Before-------->目標(biāo)方法執(zhí)行-------->環(huán)繞異常-------->環(huán)繞最終-------->@After-------->@AfterThrowing
②Spring5.28
    正常情況:環(huán)繞前置-------->@Before-------->目標(biāo)方法執(zhí)行-------->@AfterReturning-------->@After-------->環(huán)繞返回-------->環(huán)繞最終
    異常情況:環(huán)繞前置-------->@Before-------->目標(biāo)方法執(zhí)行-------->@AfterThrowing-------->@After-------->環(huán)繞異常-------->環(huán)繞最終

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容