SpringAOP的使用
1. AOP 相关术语
- Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的
连接点。 - Pointcut(切入点):
所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。 - Advice(通知/增强):
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。 - Introduction(引介):
引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方
法或 Field。 - Target(目标对象):
代理的目标对象。 - Weaving(织入):
是指把增强应用到目标对象来创建新的代理对象的过程。
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。 - Proxy(代理):
一个类被 AOP 织入增强后,就产生一个结果代理类。 - Aspect(切面):
是切入点和通知(引介)的结合。
3. 基于配置文件配置SpringAOP
先导入springc-ontext的坐标,和aspectjweaver的坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
- 提供一个类(UserServiceImpl),该类中的方法是需要被增强的。
public class UserServiceImpl implements IUserService {
/**
* 该方法需要被增强
*/
public void showString() {
System.out.println("UserServiceImpl中的showString方法!");
// int i = 1/0;
}
}
- 提供一个通知类(defor),该类定义了5中通知的方法
/**
该类中定义的方法用来增强UserServiceImpl中的方法
*/
public class defor {
public void beforeAdvice(){
System.out.println("前置通知");
}
public void afterAdvice(){
System.out.println("后置通知");
}
public void exceptionAdvice(){
System.out.println("异常通知");
}
public void finallyAdvice(){
System.out.println("最终通知");
}
public Object aroundAdvice(ProceedingJoinPoint p){
System.out.println("环绕通知");
try {
System.out.println("前置通知");
Object[] args = p.getArgs();
Object proceed = p.proceed(args);
System.out.println("后置通知");
return proceed;
} catch (Throwable t) {
System.out.println("异常通知");
t.printStackTrace();
throw new RuntimeException(t);
}finally {
System.out.println("最终通知");
}
}
}
- 配置文件中引入约束
<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:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
- 配置文件中先把这两个类配到容器中
<bean id="IUserService" class="com.itzpx.service.impl.UserServiceImpl"></bean>
<bean id="defor" class="com.itzpx.utils.defor"></bean>
- 配置通知
<aop:config>
<aop:aspect id="deforAdvice" ref="defor">
<aop:pointcut id="pointcutId" expression="execution(* com.itzpx.service.impl.*.*(..))"></aop:pointcut>
<!--<aop:before method="beforeAdvice" pointcut-ref="pointcutId"></aop:before>-->
<!--<aop:after-returning method="afterAdvice" pointcut-ref="pointcutId"></aop:after-returning>-->
<!--<aop:after-throwing method="exceptionAdvice" pointcut-ref="pointcutId"></aop:after-throwing>-->
<!--<aop:after method="finallyAdvice" pointcut-ref="pointcutId"></aop:after>-->
<aop:around method="aroundAdvice" pointcut-ref="pointcutId"></aop:around>
</aop:aspect>
</aop:config>
- 测试
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IUserService service = ac.getBean("IUserService", IUserService.class);
service.showString();
}
解释:
<aop:config>:aop的配置信息放里面
里面提供了三个标签
7. <aop:aspect id=“deforAdvice” ref=“defor”>:配置切面
id是唯一标识,随便写,ref是通知类bean的唯一标识
8. <aop:pointcut id=“pointcutId” expression=“execution(* com.itzpx.service.impl..(…))”></aop:pointcut>:配置切入点
id是唯一标识,expression指定哪个方法需要被增强
标准写法是:
public void com.包名.包名.等等.类名.方法名(参数),
通配写法是
访问修饰符省略
这种写法匹配所有的类,显然不符合我们的要求
* *..*.*(..)
我们需要加以限定
* * com.itzpx.service.impl.*.*(..)
- 在 <aop:aspect id=“deforAdvice” ref=“defor”>下面提供了
<aop:aspect id="deforAdvice" ref="defor">
<aop:pointcut id="pointcutId" expression="execution(* com.itzpx.service.impl.*.*(..))"></aop:pointcut>
<aop:before method="beforeAdvice" pointcut-ref="pointcutId"></aop:before>
<aop:after-returning method="afterAdvice" pointcut-ref="pointcutId"></aop:after-returning>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="pointcutId"></aop:after-throwing>
<aop:after method="finallyAdvice" pointcut-ref="pointcutId"></aop:after>
<aop:around method="aroundAdvice" pointcut-ref="pointcutId"></aop:around>
</aop:aspect>
五个标签,分别代表了
pointcut 提取expression
before配置前置通知
after-returning后置通知
after-throwing异常通知
after最终通知
around环绕通知
4.关于环绕通知的问题
环绕通知其实是提供了一种编码编写通知的方式
在环绕通知中,包含了其他四种通知
ProceedingJoinPoint ,是spring提供的类
与我们动态代理中的involved方法类似用法
5.注解配置AOP
<!--1.将IUserService与defor放入容器-->
<bean id="IUserService" class="com.itzpx.service.impl.UserServiceImpl"></bean>
<bean id="defor" class="com.itzpx.utils.defor"></bean>
<aop:config>
<!--2.标识出通知类-->
<aop:aspect id="deforAdvice" ref="defor">
<!--3.切入点的配置,也就是指定需要被增强的方法-->
<aop:pointcut id="pointcutId" expression="execution(* com.itzpx.service.impl.*.*(..))"></aop:pointcut>
<!--4.指定通知方法,并与需要被增强的方法联系起来-->
<aop:before method="beforeAdvice" pointcut-ref="pointcutId"></aop:before>
<aop:after-returning method="afterAdvice" pointcut-ref="pointcutId"></aop:after-returning>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="pointcutId"></aop:after-throwing>
<aop:after method="finallyAdvice" pointcut-ref="pointcutId"></aop:after>
<aop:around method="aroundAdvice" pointcut-ref="pointcutId"></aop:around>
</aop:aspect>
</aop:config>
- 将IUserService与defor放入容器
<context:component-scan base-package="com.itzpx"/>
@Component
public class defor {
@Component
public class UserServiceImpl implements IUserService {
- 标识出通知类
@Component
@Aspect//代表通知类
public class defor {
- 切入点的配置,也就是指定需要被增强的方法
@Pointcut("execution(* com.itzpx.service.impl.*.*(..))")
private void pt1(){}
- 指定通知方法,并与需要被增强的方法联系起来
@Before("pt1()")
public void beforeAdvice(){
System.out.println("前置通知");
}
@AfterReturning("pt1()")
public void afterAdvice(){
System.out.println("后置通知");
}
@AfterThrowing("pt1()")
public void exceptionAdvice(){
System.out.println("异常通知");
}
@After("pt1()")
public void finallyAdvice(){
System.out.println("最终通知");
}
@Around("pt1()")
public Object aroundAdvice(ProceedingJoinPoint p){
System.out.println("环绕通知");
try {
System.out.println("前置通知");
Object[] args = p.getArgs();
Object proceed = p.proceed(args);
System.out.println("后置通知");
return proceed;
} catch (Throwable t) {
System.out.println("异常通知");
t.printStackTrace();
throw new RuntimeException(t);
}finally {
System.out.println("最终通知");
}
}
- 开启springAOP注解配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>