詳解SpringBoot之集成Spring AOP
在開始之前,我們先把需要的jar包添加到工程里。新增Maven依賴如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
接下來,我們進(jìn)入正題。這里的涉及的通知類型有:前置通知、后置最終通知、后置返回通知、后置異常通知、環(huán)繞通知,下面我們就具體的來看一下怎么在SpringBoot中添加這些通知。
首先我們先創(chuàng)建一個(gè)Aspect切面類:
@Component
@Aspect
public class WebControllerAop {
}
指定切點(diǎn):
//匹配com.zkn.learnspringboot.web.controller包及其子包下的所有類的所有方法
@Pointcut("execution(* com.zkn.learnspringboot.web.controller..*.*(..))")
public void executeService(){
}
接著我們?cè)賱?chuàng)建一個(gè)Controller請(qǐng)求處理類:
package com.zkn.learnspringboot.web.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by zkn on 2016/11/19.
*/
@RestController
@RequestMapping("/aop")
public class AopTestController {
}
前置通知
配置前置通知:
/**
* 前置通知,方法調(diào)用前被調(diào)用
* @param joinPoint
*/
@Before("executeService()")
public void doBeforeAdvice(JoinPoint joinPoint){
System.out.println("我是前置通知!!!");
//獲取目標(biāo)方法的參數(shù)信息
Object[] obj = joinPoint.getArgs();
//AOP代理類的信息
joinPoint.getThis();
//代理的目標(biāo)對(duì)象
joinPoint.getTarget();
//用的最多 通知的簽名
Signature signature = joinPoint.getSignature();
//代理的是哪一個(gè)方法
System.out.println(signature.getName());
//AOP代理類的名字
System.out.println(signature.getDeclaringTypeName());
//AOP代理類的類(class)信息
signature.getDeclaringType();
//獲取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//從獲取RequestAttributes中獲取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//如果要獲取Session信息的話,可以這樣寫:
//HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
Enumeration<String> enumeration = request.getParameterNames();
Map<String,String> parameterMap = Maps.newHashMap();
while (enumeration.hasMoreElements()){
String parameter = enumeration.nextElement();
parameterMap.put(parameter,request.getParameter(parameter));
}
String str = JSON.toJSONString(parameterMap);
if(obj.length > 0) {
System.out.println("請(qǐng)求的參數(shù)信息為:"+str);
}
}
注意:這里用到了JoinPoint和RequestContextHolder。通過JoinPoint可以獲得通知的簽名信息,如目標(biāo)方法名、目標(biāo)方法參數(shù)信息等。通過RequestContextHolder來獲取請(qǐng)求信息,Session信息。
接下來我們?cè)贑ontroller類里添加一個(gè)請(qǐng)求處理方法來測(cè)試一下前置通知:
@RequestMapping("/testBeforeService.do")
public String testBeforeService(String key,String value){
return "key="+key+" value="+value;
}
前置通知攔截結(jié)果如下所示:

后置返回通知
配置后置返回通知的代碼如下:
/**
* 后置返回通知
* 這里需要注意的是:
* 如果參數(shù)中的第一個(gè)參數(shù)為JoinPoint,則第二個(gè)參數(shù)為返回值的信息
* 如果參數(shù)中的第一個(gè)參數(shù)不為JoinPoint,則第一個(gè)參數(shù)為returning中對(duì)應(yīng)的參數(shù)
* returning 限定了只有目標(biāo)方法返回值與通知方法相應(yīng)參數(shù)類型時(shí)才能執(zhí)行后置返回通知,否則不執(zhí)行,對(duì)于returning對(duì)應(yīng)的通知方法參數(shù)為Object類型將匹配任何目標(biāo)返回值
* @param joinPoint
* @param keys
*/
@AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys")
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
System.out.println("第一個(gè)后置返回通知的返回值:"+keys);
}
@AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys",argNames = "keys")
public void doAfterReturningAdvice2(String keys){
System.out.println("第二個(gè)后置返回通知的返回值:"+keys);
}
Controller里添加響應(yīng)的請(qǐng)求處理信息來測(cè)試后置返回通知:
@RequestMapping("/testAfterReturning.do")
public String testAfterReturning(String key){
return "key=: "+key;
}
@RequestMapping("/testAfterReturning01.do")
public Integer testAfterReturning01(Integer key){
return key;
}
當(dāng)發(fā)送請(qǐng)求為:http://localhost:8001/aop/testAfterReturning.do?key=testsss&value=855sss時(shí),處理結(jié)果如圖所示:

當(dāng)發(fā)送請(qǐng)求為:http://localhost:8001/aop/testAfterReturning01.do?key=55553&value=855sss時(shí),處理結(jié)果如圖所示:

后置異常通知
后置異常通知的配置方式如下:
/**
* 后置異常通知
* 定義一個(gè)名字,該名字用于匹配通知實(shí)現(xiàn)方法的一個(gè)參數(shù)名,當(dāng)目標(biāo)方法拋出異常返回后,將把目標(biāo)方法拋出的異常傳給通知方法;
* throwing 限定了只有目標(biāo)方法拋出的異常與通知方法相應(yīng)參數(shù)異常類型時(shí)才能執(zhí)行后置異常通知,否則不執(zhí)行,
* 對(duì)于throwing對(duì)應(yīng)的通知方法參數(shù)為Throwable類型將匹配任何異常。
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = "executeService()",throwing = "exception")
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
//目標(biāo)方法名:
System.out.println(joinPoint.getSignature().getName());
if(exception instanceof NullPointerException){
System.out.println("發(fā)生了空指針異常!!!!!");
}
}
Controller里配置響應(yīng)的請(qǐng)求處理類:
@RequestMapping("/testAfterThrowing.do")
public String testAfterThrowing(String key){
throw new NullPointerException();
}
后置異常通知方法的處理結(jié)果如下所示:

后置最終通知
后置最終通知的配置方式如下:
/**
* 后置最終通知(目標(biāo)方法只要執(zhí)行完了就會(huì)執(zhí)行后置通知方法)
* @param joinPoint
*/
@After("executeService()")
public void doAfterAdvice(JoinPoint joinPoint){
System.out.println("后置通知執(zhí)行了!!!!");
}
Controller類配置相應(yīng)的請(qǐng)求處理類:
@RequestMapping("/testAfter.do")
public String testAfter(String key){
throw new NullPointerException();
}
@RequestMapping("/testAfter02.do")
public String testAfter02(String key){
return key;
}
當(dāng)發(fā)送請(qǐng)求為:http://localhost:8001/aop/testAfter.do?key=55553&value=855sss

當(dāng)發(fā)送請(qǐng)求為:http://localhost:8001/aop/testAfter02.do?key=55553&value=855sss

環(huán)繞通知
環(huán)繞通知的配置方式如下:
/**
* 環(huán)繞通知:
* 環(huán)繞通知非常強(qiáng)大,可以決定目標(biāo)方法是否執(zhí)行,什么時(shí)候執(zhí)行,執(zhí)行時(shí)是否需要替換方法參數(shù),執(zhí)行完畢是否需要替換返回值。
* 環(huán)繞通知第一個(gè)參數(shù)必須是org.aspectj.lang.ProceedingJoinPoint類型
*/
@Around("execution(* com.zkn.learnspringboot.web.controller..*.testAround*(..))")
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("環(huán)繞通知的目標(biāo)方法名:"+proceedingJoinPoint.getSignature().getName());
try {
Object obj = proceedingJoinPoint.proceed();
return obj;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
Controller對(duì)應(yīng)的請(qǐng)求處理類如下:
@RequestMapping("/testAroundService.do")
public String testAroundService(String key){
return "環(huán)繞通知:"+key;
}
當(dāng)發(fā)送請(qǐng)求為:http://localhost:8001/aop/testAroundService.do?key=55553

當(dāng)發(fā)送請(qǐng)求為:http://localhost:8001/aop/testAfter02.do?key=55553&value=855sss時(shí),不符合環(huán)繞通知的切入規(guī)則,所以環(huán)繞通知不會(huì) 執(zhí)行。
完整的AOP配置代碼如下:
package com.zkn.learnspringboot.aop;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.Map;
/**
* Created by zkn on 2016/11/18.
*/
@Component
@Aspect
public class WebControllerAop {
//匹配com.zkn.learnspringboot.web.controller包及其子包下的所有類的所有方法
@Pointcut("execution(* com.zkn.learnspringboot.web.controller..*.*(..))")
public void executeService(){
}
/**
* 前置通知,方法調(diào)用前被調(diào)用
* @param joinPoint
*/
@Before("executeService()")
public void doBeforeAdvice(JoinPoint joinPoint){
System.out.println("我是前置通知!!!");
//獲取目標(biāo)方法的參數(shù)信息
Object[] obj = joinPoint.getArgs();
//AOP代理類的信息
joinPoint.getThis();
//代理的目標(biāo)對(duì)象
joinPoint.getTarget();
//用的最多 通知的簽名
Signature signature = joinPoint.getSignature();
//代理的是哪一個(gè)方法
System.out.println(signature.getName());
//AOP代理類的名字
System.out.println(signature.getDeclaringTypeName());
//AOP代理類的類(class)信息
signature.getDeclaringType();
//獲取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//從獲取RequestAttributes中獲取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
//如果要獲取Session信息的話,可以這樣寫:
//HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
Enumeration<String> enumeration = request.getParameterNames();
Map<String,String> parameterMap = Maps.newHashMap();
while (enumeration.hasMoreElements()){
String parameter = enumeration.nextElement();
parameterMap.put(parameter,request.getParameter(parameter));
}
String str = JSON.toJSONString(parameterMap);
if(obj.length > 0) {
System.out.println("請(qǐng)求的參數(shù)信息為:"+str);
}
}
/**
* 后置返回通知
* 這里需要注意的是:
* 如果參數(shù)中的第一個(gè)參數(shù)為JoinPoint,則第二個(gè)參數(shù)為返回值的信息
* 如果參數(shù)中的第一個(gè)參數(shù)不為JoinPoint,則第一個(gè)參數(shù)為returning中對(duì)應(yīng)的參數(shù)
* returning 限定了只有目標(biāo)方法返回值與通知方法相應(yīng)參數(shù)類型時(shí)才能執(zhí)行后置返回通知,否則不執(zhí)行,對(duì)于returning對(duì)應(yīng)的通知方法參數(shù)為Object類型將匹配任何目標(biāo)返回值
* @param joinPoint
* @param keys
*/
@AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys")
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
System.out.println("第一個(gè)后置返回通知的返回值:"+keys);
}
@AfterReturning(value = "execution(* com.zkn.learnspringboot.web.controller..*.*(..))",returning = "keys",argNames = "keys")
public void doAfterReturningAdvice2(String keys){
System.out.println("第二個(gè)后置返回通知的返回值:"+keys);
}
/**
* 后置異常通知
* 定義一個(gè)名字,該名字用于匹配通知實(shí)現(xiàn)方法的一個(gè)參數(shù)名,當(dāng)目標(biāo)方法拋出異常返回后,將把目標(biāo)方法拋出的異常傳給通知方法;
* throwing 限定了只有目標(biāo)方法拋出的異常與通知方法相應(yīng)參數(shù)異常類型時(shí)才能執(zhí)行后置異常通知,否則不執(zhí)行,
* 對(duì)于throwing對(duì)應(yīng)的通知方法參數(shù)為Throwable類型將匹配任何異常。
* @param joinPoint
* @param exception
*/
@AfterThrowing(value = "executeService()",throwing = "exception")
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
//目標(biāo)方法名:
System.out.println(joinPoint.getSignature().getName());
if(exception instanceof NullPointerException){
System.out.println("發(fā)生了空指針異常!!!!!");
}
}
/**
* 后置最終通知(目標(biāo)方法只要執(zhí)行完了就會(huì)執(zhí)行后置通知方法)
* @param joinPoint
*/
@After("executeService()")
public void doAfterAdvice(JoinPoint joinPoint){
System.out.println("后置通知執(zhí)行了!!!!");
}
/**
* 環(huán)繞通知:
* 環(huán)繞通知非常強(qiáng)大,可以決定目標(biāo)方法是否執(zhí)行,什么時(shí)候執(zhí)行,執(zhí)行時(shí)是否需要替換方法參數(shù),執(zhí)行完畢是否需要替換返回值。
* 環(huán)繞通知第一個(gè)參數(shù)必須是org.aspectj.lang.ProceedingJoinPoint類型
*/
@Around("execution(* com.zkn.learnspringboot.web.controller..*.testAround*(..))")
public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("環(huán)繞通知的目標(biāo)方法名:"+proceedingJoinPoint.getSignature().getName());
try {//obj之前可以寫目標(biāo)方法執(zhí)行前的邏輯
Object obj = proceedingJoinPoint.proceed();//調(diào)用執(zhí)行目標(biāo)方法
return obj;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
完整的Controller類代碼如下:
package com.zkn.learnspringboot.web.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by zkn on 2016/11/19.
*/
@RestController
@RequestMapping("/aop")
public class AopTestController {
@RequestMapping("/testBeforeService.do")
public String testBeforeService(String key,String value){
return "key="+key+" value="+value;
}
@RequestMapping("/testAfterReturning.do")
public String testAfterReturning(String key){
return "key=: "+key;
}
@RequestMapping("/testAfterReturning01.do")
public Integer testAfterReturning01(Integer key){
return key;
}
@RequestMapping("/testAfterThrowing.do")
public String testAfterThrowing(String key){
throw new NullPointerException();
}
@RequestMapping("/testAfter.do")
public String testAfter(String key){
throw new NullPointerException();
}
@RequestMapping("/testAfter02.do")
public String testAfter02(String key){
return key;
}
@RequestMapping("/testAroundService.do")
public String testAroundService(String key){
return "環(huán)繞通知:"+key;
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
J2EE Servlet上傳文件到服務(wù)器并相應(yīng)顯示功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了J2EE Servlet上傳文件到服務(wù)器,并相應(yīng)顯示,在文中上傳方式使用的是post不能使用get,具體實(shí)例代碼大家參考下本文2018-07-07
將java普通項(xiàng)目打包成exe可執(zhí)行文件的步驟記錄
將JAVA代碼打包為exe文件,會(huì)讓程序運(yùn)行更加方便,這篇文章主要給大家介紹了關(guān)于將java普通項(xiàng)目打包成exe可執(zhí)行文件的相關(guān)資料,需要的朋友可以參考下2021-07-07
Java后臺(tái)Controller實(shí)現(xiàn)文件下載操作
這篇文章主要介紹了Java后臺(tái)Controller實(shí)現(xiàn)文件下載操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
JAVA按字節(jié)讀取文件的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄狫AVA按字節(jié)讀取文件的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08

