咸鱼开发修炼之路

AOP思想与SpringAOP实现机制

面向切面编程(Aspect Oriented Program)

 我们知道,面向对象的思想是一切皆对象,三大特性是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是解决了软件系统中角色划分的问题,使类可重用。
 但是人们也发现,在分散代码的同时,也增加了代码的重复性。比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。
69108342.png

为了解决这一问题,AOP应运而生。这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。有了AOP,我们就可以把散布在各处与业务无关的模块,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
 AOP其实只是OOP的补充,以另一种视角进解耦。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。
50af8cfd.png
分离出核心关注点、横切关注点,将横切关注点织入核心关注点模块,优点:

  • 降低系统耦合度
  • 提高系统扩展性
  • 提高组件复用性
3b1c8a84.png

AOP术语

  • 连接点(Joinpoint):程序执行中一个精确执行点,例如一个方法调用,Spring中一般指一个方法。

    a235de42.png
  • 切入点(Pointcut):连接点的集合,这些连接点确认何时触发通知,通常采用正则、通配符语法

    bed3c476.png
  • 通知(Advice):连接点所采用的动作,如权限、日志模块

    43f3d0e3.png

    SpringAOP中,通知可以分为前置通知(Before advice)、后置通知(After Returning advice)、异常通知(After throwing advice)、最终通知(After finally advice)、环绕通知(Around Advice)

  • 切面(Aspect):切入点+通知结合,包含了横切逻辑的定义 62225053.png 即什么时候在什么地方做什么事情。
  • 引入(Introduction):为对象引入附加的方法和属性
  • 目标对象(Target Object):被通知的对象,真正的业务逻辑
  • AOP代理(AOP Proxy):AOP框架创建的代理对象170ab71e.png
  • 织入(Weaving):将切面应用到目标对象创建代理对象的过程

AOP实现方式

AOP是通过动态代理实现,Spring默认采用JDK动态代理(JdkDynamicAopProxy ),也可以支持CGLIB(CglibAopProxy),Spring会通过DefaultAopProxyFactory自动在JDK和CGLib之间转换。
关于动态代理可以参考本人之前的日志:设计模式-代理模式

使用Spring AOP

要在Spring中使用AOP,需要在配置文件上加上注释@EnableAspectJAutoProxy

简单例子,当用户请求Controller时,打印出对应方法名以及参数信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Aspect
@Component
public class ApiUsageLog {
//定义切点
@Pointcut("execution(* core..*(..)) &&@within(org.springframework.web.bind.annotation.RestController)")
private void aspectMethod(){}
//前置通知
@Before("aspectMethod()")
public void beforeAdvice(JoinPoint joinPoint){
String method = joinPoint.getTarget().getClass().getCanonicalName() + "." + joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println(method+":" + JSONObject.toJSON(args).toString());
}
}

Controller方法:
1
2
3
4
5
6
@RestController
public class TestController {
@RequestMapping(value = "/test/{test_name}", method = RequestMethod.GET)
public String getTestName(@PathVariable("test_name") String name) {
return name;
}

验证结果:
发送请求

控制台输出

扩展

Spring AOP在Java开发中有着重要的地位,Spring多个组件都是基于AOP开发,运用十分广泛。比如Spring Security 安全框架、Spring Transcation 事务管理、Spring MVC 异常处理、Spring Cache 缓存管理等。
f12f2e65.png

参考资料

什么是面向切面编程
SpringAOP官方文档