Spring Aop:三、使用 AspectJ 框架實(shí)現(xiàn) Spring AOP

本文參考實(shí)驗(yàn)樓教程:https://www.shiyanlou.com/courses/578/learning/?id=1940

AspectJ是基于注解(Annotation)的,所以需要JDK5.0版本以上。本文實(shí)驗(yàn)環(huán)境延用之Spring Aop:一、四種advice 的實(shí)驗(yàn)環(huán)境。

AspectJ支持的注解類(lèi)型如下:

  • @Before
  • @After
  • @AfterReturning
  • @AfterThrowing
  • @Around
1、準(zhǔn)備工作

首先定義一個(gè)簡(jiǎn)單的bean,CustomerBo實(shí)現(xiàn)了ICustomerBo接口。ICustomerBo接口代碼如下:

package com.shiyanlou.spring.aop.aspectj;

/**
 * Created by Administrator on 2019/11/2.
 */
public interface ICustomerBo {

    void addCustomer();
    void deleteCustomer();
    String addCustomerReturnValue();
    void addCustomerThrowException() throws Exception;
    void addCustomerAround(String name);

}

CustomerBo.java代碼如下:

package com.shiyanlou.spring.aop.aspectj;

/**
 * Created by Administrator on 2019/11/2.
 */
public class CustomerBo implements ICustomerBo {
    @Override
    public void addCustomer() {
        System.out.println("addCustomer() is running...");
    }

    @Override
    public void deleteCustomer() {
        System.out.println("deleteCustomer() is running...");
    }

    @Override
    public String addCustomerReturnValue() {
        System.out.println("addCustomerReturnValue() is running...");
        return "abc";
    }

    @Override
    public void addCustomerThrowException() throws Exception {
        System.out.println("addCustomerThrowException() is running...");
        throw new Exception("Generic Error!");
    }

    @Override
    public void addCustomerAround(String name) {
        System.out.println("addCustomerAround() is running, args: " + name);
    }
}


2、簡(jiǎn)單的AspectJ,Advice和Pointcut結(jié)合在一塊的

首先在沒(méi)有引入AspectJ之前,Advice和Pointcut是混在一塊的,步驟如下:

  1. 創(chuàng)建一個(gè)Aspect類(lèi)
  2. 配置Spring配置文件

由于接下來(lái)要使用aspectj的jar包,首先要添加maven依賴(lài),需要在pom.xml中添加:

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.2</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjtools</artifactId>
      <version>1.9.2</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.2</version>
    </dependency>

注:這在之前已經(jīng)添加過(guò)了,這里這是說(shuō)明他們的用途。



創(chuàng)建AspectJ類(lèi),LoggingAspect.java如下:

package com.shiyanlou.spring.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * Created by Administrator on 2019/11/2.
 */

@Aspect
public class LoggingAspect {

    @Before("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer(..))")
    public void logBefore(JoinPoint joinPoint){
        System.out.println("logBefore() is running...");
        System.out.println("wei:" + joinPoint.getSignature().getName());
        System.out.println("***********");
    }

    @After("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.deleteCustomer(..))")
    public void logAfter(JoinPoint joinPoint){
        System.out.println("logAfter() is running...");
        System.out.println("wei:" + joinPoint.getSignature().getName());
        System.out.println("***********");
    }
}

解釋?zhuān)?/p>

  1. 必須使用@AspectLoggingAspect之前聲明,以便被框架掃描到;
  2. 此例中Advice和Poincut結(jié)合在一起,LoggingAspect類(lèi)中的logBeforelogAfter即為Advice,要注入的代碼,即它們上方的代碼為Pointcut表達(dá)式,即定義了切入點(diǎn)。上例中@Before中代碼表示在執(zhí)行com.shiyanlou.spring.aop.aspectj.CustomerBoaddCustomer方法前注入logBefore代碼;
  3. 需要在LoggingAspect類(lèi)的方法前加上@Before,@After等注釋?zhuān)?/li>
  4. execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.addCustomer(..))是Aspect的Pointcut表達(dá)式:
  • *代表任意返回類(lèi)型
  • 后面定義要攔截的方法名(全路徑:包名.類(lèi)名.方法名)
  • (..)代表匹配參數(shù):任意多個(gè)任意類(lèi)型的參數(shù),若確認(rèn)沒(méi)有參數(shù)可以寫(xiě)();還可以用(*)代表任意一個(gè)類(lèi)型的參數(shù),(*,String)表示匹配兩個(gè)參數(shù),第一個(gè)任意類(lèi)型,第二個(gè)必須是String類(lèi)型。
  1. AspectJ表達(dá)式可以對(duì)整個(gè)包定義,例如:execution(public * com.shiyanlou.spring.aop.aspectj.*.*(..))表示切入點(diǎn)是com.shiyanlou.spring.aop.aspectj整個(gè)包的所有類(lèi)的所有方法。



配置SpringAopAspectJ.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">

    <aop:aspectj-autoproxy/>

    <bean id="customerBo" class="com.shiyanlou.spring.aop.aspectj.CustomerBo"/>
    <bean id="loggingAspect" class="com.shiyanlou.spring.aop.aspectj.LoggingAspect"/>
</beans>

<aop:aspectj-autoproxy/>代表啟動(dòng)AspectJ支持,這樣Spring會(huì)自動(dòng)尋找@Aspect注釋過(guò)的類(lèi),其他配置一致;

執(zhí)行App.java,如下:

package com.shiyanlou.spring;

import com.shiyanlou.spring.aop.advice.CustomerService;
import com.shiyanlou.spring.aop.aspectj.CustomerBo;
import com.shiyanlou.spring.aop.aspectj.ICustomerBo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Hello world!
 *
 */
public class App 
{
    private static ApplicationContext context;

    public static void main( String[] args ) {

        context = new ClassPathXmlApplicationContext("SpringAopAspectJ.xml");

        ICustomerBo cust = (ICustomerBo) context.getBean("customerBo");

        cust.addCustomer();
        System.out.println("-----------------------");
        cust.deleteCustomer();
    }
}

執(zhí)行結(jié)果如下:

logBefore() is running...
wei:addCustomer
***********
addCustomer() is running...
-----------------------
deleteCustomer() is running...
logAfter() is running...
wei:deleteCustomer
***********


3、將Advice和Pointcut 分開(kāi)

需要三步驟:

  1. 創(chuàng)建Pointcut
  2. 創(chuàng)建Advice
  3. 配置Spring的配置文件

1)、定義Pointcut
創(chuàng)建PointcutsDefinitions.java,如下:

package com.shiyanlou.spring.aop.aspectj;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * Created by Administrator on 2019/11/2.
 */
@Aspect
public class PointcutsDefinitions {

    @Pointcut("execution(public * com.shiyanlou.spring.aop.aspectj.CustomerBo.*(..))")
    public void customreLog(){
    }
}

解釋?zhuān)?/p>

  1. 類(lèi)聲明之前加上@Aspect注釋?zhuān)槐楸豢蚣軖呙璧剑?/li>
  2. @Pointcut定義了切入點(diǎn)聲明,指定需要注入代碼的位置(即在哪個(gè)類(lèi)的哪個(gè)方法中注入額外代碼),上例中指定的切入點(diǎn)為CustomerBo.java類(lèi)中的所有方法;但是往往實(shí)際應(yīng)用中,我們需要切入的是一整個(gè)業(yè)務(wù)邏輯層(簡(jiǎn)單點(diǎn)說(shuō)就是某個(gè)包),例如@Pointcut("execution(public * com.shiyanlou.spring.aop.aspectj.*.*(..))"),表示的是com.shiyanlou.spring.aop.aspectj 包中所有類(lèi)的所有方法。
  3. 方法customreLog只是一個(gè)簽名,在Advice中可以用此簽名代替切入點(diǎn)表達(dá)式,多以不需要寫(xiě)方法體,只起到助記功能,例如此處代表操作CustomerBo類(lèi)中的切入點(diǎn)。

2)、修改LoggingAspect.java

package com.shiyanlou.spring.aop.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * Created by Administrator on 2019/11/2.
 */

@Aspect
public class LoggingAspect {

@Before("com.shiyanlou.spring.aop.aspectj.PointcutsDefinitions.customreLog()")
    public void logBefore(JoinPoint joinPoint){
        System.out.println("logBefore() is running...");
        System.out.println("wei:" + joinPoint.getSignature().getName());
        System.out.println("***********");
    }

    @After("com.shiyanlou.spring.aop.aspectj.PointcutsDefinitions.customreLog()")
    public void logAfter(JoinPoint joinPoint){
        System.out.println("logAfter() is running...");
        System.out.println("wei:" + joinPoint.getSignature().getName());
        System.out.println("***********");
    }
}

解釋?zhuān)?/p>

  1. @Before@After注釋使用PointcutsDefinitions中的方法簽名代替Pointcut表達(dá)式找到相應(yīng)的切入點(diǎn), 即通過(guò)簽名找到PointcutsDefinitions方法customreLog前定義的Pointcut表達(dá)式;
  2. 對(duì)于PointcutsDefinitions來(lái)說(shuō),它的主要職責(zé)是定義Pointcut即切入點(diǎn),可以在其中定義多個(gè)切入點(diǎn),并且使用便于記憶的方法簽名表示;
  3. 單獨(dú)定義Pointcut表達(dá)式的好處有,一是使用了有意義的方法名,二是Pointcut表達(dá)式可以被多個(gè)Advice共享,修改一處其他所有使用的地方均被修改。

3)、配置Spring配置文件
配置的SpringAopAspectJ.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">

    <aop:aspectj-autoproxy/>

    <bean id="customerBo" class="com.shiyanlou.spring.aop.aspectj.CustomerBo"/>
    <bean id="loggingAspect" class="com.shiyanlou.spring.aop.aspectj.LoggingAspect"/>
</beans>

App.java不變,運(yùn)行如下:

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

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

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