Spring AOP 切面@Around注解的用法說明
@Around注解可以用來在調(diào)用一個(gè)具體方法前和調(diào)用后來完成一些具體的任務(wù)。
比如我們想在執(zhí)行controller中方法前打印出請求參數(shù),并在方法執(zhí)行結(jié)束后來打印出響應(yīng)值,這個(gè)時(shí)候,我們就可以借助于@Around注解來實(shí)現(xiàn);
再比如我們想在執(zhí)行方法時(shí)動(dòng)態(tài)修改參數(shù)值等
類似功能的注解還有@Before等等,用到了Spring AOP切面思想,Spring AOP常用于攔截器、事務(wù)、日志、權(quán)限驗(yàn)證等方面。
完整演示代碼如下:
需要說明的是,在以下例子中,我們即可以只用@Around注解,并設(shè)置條件,見方法run1();也可以用@Pointcut和@Around聯(lián)合注解,見方法pointCut2()和run2(),這2種用法是等價(jià)的。如果我們還想利用其進(jìn)行參數(shù)的修改,則調(diào)用時(shí)必須用joinPoint.proceed(Object[] args)方法,將修改后的參數(shù)進(jìn)行回傳。如果用joinPoint.proceed()方法,則修改后的參數(shù)并不會真正被使用。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
/**
* 控制器切面
*
* @author lichuang
*/
@Component
@Aspect
public class ControllerAspect {
private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);
@Autowired
private EntityManager entityManager;
/**
* 調(diào)用controller包下的任意類的任意方法時(shí)均會調(diào)用此方法
*/
@Around("execution(* com.company.controller.*.*(..))")
public Object run1(ProceedingJoinPoint joinPoint) throws Throwable {
//獲取方法參數(shù)值數(shù)組
Object[] args = joinPoint.getArgs();
//得到其方法簽名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//獲取方法參數(shù)類型數(shù)組
Class[] paramTypeArray = methodSignature.getParameterTypes();
if (EntityManager.class.isAssignableFrom(paramTypeArray[paramTypeArray.length - 1])) {
//如果方法的參數(shù)列表最后一個(gè)參數(shù)是entityManager類型,則給其賦值
args[args.length - 1] = entityManager;
}
logger.info("請求參數(shù)為{}",args);
//動(dòng)態(tài)修改其參數(shù)
//注意,如果調(diào)用joinPoint.proceed()方法,則修改的參數(shù)值不會生效,必須調(diào)用joinPoint.proceed(Object[] args)
Object result = joinPoint.proceed(args);
logger.info("響應(yīng)結(jié)果為{}",result);
//如果這里不返回result,則目標(biāo)對象實(shí)際返回值會被置為null
return result;
}
@Pointcut("execution(* com.company.controller.*.*(..))")
public void pointCut2() {}
@Around("pointCut2()")
public Object run2(ProceedingJoinPoint joinPoint) throws Throwable {
//獲取方法參數(shù)值數(shù)組
Object[] args = joinPoint.getArgs();
//得到其方法簽名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//獲取方法參數(shù)類型數(shù)組
Class[] paramTypeArray = methodSignature.getParameterTypes();
if (EntityManager.class.isAssignableFrom(paramTypeArray[paramTypeArray.length - 1])) {
//如果方法的參數(shù)列表最后一個(gè)參數(shù)是entityManager類型,則給其賦值
args[args.length - 1] = entityManager;
}
logger.info("請求參數(shù)為{}",args);
//動(dòng)態(tài)修改其參數(shù)
//注意,如果調(diào)用joinPoint.proceed()方法,則修改的參數(shù)值不會生效,必須調(diào)用joinPoint.proceed(Object[] args)
Object result = joinPoint.proceed(args);
logger.info("響應(yīng)結(jié)果為{}",result);
//如果這里不返回result,則目標(biāo)對象實(shí)際返回值會被置為null
return result;
}
}
補(bǔ)充:Spring Aop實(shí)例(AOP 如此簡單)@Aspect、@Around 注解方式配置
IoC相關(guān)的基本內(nèi)容告一段落,本次介紹Spring的第二個(gè)特性,AOP,面向切面編程,術(shù)語聽起來比較不容易理解,沒關(guān)系,一切盡在實(shí)例中,讓我們看一個(gè)簡單的實(shí)例,就能明白。
實(shí)例
項(xiàng)目工程目錄結(jié)構(gòu)和代碼獲取地址
獲取地址(版本Log將會注明每一個(gè)版本對應(yīng)的課程)
https://github.com/laiyijie/SpringLearning
目錄結(jié)構(gòu)

運(yùn)行工程
運(yùn)行具有Main函數(shù)的 App.java
得到如下輸出
method start time:1480223298250 userHello method end time:1480223299250
項(xiàng)目詳解
從App.java入手
App.java
package me.laiyijie.demo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import me.laiyijie.demo.service.HelloInterface;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("root-context.xml");
HelloInterface userService = context.getBean(HelloInterface.class);
userService.sayHello();
context.close();
}
}
調(diào)用的是HelloInterface的sayHello方法
HelloInterface.java
package me.laiyijie.demo.service;
public interface HelloInterface{
void sayHello();
}
其實(shí)現(xiàn)類為UserServiceImpl.java
UserServiceImpl.java
package me.laiyijie.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements HelloInterface {
public void sayHello() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("userHello");
}
}
誒?情況跟我們看到的代碼有出入?
sayHello 應(yīng)該只輸出 userHello,前后兩行輸出從何出現(xiàn)?
在Main函數(shù)中找不到一點(diǎn)兒線索!
這就是AOP的一個(gè)強(qiáng)大特性:
無侵入性,不改變原有的代碼,卻能增加功能!
那么究竟是如何增加功能的呢?
讓我們看看TimeMonitor.java
TimeMonitor.java
package me.laiyijie.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class TimeMonitor {
@Around("execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))")
public void monitorAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("method start time:" + System.currentTimeMillis());
Object re = pjp.proceed();
System.out.println("method end time:" + System.currentTimeMillis());
}
}
終于看到了 method start time:1480223298250 和 method end time:1480223299250這兩行輸出是從哪兒出現(xiàn)的了!
讓我們來仔細(xì)解讀一下這個(gè)類
類有兩個(gè)注釋,分別是@Service和@Aspect,第一個(gè)注解是使得TimeMonitor受Spring托管并實(shí)例化。@Aspect就是使得這個(gè)類具有AOP功能(你可以這樣理解)兩個(gè)注解缺一不可
類里面只有一個(gè)方法,名字叫做monitorAroud,其實(shí)就是為了檢測函數(shù)執(zhí)行時(shí)間的!
那么關(guān)鍵點(diǎn)來了,兩個(gè)輸出語句是怎么插入到sayHello方法的前后的呢!
看這個(gè)注解:
@Around("execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))")
@Around表示包圍一個(gè)函數(shù),也就是可以在函數(shù)執(zhí)行前做一些事情,也可以在函數(shù)執(zhí)行后做一些事情
execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))
這個(gè)比較好理解,就是使用表達(dá)式的方式指定了要對哪個(gè)函數(shù)進(jìn)行包圍?。ǔ薳xecution以外還有很多,可以搜索AspectJ語法來學(xué)習(xí))
也就是說,這個(gè)注解完整的說明了,應(yīng)該在函數(shù)的什么位置插入變化,也就是所謂的切點(diǎn)
之后是函數(shù)的定義:
public Object monitorAround(ProceedingJoinPoint pjp)
這里引入了ProceedingJoinPoint,在使用了@Around之后可以帶入這個(gè)參數(shù),代表的其實(shí)就是sayHello這個(gè)函數(shù),不過做了一些封裝
而 Object re = pjp.proceed(); 就是相當(dāng)于執(zhí)行了 sayHello方法!
剩下的代碼就不用過多解釋了,就是在執(zhí)行這個(gè)函數(shù)的前后分別進(jìn)行了系統(tǒng)時(shí)間的獲取。
我們把這個(gè)函數(shù)體,也就是定義了要做那些事情的代碼,稱作增強(qiáng)
而包含切點(diǎn)和增強(qiáng)結(jié)合起來就稱作切面
面向切面由此而來!
Spring AOP 開啟需要的配置
需要配置兩項(xiàng)
1、pom.xml增加依賴(因?yàn)橐玫紸OP還需要不同的JAR包)
2、root-context.xml中增加切面相關(guān)配置
root-context.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-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <context:component-scan base-package="me.laiyijie.demo"></context:component-scan> </beans>
root-context.xml 增加了兩行
1、xmlns:aop="http://www.springframework.org/schema/aop"
代表加入命名空間
2、<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
使用1中引入的aop命名空間開起自動(dòng)代理(自動(dòng)代理具體含義后續(xù)慢慢解釋,簡單的理解就是AOP的實(shí)現(xiàn)是依靠自動(dòng)代理實(shí)現(xiàn)的)
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.laiyijie</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> </dependencies> </project>
增加了一個(gè)依賴
AspectJ 一個(gè)強(qiáng)大的AOP框架,也就是@Aspect和@Around以及ProceedingJoinPoint這些注解和方法的提供者
小結(jié)
增強(qiáng):定義了應(yīng)該怎么把額外的動(dòng)作加入到指定函數(shù)中
切點(diǎn):定義了你應(yīng)該把增強(qiáng)插入到哪個(gè)函數(shù)的什么位置
切面:切點(diǎn)和增強(qiáng)組合起來的稱呼
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Java虛擬機(jī)JVM優(yōu)化實(shí)戰(zhàn)的過程全記錄
有人說Java之所以能夠崛起,JVM功不可沒。Java虛擬機(jī)最初服務(wù)于讓Java語言凌駕于平臺之上,實(shí)現(xiàn)“編寫一次,到處運(yùn)行”,那么下面這篇文章主要給大家分享了個(gè)關(guān)于Java虛擬機(jī)JVM優(yōu)化實(shí)戰(zhàn)的過程全記錄,需要的朋友可以參考借鑒,下面來一起看看吧。2017-08-08
Java下載Excel模板文件的簡單實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Java下載Excel模板文件的簡單實(shí)現(xiàn)方法,日常工作中可能經(jīng)常會涉及到用java開發(fā)報(bào)表,需求比較多的就是表格類的報(bào)表導(dǎo)出,單元格合并,圖表的展現(xiàn),需要的朋友可以參考下2023-07-07
解決 Spring RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)問題
本文詳解說明了RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)的問題及其原由,需要的朋友可以參考下2020-02-02
Kotlin Coroutines執(zhí)行異步加載示例詳解
這篇文章主要給大家介紹了關(guān)于Kotlin Coroutines執(zhí)行異步加載的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
java WebSocket客戶端斷線重連的實(shí)現(xiàn)方法
在工作中是否會遇到實(shí)用websocket客戶端連接服務(wù)端的時(shí)候,網(wǎng)絡(luò)波動(dòng),服務(wù)端斷連的情況,本文可以直接使用的斷線重連,感興趣的可以了解一下2021-10-10
Springboot-Starter造輪子之自動(dòng)鎖組件lock-starter實(shí)現(xiàn)
這篇文章主要為大家介紹了Springboot-Starter造輪子之自動(dòng)鎖組件lock-starter實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
Java9新特性對HTTP2協(xié)議支持與非阻塞HTTP?API
這篇文章主要為大家介紹了Java9新特性對HTTP2協(xié)議的支持與非阻塞HTTP?API,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
mybatis-plus的selectById(或者selectOne)在根據(jù)主鍵ID查詢實(shí)體對象的時(shí)候偶爾會出現(xiàn)nul
這篇文章主要介紹了mybatis-plus的selectById(或者selectOne)在根據(jù)主鍵ID查詢實(shí)體對象的時(shí)候偶爾會出現(xiàn)null的問題記錄,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
springboot+thymeleaf國際化之LocaleResolver接口的示例
本篇文章主要介紹了springboot+thymeleaf國際化之LocaleResolver的示例 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11

