太久没使用过了,今日重新学习了一下用法
1、依赖引入
使用的是SpringBoot,pom文件引入依赖,引入此一个就OK了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、配置注解
切面类上,需要加上如下注解
@Aspect
@Component
增强方法上,需要加上注解:
@Before("execution(* com.zy.testproject.childone.test.*.*(..))")
再次运行就OK了。
3、完整代码:
内容
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TestAop {
/**
* execution:匹配方法的执行(常用)
* execution(表达式)
* 表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
* 写法说明:
* 全匹配方式:
* public void com.zy.service.impl.AccountServiceImpl.saveAccount(com.zy.domain.Account)
* 访问修饰符可以省略
* void com.zy.service.impl.AccountServiceImpl.saveAccount(com.zy.domain.Account)
* 返回值可以使用*号,表示任意返回值
* * com.zy.service.impl.AccountServiceImpl.saveAccount(com.zy.domain.Account)
* 包名可以使用*号,表示任意包,但是有几级包,需要写几个*
* * *.*.*.*.AccountServiceImpl.saveAccount(com.zy.domain.Account)
* 使用..来表示当前包,及其子包
* * com..AccountServiceImpl.saveAccount(com.zy.domain.Account)
* 类名可以使用*号,表示任意类
* * com..*.saveAccount(com.zy.domain.Account)
* 方法名可以使用*号,表示任意方法
* * com..*.*( com.zy.domain.Account)
* 参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数
* * com..*.*(*)
* 参数列表可以使用..表示有无参数均可,有参数可以是任意类型
* * com..*.*(..)
* 全通配方式:
* * *..*.*(..)
* 注:
* 通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
* execution(* com.zy.service.impl.*.*(..))
*
*/
@Before("execution(* com.zy.testproject.childone.test.*.*(..))")
public void say() {
System.out.println("sss");
}
}
注解在代碼中不清晰,單獨拿出來看
execution:匹配方法的执行(常用)
execution(表达式)
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明:
全匹配方式:
public void com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
访问修饰符可以省略
void com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
返回值可以使用*号,表示任意返回值
* com.itheima.service.impl.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
包名可以使用*号,表示任意包,但是有几级包,需要写几个*
* *.*.*.*.AccountServiceImpl.saveAccount(com.itheima.domain.Account)
使用..来表示当前包,及其子包
* com..AccountServiceImpl.saveAccount(com.itheima.domain.Account)
类名可以使用*号,表示任意类
* com..*.saveAccount(com.itheima.domain.Account)
方法名可以使用*号,表示任意方法
* com..*.*( com.itheima.domain.Account)
参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数
* com..*.*(*)
参数列表可以使用..表示有无参数均可,有参数可以是任意类型
* com..*.*(..)
全通配方式:
* *..*.*(..)
注:
通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution(* com.itheima.service.impl.*.*(..))
注意
**引以为戒:**表达式是确定到方法的,本人一开始只确定到类上面,报错:
Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: warning no match for this type name: com.zy.testproject.childone.test [Xlint:invalidAbsoluteTypeName]
当方法太多的时候,写同样的execution表达式太麻烦,可以使用如下方法:
@Before("log()")
public void say() {
System.out.println("sss");
}
@Pointcut("execution(* com.zy.testproject.childone.test.*(..))")
public void log(){}
4、补充
通知的四种常用类型:
aop:before
作用:
用于配置前置通知。指定增强的方法在切入点方法之前执行
aop:after-returning
作用:
用于配置后置通知
执行时间点:
切入点方法正常执行之后。它和异常通知只能有一个执行
aop:after-throwing
作用:
用于配置异常通知
执行时间点:
切入点方法执行产生异常后执行。它和后置通知只能执行一个
aop:after
作用:
用于配置最终通知
执行时间点:
无论切入点方法执行时是否有异常,它都会在其后面执行。
环绕通知
aop:around:
作用:
用于配置环绕通知
说明:
它是spring框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
注意:
通常情况下,环绕通知都是独立使用的
/**
* 环绕通知
* 问题:
* 当配置完环绕通知之后,没有业务层方法执行(切入点方法执行)
* 分析:
* 通过动态代理的代码分析,我们现在的环绕通知没有明确的切入点方法调用
* 解决:
* spring框架为我们提供了一个接口,该接口可以作为环绕通知的方法参数来使用
* ProceedingJoinPoint。当环绕通知执行时,spring框架会为我们注入该接口的实现类。
* 它有一个方法proceed(),就相当于invoke,明确的业务层方法调用
*
* spring的环绕通知:
* 它是spring为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
*/
public String aroundPrintLog(ProceedingJoinPoint pjp) {
try {
System.out.println("前置Logger类中的aroundPrintLog方法开始记录日志了");
String s = (String) pjp.proceed();;//明确的方法调用
System.out.println("后置Logger类中的aroundPrintLog方法开始记录日志了");
} catch (Throwable e) {
System.out.println("异常Logger类中的aroundPrintLog方法开始记录日志了");
e.printStackTrace();
}finally {
System.out.println("最终Logger类中的aroundPrintLog方法开始记录日志了");
}
return s;
}