學習Spring(九) -- Spring使用AOP

AOP通知
java

Spring中經常使用的AOP通知有五種:spring

  1. 前置通知:在某方法調用以前執行;
    app

  2. 後置通知:在後方法調用以後執行;less

  3. 異常通知:在某方法發生異常時執行;ide

  4. 返回通知:在某方法進行返回時執行;函數

  5. 環繞通知:可手動進行控制以上四種通知的執行;測試

AOP配置
spa

    加入一下spring的jar包:.net


    配置文件中引入xmlns:aop,加入<aop:aspectj-autoproxy></aop:aspectj-autoproxy>,具體配置以下:代理

<?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:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/util 
http://www.springframework.org/schema/util/spring-util-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

    <context:component-scan base-package="cn.net.bysoft.lesson7">
    </context:component-scan>
    <!-- 自動生成aop代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

    接下來編寫測試類與切面類,作一個簡單的接口,其中有4個方法,有是加減乘除四個函數,需求是在執行這4個函數的時候進行日誌輸入:

package cn.net.bysoft.lesson7;

public interface Arithmentic {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}
package cn.net.bysoft.lesson7;

import org.springframework.stereotype.Component;

@Component()
public class ArithmenticImpl implements Arithmentic {

    @Override
    public int add(int i, int j) {
        return i + j;
    }

    @Override
    public int sub(int i, int j) {
        return i - j;
    }

    @Override
    public int mul(int i, int j) {
        return i * j;
    }

    @Override
    public int div(int i, int j) {
        return i * j;
    }
}

前置通知

    編寫一個日誌切面類,提供前置通知:

package cn.net.bysoft.lesson7;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    // 調用前
    @Before("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("The method " + joinPoint.getSignature().getName()
                + " begins with" + Arrays.asList(joinPoint.getArgs()));
    }
}

    其中beforeMethod使用@Before裝飾,表明前置通知,在方法調用以前執行,@Before的參數是表明對cn.net.bysoft.lesson7這個包中的Arithmentic類的全部方法進行前置通知。

    參數JoinPoint是切面的鏈接點,能夠得到執行的方法的信息,如名稱和參數值等,在前置通知時得到不到返回值,由於Arithmentic的方法還未執行。

    編寫測試類:

package cn.net.bysoft.lesson7;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Lesson7Test {

    private AbstractApplicationContext ctx;

    @Before
    public void initApplicationContext() {
        // 建立Spring容器
        ctx = new ClassPathXmlApplicationContext(
                "applicationContext-lesson7.xml");
    }

    @Test
    public void test(){
        Arithmentic arithmentic = ctx.getBean(Arithmentic.class);
        int result = arithmentic.add(3, 6);
        System.out.println(result);
        ctx.close();
    }

}


後置通知

    加入後置通知,使用@After修飾方法,後置通知表示不管調用的方法是否異常,都會執行:

// 調用後,不管是否異常
    @After("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("The method " + joinPoint.getSignature().getName()
                + " begins with" + Arrays.asList(joinPoint.getArgs()));
    }
@Test
    public void testAfter(){
        Arithmentic arithmentic = ctx.getBean(Arithmentic.class);
        int result = arithmentic.div(3, 6);
        System.out.println(result);
        ctx.close();
    }


    進行測試,是否若是方法發生異常,也會執行後置通知,將除數設置爲0:

異常通知

    若方法如上面的代碼會發生異常,也能夠進行通知。使用@AfterThrowing修飾方法,value等於要調用的方法,throwing等於異常,以下:

@AfterThrowing(value = "execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))", throwing = "ex")
    public void exceptionMethod(JoinPoint joinPoint, Exception ex) {
        System.out.println("The method " + joinPoint.getSignature().getName()
                + " exception: " + ex);
    }

返回通知

    當方法執行成功,沒有發生異常,並返回結果,可使用返回通知進行日誌輸出。使用@AfterReturning修飾方法,value等於要進行切面的方法,returing等於返回值,以下:

    // 返回通知
    @AfterReturning(value = "execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))", returning = "result")
    public void returnMethod(JoinPoint joinPoint, Object result) {
        System.out.println("The method " + joinPoint.getSignature().getName()
                + " returning: " + result);
    }


環繞通知

    環繞通知功能很強大,能夠將上面的幾種通知進行自定義控制,它至關於咱們編寫的動態代理,使用@Around對方法進行修飾,傳遞一個ProceedingJoinPoint方法,能夠決定是否決定執行目標方法,而且環繞通知必須有返回值,該返回值即爲目標方法的返回值。代碼以下:

// 環繞通知
    @Around("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("aroundMethod");
        return 100;
    }

    爲了區分環繞通知與以前的通知,現將以前的通知代碼註釋掉:


    結果爲100,今後處能夠看出環繞通知功能很是強大,接下來,完成環繞通知的代碼,調用參數proceedingJoinPoint對象的proceed方法,能夠執行目標方法,並得到返回值,並在該方法以前、以後、異常和返回時加入想要的通知:

// 環繞通知
    @Around("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;
        String methodName = proceedingJoinPoint.getSignature().getName();
        try {
            // 前置通知。
            System.out.println("after " + methodName);
            result = proceedingJoinPoint.proceed();
            // 返回通知。
            System.out.println("returing " + methodName);
        } catch (Throwable e) {
            // 異常通知。
            System.out.println("exception " + e);
            throw new RuntimeException(e);
        }
        // 後置通知。
        System.out.println("afte " + methodName);
        return result;
    }

測試一下,執行目標方法,不帶有異常,結果以下:


    在測試一個目標方法發生異常的狀況:


    以上,就是spring經常使用的5種aop通知。

切面的優先級

    若是有多個切面類,好比一個用來輸出日誌,一個用來驗證參數是否合法,還有一些其餘的……如何設置切面的優先級,好比驗證參數的切面類要在輸出日誌的切面類以前執行,具體來看代碼,編寫一個驗證切面類:

package cn.net.bysoft.lesson7;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ValidationAspect {

    @Before("execution(public int cn.net.bysoft.lesson7.Arithmentic.*(int, int))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("validate args" + joinPoint.getSignature().getName()
                + " begins with" + Arrays.asList(joinPoint.getArgs()));
    }
}

進行測試:


    目前日誌切面類在驗證切面類以前執行,可使用@Order對切面類進行修飾,輸入須要進行排序:

@Order(1)
public class ValidationAspect {
@Order(2)
public class LoggingAspect {

    再一次進行測試:

    以上就是spring的aop的簡單使用。

相關文章
相關標籤/搜索