springMVC之AOP


AOP(Aspect-Oriented Programming,面向切面編程)

切面(Aepect):橫切關注點(跨越應用程序多個模塊的功能)被模塊化的對象;

通知(Advice):切面必須要完成的工作;

目標(Target):被通知的對象;

代理(Proxy):像目標對象應用通知之后創建的對象;

連接點(Joinpoint):程序執行的某個特殊位置,如類某個方法調用前、調用后、方法拋出異常后等。連接點由兩個信息確定:方法表示的程序執行點;想對點表示的方位。

切點(pointcut):每個類都擁有多個連接點,即連接點是程序類中客觀存在的事務;  

AOP通過切點定位到特定的連接點。

AOP的主要編程對象是切面(aopect),而切面模塊化橫切關注點

在應用AOP編程時,仍需要定義公共功能,但可以明確這個功能在哪里,以什么方式應用,並且不必修改受影響的類,這樣的話橫切關注點就被模塊化到特殊的對象(切面)里。

AOP的好處:

  每個事物邏輯位於一個位置,代碼不分散,便於維護和升級;

  業務模塊更簡潔,值包含核心業務代碼

1、基於AspectJ注解

(1)Maven引入依賴包

<dependency>  
  <groupId>org.springframework</groupId>  
  <artifactId>spring-aop</artifactId>  
  <version>2.5.6</version>  
</dependency>  
<dependency>  
  <groupId>org.aspectj</groupId>  
  <artifactId>aspectjweaver</artifactId>  
  <version>1.6.1</version>  
</dependency>  
<dependency>  
  <groupId>org.aspectj</groupId>  
  <artifactId>aspectjrt</artifactId>  
  <version>1.6.1</version>  
</dependency>  

(2)在spring配置文件中添加aop的命名空間,同時配置使得項目支持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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <!--使Spring支持自動檢測組件,如注解的Controller -->
    <context:component-scan base-package="com.parry.redisCluster.*" />
    <!--*************** 支持aop **************** -->
    <aop:aspectj-autoproxy proxy-target-class="true" />
</beans>

(3)實現一個日志切面

  • 切面首先是IOC容器中的一個bean,即在切面類前加上標簽@Component標簽
  • 切面還需要添加標簽@Aspect標簽,聲明該類是一個切面
  • 實現需要的通知,在方法之前添加如下的通知類型:
    • @Before:前置通知,在方法前通知;
    • @After :后置通知,在方法執行后通知;
    • @AfterRunning:返回通知,在方法返回結果之后通知;
    • @AfterThrowing:異常通知,在方法拋出異常之后通知;
    • @Around:環繞通知,圍繞着方法執行;
  • 切入點表達式的書寫:
    • execution(* com.cn21.redisCluster.controller.*(..)) :第一個*表示任意的修飾符(public/private/protected)及任意的返回值(void/Object);第二個*表示任意的方法,‘..’表示任意數量的參數;
    • execution(public * com.cn21.redisCluster.controller.*Controller.*(..)):表示com.cn21.redisCluster.controller包下+以Controller結尾的+公共的方法(public)的方法;
    • execution(public void com.cn21.redisCluster.controller.*Controller.*(..)):表示com.cn21.redisCluster.controller包下+以Controller結尾的+公共的方法(public)+返回類型是void的方法;
    • execution(public void com.cn21.redisCluster.controller.*Controller.*(int,..)):表示com.cn21.redisCluster.controller包下+以Controller結尾+公共的方法(public)+返回類型是void的類+第一個參數是int的方法;
    • execution(public void com.cn21.redisCluster.controller.*Controller.*(int,int)):表示com.cn21.redisCluster.controller包下+以Controller結尾+公共的方法(public)+返回類型是void的類+第一個參數是int+第二個參數是int的方法;
  • 可以在方法中聲明一個類型為JoinPoint的參數,然后就可以訪問鏈接細節,如方法名稱和參數值
import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect    //該標簽把LoggerAspect類聲明為一個切面
@Order(1)  //設置切面的優先級:如果有多個切面,可通過設置優先級控制切面的執行順序(數值越小,優先級越高)
@Component //該標簽把LoggerAspect類放到IOC容器中
public class LoggerAspect {
    
    /**
     * 定義一個方法,用於聲明切入點表達式,方法中一般不需要添加其他代碼
     * 使用@Pointcut聲明切入點表達式
     * 后面的通知直接使用方法名來引用當前的切點表達式;如果是其他類使用,加上包名即可
     */
    @Pointcut("execution(public * com.parry.redisCluster.controller.*Controller.*(..))")
    public void declearJoinPointExpression(){}
    
    /**
     * 前置通知
     * @param joinPoint
     */
    @Before("declearJoinPointExpression()") //該標簽聲明次方法是一個前置通知:在目標方法開始之前執行
    public void beforMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("this method "+methodName+" begin. param<"+ args+">");
    }
    /**
     * 后置通知(無論方法是否發生異常都會執行,所以訪問不到方法的返回值)
     * @param joinPoint
     */
    @After("declearJoinPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("this method "+methodName+" end.");
    }
    /**
     * 返回通知(在方法正常結束執行的代碼)
     * 返回通知可以訪問到方法的返回值!
     * @param joinPoit
     */
    @AfterReturning(value="declearJoinPointExpression()",returning="result")
    public void afterReturnMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("this method "+methodName+" end.result<"+result+">");
    }
    /**
     * 異常通知(方法發生異常執行的代碼)
     * 可以訪問到異常對象;且可以指定在出現特定異常時執行的代碼
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(value="declearJoinPointExpression()",throwing="ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("this method "+methodName+" end.ex message<"+ex+">");
    }
    /**
     * 環繞通知(需要攜帶類型為ProceedingJoinPoint類型的參數)
     * 環繞通知包含前置、后置、返回、異常通知;ProceedingJoinPoin 類型的參數可以決定是否執行目標方法
     * 且環繞通知必須有返回值,返回值即目標方法的返回值
     * @param joinPoint
     */
    @Around(value="declearJoinPointExpression()")
    public Object aroundMethod(ProceedingJoinPoint point){
        
        Object result = null;
        String methodName = point.getSignature().getName();
        try {
            //前置通知
            System.out.println("The method "+ methodName+" start. param<"+ Arrays.asList(point.getArgs())+">");
            //執行目標方法
            result = point.proceed();
            //返回通知
            System.out.println("The method "+ methodName+" end. result<"+ result+">");
        } catch (Throwable e) {
            //異常通知
            System.out.println("this method "+methodName+" end.ex message<"+e+">");
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method "+ methodName+" end.");
        return result;
    }
}

2、基於XML配置的AOP

(1)在spring配置文件中添加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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    <!--使Spring支持自動檢測組件,如注解的Controller -->
    <context:component-scan base-package="com.parry.redisCluster.*" />
    
    <!-- 配置切面的Bean -->
    <bean id="validationAspect" class="com.cn21.redisCluster.aspect.ValidationAspect"></bean>
    
    <!-- 配置AOP -->
    <aop:config>
        <!-- 配置切點表達式  -->
        <aop:pointcut id="pointcut" expression="execution(!void com.parry.redisCluster.controller.*.*(..))" />
        <!-- 配置切面及配置 -->
        <aop:aspect order="3" ref="validationAspect">
            <!-- 前置通知 -->
            <aop:before method="beforMethod" pointcut-ref="pointcut"/>
            <!-- 后置通知 -->
            <aop:after method="afterMethod" pointcut-ref="pointcut"/>
            <!-- 返回通知 -->
            <aop:after-returning method="afterReturnMethod" pointcut-ref="pointcut" returning="result"/>
            <!-- 異常通知 -->
            <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/>
        </aop:aspect>
    </aop:config>
</beans>

(2)實現切面類:

package com.parry.redisCluster.aspect;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;

public class ValidationAspect {
    
    /**
     * 前置通知
     * @param joinPoint
     */
    public void beforMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("ValidationAspect this method "+methodName+" begin. param<"+ args+">");
    }
    /**
     * 后置通知(無論方法是否發生異常都會執行,所以訪問不到方法的返回值)
     * @param joinPoint
     */
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("ValidationAspect this method "+methodName+" end.");
    }
    /**
     * 返回通知(在方法正常結束執行的代碼)
     * 返回通知可以訪問到方法的返回值!
     * @param joinPoit
     */
    public void afterReturnMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("ValidationAspect this method "+methodName+" end.result<"+result+">");
    }
    /**
     * 異常通知(方法發生異常執行的代碼)
     * 可以訪問到異常對象;且可以指定在出現特定異常時執行的代碼
     * @param joinPoint
     * @param ex
     */
    public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("ValidationAspect this method "+methodName+" end.ex message<"+ex+">");
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM