1. AOP的使用
1.1 环境准备
1.1.1 pom引入
要让sping的aop生效,需要先引入aop的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
1.1.2 定义一个业务模块
package com.bjc.aop;
public class MathCalculator {
public int div(int i,int j){
System.out.println("MathCalculator...div...");
return i/j;
}
}
1.1.3 定义一个日志切面类
package com.bjc.aop;
/**
* 切面类
*/
public class LogAspects {
public void logStart(){
System.out.println("除法运行。。。参数列表是{}");
}
public void logEnd(){
System.out.println("除法结束。。。");
}
public void logReturn(){
System.out.println("除法正常返回。。。运行结果:{}");
}
public void logException(){
System.out.println("除法异常。。。异常信息{}");
}
}
1.2 通知方法
给切面类的目标方法标注何时何地运行(通知注解)
1.2.1 前置通知——@Before
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before"execution(public * com.bjc.aop.MathCalculator.*(..))"
public void logStart(){
System.out.println("除法运行。。。参数列表是{}");
}
在logStart方法中可以加入参数JoinPoint,用于获取方法的相关信息,例如:
public void logStart(JoinPoint joinpoint){
// 获取方法签名
Signature signature = joinpoint.getSignature();
// 获取调用的方法名称
String methodname = signature.getName();
// 获取参数列表
Object[] args = joinpoint.getArgs();
System.out.println("方法" + methodname + ",除法运行。。。参数列表是{ "+ Arrays.asList(args) +" }");
}
1.2.2 后置通知——@After
@After("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logEnd(){
System.out.println("除法结束。。。");
}
loginEnd也可以加入参数JoinPoint
@After("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logEnd(JoinPoint joinpoint){
System.out.println(joinpoint.getSignature().getName() + "除法结束。。。");
}
1.2.3 返回通知——@AfterReturning
@AfterReturning("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logReturn(){
System.out.println("除法正常返回。。。运行结果:{}");
}
在logReturn中不但可以加入JoinPoint参数,还可以获取返回值
// returning的属性值与参数列表中的result对应
@AfterReturning("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logReturn(JoinPoint joinpoint,Object result){
System.out.println(joinpoint.getSignature().getName() + "除法正常返回。。。运行结果:{"+ result +"}");
}
1.2.4 异常通知——@AfterThrowing
@AfterThrowing("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void logException(){
System.out.println("除法异常。。。异常信息{}");
}
同样的,我们也可以获取异常信息
@AfterThrowing(value="execution(public * com.bjc.aop.MathCalculator.*(..)))",throwing = "e")
public void logException(JoinPoint joinpoint,Exception e){
System.out.println("除法异常。。。异常信息{"+e+"}");
}
注意:throwing的属性值e就是logexception的参数Exception的形参名。
1.2.5 环绕通知——@Around
动态代理,通过连接点(joinpoint.proced)手动推进目标方法运行
注意:通知的形参如果有多个的时候,JoinPoint必须为第一个参数,像返回值、异常,都要位于JoinPoint参数的后面。
1.3 将切面类与业务类加入容器
@Configuration
public class ConfigOfAop {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
1.4 指定切面类——@Aspect
给切面类上加上注解@Aspect,spring就可以知道该类代表切面类。
1.5 开启基于注解的AOP模式——@EnableAspectJAutoProxy
在之前的基于配置的AOP中,我们使用的是如下配置
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
现在,我们没有这个配置文件,该怎么办了?我们可以在配置类中加上注解@EnableAspectJAutoProxy,来代替之前的配置
1.6 测试
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConfigOfAop.class);
MathCalculator bean = context.getBean(MathCalculator.class);
bean.div(10, 2);
}
运行结果:
将参数2改成0,运行结果:
使用try catch捕获异常,如图:
运行结果如下:
1.7 总结
通过前面的案例,可以知道基于注解的AOP的步骤总结起来就三步:
1)将业务逻辑组件和切面类都加入到容器中,并告诉Spring哪个是切面类(@Aspect)
2)在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3)开启基于注解的AOP模式(@EnableAspectJAutoProxy)
2. 切入点表达式提取 ——@Pointcut
我们发现,上例中,在每一个通知方法上都要写一个切入点表达式,比较繁琐,我们可以定义一个切入点方法然后通过注解@Pointcut将表达式抽取成公共的表达式,例如:
之后,将该方法应用到各个通知方法上,改进后的切面内如下:
package com.bjc.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 切面类
*/
@Aspect
public class LogAspects {
//抽取公共的切入点表达式
//1、本类引用:直接引用
//2、其他的切面引用:使用全名 com.bjc.aop.LogAspects.cut1()
@Pointcut("execution(public * com.bjc.aop.MathCalculator.*(..)))")
public void cut1() {}
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("cut1()")
public void logStart(JoinPoint joinpoint){
// 获取方法签名
Signature signature = joinpoint.getSignature();
// 获取调用的方法名称
String methodname = signature.getName();
// 获取参数列表
Object[] args = joinpoint.getArgs();
System.out.println("方法" + methodname + ",除法运行。。。参数列表是{ "+ Arrays.asList(args) +" }");
}
@After("cut1()")
public void logEnd(JoinPoint joinpoint){
System.out.println(joinpoint.getSignature().getName() + "除法结束。。。");
}
// returning的属性值与参数列表中的result对应
@AfterReturning(value="cut1()",returning = "result")
public void logReturn(JoinPoint joinpoint,Object result){
System.out.println(joinpoint.getSignature().getName() + "除法正常返回。。。运行结果:{"+ result +"}");
}
@AfterThrowing(value="cut1()",throwing = "e")
public void logException(JoinPoint joinpoint,Exception e){
System.out.println("除法异常。。。异常信息{"+e+"}");
}
}
注意:如果想对aop下的所有的类进行切面注入,可以将MathCalculator去掉,即切入点表达式为:
@Pointcut("execution(public * com.bjc.aop..*(..)))")