spingmvc配置AOP有兩種方式,一種是利用注解的方式配置,另一種是XML配置實現。
應用注解的方式配置:
先在maven中引入AOP用到的依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.1.RELEASE</version> </dependency> <!-- aop aspect注解導包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
然后在springmvc的配置文件中加入AOP的配置,即掃描AOP的包以及讓AOP生效
<!-- AOP 注解方式 ;定義Aspect --> <!-- 激活組件掃描功能,在包"com.example.aop及其子包下面自動掃描通過注解配置的組件--> <context:component-scan base-package="com.example.aop"/> <!-- 啟動AspectJ支持 只對掃描過的bean有效--> <aop:aspectj-autoproxy proxy-target-class="true" />
然后加入AOP邏輯處理類
package com.example.aop; 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.example.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 joinPoint */ @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 point */ @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; } }
在類上加上注解Aspect聲明這個類是一個切面,加上component注解加入IOC容器中。Order注解是優先級,如果只有一個切面類可以不用。注解pointcut是聲明注解的作用范圍。execution(public * com.example.controller.*Controller.*(..))我這里用的execution表達式,代表作用com.example.controller包下所有以Controller結尾類的所有Public方法。@pointcut所描述的方法里面通常不需要內容。切面通知包含:
前置通知(@Before,在執行方法之前,參數為JoinPoint)
后置通知(@After,無論方法拋不拋異常都會執行,所以獲取不到方法的返回值。參數為JoinPoint)
返回通知(@AfterReturning,在方法正常結束后執行,可以獲取到方法的返回值。參數為JoinPoint和result(Object))
異常通知(@AfterThrowing,在方法拋出異常時執行,可以獲取到異常對象,且可以指定在出現特定異常時執行的代碼,參數為JoinPoint何Exception)
環繞通知(@Around,環繞通知需要攜帶的類型為ProceedingJoinPoint類型的參數, 環繞通知包含前置、后置、返回、異常通知;ProceedingJoinPoin 類型的參數可以決定是否執行目標方法,且環繞通知必須有返回值,返回值即目標方法的返回值)
應用XML方式配置
依賴的話,應該不用加入AOP注解的依賴。先定義一個類,作為切面類。
package com.example.aop; import java.util.Arrays; import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.springframework.stereotype.Component; @Component //該標簽把LoggerAspect類放到IOC容器中 public class SysAspect { /** * 前置通知 * @param joinPoint */ 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 */ public void afterMethod(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); System.out.println("this method "+methodName+" end."); } /** * 返回通知(在方法正常結束執行的代碼) * 返回通知可以訪問到方法的返回值! * @param joinPoint */ 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 */ 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 point */ 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; } }
可以看到,這個類除了沒有@Aspect以及相關切面的注解外,跟正常的AOP切面類沒有差別。如果聲明其讓其為一個切面。需要我們在xml文件中手動配置。
<!-- 配置切面的Bean -->
<bean id="sysAspect" class="com.example.aop.SysAspect"/>
<!-- 配置AOP -->
<aop:config>
<!-- 配置切點表達式 -->
<aop:pointcut id="pointcut" expression="execution(public * com.example.controller.*Controller.*(..))"/>
<!-- 配置切面及配置 -->
<aop:aspect order="3" ref="sysAspect">
<!-- 前置通知 -->
<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:around method="aroundMethod" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
定義的切面bean就是我們自定義的切面類,然后配置AOP(<aop:config>)。先定義切面范圍pointcut。余下的就是制定aop的切面類、優先級然后再詳細配置切面的通知。這樣就跟注解的方式產生的效果一致了。