文章目录
SpringBoot中的AOP使用
1.AOP介绍
Spring的AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,
简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
总结:AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。
2. AOP 编程术语
2.1切面(Aspect)
切面泛指交叉业务逻辑,或是公共的,通用的业务。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。
切面有切点和通知组成,即包括横切逻辑的定义也包括连接点的定义
2.2连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
程序执行的某个特定位置,如某个方法调用前后等。
2.3切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
在程序应用中就是你要增强功能的类或方法(确认位置)。
2.4目标对象(Target)
目标对象指 将要被增强 的对象。 即包含主业 务逻辑的 类的对象。 上例中 的
BookServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然, 不被增强,也就无所谓目标不目标了。
目标对象,通知织入的目标类。
2.5通知(Advice)
通知表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时机。
通知,是织入到目标类连接点上的一段代码,就是增强到什么地方?增强什么内容?
3.切入点表达式
3.1AspectJ表达式
表达式共 4 个部分可简化如下:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
AspectJ 定义了专门的表达式用于指定切入点。
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
3.2表达式举例:
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.impl.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..)) * com.xyz.service.power2.aa.*.*(..)
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..)) a.b.service.*.*(..) a.b.c.d.service.*.*(..)
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
4.Aspect中的通知类型
4.1常用四种类型
(1)前置通知@Before
切入点方法执行之前执行的方法@Before
(2)环绕通知@Around
切入点前后都执行 @Around
(3)异常通知@AfterThrowing
切入发点方法执行异常后执行的方法@AfterThrowing:切入点里面的出现异常如10/0,才会执行的方法。
(4)后置通知@After
后置通知,在方法执行之后执行
4.2相关概念
Joinpoint(连接点)
所谓连接点是指那些被拦截到的点,在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点,通俗的说就是被增强类中的所有方法
PointCut(切入点)
所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义,通俗的说就是被增强类中的被增强的方法,因为被增强类中并不是所有的方法都被代理了
Advice(通知/增强)
所谓通知是指拦截到 Joinpoint (被增强的方法)之后所要做的事情就是通知,通俗的说就是对被增强的方法进行增强的代码
4.3相关注解
@Component 将当前类注入到Spring容器内
@Aspect :表明是一个切面类
@Pointcut :切入点,PointCut(切入点)表达式有很多种,其中execution用于使用切面的连接点。
5.spring boot集成AOP
5.1环境准备
引入AOP相关的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
创建controller测试访问
5.2实例一
使用aop来完成全局请求日志处理
步骤一:创建一个aspect切面类
@Component
@Aspect
public class MyAop {
//切入点
//表示public 这个包下的任意方法作为切入点
@Pointcut("execution(public * com.jierlung.controller.*.*(..))")
public void log(){
System.out.println("pointCut签名......");
}
//前置通知
@Before(value = "log()")
public void deBefore(){
System.out.println("我是前置通知呀...");
}
//返回通知
@AfterReturning(returning = "ret", pointcut = "log()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
System.out.println("返回通知:方法的返回值 : " + ret);
}
//异常通知
@AfterThrowing(throwing = "ex", pointcut = "log()")
public void throwss(JoinPoint jp,Exception ex){
System.out.println("异常通知:方法异常时执行.....");
System.out.println("产生异常的方法:"+jp);
System.out.println("异常种类:"+ex);
}
//后置通知
@After("log()")
public void after(JoinPoint jp){
System.out.println("后置通知:最后且一定执行.....");
}
}
测试访问
分析:
第一步:首先通过注解形式定义切面类,把切面类注入容器,交给spring管理
第二步:定义一个方法,加上切入点的注解,指定切哪个包下的类的所有方法,这个作为切入点,该类的方法执行时,例如我访问controller里面的方法时,这个controller就被我作为切入点,该方法执行时,会执行到切入的方法,执行相关业务代码。
第三步:注解方式加上通知,切入点写之前定义的方法,构成一个切面(由切入点和通知组成)。
5.3实例二
@annotation方式
编写一个自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface MyLogAnnotation {
//定义一个变量,可以接受参数
String desc() default " ";
}
定义一个切面类
@Component
@Aspect
public class MyAopAnnotation {
@Pointcut(value = "@annotation(com.jierlung.annotation.MyLogAnnotation)")
//切入点签名
public void logAnnotation(){
System.out.println("pointCut签名。。。");
}
//前置通知
@Before("logAnnotation()")
public void deBefore(JoinPoint jp) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("URL : " + request.getRequestURL().toString());
}
//返回通知
@AfterReturning(returning = "ret", pointcut = "logAnnotation()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
System.out.println("返回通知:方法的返回值 : " + ret);
}
//异常通知
@AfterThrowing(throwing = "ex", pointcut = "logAnnotation()")
public void throwss(JoinPoint jp,Exception ex){
System.out.println("异常通知:方法异常时执行.....");
System.out.println("产生异常的方法:"+jp);
System.out.println("异常种类:"+ex);
}
//后置通知
@After("logAnnotation()")
public void after(JoinPoint jp){
System.out.println("后置通知:最后且一定执行.....");
}
}
在目标方法上加上注解
测试访问