1. AOP 切面应用
下面是一个AOP切面的一个简单的应用实例
- 引入AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建切面类对象
@Aspect
@Component
public class ItemCacheAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(ItemCacheAspect.class);
private static final Integer SECONDS =60*60*2;
@Autowired
private JedisCluster jedis;
/**
* execution表达式切入点
* execution表达式用法execution(<修饰符></返回值类型> 包名.类名.方法名(参数列表))
* 其中*表示所有可以模糊匹配, ..表示任意类型参数或任意子包下
*/
@Pointcut("execution(* com.changgou.item.service.*Service.query*(..))")
public void doItemCache() {}
@Around("doItemCache()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("已进入环绕通知");
Object result = null;
try {
......
result = joinPoint.proceed();
......
} catch (Exception e) {
LOGGER.error("error: ", e);
result = joinPoint.proceed();
}
return result;
}
}
说明:
@Aspect 注解用于标识或者描述AOP中的切面类型,基于切面类型构建的对象用于为目标对象进行功能扩展或控制目标对象的执行。
@Pointcut 注解用于描述切面中的方法,并定义切面中的切入点,后面会对切入点表达式进行详解
@Around注解 用于描述切面中方法,这样的方法会被认为是一个环绕通知,后面会对aop各个通知类型详解
ProceedingJoinPoint 类为一个连接点类型,此类型的对象用于封装要执行的目标方法相关的一些信息。一般用于@Around注解描述的方法参数。
2. 切面通知应用详解
2.1 通知类型
spring中定义了五种类型的通知,基于AspectJ框架标准,它们分别是:
环绕通知 (@Around) : 包围一个连接点的通知,最强大的一种通知类型,环绕通知可以在方法前后完成自定义的行为,它可以自己选择是否继续执行连接点或直接返回方法的返回值或抛异常结束执行
前置通知 (@Before) : 在指定连接点(join point)前执行的通知,但它不能阻止连接点前的执行(除非抛异常)
后置通知 (@After): 在指定连接点(join point)退出的时候执行(不管是正常返回还是异常退出)
返回通知 (@AfterReturning) : 在指定连接点(join point)正常返回后执行,如果抛出异常则不执行(和After通知同时存在则在After通知执行完之后再执行)
异常通知 (@AfterThrowing) : 在目标方法抛出异常退出时执行
2.2 通知执行顺序
假如这些通知全部写到一个切面对象中,其执行顺序及过程,如图
- 进入目标方法前先进入环绕通知(@Aroud)
- 在环绕通知里调用连接点(joinPoint)的proceed方法后进入前置通知(@Before)
- 前置通知执行完后进入目标方法(targetMethod)
- 目标方法逻辑执行完进入环绕通知里调用proceed方法后的逻辑
- 环绕通知全部执行完后进入后置通知(@After)
- 后置通知执行完后若目标方法正常返回后则进入返回通知(@AfterReturning),若目标方法抛出异常则进入异常通知(@AfterThrowing)
注:若是存在环绕通知(@Aroud)一定要调用连接点的proceed()方法,否则会在环绕通知后直接返回,跳过目标方法
3 切入点表达式增强
Spring中通过切入点表达式定义具体切入点,主要有以下集中切入点表达式:
bean: 用于匹配指定bean对象的方法
within: 用于匹配指定包下所有类内的方法
execution: 用于按指定语法规则匹配到具体方法
@annotation: 用于匹配指定注解修饰的方法
表达式应用:
@Pointcut(“切入点表达式”)
@通知类型("@Pointcut标记的方法名")
或者
@通知类型(“切入点表达式”)
3.1 bean表达式
bean表达式一般应用于类级别,实现粗粒度的切入点定义,案例分析:
语法:bean(“类名”)
bean(“userServiceImpl”)指定一个userServiceImpl类中所有方法。
bean("*ServiceImpl")指定所有的后缀为ServiceImpl的类。
说明:bean表达式内部的对象是由spring容器管理的一个bean对象,表达式内部的内部的名字应该是spring容器中某个bean的key。
3.2 within表达式
within表达式应用于类级别,实现粗粒度的切入点表达式定义,案例分析:
within(“aop.service.UserServiceImpl”)指定当前包中这个类内部的所有方法。
within(“aop.service.*”) 指定当前目录下的所有类的所有方法。
within(“aop.service…*”) 指定当前目录以及子目录中类的所有方法。
3.3 execution表达式
execution表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析:
语法:execution(返回值类型 包名.类名.方法名(参数列表))。
execution(void aop.service.UserServiceImpl.addUser())匹配addUser方法。
execution(void aop.service.PersonServiceImpl.addUser(String)) 方法参数必须为String的addUser方法。
execution(* aop.service….(…)) 万能配置。
3.4 @annotation表达式
@annotaion表达式应用于方法级别,实现细粒度的切入点表达式定义,案例分析
语法:@annotation(“注解类路径”)
@annotation(anno.RequiredLog) 匹配有RequiredLog注解描述的方法。
@annotation(anno.RequiredCache) 匹配有RequiredCache注解描述的方法。
4. 切面优先级设置实现
切面的优先级需要借助@Order注解进行描述,数字越小优先级越高,默认优先级比较低。例如:
定义日志切面并指定优先级。
@Order(1)
@Aspect
@Component
public class SysLogAspect {
…
}