Spring框架學習筆記3-----AspectJ

目錄

1、介紹

2、切入點表達式【掌握】 

2.1、execution()

2.2、within(瞭解)

2.3、this(瞭解)

2.4、target(瞭解)

2.5、args(瞭解)

2.6、bean(id)(瞭解)

3、AspectJ通知類型

3.1、前置通知(before)

3.1.1、方法聲明

3.1.2、配置文件

3.2、後置通知(afterReturning)

3.2.1、方法聲明

3.2.2、配置文件

3.3、環繞通知(around)

3.3.1、方法聲明

3.3.2、配置文件

3.4、拋出異常通知

3.4.1、方法聲明

3.4.2、配置文件

3.5、最終通知

3.5.1、方法聲明

3.5.2、配置文件

4、導入jar包

5、基於xml

5.1、目標類:接口+實現

5.2、切面類:編寫多個通知,採用aspectj 通知名稱任意(方法名任意)

5.3、測試類

5.4、aop編程:將通知應用到目標類(增強)(配置文件)和測試結果

5.4.1、前置通知測試

5.4.2、後置通知測試

5.4.3、環繞通知測試

5.4.4、拋出異常通知

5.4.5、最終通知測試

6、基於註解

6.1、aop註解總結

6.2、配置文件

6.3、切面類


 


1、介紹

  • AspectJ是一個基於Java語言的AOP框架
  • Spring2.0以後新增加了對AspectJ切入點表達式支持
  • @Aspect是AspectJ1.5新增功能,通過JDK5註解技術,允許直接在Bean類中定義切面
  • 新版本Spring框架,建議使用AspectJ方式來開發AOP
  • 主要用途:自定義開發

2、切入點表達式【掌握】 

2.1、execution()

用於描述方法

語法:execution(修飾符 返回值 包.方法(參數) throws異常)

  • 修飾符:一般省略
  1. public        公共方法
  2. *                任意
  • 返回值
  1. void           沒有返回值
  2. String        返回字符串 
  3. *                任意
  • 包  [可以省略 ]
  1. com.itheima.crm                            固定包
  2. com.itheima.crm.*.service             crm包下面子包任意(例如:com.itheima.crm.staff.service)
  3. com.itheima.crm..                          crm包下面的所有子包(含自己的)
  4. com.itheima.*.service..                  crm包下面任意的子包,固定目錄service,service目錄下的任意包
  • 類  [可以省略]
  1. UserServiceImpl                  指定類
  2. *Impl                                    以Impl結尾的所有類
  3. User*                                   以User開頭的所有類
  4. *                                           任意
  • 方法  [不能省略]
  1. addUser                               指定方法
  2. add*                                     以add開頭
  3. *User                                   以User結尾
  4. *                                            任意
  • (參數)
  1. ()                  無參
  2. (int)              一個整型
  3. (int,int)      兩個整型
  4. (..)               參數任意
  • throws  [可以省略] 一般情況下不寫

綜合案例:
1、execution(*com.itheima.crm.*.service..*.*(..))

返回值任意,com.itheima.crm下的子包任意,固定目錄service下的任意子包,任意實現類,任意方法名,任意參數

2、<aop:pointcut expression="execution(省略)" || execution(省略) id = "myPointCut"/>

匹配任意一個表達式都可以

2.2、within(瞭解)

匹配包或者子包中的方法

within(com.itheima.aop..*)

2.3、this(瞭解)

匹配實現接口的代理對象中的方法

this(com.itheima.aop.user.UserDAO)

2.4、target(瞭解)

匹配實現接口的目標對象中的方法

target(com.itheima.aop.user.UserDAO)

2.5、args(瞭解)

匹配參數格式符合標準的方法

args(int,int)

2.6、bean(id)(瞭解)

對指定的bean中的所有方法

bean("UserServiceId")

3、AspectJ通知類型

  • aop聯盟定義的通知類型,具有特定的接口,必須實現,從而確定方法名稱
  • aspectj通知類型,只定義類型名稱,以及方法格式
  • 個數:6種,知道5種,掌握一種

3.1、前置通知(before)

  • 在方法執行前執行,如果通知拋出異常,通知就無法執行
  • 應用:各種校驗

3.1.1、方法聲明

import org.aspectj.lang.JoinPoint;
public void myBefore(JoinPoint joinPoint)
  • JoinPoint:用於描述連接點(目標方法),獲得當前目標方法的方法名等信息

3.1.2、配置文件

在<aop:config>標籤中配置:

<aop:before method="" pointcut-ref=""></aop:before>
  • method:通知方法名
  • pointcut-ref:切入點的引用,可以與其他通知共享切入點

3.2、後置通知(afterReturning)

  • 在方法正常返回後執行,如果方法中拋出異常,通知無法執行。
  • 可以獲得方法的返回值

3.2.1、方法聲明

import org.aspectj.lang.JoinPoint;
public void myAfterReturning(JoinPoint joinPoint,Object obj)
  • JoinPoint:用於描述連接點(目標方法)
  • Object obj:目標方法的返回值

3.2.2、配置文件

<aop:after-returning method="" pointcut-ref="" returning=""></aop:after-returning>
  • method:聲明的後置方法名
  • pointcut-ref:切入點引用
  • returning:聲明的後置方法中返回值名,即returning = "obj"

3.3、環繞通知(around)

  • 在目標方法執行前後分別執行,可以阻止目標方法的執行,可以獲得目標方法的返回值。
  • 必須手動執行目標方法
  • 必須拋出

3.3.1、方法聲明

import org.aspectj.lang.ProceedingJoinPoint;
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
  • ProceedingJoinPoint :用來描述目標方法
  • 返回值:Object,目標方法的返回值

3.3.2、配置文件

<aop:around method="" pointcut-ref=""></aop:around>
  • method:通知方法名
  • pointcut-ref:切入點的引用

3.4、拋出異常通知

  • 在目標方法拋出異常後執行,如果方法沒有拋出異常,該通知就不會執行

3.4.1、方法聲明

import org.aspectj.lang.JoinPoint;
public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable)
  • Throwable throwable:捕獲的異常
  • JoinPoint joinPoint:描述目標方法

3.4.2、配置文件

<aop:after-throwing method="" pointcut-ref="" throwing=""></aop:after-throwing>
  • method:通知方法名
  • pointcut-ref:切入點的引用
  • throwing:與通知方法中的參數異常名相同,即爲throwable

3.5、最終通知

  • 方法執行完畢後執行,無論方法中是否出現異常,最終都會執行

3.5.1、方法聲明

public void myAfter()

一般情況下沒有參數

3.5.2、配置文件

<aop:after method="" pointcut-ref=""></aop:after>

4、導入jar包

  • 四個jar包:aop聯盟、spring aop實現、aspect規範、spring aspect的實現

5、基於xml

5.1、目標類:接口+實現

public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("add a user");
    }
    @Override
    public String updateUser() {
        System.out.println("update a user");
        return "測試後置方法獲得該目標方法的返回值";
    }
    @Override
    public void deleteUser() {
        System.out.println("delete a user");
    }
}

5.2、切面類:編寫多個通知,採用aspectj 通知名稱任意(方法名任意)

public class MyAspect {
    public void myBefore(JoinPoint joinPoint){
        System.out.println("前置通知:" + joinPoint.getSignature().getName());
    }
    public void myAfterReturning(JoinPoint joinPoint,Object ret){
        System.out.println("後置通知:" + joinPoint.getSignature().getName() + ",--->" + ret);
    }
    //環繞通知的參數必須是JoinPoint的子接口,必須要進行拋出異常
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("********************");
        System.out.println("前");
        //手動執行目標方法
        Object obj = proceedingJoinPoint.proceed();//這個返回值就是目標方法的返回值
        System.out.println("後:--->" + obj);
        return obj;
    }
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("拋出異常通知:" + e.getMessage());
    }
    public void myAfter(){
        System.out.println("最終通知");
    }
}

5.3、測試類

public class TestAspectXml {
    @Test
    public void demo(){
        String xmlPath = "test_d_aspect_a_xml/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        UserService userService = (UserService) applicationContext.getBean("userServiceId");
        userService.addUser();
        userService.updateUser();
        userService.deleteUser();
    }
}

5.4、aop編程:將通知應用到目標類(增強)(配置文件)和測試結果

5.4.1、前置通知測試

<?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"> 

    <!-- 1、目標類 -->
    <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean>
    <!-- 2、切面類 (通知)-->
    <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean>
    <!-- 3、aop編程
    <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法)
        ref:切面類的引用
    <aop:pointcut:聲明一個切入點表達式
        expression:切入點表達式
        id:名稱,用於其他通知引用
    -->
    <aop:config>
        <aop:aspect ref = "myAspectId">
            <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/>
            <!-- 3.1 前置通知
                <aop:before method="" pointcut="" pointcut-ref=""/>
                    method:通知,就是方法名
                    pointcut:切入點表達式,此表達式只能當前通知使用
                    pointcut-ref:切入點的引用,可以與其他通知共享切入點
                pointcut和pointcut-ref二選一即可
                通知方法格式:public void myBefore(JoinPoint joinPoint)
                    參數1:org.aspectj.lang.JoinPoint:用於去描述連接點(目標方法),獲得當前目標方法的方法名字
            -->
            <aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

測試結果:

5.4.2、後置通知測試

配置文件:

<?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"> 

    <!-- 1、目標類 -->
    <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean>
    <!-- 2、切面類 (通知)-->
    <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean>
    <!-- 3、aop編程
    <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法)
        ref:切面類的引用
    <aop:pointcut:聲明一個切入點表達式
        expression:切入點表達式
        id:名稱,用於其他通知引用
    -->
    <aop:config>
        <aop:aspect ref = "myAspectId">
            <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/>

            <!--3.2 後置通知
            <aop:after-returning method="" pointcut-ref="" returning=""></aop:after-returning>
                returning:通知方法第二個參數的名稱
            通知方法的格式:public void myAfterReturning(JoinPoint joinPoint,Object ret)
                參數1:連接點描述
                參數2:Object,參數名字是由returing配置的
            -->
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"></aop:after-returning>
        </aop:aspect>
    </aop:config>
</beans>

測試結果:

5.4.3、環繞通知測試

配置文件:

<?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"> 

    <!-- 1、目標類 -->
    <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean>
    <!-- 2、切面類 (通知)-->
    <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean>
    <!-- 3、aop編程
    <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法)
        ref:切面類的引用
    <aop:pointcut:聲明一個切入點表達式
        expression:切入點表達式
        id:名稱,用於其他通知引用
    -->
    <aop:config>
        <aop:aspect ref = "myAspectId">
            <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/>
            <!--3.3環繞通知
            <aop:around method="" pointcut-ref=""></aop:around>
            方法通知的格式:public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
                返回值:Object
                參數:ProceedingJoinPoint
                必須拋出異常
                必須手動執行目標方法
            -->
            <aop:around method="myAround" pointcut-ref="myPointCut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

測試結果:

5.4.4、拋出異常通知

配置文件:
 

<?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"> 

    <!-- 1、目標類 -->
    <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean>
    <!-- 2、切面類 (通知)-->
    <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean>
    <!-- 3、aop編程
    <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法)
        ref:切面類的引用
    <aop:pointcut:聲明一個切入點表達式
        expression:切入點表達式
        id:名稱,用於其他通知引用
    -->
    <aop:config>
        <aop:aspect ref = "myAspectId">
            <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/>
            <!-- 3.4拋出異常通知
            <aop:after-throwing method="" pointcut-ref="" throwing=""></aop:after-throwing>
            throwing:通知方法的第二個參數的名稱
            通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e)
                參數1 :連接點的描述對象
                參數2 :獲得異常信息,類型Throwable 參數名由throwing設置
            -->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>

測試結果:

5.4.5、最終通知測試

配置文件:

<?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"> 

    <!-- 1、目標類 -->
    <bean id = "userServiceId" class = "test_d_aspect_a_xml.UserServiceImpl"></bean>
    <!-- 2、切面類 (通知)-->
    <bean id = "myAspectId" class = "test_d_aspect_a_xml.MyAspect"></bean>
    <!-- 3、aop編程
    <aop:aspect>:將切面類聲明成"切面",從而獲得通知(方法)
        ref:切面類的引用
    <aop:pointcut:聲明一個切入點表達式
        expression:切入點表達式
        id:名稱,用於其他通知引用
    -->
    <aop:config>
        <aop:aspect ref = "myAspectId">
            <aop:pointcut id="myPointCut" expression="execution(* test_d_aspect_a_xml.UserServiceImpl.*(..))"/>
            <!--3.5最終通知
            <aop:after method="" pointcut-ref=""></aop:after>
            -->
            <aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after>
        </aop:aspect>
    </aop:config>
</beans>

測試結果:

6、基於註解

此部分與上部分的測試相同,是將xml配置aop換成註解配置aop,僅僅給出修改後的切面類和配置文件,目標類、測試類以及測試結果與上面相同。

6.1、aop註解總結

切面:

  • @Aapect:聲明切面,修飾切面類,從而獲得通知

通知

  • @Before(value="切入點表達式||切入點引用"):前置通知
  • @AfterReturning(value = "切入點表達式||切入點引用",returning="返回值"):後置通知
  • @Around(value = ''切入點表達式||切入點引用"):環繞
  • @AfterThrowing(value = "切入點表達式||切入點引用",throwing="異常名"):拋出異常通知
  • @After(value = "切入點表達式||切入點引用")

注意,當參數只有一個的時候,“value”可以省略,例如:@After("切入點表達式")。

切入點:

  • @PointCut("切入點表達式") 修飾方法private void xxx(){} 之後通過方法名來獲得切入點引用

6.2、配置文件

相比於xml配置aop,註解需要添加命名空間

<?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 
    					http://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">
    <!--1 掃描所有的註解類-->
    <context:component-scan base-package="test_d_aspect_b_anno"></context:component-scan>
    <!--2 確定aop註解生效-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

 

6.3、切面類

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

@Component
@Aspect
public class MyAspect {
    //切入點當前有效(在配置別的方法的時候,需要重新寫切入點表達式)
    //@Before("execution(* test_d_aspect_b_anno.UserServiceImpl.*(..))")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("前置通知:" + joinPoint.getSignature().getName() );
    }
    //聲明公共的切入點表達式
    @Pointcut("execution(* test_d_aspect_b_anno.UserServiceImpl.*(..))")
    public void myPointcut(){
    }

    /*
    @AfterReturning(value = "",returning = "")
    value:
    returning:返回值
     */
    //@AfterReturning(value = "myPointcut()",returning = "obj")
    public void myAfterReturning(JoinPoint joinPoint,Object obj){
        System.out.println("後置方法:--->"+"後置方法獲得的目標方法的返回值:" + obj);
    }
    /*
    @Around(value = "")
    注意:1、如果只有一個參數,value是可以省略的,
         2、value如果是一個數組,使用{},若數組中只有一個值,{}可以省略
     */
    //@Around("myPointcut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("****************");
        System.out.println("前");
        Object obj = proceedingJoinPoint.proceed();
        System.out.println("後");
        return obj;
    }
    //@AfterThrowing(value = "myPointcut()",throwing = "throwable")
    public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){
        System.out.println("拋出異常通知:" + throwable.getMessage());
    }
    @After("myPointcut()")
    public void myAfter(){
        System.out.println("最終通知");
    }
}

 

 

發佈了29 篇原創文章 · 獲贊 0 · 訪問量 980
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章