博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Java]-Spring中面向切面编程AOP学习
阅读量:2056 次
发布时间:2019-04-28

本文共 5178 字,大约阅读时间需要 17 分钟。

文章目录

AOP是OOP的延续,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高 了开发的效率。

AOP基础

AOP(Aspect Oriented Programming)面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP术语

先了解一下AOP相关的重要术语:

描述
切面
Aspect
一个具有一组API的模块,提供交叉要求;应用程序可以拥有任意数量的切面。
连接点
Join point
应用程序中可以插入AOP 切面的点(java的方法和异常处理代码块)。即,应用程序中使用Spring AOP框架采取操作的实际位置。
通知
Advice
方法执行之前或之后采取的实际操作。即Spring AOP框架的程序执行期间调用的实际代码片段。
切点
Pointcut
是一组一个或多个连接点,在其中应该执行的通知。可使用表达式或模式指定切入点。
介绍
Introduction
允许添加新方法或属性到现有的类中。
目标对象
Target object
被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。
编织
Weaving
将切面与其他应用程序类型或对象进行链接以创建通知对象的过程。这些可以在编译时,类加载时和运行时完成。

通知类型

Spring中使用的五种通知类型:

通知 描述
before
前置通知
在方法执行之前通知。
after
后置通知
在方法执行之后(不考虑执行结果)执行通知。
after-returning
返回后通知
只有在方法执行成功后,才会通知。
after-throwing
抛出异常后通知
只有在方法因抛出异常完成时,才会通知。
around
环绕通知
在调用通知方法之前和之后,都可运行通知。

切点指示符

使用@Pointcut来定义切点:

@Pointcut("within(@org.springframework.stereotype.Repository *)")public void repositoryClassMethods() {
}@Pointcut("@annotation(com.example.study.service.aop.RequestLogger)")public void logPointcut(){
}

切点指示符是切点定义的关键字,切点表达式以切点指示符开始。有以下9种切点指示符:execution、within、this、target、args、@target、@args、@within、@annotation

切点匹配

可使用与(&&)、或(||)、非(!)来组合切入点表达式。

类型匹配时,可使用通配符:

  • *:匹配任意数量字符;
  • ..:匹配任何数量字符的重复;在类型模式中匹配任何数量子包,而在方法参数模式中匹配任意数量参数(0或多个)。
  • +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边
java.*.String : 匹配java包下的任何“一级子包”下的String类型;java..* : 匹配java包及任何子包下的任何类型;java.lang.*ing : 匹配任何java.lang包下的以ing结尾的类型;  java.lang.Number+ : 匹配java.lang包下的任何Number的自类型;如java.lang.Integer,java.math.BigInteger
模式 描述
public * *(…) 任何公共方法的执行
* cn.javass…IPointcutService.*() cn.javass包及所有子包下IPointcutService接口中的任何无参方法
* cn.javass….(…) cn.javass包及所有子包下任何类的任何方法
* (!cn.javass…IPointcutService+).*(…) 非“cn.javass包及所有子包下IPointcutService接口及子类型”的任何方法
* cn.javass…IPointcutService+.*() cn.javass包及所有子包下IPointcutService接口及子类型的的任何无参方法
* cn.javass…IPointcut*(…) throws IllegalArgumentException cn.javass包及所有子包下IPointcut前缀类型的的任何方法,且抛出IllegalArgumentException异常
@java.lang.Deprecated * *(…) 任何持有@java.lang.Deprecated注解的方法
@java.lang.Deprecated @cn.javass…Secure * *(…) 任何持有@java.lang.Deprecated和@cn.javass…Secure注解的方法
@(java.lang.Deprecated || cn.javass…Secure) * *(…) 任何持有@java.lang.Deprecated或@ cn.javass…Secure注解的方法
(@cn.javass…Secure *) *(…) 任何返回值类型持有@cn.javass…Secure的方法

execution

execution是一种使用频率比较高的切点指示符,用来匹配方法签名(使用全限定名,包括访问修饰符(public/private/protected)、返回类型,包名、类名、方法名、参数)。

@Pointcut("execution(public String com.example.study.findById(Long))")

within

within用来匹配指定类型内的方法,如匹配某个包下面所有类的方法(包括子包下面的所有类方法):

@Pointcut("within(com.example.study..*)")

this和target

this用来匹配的连接点所属的对象引用是某个特定类型的实例,target用来匹配的连接点所属目标对象必须是指定类型的实例。通俗的来讲就是,如果当前要代理的类对象没有实现某个接口的话,则使用this;如果当前要代理的目标对象有实现了某个接口的话,则使用target:

@Pointcut("target(com.example.study.BarDao)")

args

匹配方法中的参数,匹配第一个参数类型为UserModel的所有方法:

@Pointcut("args(com.example.study.UserModel,..)")

@target与@within

@target匹配的目标对象的类有一个指定的注解:

@target(com.example.study.Anno1)

@within指定匹配必须包含某个注解的类里的所有连接点:

@within(com.example.study.Anno1)

两种的区别:

  • @target(Anno1):判断被调用的目标对象中是否声明了注解Anno1,如果有,会被拦截;
  • @within(Anno1): 判断被调用的方法所属的类中是否声明了注解Anno1,如果有,会被拦截;
  • @target关注的是被调用的对象,@within关注的是调用的方法所在的类;

@annotation

匹配有指定注解的方法(注解作用在方法上面)

@annotation(com.example.study.Anno1)

@args

方法参数所属的类型的类上有指定的注解,被匹配:

@args(com.example.study.Anno1)

第一个参数类型的类上有注解Anno1时匹配。

注解AOP示例

以定义一个controller上的AOP注解为例。

依赖包

为例使用AOP,需要添加对应依赖:

org.springframework.boot
spring-boot-starter-aop

定义注解

定义函数上的注解,用于记录函数相关日志:

@Documented@Target({
ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface RequestLogger {
}

实现AOP

通过@Aspect来定义一个切面类,通过@Component注册到容器中。

通过@Pointcut定义切面与注解关联;然后定义对应的通知:

@Aspect@Componentpublic class RequestLogImpl {
private final Logger _logger = LoggerFactory.getLogger(this.getClass()); public RequestLogImpl(){
_logger.info("AOP load"); } @Pointcut("@annotation(com.example.study.service.aop.RequestLogger)") public void logPointcut(){
} @Before("logPointcut()") public void doBeforeLog(JoinPoint point){
_logger.info("before"); } @Around("logPointcut()") public Object doAroundLog(ProceedingJoinPoint point) throws Throwable {
String methodName = point.getSignature().toShortString(); Object[] methodArgs = point.getArgs(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); _logger.info("around before {}", methodName); for(Object arg:methodArgs){
_logger.info("arg: {}", arg); } Object result = point.proceed(); _logger.info("around after {}", methodName); return result; } @After("logPointcut()") public void doAfterLog(JoinPoint point){
_logger.info("after"); }}

通过RequestContextHolder.getRequestAttributes()可以获取rest请求与应答。

使用AOP

在rest请求函数上添加注解,即会自动注入日志记录功能:

@RequestLogger@GetMapping("testApi")public String testApi(HttpServletRequest request, String echo){
_logger.info("testApi"); return echo;}

各切面的执行顺序是:

  • around before
  • before
  • testApi
  • around after
  • after

转载地址:http://bzilf.baihongyu.com/

你可能感兴趣的文章
mininet+floodlight搭建sdn环境并创建简答topo
查看>>
【UML】《Theach yourself uml in 24hours》——hour2&hour3
查看>>
【linux】nohup和&的作用
查看>>
【UML】《Theach yourself uml in 24hours》——hour4
查看>>
Set、WeakSet、Map以及WeakMap结构基本知识点
查看>>
【NLP学习笔记】(一)Gensim基本使用方法
查看>>
【NLP学习笔记】(二)gensim使用之Topics and Transformations
查看>>
【深度学习】LSTM的架构及公式
查看>>
【深度学习】GRU的结构图及公式
查看>>
【python】re模块常用方法
查看>>
剑指offer 19.二叉树的镜像
查看>>
剑指offer 20.顺时针打印矩阵
查看>>
剑指offer 21.包含min函数的栈
查看>>
剑指offer 23.从上往下打印二叉树
查看>>
剑指offer 25.二叉树中和为某一值的路径
查看>>
剑指offer 26. 数组中出现次数超过一半的数字
查看>>
剑指offer 27.二叉树的深度
查看>>
剑指offer 29.字符串的排列
查看>>
剑指offer 31.最小的k个树
查看>>
剑指offer 32.整数中1出现的次数
查看>>