Java SpringBoot實(shí)現(xiàn)AOP
1、AOP基本總結(jié)
連接點(diǎn)(
JoinPoint):連接點(diǎn)是程序運(yùn)行的某個(gè)階段點(diǎn),如方法調(diào)用、異常拋出等
切入點(diǎn)(
Pointcut):切入點(diǎn)是
JoinPoint的集合
是程序中需要注入Advice的位置的集合,即Advice在什么條件下才能被觸發(fā)增強(qiáng)(
Advisor):增強(qiáng)是切入點(diǎn)
Pointcut和Advice的綜合體,即在連接點(diǎn)JoinPoint上執(zhí)行的行為
通過JDK/CGLIB代理模式實(shí)現(xiàn)AOP切面(
Aspect):@Aspect通常是一個(gè)類的注解,通常與
@Component搭配使用AOP代理(
AOP Proxy):AOP使用動(dòng)態(tài)代理模式創(chuàng)建對象,從而實(shí)現(xiàn)在連接點(diǎn)
JoinPoint處插入增強(qiáng)
其中JDK只能代理接口,CGLIB基于子類但不能代理final類
2、常用方法

3、增強(qiáng)類型
@Before:前置增強(qiáng),在某個(gè)JoinPoint執(zhí)行前的增強(qiáng)@After:final增強(qiáng),不管拋異常還是正常退出都執(zhí)行的增強(qiáng)@AfterReturning:后置增強(qiáng),方法正常退出時(shí)執(zhí)行@AfterThrowing:異常拋出增強(qiáng),拋出異常后執(zhí)行@Around:環(huán)繞增強(qiáng),包圍一個(gè)連接點(diǎn)的增強(qiáng),最強(qiáng)大的一個(gè)方式,且常用
4、示例說明
學(xué)了一下AOP的使用,寫了個(gè)@Around的demo,將幾個(gè)查詢操作存入數(shù)據(jù)庫作為Log并且定時(shí)清理過期數(shù)據(jù)
本人的Demo用的是Dubbo框架,而AOP的示例寫在了Provider中,大概結(jié)構(gòu)如下:

monitor:
annotation:注解類aop:切面的定義及實(shí)現(xiàn)impl:UserAopTask接口的實(shí)現(xiàn)類
1)UserLog實(shí)體類
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class UserLog implements Serializable {
private Integer id;
private String methodName;
private String methodArgs;
private String classFullName;
private String className;
private Date invokeTime;
private Double costTime;
}
2)LogTaskMapper對應(yīng)mapper接口
public interface LogTaskMapper {
/**
* TEST AOP INSERT INFO INTO TABLE
* @param userLog
*/
void insertUserLog(UserLog userLog);
/**
* DELETE LOGS IN TABLE LAST x MINUTES
* @param minutes
*/
void deleteUserLog(int minutes);
}
3)UserAopTask接口
public interface UserAopTask {
void insertUserLog(UserLog log);
}
4)UserAopTaskImpl實(shí)現(xiàn)類
@Component
public class UserAopTaskImpl implements UserAopTask {
private static final Logger logger = LoggerFactory.getLogger(UserAopTask.class);
@Autowired
private LogTaskMapper logTaskMapper;
private ExecutorService logHandler = Executors.newFixedThreadPool(1);//采用線程池復(fù)用一個(gè)線程執(zhí)行
private static final int MINUTES_LOG_RETAIN = 30;//數(shù)據(jù)庫中數(shù)據(jù)保留時(shí)間
@Override
public void insertUserLog(UserLog log) {
logHandler.submit(new logSubmitTask(log));
}
//內(nèi)部類
class logSubmitTask implements Runnable{
private UserLog userLog;
public logSubmitTask(UserLog userLog){
this.userLog = userLog;
}
@Override
public void run() {
logTaskMapper.insertUserLog(userLog);
}
}
//定時(shí)清理任務(wù)
@Scheduled(cron = "0 30 * * * *")
public void scheduledDeleteLog(){
logger.info("開始清除[{}]分鐘之前的圖表查詢?nèi)罩?..", MINUTES_LOG_RETAIN);
logTaskMapper.deleteUserLog(-1 * MINUTES_LOG_RETAIN);
}
}
5)TestUserAop切面類
@Aspect//切面
@Component//Spring容器管理
public class TestUserAop {
private static final Logger logger = LoggerFactory.getLogger(TestUserAop.class);
@Autowired
private UserAopTask userAopTask;
//使用環(huán)繞增強(qiáng),第一參數(shù)必須是ProceedingJoinPoint
@Around(value = "@annotation(annotation)")//和注解類參數(shù)名保持一致
public Object aroundUserInfo(ProceedingJoinPoint pjp, TestUserAnnotation annotation) throws Throwable{
UserLog userLog = new UserLog();
System.out.println("=====================ANNOTATION BEGIN=====================");
Date date = new Date();
Long methodStart = date.getTime();//timestamp
System.out.println("ANNOTATION 開始耗時(shí)統(tǒng)計(jì): "+ date);
userLog.setInvokeTime(date);
Object[] argsObj = pjp.getArgs();
Object res = pjp.proceed(argsObj);//利用反射調(diào)用目標(biāo)方法
Long methodCost = System.currentTimeMillis() - methodStart;
double cost = methodCost/1000d;//timestamp 轉(zhuǎn)換為 seconds
System.out.println("ANNOTATION 調(diào)用方法總耗時(shí): "+ String.format("%.3f",cost) +" s");//保留3位小數(shù)
System.out.println("ANNOTATION 調(diào)用方法: "+annotation.methodName());//目標(biāo)方法
System.out.println("ANNOTATION 調(diào)用方法參數(shù): "+ new Integer((Integer) argsObj[0]));//我的參數(shù)就1個(gè)或者無參
System.out.println("ANNOTATION 調(diào)用類: "+pjp.getSignature().getDeclaringTypeName());//全類名
System.out.println("ANNOTATION 調(diào)用類名: "+pjp.getSignature().getDeclaringType().getSimpleName());//類名
System.out.println("ANNOTATION 調(diào)用結(jié)果: "+ JSON.toJSON(res));
System.out.println("=====================ANNOTATION FINISHED=====================");
userLog.setCostTime(Double.parseDouble(String.format("%.3f",cost)));
userLog.setClassFullName(pjp.getSignature().getDeclaringTypeName());
userLog.setClassName(pjp.getSignature().getDeclaringType().getSimpleName());
userLog.setMethodName(annotation.methodName());
userLog.setMethodArgs(Integer.toString(new Integer((Integer) argsObj[0])));
userAopTask.insertUserLog(userLog);
return res;
}
}
6)TestUserAnnotation注解類
我在service層寫的AOP demo,對目標(biāo)方法使用注解,注解名為注解類名即可,如@TestUserAnnotation
@Retention(RetentionPolicy.RUNTIME)//運(yùn)行時(shí)有效
@Target(ElementType.METHOD)//作用于方法
@Documented
public @interface TestUserAnnotation {
String methodName() default "";//方法名,默認(rèn)為空字符串
}
7)LogTaskMapper.xml
最后貼個(gè)代碼,為上面提到的定時(shí)任務(wù),用到的是date_add()方法,其中的 "<" 意為 "<"
<delete id="deleteUserLog" parameterType="java.lang.Integer">
delete from invoke_log
where invoke_time < date_add(current_timestamp,interval #{minutes} minute)
</delete>
5、結(jié)果展示
演示一下AOP的效果,將@TestUserAnnotation注解在方法getUserInfo(),即獲取用戶信息
Demo中利用AOP的@Around環(huán)繞增強(qiáng),實(shí)現(xiàn)了統(tǒng)計(jì)方法調(diào)用運(yùn)行消耗時(shí)間,以及統(tǒng)計(jì)調(diào)用方法名、類名等信息:

調(diào)用方法getUserInfo后的統(tǒng)計(jì)結(jié)果:

到此這篇關(guān)于Java SpringBoot實(shí)現(xiàn)AOP 的文章就介紹到這了,更多相關(guān)SpringBoot實(shí)現(xiàn)AOP 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IKAnalyzer結(jié)合Lucene實(shí)現(xiàn)中文分詞(示例講解)
下面小編就為大家?guī)硪黄狪KAnalyzer結(jié)合Lucene實(shí)現(xiàn)中文分詞(示例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法
這篇文章主要介紹了java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法,在具體生成id的時(shí)候,我們的操作順序一般是:先在主表中插入記錄,然后獲得自動(dòng)生成的id,以它為基礎(chǔ)插入從表的記錄,需要的朋友可以參考下2019-06-06
mybatis學(xué)習(xí)筆記之mybatis注解配置詳解
本篇文章主要介紹了mybatis學(xué)習(xí)筆記之mybatis注解配置詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
JAVA中通過Hibernate-Validation進(jìn)行參數(shù)驗(yàn)證
這篇文章主要介紹了JAVA中通過Hibernate-Validation進(jìn)行參數(shù)驗(yàn)證,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
java如何將實(shí)體類轉(zhuǎn)換成json并在控制臺(tái)輸出
這篇文章主要介紹了java如何將實(shí)體類轉(zhuǎn)換成json并在控制臺(tái)輸出問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Json字符串轉(zhuǎn)Java對象和List代碼實(shí)例
這篇文章主要介紹了Json字符串轉(zhuǎn)Java對象和List代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06

