一、Spring AOP
1、Spring AOP的兩種實現方式
1.基於xml的Spring AOP開發
2.基於註解的Spring AOP開發
2.引入的jar包
spring-aop-4.3.3.RELEASE.jar,
aspectjweaver-1.8.5.jar
aspectjrt-1.8.5.jar
二、基於xml的Spring AOP開發
(1)創建applicationContext-xml-aop.xml,引入aop命名空間和xsd文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
</beans>
(2)在xml文件中將業務bean注入
<bean id="bankService" class="springboot.aop.xml.BankServiceImpl">
</bean>
<bean id="loggerAspect" class="springboot.aop.xml.LoggerAspect">
</bean>
(3)使用aop:aspect標籤配置代理信息
<aop:config>
<!--execution(* springboot.aop.xml.*.*(..))解釋 第一個*代表匹配所有參數,第二個代表匹配springboot.aop.xml包下的所有類,第三個表示匹配到的類中的所有方法,(..)表示匹配所匹配到方法的所有參數-->
<aop:pointcut expression="execution(* springboot.aop.xml.*.*(..))" id="loggerPointCut"/>
<aop:aspect ref="loggerAspect">
<!-- 前置通知,在被代理方法執行前執行LoggerAspect中的logerBefore()方法-->
<aop:before method="logerBefore" pointcut-ref="loggerPointCut"/>
<!-- 後置通知-->
<aop:after method="logerAfter" pointcut-ref="loggerPointCut"/>
<!-- 後置返回通知,屬性returning的值必須和方法logerAfterReturning返回值的參數名保持一致-->
<aop:after-returning method="logerAfterReturning" pointcut-ref="loggerPointCut" returning="returnValue"/>
<!-- 環繞通知-->
<aop:around method="logerAround" pointcut-ref="loggerPointCut"/>
<!-- 後置異常通知通知,拋出異常的時候執行-->
<aop:after-throwing method="loggerAfterThrowing" pointcut-ref="loggerPointCut" throwing="exception"/>
</aop:aspect>
</aop:config>
(4)LoggerAspect中的各種方法
public void logerBefore(JoinPoint jp) {
String methodName = jp.getSignature().getName();//可以獲取當前執行的方法名
System.out.println("前置通知: " + methodName + "將要被執行!");
Object[] args = jp.getArgs();//可以獲取參數
for(Object arg : args) {
System.out.println("參數爲:" + arg);
}
}
public void logerAfter(JoinPoint jp) {
String methodName = jp.getSignature().getName();
System.out.println("後置通知: " + methodName + "已經被執行!");
Object[] args = jp.getArgs();
for(Object arg : args) {
System.out.println("參數爲:" + arg);
}
}
public void logerAfterReturning(JoinPoint jp, Object returnValue) {
String methodName = jp.getSignature().getName();
System.out.println("後置返回通知 " + methodName + "已經被執行!");
System.out.println("後置返回通知 返回值:" + returnValue);
}
/**
* 環繞通知 需要配置返回值,否則目標方法所有的返回值都爲null
* @param pjp
* @return
*/
public Object logerAround(ProceedingJoinPoint pjp) {
String methodName = pjp.getSignature().getName();
System.out.println("環繞通知: " + methodName );
Object[] args = pjp.getArgs();數
args[0] = "改變參數";
try {
Object returnValue = pjp.proceed(args);
System.out.println("環繞通知: " + methodName +“返回值:” + returnValue);
return new BigDecimal("999999999999999");//可以改變參數的返回值
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public void loggerAfterThrowing(JoinPoint jp, Exception exception) {
System.out.println("後置異常通知:" + exception);
}
(4)被代理類的代碼
public class BankServiceImpl implements BankService{
public BigDecimal transfer( String source, String target,BigDecimal money) {
System.out.println(source + "向" + target + "轉賬:" + money);
// throw new RuntimeException("故意出的異常");
return new BigDecimal("12345678");
}
}
(5)Main類中方法調用
public class Main {
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-xml-aop.xml");
BankService bs = ioc.getBean("bankService", BankService.class);
bs.transfer("你", "我", new BigDecimal("100000000"))
}
}
(6)分析
調用main方法時,首先創建了一個ioc容器,後面的參數(“applicationContext-xml-aop.xml”)代表的是該容器所對應的文件,然後通過ioc容器調用了getBean方法生成了一個bs對象,然後bs對象調用transfer方法。
執行過程中,因爲xml文件中註冊了LoggerAspect,所以transfer方法會被截住,執行一系列通知方法。其中後置返回通知方法能夠改變方法的參數以及返回值
三、基於註解的Spring AOP開發
1.開發過程
(1)引入aop命名空間和xsd文件(同xml)
(2)在xml中進行配置
<!-- 自動掃描注入springboot.aop.anno包下的內容-->
<context:component-scan base-package="springboot.aop.anno"></context:component-scan>
<!-- 開啓Spring AOP 自動代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
(3)業務類BankServiceImpl加註解@Service(“bankService”),將其注入到IOC容器中
(4)在通知類LoggerAspect中編寫代碼
//@Aspect:作用是把當前類標識爲一個切面供容器讀取
//@Order(0):當有多個通知類時決定優先級,數字越小優先級越高
//@Component:將該類注入到ioc容器
@Aspect
@Order(0)
@Component
public class TrancationAspect {
@Pointcut("execution(* springboot.aop.anno.*.*(..))")
public void logPointCut() {
}
//PointCut封裝,封裝之後在註解中execution(* springboot.aop.anno.*.*(..))等價於logPointCut()
//@Before("execution(* springboot.aop.anno.*.*(..))")因爲PointCut已經被封裝,所以本行跟下一行效果相同
@Before("logPointCut()")
public void logerBefore(JoinPoint jp) {
//AspectJ使用org.aspectj.lang.JoinPoint接口表示目標類連接點對象
String methodName = jp.getSignature().getName();
System.out.println("TrancationAspect 事務前置通知:method: " + methodName + "將要被執行!");
Object[] args = jp.getArgs();
for(Object arg : args) {
System.out.println("=============參數:>" + arg);
}
}
}
(5)Main類中調用,代碼如下
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-anno-aop.xml");
BankService bs = ioc.getBean("bankService", BankService.class);
bs.transfer("你", "我", new BigDecimal("100"));
}
2、分析
通過註解的方式進行Spring AOP開發,主要分三步
(1)將業務類以及通知類注入到IOC容器
(2)編寫業務類業務代碼,在通知類中編寫出發通知時的方法
(3)編寫主函數出發通知類的通知
(4)執行邏輯跟xml方式一樣,只不過是實現方式不同