深度分析SpringAOP,一文帶你徹底搞懂SpringAOP底層原理!


SpringAOP

我們為什么要使用AOP(面向切面編程)?當我們在現實中完成實際的項目時,我們總是需要在一個“動作”進行前,進行中,或進行后進行一些操作,比如當我們在運行程序時,我們想要進行日志保存,或者在每一個方法調用后輸出一句話,這就表示我們每一次進行一個“動作”都需要進行同樣的操作,這就導致程序員會進行大量的、無用的重復性動作,面對這種情況,AOP應運而生。

AOP概述

AOP,即Aspect Oriented Rrogramming的縮寫,意為:面向切面編程,通過預編譯方式和運行期間動態代理實現程序功能統一維護的一種技術。AOP可以對業務漏極的各個部分進行隔離,從而使得業務邏輯之間得耦合性降低,提高程序得可重用性,同時提高了開發得效率。

AOP和OOP是兩種不同的設計思想。OOP(面向對象編程)針對業務處理過程得實體及其屬性和行為進行抽象封裝,獲得清晰高效得邏輯單元划分。AOP則是針對業務處理過程中得切面進行提取,是面對業務處理過程中的某個步驟或階段,獲得邏輯過程中各部分之間低耦合性得隔離效果。

面向切面編程的好處就是:減少重復,專注業務。它是面向對象編程的一種補充。
個人整理了一些資料,有需要的朋友可以直接點擊領取。

Java基礎知識大全

22本Java架構師核心書籍

從0到1Java學習路線和資料

1000+道2021年最新面試題

核心原理及使用案例

原理:使用動態代理的方式在執行方法前后或出現異常時加入相關的邏輯。

使用:

事務處理:開啟事務,關閉事務,出現異常回滾事務.....
權限判斷:執行方法前,判斷是否具有權限;
日志處理;
......
事務處理:開啟事務,關閉事務,出現異常回滾事務.....
權限判斷:執行方法前,判斷是否具有權限;
日志處理;
......

AOP的基本概念(Spring的專業術語)

0.增強:向各個程序內部注入一些邏輯代碼從而增強原有程序的功能。

1.連接點(JoinPoint):類中可以被增強的方法,這個方法就就被稱為連接點,切記連接點並不是一定會被增強。

2.切入點(Pointcut):類中實際被增強的方法。

3.通知(Advice):指一個切面在特定的連接點要做的事情,簡單來說就是“增強”。可以分為方法執行前通知,方法執行后通知,環繞通知等等。

4.切面(Aspect):把通知添加到切入點的過程就叫切面。

5.目標(Target):代理的目標對象,即要增強的方法所在的類。

6.代理(Proxy):向目標對象應用通知之后創建的代理對象。

SpringAOP實現

很多的框架都對AOP這種編程思想進行了實現。Spring只是其中的一種,可以完成面向切面編程。AspectJ也是一個面向切面的框架,並且實現方式更為簡捷,更為方便,並且支持注解式開發。所以,Spring又將AspectJ對於AOP的實現引入到自己的框架之中。

Spring中使用AOP開發時,通常使用AspectJ的實現方式。其中常用的通知有五種類型:

  • 前置通知:方法執行前執行;
  • 后置通知:方法執行后執行;
  • 環繞通知:前后都執行;
  • 異常通知:出異常時通知;
  • 最終通知:如return后執行。

SpringAOP的使用

導入實現AOP的AspectJ的jar

<!--Spring實現AOP是依靠Aspects框架實現-->
<!--Aspects相關jar-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

基於AspectJ的xml配置實現

所有的配置都在spring.xml文件中進行。

1.創建一個增強功能的類。

import org.aspectj.lang.ProceedingJoinPoint;
//通知(Advice):在連接點要做的事情
public class Aop {

    public void doLog() {
        System.out.println("=====保存日志=====");
    }

    public void commit() {
        System.out.println("=====提交事務=====");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("======方法前通知======");
        try {
            proceedingJoinPoint.proceed();//調用自己的方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("======方法后通知======");
    }

    public void throwable(Throwable throwable) {
        System.out.println("======出異常了======");
        System.out.println(throwable.getMessage());
    }
}

2.將裝有增強功能的類交給交由spring管理

<?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:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--把裝有通知(Advice)的類交給Spring管理-->
    <bean id="aop" class="com.cwd.spring4pro.demo1.aop.Aop"></bean>
    
    <!--在這里進行織入,即切面(Aspect):將通知添加到切入點-->

</beans>

3.配置切面(Aspect)

先准備一個被增強的類,即目標(Target)

import org.springframework.stereotype.Component;

//目標(Target):代理的目標對象,即要增強的類
@Component(value = "target")
public class Target {
    /*
    連接點(Joinpoint),可以被增強的方法
    切入點(pointcut),實際被增強的方法,被增強了
    */
    public void pointCut() {
        System.out.println("這是一個保存的操作!!!");
        return;
    }
}

將通知添加到切入點。

<!--織入-->
    <aop:config>
        <!--
        配置切入點 
        execution表達式 前*表示返回值 saveUser(..)表示要增強的方法 ..表示參數
        -->
        
        <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>

        <!--配置通知 ref中引用的是通知類的id-->
        <aop:aspect ref="aop">
        
           <!--前置通知-->
           <aop:before method="doLog" pointcut-ref="pointCut"/>

        </aop:aspect>
    </aop:config>

五種通知類型配置

1.前置通知

<!--織入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--前置通知-->
        <aop:before method="doLog" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

2.后置通知

<!--織入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--后置通知-->
        <aop:after method="commit" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

3.環繞通知

<!--織入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--環繞通知-->
        <aop:around method="around" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

4.異常通知

public void pointCut() {
    System.out.println("這是一個保存的操作!!!");
    int a = 10 / 0;
    return;
}

<!--織入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--異常通知-->
        <aop:after-throwing method="throwable" pointcut-ref="pointCut" throwing="throwable"/>
    </aop:aspect>
</aop:config>

5.最終通知

<!--織入-->
<aop:config>
    <aop:pointcut id="pointCut" expression="execution(* com.cwd.spring4pro.demo.Target.pointCut(..))"/>
    <aop:aspect ref="aop">
        <!--最終通知-->
        <aop:after-returning method="commit" pointcut-ref="pointCut"/>
    </aop:aspect>
</aop:config>

最終通知一般在return之后執行。

注解實現

開啟aop注解掃描

<!--開啟注解標簽-->
<aop:aspectj-autoproxy/>

在通知類中進行配置,如下所示:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component//將這個類交給Spring管理
@Aspect//標注這個類時裝有通知的類
public class Aop {

    @Before("execution(* com.cwd.spring4pro.demo.Target.pointCut(..))")
    public void doLog() {
        System.out.println("=====保存日志=====");
    }

    @After("execution(* com.cwd.spring4pro.demo.Target.pointCut(..))")
    public void commit() {
        System.out.println("=====提交事務=====");
    }

    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("======方法前通知======");
        try {
            proceedingJoinPoint.proceed();//調用自己的方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("======方法后通知======");
    }


    @AfterThrowing(value = "execution(* com.cwd.spring4pro.demo.Target.pointCut(..))",throwing = "throwable")
    public void throwable(Throwable throwable) {
        System.out.println("======出異常了======");
        System.out.println(throwable.getMessage());
    }

    @AfterReturning("execution(* com.cwd.spring4pro.demo.Target.pointCut(..))")
    public  void returnAfter() {
        System.out.println("======return后=====");
    }
}

最后

都看到這里了,覺得文章對你有幫助的話記得點個贊,感謝支持!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM