SpringAOP应用<一>
什么是AOP
-
AOP表示面向切面编程,是Spring Framework中众多特性中的一个特性,AOP可以对某个对象或某些对象的功能进行增加,比如对象中的方法进行增加,可以执行某个方法之前额外的一些事情,在某个方法执行之后额外的做一些事情,通常,我们会利用AOP机制来实现权限控制,日志记录,缓存,包括Spring中的事务也是通过AOP来实现的。在Spring中,AOP是通过动态代理来实现的。
-
与OOP对比,面向切面,传统的OOP开发中的代码逻辑是至上而下的,在这些至上而下的过程中会产生一下横切性的问题,这些横切性的问题和我们的主业务逻辑关系不大,会散落在代码的各个地方,造成难以维护,AOP的编程思想就是把业务逻辑和横切的问题进行分离,从而达到解藕的目的,使代码的重用性和开发效率高
-
OOP如下图:
-
⚠️ :Spring Aop 是Aop的一种实现方式,Aop是一种理念,Spring Aop是实现Aop的一种手段,类似于IOC和DI的关系
AOP Concepts(概念)
- 想要对aop有个比较深刻的学习就必须要了解一下核心的AOP概念和术语,这些术语不是特定于Spring的。不幸的是,AOP 术语并不是特别直观。 但是,如果 Spring 使用自己的术语会更加混乱。
-
- Aspect(切面):通常是指封装的用于横向插入系统功能,如上图的日志记录、权限验证、事务管理等功能抽取出来,也就是说咱们只关注业务本身即可。
-
- Join point(连接点):程序执行过程中的一个点,例如方法的执行或异常的处理。 在 Spring AOP 中,一个连接点总是代表一个方法的执行。(这里串联切入点来理解就是让我们更好的理解切入点)
-
- Advice(通知):想要用到的功能,即上图的日志记录、权限验证、事务管理等功能。我们这边先定义好,然后在想用的地方用上
-
- Pointcut(切入点):在调用类中某个方法的之前或者之后做一些增强操作,比如打印一些日志,抛出异常,token校验啥的
-
- Introduction(引入):允许我们向现有的类添加新方法属性。把切面(也就是新方法属性:通知定义的)用到目标类中
-
- Target object(目标对象):引入中所提到的目标类,也就是要被通知的对象(需要被代理的对象),也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。
-
- AOP proxy(代理):aop实现代理的机制
-
- weaving(织入):在切面与其他业务逻辑或者对象联系起来以创建目标对象(也称为代理对象),这可以在编译时(例如,使用 AspectJ 编译器)、加载时或运行时完成。
- Spring AOP 包括以下类型的通知:
-
- Before advice(通知前): 在连接点之前运行的通知,但不能阻止执行流继续到连接点(除非它抛出异常)。
-
- After returning advice(返回后通知):在连接点正常完成后运行的通知(例如,如果一个方法返回而没有抛出异常)。
-
- After throwing advice(抛出通知后):如果方法通过抛出异常退出,则运行通知。
-
- After (finally) advice(必运行后通知):不管连接点退出的方式(正常或异常返回)都将运行的通知。
-
- Around advice(环绕通知):环绕连接点的通知,例如方法调用。 这是最有力的建议。 环绕通知可以在方法调用之前和之后执行自定义行为。 它还负责选择是继续连接点还是通过返回自己的返回值或抛出异常来缩短建议的方法执行。
@AspectJ support
@AspectJ 指的是一种将切面声明为带有注解的常规 Java 类的风格。 @AspectJ 样式由 AspectJ 项目作为 AspectJ 5 版本的一部分引入。 Spring 解释与 AspectJ 5 相同的注解,使用 AspectJ 提供的库进行切入点解析和匹配。 但是,AOP 运行时仍然是纯 Spring AOP,并且不依赖于 AspectJ 编译器或编织器。
-
- 要使用 Java @Configuration 启用 @AspectJ 支持,请添加 @EnableAspectJAutoProxy 注释,如以下图所示:
- 要使用 Java @Configuration 启用 @AspectJ 支持,请添加 @EnableAspectJAutoProxy 注释,如以下图所示:
-
- 要使用基于 XML 的配置启用 @AspectJ 支持,请使用 aop:aspectj-autoproxy 元素,如以下示例所示:
<aop:aspectj-autoproxy/>
-
- 使用Aspect注解,并把Bean注入容器
- 使用Aspect注解,并把Bean注入容器
-
- 创建一个dao
- 创建一个dao
-
- 运行并加载我们的Bean
- 运行并加载我们的Bean
-
- 运行结果
- 运行结果
支持的切入点指示符
Spring AOP 支持以下用于切入点表达式的 AspectJ 切入点指示符
-
- execution:用于匹配方法执行连接点。 这是使用 Spring AOP 时要使用的主要切入点指示符。
-
- within:用于匹配指定类型内的方法执行;
-
- this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
-
- target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
-
- args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
-
- @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
-
- @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
-
- @annotation:用于匹配当前执行方法持有指定注解的方法;
-
- bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;
-
- reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。
-
- 切入点表达式测试的几个点如下
package com.csw.appconfig;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TestAspectj {
//切点
@Pointcut("execution(* com.csw.dao.*.*(..))")
public void pointCutExecution() {
}
//切点
@Pointcut("within(com.csw.dao.*)")
public void pointCutWithin() {
}
@Pointcut("args(java.lang.String)")
public void pointCutArgs() {
}
@Pointcut("@annotation(com.csw.anno.AopTest)")
public void pointCutAnnotation() {
}
@Pointcut("this(com.csw.dao.IndexDao)")
public void pointCutThis() {
}
//通知到pointCut()
@Before("pointCut()")
public void before() {
System.out.println("在调用pointCut方法之前我需要增强的操作");
}
}
-
- 切入表达式的一些使用方式,面试有可能会问到execution和within的区别,within的细粒度比较大,execution使用的方式比较多,详细看下方描述
应用 | 描述 |
---|---|
execution(public * *(…)) | 任何公共方法的执行 |
execution(* set*(…)) | 执行名称以 set 开头的任何方法 |
execution(* com.xyz.service.AccountService.*(…)) | 执行 AccountService 接口定义的任何方法 |
execution(* com.xyz.service..(…)) | 服务包中定义的任何方法的执行 |
execution(* com.xyz.service….(…)) | 服务包或其子包之一中定义的任何方法的执行 |
within(com.xyz.service.*) | 服务包内的任何连接点(方法只在 Spring AOP 中执行) |
within(com.xyz.service…*) | 服务包或其子包之一中的任何连接点(仅在 Spring AOP 中执行方法) |
this(com.xyz.service.AccountService) | 代理实现 AccountService 接口的任何连接点(仅在 Spring AOP 中执行方法) |
target(com.xyz.service.AccountService) | 目标对象实现 AccountService 接口的任何连接点(仅在 Spring AOP 中的方法执行) |
args(java.io.Serializable) | 任何连接点(仅在 Spring AOP 中的方法执行)接受单个参数并且在运行时传递的参数是可序列化的 |
@target(org.springframework.transaction.annotation.Transactional) | 目标对象具有@Transactional 注释的任何连接点(仅在 Spring AOP 中执行方法) |
@within(org.springframework.transaction.annotation.Transactional) | 目标对象的声明类型具有@Transactional 注释的任何连接点(仅在 Spring AOP 中执行方法) |
@annotation(org.springframework.transaction.annotation.Transactional) | 任何连接点(方法仅在 Spring AOP 中执行),其中执行方法具有 @Transactional 注释 |
@args(com.xyz.security.Classified) | 任何带有单个参数的连接点(仅在 Spring AOP 中的方法执行),并且传递的参数的运行时类型具有 @Classified 注释 |
bean(tradeService) | 名为 tradeService 的 Spring bean 上的任何连接点(仅在 Spring AOP 中执行方法) |
bean(*Service) | Spring bean 上具有与通配符表达式 *Service 匹配的名称的任何连接点(仅在 Spring AOP 中执行方法) |
以上就是一些使用注解方式的切入点表达式使用方法,xml的使用方式请跳转到Spring