详解Spring AOP切面编程应用+ 基于AspectJ的AOP

一、AOP概述

Spring AOP官方

  • AOP:面向切面编程,相对于OOP面向对象编程。存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能继承和实现接口,且类继承只能单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。
  • 还有就是为了清晰的逻辑,让业务逻辑关注业务本身,不用去关心其它的事情,比如事务。
  • Spring的AOP是通过JDK的动态代理和CGLIB实现的。

二、相关术语

术语描述
通知 / 增强(Advice)需要完成的工作叫做通知,比如事务、日志等先定义好,然后需要的地方再去用
连接点(Join point)就是spring中允许使用通知的地方
切点(Poincut)与连接点匹配的谓词,其实就是筛选出的连接点,一个类中的所有方法都是连接点,但又不全需要,会筛选出某些连接点做为切点。如果说通知定义了切面的动作或者执行时机的话,切点则定义了执行的地点
切面(Aspect)其实就是通知和切点的结合,通知和切点共同定义了切面的全部内容,它是干什么的,什么时候在哪执行
引入(Introduction)在不改变一个现有类代码的情况下,为该类添加属性和方法,可以在无需修改现有类的前提下,让它们具有新的行为和状态。
目标(target)被通知的对象
织入(Weaving)把切面加入程序代码的过程,切面在指定的连接点被织入到目标对象中,在目标对象的生命周期里(编译期/加载期/运行期)可以进行织入
1、通知类型
通知类型描述
before(前置通知)在方法开始执行前执行
after(后置通知)在方法执行后执行 ,无论是正常还是异常返回
afterReturning(返回后通知)在方法正常返回后执行
afterThrowing(异常通知)在抛出异常时执行
around(环绕通知))在方法执行前和执行后都会执行 ,相当于before + after

执行顺序:around > before > around > after > afterReturning

三、基于AspectJ的AOP注解

1、切面切点
注解参数
@Aspectvalue = “”
@PointCutvalue = “” ,argNames=""
2、通知类型
注解参数
@Beforevalue = “” ,argNames=""
@Aftervalue = “” ,argNames=""
@Aroundvalue = “” ,argNames=""
@AfterReturningvalue = “” ,argNames="", pointcut="", returning = “”
@AfterThrowingvalue = “” ,argNames="", pointcut=“”, throwing=""
3、@PointCut支持的表达式
表达式描述示例
execution用于匹配方法执行的连接点。 语法:execution(方法修饰符(可选) 返回类型 类路径 方法名 参数 异常模式(可选))execution(public * (…) 所有public方法; execution( set*(…)) 任何以set开头的方法); execution(* com.xyz.service..(…)) 匹配service包下所有的方法; execution(* com.xyz.service….(…)) 表示匹配service包和它的子包下的方法
within将匹配限制为某些类型内的连接点within(com.xyz.service.) service包的任何连接点; within(com.xyz.service…) service包和它的子包的任何连接点
this指定AOP代理类的类型this(com.xyz.service.AccountService) 代理实现AccountService接口的任何连接点
target指定目标对象的类型target(com.xyz.service.AccountService) 目标对象实现AccountService接口的任何连接点
args指定参数的类型args(java.io.Serializable) 任何采用单个参数且在运行时传递的参数为Serializable的连接点
bean指定参数的类型bean(tradeService) 名为tradeService的Spring bean上的任何连接点; bean(Service) Bean上具有与通配符表达式 Service匹配的名称的任何连接点
@target带有指定注解的类型@target(org.springframework.transaction.annotation.Transactional)目标对象带有@Transactional注释的任何连接点
@args指定运行时传的带有注解的参数@args(com.xyz.security.Classified)任何采用单个参数的连接点,并且传递的参数的运行时类型具有@Classified批注
@within匹配使用指定注解的类@within(org.springframework.transaction.annotation.Transactional)目标对象的声明类型具有@Transactional批注的任何连接点
@annotation指定方法所应用的注解@annotation(org.springframework.transaction.annotation.Transactional) 执行方法带有@Transactional注释的任何连接点

四、AOP应用场景

1、记录日志
  • 切面类
package com.spring.demo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Description: 日志切面类
 * @Version: 1.0
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

	/**
	 * 确定切入点位置,通过些规则来确定哪些方法是要增强的
	 */
	@Pointcut("within(com.spring.demo.controller.*Controller)") // controller包中所有Controller结尾的类
	public void controllerLogPointCut(){
	}

	/**
	 * 前置通知,具体的增强代码片
	 *
	 * @Param joinPoint 连接点
	 */
	@Before("controllerLogPointCut()")
    public void controllerBeforeLog(JoinPoint joinPoint) {
		// mvc获取请求的方式
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

		// 记录请求日志
		log.info("Request type is {}", request.getMethod());
		log.info("Request url is {}", request.getRequestURL());
		log.info("Target is {}", joinPoint.getTarget().getClass());
	}

	/**
	 * 后置通知,具体的增强代码片
	 *
	 * @Param returnResult 目标类方法的返回结果
	 */
	@AfterReturning(pointcut = "controllerLogPointCut()", returning = "returnResult")
	public void controllerAfterLog(Object returnResult) {
		// mvc获取响应的方式
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();

		log.info("Response status is {}, data is {}", response.getStatus(), returnResult.toString());
	}

	/**
	 * 异常时通知,具体的增强代码片
	 * 
	 * @Param exception 目标类方法执行过程中出现的异常
	 */
	// @AfterThrowing(pointcut = "controllerLogPointCut()", throwing = "exception") // 方式1
	@AfterThrowing(pointcut = "execution(* com.spring.demo.controller.*.*(..))", throwing = "exception") // 方式2
	public void controllerAfterLog(Exception exception) {
		// mvc获取响应的方式
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

		log.info("Request url is {}", request.getRequestURL());
		log.info("Inner exception is {}", exception.getMessage());
	}
}
  • 测试

正常返回时,Before和AfterReturning通知生效,前置通知代码在方法执行前执行,后置返回通知是在方法调用完后才执行的

在这里插入图片描述

异常返回时,Before和AfterThrowing通知生效,前置通知代码在方法执行前执行,异常后异常时通知代码先执行,然后再抛出异常片段
在这里插入图片描述

2、事务管理

后补。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值