目录
AOP的相关概念
- Joinpoint(连接点):就是方法
- Pointcut(切入点):就是挖掉共性功能的方法
- Advice(通知):就是共性功能,最终以一个方法的形式呈现
- Aspect(切面):就是共性功能与挖的位置的对应关系
- Target(目标对象):就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接完成最终工作的
- Weaving(织入):就是将挖掉的功能回填的动态过程
- Proxy(代理):目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现
- Introduction(引入/引介) :就是对原始对象无中生有的添加成员变量或成员方法
AOP入门案例(xml)
1、引入坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2、抽取通用代码
package com.jjs.config;
public class AOPAdvice {
public void advice(){
System.out.println("公共方法");
}
}
3、编写配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.jjs.service.UserServiceImpl" ></bean>
<bean id="aop" class="com.jjs.config.AOPAdvice"></bean>
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"></aop:pointcut>
<aop:aspect ref="aop">
<aop:before method="advice" pointcut-ref="pt"></aop:before>
</aop:aspect>
</aop:config>
</beans>
APOXML配置详解
抽取的公共功能类
package com.jjs.config;
public class AOPAdvice {
public void advice(){
System.out.println("公共方法");
}
}
AOP的XML配置,接下来的讲解都以这套为模板
<bean id="aop" class="com.jjs.config.AOPAdvice"></bean> 抽取出来的公共功能,对应一个类
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"></aop:pointcut>
<aop:aspect ref="aop">
<aop:before method="advice" pointcut-ref="pt"></aop:before>
</aop:aspect>
</aop:config>
</aop:config>
aop:config 标签
一组aop:config代表一套AOP,可以设置多套
<beans>
<aop:config>……</aop:config>
<aop:config>……</aop:config>
</beans>
aop:aspect 标签
设置具体(位置以及内容)的切入点,例如在目标方法的前面通知还是在目标方法的后面通知,以及通知的内容(方法)
<aop:aspect ref="aop"> 公共功能类对应的beanID
<aop:before method="advice" pointcut-ref="pt"></aop:before>
</aop:aspect>
aop:pointcut 标签
设置切入的时机(当直接expression表达式中所涉及的方法就会开始通知)
<aop:pointcut id="pointcutId" expression="execution(* *..*(..))/>
excution的表达式
execution(访问修饰符 返回值 包名.类名.方法名(参数)异常名)
访问修饰符:方法的访问控制权限修饰符 可省略
类名:方法所在的类(此处可以配置接口名称)
异常:方法定义中指定抛出的异常
示例:
execution(* *(..))
execution(* *..*(..))
execution(* *..*.*(..))
execution(public * *..*.*(..))
execution(public int *..*.*(..))
execution(public void *..*.*(..))
execution(public void com..*.*(..))
execution(public void com..service.*.*(..))
execution(public void com.jjs.service.*.*(..))
execution(public void com.jjs.service.User*.*(..))
execution(public void com.jjs.service.*Service.*(..))
execution(public void com.jjs.service.UserService.*(..))
execution(public User com.jjs.service.UserService.find*(..))
execution(public User com.jjs.service.UserService.*Id(..))
execution(public User com.jjs.service.UserService.findById(..))
execution(public User com.jjs.service.UserService.findById(int))
execution(public User com.jjs.service.UserService.findById(int,int))
execution(public User com.jjs.service.UserService.findById(int,*))
execution(public User com.jjs.service.UserService.findById(*,int))
execution(public User com.jjs.service.UserService.findById())
execution(List com.jjs.service.*Service+.findAll(..))
通配符
-
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution(public * com.jjs.*.UserService.find*(*))
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
-
.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution(public User com..UserService.findById(..))
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
-
+:专用于匹配子类类型
execution(* *..*Service+.*(..))
切入点表达式——逻辑运算符
-
&& :连接两个切入点表达式,表示两个切入点表达式同时成立的匹配
-
|| :连接两个切入点表达式,表示两个切入点表达式成立任意一个的匹配
-
! :连接单个切入点表达式,表示该切入点表达式不成立的匹配
AOP五种通知类型
xml配置
<bean id="userService" class="com.jjs.service.UserServiceImpl" ></bean>
<bean id="aop" class="com.jjs.config.AOPAdvice"></bean>
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..*(..))"></aop:pointcut>
<aop:aspect ref="aop">
<!--<aop:before method="before" pointcut-ref="pt"></aop:before>-->
<!--<aop:after method="after" pointcut-ref="pt"></aop:after>-->
<!--<aop:after-returning method="after_eturning" pointcut-ref="pt"></aop:after-returning>-->
<!--<aop:after-throwing method="after_throwing" pointcut-ref="pt"></aop:after-throwing>-->
<aop:around method="around" pointcut-ref="pt"></aop:around>
</aop:aspect>
</aop:config>
通知内容:
package com.jjs.config;
import org.aspectj.lang.ProceedingJoinPoint;
public class AOPAdvice {
public void before(){
System.out.println("前置通知");
}
public void after(){
System.out.println("后置通知");
}
public void after_eturning(){
System.out.println("返回后通知");
}
public void after_throwing(){
System.out.println("抛出异常后通知");
}
public Object around(ProceedingJoinPoint pdj) throws Throwable {
System.out.println("环绕前通知");
Object proceed = pdj.proceed();
System.out.println("环绕后通知");
return proceed;
}
}
前置通知:
<aop:before method="before" pointcut-ref="pt"></aop:before>
后置通知:
无论是否正常执行,在掉用方法完毕后都会执行后置通知
<aop:after method="after" pointcut-ref="pt"></aop:after>
返回后通知:
只有方法内正常运行、无异常才会执行返回后通知
<aop:after-returning method="after_eturning" pointcut-ref="pt"></aop:after-returning>
出现异常后通知:
只有当方法内部出现了异常后才会通知
<aop:after-throwing method="after_throwing" pointcut-ref="pt"></aop:after-throwing>
环绕通知:
该方法较为特殊,从上面的通知内容可以看的,环绕通知的通知内容必须要在第一个参数添加ProceedingJoinPoint,然后通过它调取proceed,相当于执行核心方法,不然的话就会忽略核心方法(特有方法,通知修饰的内容)
<aop:around method="around" pointcut-ref="pt"></aop:around>
AOP通知的顺序
当多个同种通知类型同时出现的时候,会按照配置的顺序先后进行执行
例如我这里设置了3个前置通知
然后在xml配置中配置的顺序为2、3、1
看看执行的输出结果,会根据配置的顺序先后执行
当多个不同的通知类型同时出现,会根据功能然后根据配置的顺序先后执行
比如说,一个是前置通知,一个是后置通知,那肯定是先执行前置通知,等方法执行完后再执行后置通知。但是如果是一个环绕通知和一个前置通知同时出现,那么就根据谁先配置先执行谁,如果先配置前置通知,那么就会先执行前置通知完之后再执行环绕通知的前置通知。
这时候看执行结果
AOP通知中获取方法返回值
首先要先知道有哪些通知类型不可以获取方法返回值
- 前置通知before不能获取方法返回值,因为方法还没执行
- 后置通知after也不能获取方法返回值,因为后置通知不管是否出现异常都能执行,而在执行的时候也不知道会不会出现异常,所以干脆不获取返回值
- 异常后通知after-throwing也不能获取返回值,都出现异常了何有返回值之说
除开三种,剩下的两种通知都可以获取方法返回值
after-returning获取返回值
首先要在通知内容参数加上返回值类型,如果不知道返回值类型就使用Object
然后再xml配置中,通过returning来接收,参数写的正是java代码中的接收变量名。
环绕通知获取返回值的方法
通过调用proceedingJoinPoint 的proceed方法相当于执行了目标方法,然后Object接收返回值即可,但是要retrun出去,不然会报错
AOP中获取异常信息
首先要先知道有哪些通知类型不可以获取异常信息
- 前置通知before不能获取异常信息,因为方法还没执行
- 后置通知after也不能获取异常信息,因为后置通知不管是否出现异常都能执行,而在执行的时候也不知道会不会出现异常,所以干脆不获取
- 返回后通知after-returning是正常执行返回,肯定没有异常信息,所以也不行
除开三种,剩下的两种通知都可以获取方法返回值
抛出异常通知after-throwning获取异常信息
首先也是在通知方法的参数上加上一个参数用于接收异常
在xml中通过throwing来接收,参数写的正是java代码中的接收变量名。
环绕通知获取异常的方法
只需要在调用proceedingJoinPoint的proceed中处理异常即可