Spring AOP基礎(chǔ)
AOP
1.什么是AOP
AOP(Aspect Oriented Programming)面向切面的編程,AOP是基于OOP,并建立在OOP之上的編程思想,OOP主要面對(duì)的是對(duì)象,而AOP是面向?qū)ο蟮那忻?在處理日志,安全管理,事務(wù)管理等方面有重要作用.
AOP的基礎(chǔ)概念就是在不修改原有代碼的情況下,增強(qiáng)和主要業(yè)務(wù)無(wú)關(guān)的公共功能到原本已經(jīng)完成的指定代碼位置.
AOP的底層設(shè)計(jì)模式是使用的代理模式,通過(guò)代理去處理原本的業(yè)務(wù),并在原本業(yè)務(wù)的前后增加一些額外的功能;
代理模式有兩種實(shí)現(xiàn):
1. 靜態(tài)代理
需要為每一個(gè)被代理類創(chuàng)建一個(gè)代理類,在代理類中開發(fā)對(duì)被代理類的額外功能處理.
2. 動(dòng)態(tài)代理
不去創(chuàng)建確定的代理類,在java通過(guò)反射等方式動(dòng)態(tài)創(chuàng)建代理類,進(jìn)行方法的代理
通過(guò)jdk提供proxy實(shí)現(xiàn)一個(gè)簡(jiǎn)單動(dòng)態(tài)代理
使用環(huán)境類
package com.learn.bean;
import org.springframework.stereotype.Component;
/**
* @author wangxing
* @version 2020/6/28 15:05 Administrator
*/
@Component
public class User {
private Long id;
private String userName;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.learn.dao;
import com.learn.bean.User;
/**
* @author wangxing
* @version 2020/6/28 15:06 Administrator
*/
public interface IUserDao {
User select(Long id);
void add(User user);
void delete(Long id);
void update(Long id,User user);
}
package com.learn.dao.impl;
import com.learn.bean.User;
import com.learn.dao.IUserDao;
import org.springframework.stereotype.Repository;
/**
* @author wangxing
* @version 2020/6/28 15:06 Administrator
*/
@Repository
public class UserDaoImpl implements IUserDao {
public User select(Long id) {
if (id == 0) {
throw new RuntimeException("select exception: id is 0");
}
System.out.println("查詢user");
User user = new User();
user.setId(id);
user.setPassword(String.valueOf(System.currentTimeMillis()).substring(8));
user.setUserName(String.valueOf(System.currentTimeMillis()).substring(5));
return user;
}
public void add(User user) {
if (user == null) {
throw new RuntimeException("add exception: user is null");
}
System.out.println("創(chuàng)建 user");
}
public void delete(Long id) {
if (id == 0) {
throw new RuntimeException(" delete exception: id is 0");
}
System.out.println("刪除user");
}
public void update(Long id, User user) {
if (id == 0) {
throw new RuntimeException("update exception: id is 0");
}
System.out.println("更新use");
}
}
自定義動(dòng)態(tài)代理類
package com.learn.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 根據(jù)jdk提供的proxy自定義動(dòng)態(tài)代理,僅供實(shí)現(xiàn)接口的類使用
*
* @author wangxing
* @version 2020/6/29 9:17 Administrator
*/
public class MyProxy {
public static Object getProxy(final Object t) {
//類加載器:被代理類的接口的類加載
ClassLoader loader = t.getClass().getClassLoader();
//類型:被代理類的接口的類型
Class<?>[] interfaces = t.getClass().getInterfaces();
//委托執(zhí)行的處理類:在類內(nèi)部的invoke方法中進(jìn)行對(duì)方法的加強(qiáng)
InvocationHandler h = new InvocationHandler() {
@Override
/**
* 執(zhí)行目標(biāo)對(duì)象的方法
* @param proxy 代理對(duì)象,用于給jdk使用,無(wú)需我們自己操作
* @param method 當(dāng)前將要執(zhí)行的目標(biāo)對(duì)象的方法
* @param args 當(dāng)前方法外部調(diào)用使用的參數(shù)
* @return
* @throws Throwable
*/
public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
Object result = null;
try {
System.out.println("執(zhí)行前執(zhí)行");
result = method.invoke(t, args);
System.out.println("執(zhí)行后執(zhí)行");
} catch (Exception e) {
System.out.println("執(zhí)行異常退出時(shí)執(zhí)行");
} finally {
System.out.println("執(zhí)行退出時(shí)執(zhí)行");
}
return result;
}
};
return Proxy.newProxyInstance(loader, interfaces, h);
}
}
測(cè)試類
package com.learn.proxy;
import com.learn.dao.IUserDao;
import com.learn.dao.impl.UserDaoImpl;
import org.junit.Test;
/**
* @author wangxing
* @version 2020/6/29 9:33 Administrator
*/
public class MyProxyTest {
@Test
public void getProxy() {
IUserDao userDao = new UserDaoImpl();
System.out.println("直接調(diào)用方法");
userDao.select(1L);
System.out.println(userDao.getClass());
System.out.println("--------分割線--------");
userDao = (IUserDao) MyProxy.getProxy(userDao);
System.out.println("使用代理調(diào)用方法");
System.out.println(userDao.getClass());
userDao.select(1L);
}
}
通過(guò)輸出結(jié)果可以看到userDao的類型從UserDaoImpl變?yōu)榱?class com.sun.proxy.$Proxy24
證明新的對(duì)象是通過(guò)動(dòng)態(tài)代理生成的實(shí)例而不是原本的實(shí)現(xiàn)類
SpringAOP有兩種動(dòng)態(tài)實(shí)現(xiàn)方式:
- 使用jdk提供的動(dòng)態(tài)代理Proxy:使用JDK提供的動(dòng)態(tài)代理需要類本身要進(jìn)行代理的類實(shí)現(xiàn)了某個(gè)接口,在動(dòng)態(tài)代理過(guò)程中對(duì)這個(gè)接口的暴露方法進(jìn)行代理,在方法的前后提供額外的操作.
- 使用CGLIB動(dòng)態(tài)代理:不需要實(shí)現(xiàn)接口,直接使用類就可以進(jìn)行代理,但是效率方面低于jdk提供的動(dòng)態(tài)代理,實(shí)現(xiàn)功能上是一致的
這兩種實(shí)現(xiàn)在我們使用時(shí)是體驗(yàn)不到區(qū)別的,spring會(huì)根據(jù)情況自行選擇
進(jìn)行SpringAOP實(shí)現(xiàn)
基礎(chǔ)配置引入spring aop所需依賴
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
1.通過(guò)配置文件方式進(jìn)行AOP操作
1.配置spring.xml文件進(jìn)行aop設(shè)置
<bean class="com.learn.dao.impl.UserDaoImpl" id="userDao" ></bean>
<bean class="com.learn.aspect.LogXmlAspect" id="logXmlAspect"></bean>
<aop:config>
<!-- 配置aop匹配方式
execution模式細(xì)粒度配置匹配方式 配置切入點(diǎn),將AOP切入到什么類,什么方法中
第一段 切點(diǎn)的標(biāo)識(shí)符(public,private等) 可以不寫,不寫為全部標(biāo)識(shí)符號(hào)
<aop:pointcut id="globalPoint" expression="execution(* com.learn.dao.*.*(..))"/>
<aop:pointcut id="globalPointPublic" expression="execution(public * com.learn.dao.*.*(..))"/>
第二段 * 表示返回類型,*表示全部返回類型,可以指定特定的返回類型,系統(tǒng)類和void直接寫入類名即可,自定義類需要寫入全路徑名
<aop:pointcut id="myPoint" expression="execution(* com.learn.service..*.*(..))"/>
<aop:pointcut id="myPointUser" expression="execution(com.learn.bean.User com.learn.service..*.*(..))"/>
<aop:pointcut id="myPointVoid" expression="execution(void com.learn.service..*.*(..))"/>
第三段 要進(jìn)行匹配的的類名包名,寫到類名就是匹配到當(dāng)前類,寫到包名后一個(gè).表示匹配當(dāng)前包之下的類(com.learn.dao.)寫..表示當(dāng)前包和當(dāng)前包的子孫包都進(jìn)行匹配當(dāng)前規(guī)則(com.learn.service..)
<aop:pointcut id="myPoint" expression="execution(* com.learn.service.*.*(..))"/>
<aop:pointcut id="myPointAffiliatePacket" expression="execution(* com.learn.service..*.*(..))"/>
<aop:pointcut id="myPointUserService" expression="execution(* com.learn.service.IUserService.*(..))"/>
第四段 匹配的方法名,寫*表示匹配全部方法名稱,可以指定具體方法名
<aop:pointcut id="globalPointUserDao" expression="execution(* com.learn.dao.IUserDao.*(..))"/>
<aop:pointcut id="globalPointUserDaoSelect" expression="execution(public * com.learn.dao.IUserDao.select(..))"/>
第五段 匹配的方法參數(shù) 可寫入確認(rèn)內(nèi)容根據(jù)參數(shù)去進(jìn)行匹配,也可以使用..表示匹配所有的參數(shù)情況
<aop:pointcut id="globalPointUserDaoSelect" expression="execution(public * com.learn.dao.IUserDao.select(Long))"/>
<aop:pointcut id="globalPointUserDaoSelect" expression="execution(public * com.learn.dao.IUserDao.select(..))"/>
-->
<aop:pointcut id="globalPoint" expression="execution(* com.learn.dao.*.*(..))"/>
<aop:pointcut id="globalPointPublic" expression="execution(public * com.learn.dao.*.*(..))"/>
<aop:pointcut id="globalPointUserDao" expression="execution(* com.learn.dao.IUserDao.*(..))"/>
<aop:pointcut id="globalPointUserDaoSelectLong" expression="execution(public * com.learn.dao.IUserDao.select(Long))"/>
<aop:pointcut id="globalPointUserDaoSelect" expression="execution(public * com.learn.dao.IUserDao.select(..))"/>
<aop:pointcut id="myPoint" expression="execution(* com.learn.service.*.*(..))"/>
<aop:pointcut id="myPointUserService" expression="execution(* com.learn.service.IUserService.*(..))"/>
<aop:pointcut id="myPointAffiliatePacket" expression="execution(* com.learn.service..*.*(..))"/>
<aop:pointcut id="myPointUser" expression="execution(com.learn.bean.User com.learn.service..*.*(..))"/>
<aop:pointcut id="myPointVoid" expression="execution(void com.learn.service..*.*(..))"/>
<!-- 配置一個(gè)切面類-->
<aop:aspect ref="logXmlAspect" id="log" >
<!-- 配置執(zhí)行方法前切點(diǎn)切入-->
<aop:before method="before" pointcut-ref="globalPoint" ></aop:before>
<!-- 配置執(zhí)行方法后切點(diǎn)切入-->
<aop:after method="after" pointcut-ref="globalPoint"></aop:after>
<!-- 配置返回后方法切入-->
<aop:after-returning method="afterReturning" pointcut-ref="globalPointPublic"></aop:after-returning>
<!-- 配置異常后方法切點(diǎn)切入 需要設(shè)置異常參數(shù)throwing值為異常參數(shù)名-->
<aop:after-throwing method="afterThrowing" pointcut-ref="globalPointUserDao" throwing="ex"></aop:after-throwing>
<!-- 配置環(huán)繞方法切入-->
<aop:around method="around" pointcut-ref="globalPointUserDao"></aop:around>
</aop:aspect>
</aop:config>
2.設(shè)定切面類
package com.learn.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import java.util.Arrays;
/**
* @author wangxing
* @version 2020/6/29 10:16 Administrator
*/
public class LogXmlAspect {
public void before(JoinPoint joinPoint) {
String name=joinPoint.getSignature().getName();
System.out.println(name+"前置通知");
}
public void after(JoinPoint joinPoint) {
String name=joinPoint.getSignature().getName();
System.out.println(name+"后置通知");
}
public void afterReturning(JoinPoint joinPoint) {
String name=joinPoint.getSignature().getName();
System.out.println(name+"方法后置返回通知");
}
public void afterThrowing(JoinPoint joinPoint,Exception ex) {
String name=joinPoint.getSignature().getName();
System.out.println(name+"方法后置異常通知");
}
/**
* 環(huán)繞通知
*
* @return
*/
public Object around(ProceedingJoinPoint joinPoint) {
Object result = null;
//獲取方法參數(shù)
Object[] args = joinPoint.getArgs();
String methodName = joinPoint.getSignature().getName();
try {
if (args != null || args.length >= 1) {
System.out.println("方法參數(shù)為:" + Arrays.asList(args));
}
System.out.println("環(huán)繞:前置通知:" + methodName + "方法開始執(zhí)行");
result = joinPoint.proceed(args);
System.out.println("環(huán)繞:返回通知:" + methodName + "方法返回值為:"+result);
} catch (Throwable throwable) {
System.out.println("環(huán)繞:后置異常通知:" + methodName + "方法出現(xiàn)異常,異常信息為"+throwable);
} finally {
System.out.println("環(huán)繞:后置通知:" + methodName + "方法執(zhí)行結(jié)束");
}
return result;
}
}
3.進(jìn)行測(cè)試
public class SpringAOPTest {
@Test
public void testXmlAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:/spring-aop.xml");
IUserDao userDao = context.getBean("userDao",IUserDao.class);
//在沒有使用AOP的情況下輸出的類是當(dāng)前接口的實(shí)現(xiàn)類
System.out.println(userDao.getClass());
System.out.println(userDao.select(1L));
}
}
2.通過(guò)注釋方式進(jìn)行AOP操作
1.配置spring.xml文件進(jìn)行aop設(shè)置
<!--掃描包-->
<context:component-scan base-package="com.learn"></context:component-scan>
<!-- 啟動(dòng)AOP注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.建立AOP切面類
package com.learn.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author wangxing
* @version 2020/6/28 15:47 Administrator
*/
@Component//標(biāo)記為組件
@Aspect//標(biāo)記為切面
public class LogAspect {
//使用一個(gè)通用的規(guī)則去進(jìn)行匹配要進(jìn)行切面的節(jié)點(diǎn)
@Pointcut("execution(* com.learn..*.*(..))")
public void pointcut(){
}
// 前置通知
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"方法前置通知");
}
// 后置通知
@After(pointcut())
public void after(JoinPoint joinPoint){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"后置通知");
}
// 后置返回通知
@AfterReturning("execution(* com.learn.service..*.*(..))")
public void afterReturning(JoinPoint joinPoint){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"后置返回通知");
}
// 后置異常通知
@AfterThrowing(value = "execution(* com.learn.service..*.*(..))",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"后置異常通知"+ex);
}
}
SpringAOP的注解方式細(xì)節(jié)說(shuō)明
1.使用注解模式要確保在xml文件中配置開啟aop注解
<!-- 啟動(dòng)AOP注解方式-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
2.要進(jìn)行切面編程的類都需要在ioc容器中
<!--掃描包-->
<context:component-scan base-package="com.learn"></context:component-scan>
3.構(gòu)建AOP切面類,切面類需要用@Aspect標(biāo)記為切面,并且切面類也要在容器中存在
@Component//標(biāo)記為組件
@Aspect//標(biāo)記為切面
public class LogAspect
4.在切面類中可以建立通用的匹配規(guī)則方法,其余的匹配可以調(diào)用這個(gè)方法來(lái)實(shí)現(xiàn)設(shè)立通用匹配方法的注解為:@Pointcut,在其參數(shù)中設(shè)置匹配規(guī)則,使用時(shí)直接用方法名替代原本的匹配規(guī)則
@Pointcut("execution(* com.learn..*.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"方法前置通知");
}
5.匹配方法的設(shè)定規(guī)則,和xml的配置規(guī)則方式相同
具體如下:
通過(guò)@Pointcut注解進(jìn)行設(shè)置,在value屬性中設(shè)置匹配規(guī)則,支持使用AspectJ切點(diǎn)標(biāo)識(shí)符(PCD)用來(lái)進(jìn)行切點(diǎn)表達(dá)式
標(biāo)識(shí)符有:
1.execution:用于匹配方法執(zhí)行連接點(diǎn),使用Spring AOP時(shí)使用的主要切點(diǎn)標(biāo)識(shí)符
2.within:只能匹配類這級(jí)覆山,只能指定類竹伸, 類下面的某個(gè)具體的方法無(wú)法指定
3.this: 匹配實(shí)現(xiàn)了某個(gè)接口
4.target:限制匹配到連接點(diǎn)(使用Spring AOP時(shí)方法的執(zhí)行),其中目標(biāo)對(duì)象(正在代理的應(yīng)用程序?qū)ο螅┦墙o定類型的實(shí)例
5.args:限制與連接點(diǎn)的匹配(使用Spring AOP時(shí)方法的執(zhí)行)簇宽,其中變量是給定類型的實(shí)例
@Pointcut 源碼:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Pointcut {
String value() default "";
String argNames() default "";
}
6.匹配語(yǔ)法
1- 類匹配
within表達(dá)式
通過(guò)類名進(jìn)行匹配 粗粒度的切入點(diǎn)表達(dá)式
語(yǔ)法:within(包名.類名)
2- 類.方法匹配
execution()表達(dá)式
通過(guò)類名,方法名進(jìn)行匹配 細(xì)粒度的切入點(diǎn)表達(dá)式
語(yǔ)法:execution(返回值類型 包名.類名.方法名(參數(shù)類型,參數(shù)類型…))
1.訪問修飾符:可不寫 可以匹配任何一個(gè)訪問修飾符
2.返回值:如果是jdk自帶類型可以不用寫完整限定名勋篓,如果是自定義類型需要寫上完整限定名,如果被切入的方法返回值不一樣可以使用代表所有的方法值都能匹配
3.包名:cn. cn.任意名字 但是只能匹配一級(jí),cn..匹配所有子包和子孫包
4.類名: 可以寫魏割,代表任何名字的類名譬嚣。 也可以模糊匹配 ServiceImpl==> UserServiceImpl ==>RoleServiceImpl
5.方法名:可以寫,代表任何方法钞它。 也可以模糊匹配 *add==> useradd ==>roleadd
6.參數(shù):如果是jdk自帶類型可以不用寫完整限定名拜银,如果是自定義類型需要寫上完整限定名。 如果需要匹配任意參數(shù) 可以寫: ..
-3 合并切點(diǎn)表達(dá)式
可以使用 &&, || 和 !等符號(hào)進(jìn)行合并操作,也可以通過(guò)名字來(lái)指向切點(diǎn)表達(dá)式
//&&:兩個(gè)表達(dá)式同時(shí)
execution( public int cn.tulingxueyuan.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )
//||:任意滿足一個(gè)表達(dá)式即可
execution( public int cn.tulingxueyuan.inter.MyCalculator.*(..)) && execution(* *.*(int,int) )
//T舛狻:只要不是這個(gè)位置都可以進(jìn)行切入
execution( public int cn.tulingxueyuan.inter.MyCalculator.*(..)) && ! execution(* *.*(int,int) )
7.通知方法的執(zhí)行順序
正常執(zhí)行情況下,AOP通知的執(zhí)行順序
@Before-->方法-->@After-->@AfterReturning
異常執(zhí)行情況下,AOP通知的執(zhí)行順序
@Before-->方法-->@After-->@AfterThrowing
8.獲取方法的詳細(xì)信息
需要進(jìn)行獲取執(zhí)行方法的參數(shù)可以在AOP切面類的方法參數(shù)中增加一個(gè)參數(shù)即可,參數(shù)類型為:
JoinPoint---(import org.aspectj.lang.JoinPoint;)
示例
// 后置返回通知
@AfterReturning("execution(* com.learn.service..*.*(..))")
public void afterReturning(JoinPoint joinPoint){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"后置返回通知");
}
如果需要獲取結(jié)果尼桶,還需要添加另外一個(gè)方法參數(shù),并且告訴spring使用哪
個(gè)參數(shù)來(lái)進(jìn)行結(jié)果接收
示例:
// 后置返回通知
@AfterReturning(value = "execution(* com.learn.service.impl.UserServiceImpl.select(..))",returning = "r1")
public void afterReturning(JoinPoint joinPoint,Object r1){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"后置返回通知方法執(zhí)行完成耻卡,結(jié)果是:"+r1);
}
可以通過(guò)相同的方式來(lái)獲取異常的信息
// 后置異常通知
@AfterThrowing(value = "execution(* com.learn.service..*.*(..))",throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"后置異常通知"+ex);
}
9.spring對(duì)通過(guò)方法的要求
spring對(duì)于通知方法的要求并不是很高疯汁,你可以任意改變方法的返回值和方法的訪問修飾符,但是唯一不能修改的就是方法的參數(shù)卵酪,會(huì)出現(xiàn)參數(shù)綁定的錯(cuò)誤幌蚊,原因在于通知方法是spring利用反射調(diào)用的,每次方法調(diào)用得確定這個(gè)方法的參數(shù)的值
10.表達(dá)式的抽取
在實(shí)際使用過(guò)程中溃卡,多個(gè)方法的表達(dá)式是一致的話溢豆,那么可以考慮將切入點(diǎn)表達(dá)式抽取出來(lái)
1.隨便生命一個(gè)沒有實(shí)現(xiàn)的返回void的空方法
2.給方法上標(biāo)注@Potintcut注解
示例
//使用一個(gè)通用的規(guī)則去進(jìn)行匹配要進(jìn)行切面的節(jié)點(diǎn)
@Pointcut("execution(* com.learn..*.*(..))")
public void pointcut(){}
// 前置通知
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("annotation:"+joinPoint.getSignature().getName()+"方法前置通知");
}
11.環(huán)繞通知的使用
環(huán)繞通知是使用一個(gè)方法將需要切面的方法進(jìn)行包圍,在執(zhí)行對(duì)應(yīng)方法前后進(jìn)行切面方法操作
使用標(biāo)簽為:@Around
示例
/**
* 環(huán)繞通知
* @param proceedingJoinPoint
* @return
*/
@Around("pointcut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint){
Object[] args = proceedingJoinPoint.getArgs();
String name = proceedingJoinPoint.getSignature().getName();
Object proceed = null;
try {
System.out.println("環(huán)繞前置通知:"+name+"方法開始,參數(shù)是"+ Arrays.asList(args));
//利用反射調(diào)用目標(biāo)方法瘸羡,就是method.invoke()
proceed = proceedingJoinPoint.proceed(args);
System.out.println("環(huán)繞返回通知:"+name+"方法返回漩仙,返回值是"+proceed);
} catch (Throwable e) {
System.out.println("環(huán)繞異常通知"+name+"方法出現(xiàn)異常,異常信息是:"+e);
}finally {
System.out.println("環(huán)繞后置通知"+name+"方法結(jié)束");
}
return proceed;
}
環(huán)繞通知的執(zhí)行順序是優(yōu)先于普通通知的,在有環(huán)繞通知和普通AOP通知的情況下執(zhí)行順序?yàn)?
環(huán)繞前置-->普通前置-->目標(biāo)方法執(zhí)行-->環(huán)繞正常結(jié)束/出現(xiàn)異常-->環(huán)繞后置-->普通后置-->普通返回或者異常
但需要注意的是:
如果出現(xiàn)了異常,那么環(huán)繞通知會(huì)處理或者捕獲異常队他,普通異常通知是接收不到的卷仑,因此最好的方式是在環(huán)繞異常通知中向外拋出異常
總結(jié)
1.什么是AOP
2.代理模式:靜態(tài)代理,動(dòng)態(tài)代理,和相關(guān)實(shí)現(xiàn)
3.SpringAOP的實(shí)現(xiàn)原理
4.使用xml配置文件進(jìn)行AOP操作
5.使用注解進(jìn)行AOP操作