1. 程式人生 > >spring AOP基礎(實現AOP兩種方法)

spring AOP基礎(實現AOP兩種方法)

1.AOP的作用

  在OOP中,正是這種分散在各處且與物件核心功能無關的程式碼(橫切程式碼)的存在,使得模組複用難度增加。AOP則將封裝好的物件剖開,找出其中對多個物件產生影響的公共行為,並將其封裝為一個可重用的模組,這個模組被命名為“切面”(Aspect),切面將那些與業務無關,卻被業務模組共同呼叫的邏輯提取並封裝起來,減少了系統中的重複程式碼,降低了模組間的耦合度,同時提高了系統的可維護性。

上面這段話可能不是那麼好理解,那麼AOP到底有什麼作用呢?我們通過下面的代買來慢慢解釋:

    public int add(int i, int j) {
        System.out.println("the method begin");
        int result = i + j;
        System.out.println("the method end");
        return result;
    }

    public int sub(int i, int j) {
        System.out.println("the method begin");
        int result = i - j;
        System.out.println("the method end");
        return result;
    }
我們需要在計算出結果前後各輸出一句話,其中我們想更改輸出語句(橫向程式碼)那麼我們就需要逐個的更改每個方法中的輸出語句,那麼AOP作業就是將這些與核心程式碼無關的程式碼抽取出來進行單獨管理。

2.基本概念:

1、橫切關注點

對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點

2、切面(aspect)

類是對物體特徵的抽象,切面就是對橫切關注點的抽象

3、連線點(joinpoint)

被攔截到的點,因為Spring只支援方法型別的連線點,所以在Spring中連線點指的就是被攔截到的方法,實際上連線點還可以是欄位或者構造器

4、切入點(pointcut)

對連線點進行攔截的定義

5、通知(advice)

所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類

6、目標物件

代理的目標物件

7、織入(weave)

將切面應用到目標物件並導致代理物件建立的過程

8、引入(introduction)

在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位

3.使用註解配置AOP

介面類:ArithmeticCalculator.java

package com.yuehailin.aop;

/**
 * Created by yuehailin on 2018/1/31.
 */
public interface ArithmeticCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}
介面實現類:ArithmeticCalculatorImpl.java

package com.yuehailin.aop;

import org.springframework.stereotype.Component;

/**
 * Created by yuehailin on 2018/1/31.
 */
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator{

    @Override
    public int add(int i, int j) {
        System.out.println("the method begin");
        int result = i + j;
        System.out.println("the method end");
        return result;
    }

    @Override
    public int sub(int i, int j) {
        System.out.println("the method begin");
        int result = i - j;
        System.out.println("the method end");
        return result;
    }

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

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}
LoggingAspect.java
package com.yuehailin.aop;

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

import java.util.Arrays;

/**
 * Created by yuehailin on 2018/1/31.
 */
@Order(2)
@Aspect
@Component
/**
 * 定義一個方法,用於宣告切入點表示式該方法不需要添入其他的程式碼
 */
public class LoggingAspect {
    @Pointcut("execution(public  int com.yuehailin.aop.ArithmeticCalculator.*(..))")
    public void declareJointPointExpression(){}

    /**
     * 在介面的每一個實現類的的方法開始之前執行一段程式碼
     */
    @Before("declareJointPointExpression()")
    public  void  beforeMethod(JoinPoint joinPoint){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" begins "+ Arrays.asList(args));
    }

    /**
     * 在方法執行之後執行的程式碼,無論該方法是否有異常
     * @param joinPoint
     */
    @After("declareJointPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" end "+ Arrays.asList(args));
    }

    /**
     * 在方法正常結束後執行的程式碼,叫做返回通知
     * 返回通知是可以訪問到方法的返回值的
     * @param joinPoint
     */
    @AfterReturning(value = "declareJointPointExpression()",returning = "result")
    public void  afterReturning(JoinPoint joinPoint,Object result){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" end "+ result);
    }

    /**
     * 在方法出現異常時會執行的程式碼,而且可以訪問到異常物件
     * 可以指定在出現特定異常時在執行
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(value = "declareJointPointExpression()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Exception ex){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" occurs excepetion with "+ ex);
    }

    /**
     * 環繞通知需要攜帶ProceedingJoinPoint的引數
     * 環繞通知類似於動態代理的全過程,ProceedingJoinPoint這個型別的引數可以決定是否執行目標方法】
     * 且必須返回值,即為目標方法的返回值
     * @param pjd
     */
    @Around(value = "execution(public  int com.yuehailin.aop.ArithmeticCalculator.*(..))")
    public Object  aroundMethod(ProceedingJoinPoint pjd){
//        執行目標方法·
        Object result =null;
        String methodname = pjd.getSignature().getName();

        try {
//            前置通知
            System.out.println("the method "+ methodname+" begin with "+ Arrays.asList(pjd.getArgs()));
            result=pjd.proceed();
//            後置通知
            System.out.println("the method "+" end with "+result);
        } catch (Throwable throwable) {
//            異常通知
            System.out.println("異常通知"+throwable+methodname);
        }
//        後置通知
        System.out.println("the method end with " +methodname);
        return result;
    }
}
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: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/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        ">

<!--配置自動掃描的包-->
<context:component-scan base-package="com.yuehailin.aop"></context:component-scan>
    <!--配置自動為匹配aspectj 註解的java類生成代理物件-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
測試類:
package com.yuehailin.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by yuehailin on 2018/1/31.
 */
public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
        int result = arithmeticCalculator.add(1,2);
        System.out.println(result);
        int result01 = arithmeticCalculator.div(1000,10);
        System.out.println(result01);
    }
}

4.基於XML配置的AOP

介面類:ArithmeticCalculator.java

package com.yuehailin.aop;

/**
 * Created by yuehailin on 2018/1/31.
 */
public interface ArithmeticCalculator {
    int add(int i,int j);
    int sub(int i,int j);
    int mul(int i,int j);
    int div(int i,int j);
}
介面實現類:ArithmeticCalculatorImpl.java

package com.yuehailin.aop;

import org.springframework.stereotype.Component;

/**
 * Created by yuehailin on 2018/1/31.
 */
@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator{

    @Override
    public int add(int i, int j) {
        System.out.println("the method begin");
        int result = i + j;
        System.out.println("the method end");
        return result;
    }

    @Override
    public int sub(int i, int j) {
        System.out.println("the method begin");
        int result = i - j;
        System.out.println("the method end");
        return result;
    }

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

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}
LoggingAspect.java
package com.yuehailin.aopxml;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

import java.util.Arrays;

/**
 * Created by yuehailin on 2018/1/31.
 */

public class LoggingAspect {
    public  void  beforeMethod(JoinPoint joinPoint){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" begins "+ Arrays.asList(args));
    }


    public void afterMethod(JoinPoint joinPoint){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" end "+ Arrays.asList(args));
    }


    public void  afterReturning(JoinPoint joinPoint,Object result){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" end "+ result);
    }



    public void afterThrowing(JoinPoint joinPoint,Exception ex){
        String methodname = joinPoint.getSignature().getName();
        Object [] args = joinPoint.getArgs();

        System.out.println("the method "+methodname+" occurs excepetion with "+ ex);
    }


    public Object  aroundMethod(ProceedingJoinPoint pjd){
//        執行目標方法·
        Object result =null;
        String methodname = pjd.getSignature().getName();

        try {
//            前置通知
            System.out.println("the method "+ methodname+" begin with "+ Arrays.asList(pjd.getArgs()));
            result=pjd.proceed();
//            後置通知
            System.out.println("the method "+" end with "+result);
        } catch (Throwable throwable) {
//            異常通知
            System.out.println("異常通知"+throwable+methodname);
        }
//        後置通知
        System.out.println("the method end with " +methodname);
        return result;
    }

}
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: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/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        ">
    <!--配置bean-->
    <bean id="arithmeticCalculator" class="com.yuehailin.aopxml.ArithmeticCalculatorImpl">

    </bean>
    <!--配置切面的bean-->
    <bean id="loggingAspect" class="com.yuehailin.aopxml.LoggingAspect">

    </bean>
    <bean id="vlidationAspect" class="com.yuehailin.aopxml.VlidationAspect">

    </bean>
    <!--配置aop-->
    <aop:config>
        <!--配置切點表示式-->
        <aop:pointcut id="pointcut" expression="execution(* com.yuehailin.aopxml.ArithmeticCalculator.*(int,int))"/>
    <!--配置切面及通知-->
    <aop:aspect ref="loggingAspect" order="2">
        <!--<aop:before method="beforeMethod" pointcut-ref="pointcut"/>-->
        <!--<aop:after method="afterMethod" pointcut-ref="pointcut"/>-->
        <!--<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="ex"/>-->
        <!--<aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>-->
        <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
    </aop:aspect>
    <aop:aspect ref="vlidationAspect" order="1">
    <aop:before method="validateArgs" pointcut-ref="pointcut"/>
    </aop:aspect>
    </aop:config>
</beans>
測試類:
package com.yuehailin.aopxml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by yuehailin on 2018/1/31.
 */
public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-xml.xml");
        ArithmeticCalculator arithmeticCalculator = (com.yuehailin.aopxml.ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
        int result = arithmeticCalculator.add(1,2);
        System.out.println(result);
        int result01 = arithmeticCalculator.div(1000,10);
        System.out.println(result01);
    }
}
說明:

①在使用aspectj相應的jar包的時候,有的時候程式碼沒有錯誤而是jar版本過老而導致錯誤。

②在使用註解的時候使用了AspectJ通知註解,需要匯入相應的jar包。

本人小白,如有錯誤,請指正。原始碼連線明天補上。