Spring学习笔记之AOP

什么是AOP

定义

Aspect Oriented Programming,顾名思义:面向切面编程

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)

One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP, meaning you do not need to use AOP if you don’t want to, AOP complements Spring IoC to provide a very capable middleware solution.

大致的意思就是,OOP(面向对象编程)的单元是class,而AOP(面向切面编程)的单元是aspect。切面可以让模块化的代码比如transaction management(事务管理)切分成多种类型和对象。AOP是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP主要用于日志记录,性能统计,安全控制,事务处理,异常处理等方面。

理解

简单来说,OOP是针对业务对事务的属性及其方法进行封装,逻辑单元划分是类,那么如果想在此基础上增加一些方法,势必就要我们手动去修改已经写好的接口和类。但是软件开发的原则就是:尽量不要去修改原有的代码,而是在原有的接口上重新定义方法。看到这里,我们就可以想到前面学到的动态代理。而AOP正是基于动态代理实现的,它可以对某一个封装好的类进行切分,在执行某个方法之前或者之后定义好切入点,就可以实现在不改变原有的接口基础上动态的执行一些方法。

AOP的这种做法,不但可以解决代码迭代的问题,同时也可以降低耦合度。举例来说,我们去查询数据库,需要进行初始化操作,类里面每个方法都需要进行初始化操作,那么势必会有大量的重复代码,而如果在这个类的接口中定义一个切入点,在切面上我们定义好增强的advice(通知,这样就省去了很多重复代码,而对这些初始化操作进行修改,比如需要添加日志的时候,就可以不修改这个类里面的方法,而是修改我们定义好的advice,或者在通知的基础上再定义切入点,这样既可以不用修改原来的代码,同时也能动态的添加新的方法。

总的来说,AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。


AOP的概念

概念

术语 描述
切面(Aspect) 切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。
连接点(Join point) 这表示您的应用程序中可以插入AOP方面的一点,连接点就是被拦截到的程序执行点。
通知(Advice) 这是在方法执行之前或之后采取的实际操作。 这是在Spring AOP框架的程序执行期间调用的实际代码片段。
切入点(Pointcut) 这是一组一个或多个连接点,其中应该执行通知(Advice)。
介绍(Introduction) 介绍允许向现有类添加新的方法或属性。
目标对象(Target object) 目标对象指将要被增强的对象,也称为通知(Advice)对象。
编织(Weaving) 编织是将方面与其他应用程序类型或对象进行链接以创建通知(Advice)对象的过程。 这可以在编译时,加载时间或运行时完成。

结构

structure

实现AOP的方式

基于注解

  1. 通知方法

    注解 说明
    @Before 前置通知,在连接点方法前调用
    @Around 环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法
    @After 后置通知,在连接点方法后调用
    @AfterReturning 返回通知,在连接点方法执行并正常返回后调用,要求连接点方法在执行过程中没有发生异常
    @AfterThrowing 异常通知,当连接点方法异常时调用
  2. @PointCut:公共切入点表达式

  3. JoinPoint: 作为函数的参数传入切面方法,可以得到目标方法的相关信息

  4. @Aspect : 指定切面类

  5. @EnableAspectJAutoProxy : 开启基于注解的AOP模式

  6. execution表达式
    举例:execution (* com.sample.service.impl..* .* (..))

    1. 第一个* 号:表示返回类型,有public、private等,*号表示所有的类型。

    2. 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包

    3. 第二个* 号:表示类名,*号表示所有的类。

    4. 最后的* 号表示方法名,*号表示所有的方法。

    5. 括弧里面表示方法的参数,两个句点表示任何参数。

实例

转自 https://cloud.tencent.com/developer/article/1397691

这段代码是一个简单的日志相关的切面,依次定义了切入点和通知,而连接点作为log函数的参数传入进来,进行一定的操作,比如说获取连接点函数的名称,参数等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
@Aspect // 切面
public class LogAspect {
private final static Logger LOGGER = LoggerFactory.getLogger(LogAspect.class.getName());
// 切入点,表达式是指com.remcarpediem.test.aop.service
// 包下的所有类的所有方法
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))")
public void aspect() {}
// 通知,在符合aspect切入点的方法前插入如下代码,并且将连接点作为参数传递
@Before("aspect()")
public void log(JoinPoint joinPoint) { //连接点作为参数传入
if (LOGGER.isInfoEnabled()) {
// 获得类名,方法名,参数和参数名称。
Signature signature = joinPoint.getSignature();
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

String[] argumentNames = methodSignature.getParameterNames();

StringBuilder sb = new StringBuilder(className + "." + methodName + "(");

for (int i = 0; i< arguments.length; i++) {
Object argument = arguments[i];
sb.append(argumentNames[i] + "->");
sb.append(argument != null ? argument.toString() : "null ");
}
sb.append(")");

LOGGER.info(sb.toString());
}
}
}
文章目录
  1. 1. 什么是AOP
    1. 1.1. 定义
    2. 1.2. 理解
  2. 2. AOP的概念
    1. 2.1. 概念
    2. 2.2. 结构
  3. 3. 实现AOP的方式
    1. 3.1. 基于注解
    2. 3.2. 实例
|