配置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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置spring創建容器時要掃描的包-->
<context:component-scan base-package="com.hr"></context:component-scan>
<!--配置spring開啓註解AOP的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
將業務類和日誌類加入容器中:
package com.hr.service.impl;
import com.hr.service.IAccountService;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
public void saveAccount() {
System.out.println("執行了保存");
// int i = 1 /0;
}
public void updateAccount(int i) {
System.out.println("執行了更新" + i);
}
public int deleteAccount() {
System.out.println("執行了刪除");
return 0;
}
}
Logger:
package com.hr.service.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 日誌工具類
*/
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* com.hr.service.impl.*.*(..))")
private void pt1(){}
/**
* 打印日誌, 計劃讓其在切入點方法執行之前執行(切入點方法就是要被增強的業務層方法)
* 前置通知
*/
@Before("pt1()")
public void beforPrintLog() {
System.out.println("前置通知Logger類中的beforPrintLog方法開始記錄日誌了");
}
/**
* 後置通知
*/
@AfterReturning("pt1()")
public void afterReturnPrintLog() {
System.out.println("後置通知Logger類中的beforPrintLog方法開始記錄日誌了");
}
/**
* 異常通知
*/
@AfterThrowing("pt1()")
public void afterThrowingPrintLog() {
System.out.println("異常通知Logger類中的beforPrintLog方法開始記錄日誌了");
}
/**
* 最終通知
*/
@After("pt1()")
public void afterPrintLog() {
System.out.println("最終通知Logger類中的beforPrintLog方法開始記錄日誌了");
}
/**
* 環繞通知
* 問題:
* 當配置了環繞通知之後,切入點方法沒有執行,而通知方法執行了
* 分析:
* 通過對比動態代理中環繞通知的代碼,發現動態代理的環繞通知有明確的切入點方法調用,而我們的代碼沒有
* 解決:
* Spring框架爲我們提供了一個接口,ProceedingJoinPoint, 該接口有一個方法proceed(),此方法就相當於明確調用切入點方法
* 該接口可以作爲環繞通知的方法參數, 在程序執行時,spring會爲我們提供該接口的實現類供我們使用
* spring環繞通知作用:
* 它是spring框架爲我們提供的一種可以在代碼中手動控制增強方法何時執行的方式
*/
// @Around("pt1()")
public Object aroundPrintLog(ProceedingJoinPoint pjp) {
Object rtValue = null;
try {
Object[] args = pjp.getArgs();
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了...前置");
rtValue = pjp.proceed(args); //明確調用業務層方法(切入點方法)
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了... 後置");
return rtValue;
} catch (Throwable throwable) {
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了... 異常");
throw new RuntimeException(throwable);
}finally {
System.out.println("Logger類中的aroundPrintLog方法開始記錄日誌了... 最終");
}
}
}
注意: 四種通知和環繞通知不能一起實現
測試類:
package com.hr.text;
import com.hr.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService iAccountService = ac.getBean("accountService",IAccountService.class);
iAccountService.saveAccount();
}
}
接上一講xml配置AOP, 註解配置相對簡單,在對要織入的Logger類中方法上寫各個通知類型即可, Spring對註解上面的命名很規範,但還是要注意切入點表達式的書寫
github: https://github.com/2402zmybie/spring03_05annotationAop