天天看點

spring實作aop的兩種方式前言一、XML實作AOP方式二、注解實作AOP方式(AspectJ)總結

文章目錄

  • 前言
  • 一、XML實作AOP方式
    • 1.XML配置
    • 2.切面通知類的實作
    • 3.目标類的實作
    • 4.測試實作類
    • 5.實作結果
    • 6.業務實作
  • 二、注解實作AOP方式(AspectJ)
    • 1.通知實作類(advice)
    • 2.通知實作類,附帶請求參數的擷取
    • 3.目标類
    • 4.測試類
    • 5.列印結果
  • 總結

前言

提示:實作AOP的方式分為XML方式和注解(AspectJ)實作方式。

一、XML實作AOP方式

1.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">

    <!-- 聲明一個目标類 -->
    <bean id="TmplController" class="org.example.aop.controller.IndexController"/>  
    
    <!-- 聲明通知類 -->
    <bean id="aspectBean" class="org.example.aop.config.AopAspect" />
    <!-- AOP配置 -->
    <aop:config>
        <!-- 切入點 -->
        <aop:pointcut id="pointcut" expression="execution(* org.example.aop.controller..*(..))"/>
        <!--定義通知-->
        <aop:aspect ref="aspectBean">
            <aop:before method="beforeAspect" pointcut-ref="pointcut"/>
            <aop:after-returning method="afterReturnAspect"  pointcut-ref="pointcut" />
            <aop:after method="afterAspect" pointcut-ref="pointcut" />
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"  />
        </aop:aspect>
    </aop:config>
</beans>
           

2.切面通知類的實作

代碼如下(示例):

package org.example.aop.config;
import org.aopalliance.intercept.Joinpoint;
/**
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/3 23:02
 */
public class AopAspect {
    //前置操作
    public void beforeAspect(){
        System.out.println("===前置操縱方法執行===");
    }
    //後置操作
    public void afterAspect(){
        System.out.println("===後置操縱方法執行===");
    }
    //後置最終執行方法
    public void afterReturnAspect(){
        System.out.println("===後置最終方法執行===");
    }
    //異常通知:目标方法抛出異常時執行的代碼
    public void afterThrowing(){
        System.out.println("===========執行異常通知============");
    }
}

           

3.目标類的實作

代碼如下(示例):

package org.example.aop.controller;

/**
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/3 22:57
 */
public class IndexController {
    public void index(){
        System.out.println("index");
    }

}

           

4.測試實作類

代碼如下(示例):

package org.example.aop;
import org.example.aop.controller.IndexController;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/3 23:49
 */
public class TestAop {
    @Test
    public void index(){
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("aop-config.xml");
        IndexController bean = app.getBean(IndexController.class);
        bean.index();
    }
}

           

5.實作結果

代碼如下(示例):

===前置操縱方法執行===
index
===後置最終方法執行===
===後置操縱方法執行===
           

6.業務實作

AOP實作token驗證登陸資訊?

xml配置多個目标類

測試類中增加調用多個目标類的方法:

ArticleController bean2 = app.getBean(ArticleController.class);
        bean2.test();
           

結果:

===前置操縱方法執行===
index
===後置最終方法執行===
===後置操縱方法執行===
===前置操縱方法執行===
===article test=
===後置最終方法執行===
===後置操縱方法執行===
           

二、注解實作AOP方式(AspectJ)

1.通知實作類(advice)

代碼如下(示例):

package org.example.aop.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * AOP配置類,替代XML檔案配置方式,開啟AOP以及注入需要的bean(類)
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/3 22:58
 */
//用于定義配置類,替換xml配置檔案
@Configuration 
//包掃描,根據需求實作路徑處理 将此路徑下包含注解的類實作bean注入
@ComponentScan(value = "org.example.aop")
//aop開啟
@EnableAspectJAutoProxy 
public class AspectConfig {


}

           

2.通知實作類,附帶請求參數的擷取

代碼如下(示例):

package org.example.aop.config;

import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 通知實作類
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/3 23:02
 */
@Aspect  //定義切面類
@Component
public class AspectAdvice {
    public static  final String EXE= "execution(public * org.example.aop.controller..*(..))";
    //前置操作
    @Before(value = EXE)
    public void beforeAspect(JoinPoint joinPoint){
        System.out.println("===前置操縱方法執行===");
        //處理請求參數
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramValues.length; i++) {
            param.put(paramNames[i], paramValues[i]);
        }
        System.out.println(param);
    }

    //後置操作
    @After(value = EXE)
    public void afterAspect(){
        System.out.println("===後置操縱方法執行===");
    }
    //後置最終執行方法
    @AfterReturning(value = EXE, returning = "result")
    public void afterReturnAspect(){
        System.out.println("===後置最終方法執行===");
    }



    //異常通知:目标方法抛出異常時執行的代碼
    @AfterThrowing(value = EXE,throwing = "th")
    public void afterThrowing(JoinPoint joinPoint , Exception th){
        System.out.println("===========執行異常通知============");
    }


}

           

3.目标類

代碼如下(示例):

package org.example.aop.controller;

import org.springframework.stereotype.Controller;

/**
 * 業務實作類(目标類)
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/3 22:57
 */
@Controller
public class IndexController   {
    public Object index(Object data){
        System.out.println("index");
        return data;
    }
}

           

4.測試類

代碼如下(示例):

package org.example.aop;

import org.example.aop.config.AspectConfig;
import org.example.aop.controller.ArticleController;
import org.example.aop.controller.IndexController;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author mr.monster
 * @version 1.0
 * @Description
 * @date 2021/5/4 15:20
 */
public class ConfigAopTest {

    public class  Data{
        String a="1";
        String b="2";
        Integer c =3;

        public String getA() {
            return a;
        }

        public void setA(String a) {
            this.a = a;
        }

        public String getB() {
            return b;
        }

        public void setB(String b) {
            this.b = b;
        }

        public Integer getC() {
            return c;
        }

        public void setC(Integer c) {
            this.c = c;
        }

        @Override
        public String toString() {
            return "Data{" +
                    "a='" + a + '\'' +
                    ", b='" + b + '\'' +
                    ", c=" + c +
                    '}';
        }
    }

    @Test
    public void index(){

        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AspectConfig.class);

        IndexController bean = app.getBean(IndexController.class);
        Data d = new Data();

        bean.index(d);
        ArticleController bean1 = app.getBean(ArticleController.class);
        bean1.test();

    }
}

           

5.列印結果

代碼如下(示例):

===前置操縱方法執行===
{data=Data{a='1', b='2', c=3}}
index
===後置最終方法執行===
===後置操縱方法執行===
===前置操縱方法執行===
{}
===article test=
===後置最終方法執行===
===後置操縱方法執行===
           

總結

在學習其他大佬的文章或者視訊是會發現使用了接口類的繼承,我在編碼時直接抛棄接口類的實作是因為,個人認為若通過接口類實作完全失去了AOP解耦的作用,是以我在測試跑代碼時出現了比較多的異常,特别是切入點的路徑問題,差點放棄了~~~~還好各種嘗試解決嘞

類似這樣子的異常