Spring AOP技术本质认识


一、AOP简介

  AOP(Aspect Oriented Programming,面向切面编程),把某一类问题集中在一个地方进行处理,比如处理程序中的点击事件、打印日志等。

1、Join Points:

  简称 JPoints,是 AspectJ 中最关键的一个概念,表示的是程序运行时的一些执行点。理论上说,一个程序中很多地方都可以被看做是JPoint,但是AspectJ中,只有几种执行点被认为是JPoints,如构造方法调用、方法调用、方法执行、异常等等。JPoints实际上就是表示想把AspectJ的代码插入到程序哪个地方,是插入在方法中,还是插入在方法调用前后。需要说明的是:在AspectJ中,方法调用(call)和方法执行(execution)是不一样的,这个后面再做介绍。

2、Pointcuts

  一个程序会有很多的JPoints,即使是同一个函数,还分为call类型和execution类型的JPoint,但是并不是所有的JPoint都是我们需要关心的。比如我们可能只需要关心点击事件方法,那么如何从众多的JPoints中选择我们感兴趣的 JPoint呢?这个时候可以用Pointcut。

3、Advice

  Advice简单来说就是表示AspectJ的hook点,在AspectJ中常用的是before、after、around等。before表示在JPoint执行之前,需要干的事情。after表示的是在JPoint执行之后,around表示的是在JPoint执行前后。

4、Aspect

  前面我们讲了AspectJ中使用过程中需要用到了一个概念,对于问题的处理需要统一放到一个地方去处理,这个地方就是Aspect,意为“切面”。在Java开发中主要是使用@Aspect注解来表示一个切面。


二、AspectJ 介绍

  AspectJ是一个面向切面编程的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。AspectJ还支持原生的Java,只需要加上AspectJ提供的注解即可。

  AspectJ 实现 AOP 效果, AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类:

  静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段通过AOP框架指令生成 AOP 代理类,因此也称为编译时增强;还有一种静态代理是编写代码实现不用工具;这种方式一般是代理模式会使用。

  动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强

  基于 AspectJ 的编译时增强进行 AOP POM 依赖 原生 AspectJ 不依赖Spring案例, 基于 AspectJ 的编译时增强进行 AOP 它是在编译期修改字节码,增强功能;并不会生成新的代理类字节码。

  JDK动态代理实现:https://github.com/cyneck/demo/blob/master/src/main/java/com/example/demo/aopJdk/testAopJDKProxy.java

  CGLIB动态代理实现:https://github.com/cyneck/demo/blob/master/src/main/java/com/example/demo/aopCglib/testAopCglibKProxy.java


三、Spring aop的快速使用

  Show my code:https://github.com/cyneck/demo/blob/master/src/main/java/com/example/demo/aop/log/LogAspect.java

  该示例,是一个日志切面的实例,在开发过程中,主要使用的还是spring提供的@Aspect注解来实现切面。类似的还有全局异常统一处理注解@ControllerAdvice(该注解,主要是捕获最终从Controller层抛出的异常,属于Controller层的增强器。),常常结合使用@ExceptionHandler来处理抛出的异常处理逻辑。

  ControllerAdvice是在类上声明的注解,其用法主要有三点:

  • 结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的;
  • 结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的;
  • 结合方法型注解@ModelAttribute,表示其标注的方法将会在目标Controller方法执行之前执行。

四、扩展内容:动态代理

  1、jdk动态代理和cglib动态代理的区别:

  • jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  • cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
  • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  • 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  • 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

  2、强制使用CGLIB实现AOP

  (1)添加CGLIB库,SPRING_HOME/cglib/*.jar;
  (2)在spring配置文件中加入

<aop:aspectj-autoproxy proxy-target-class="true"/>

  3、JDK动态代理和CGLIB字节码生成的区别
  (1)JDK动态代理只能对实现了接口类生成代理,而不能针对类;
  (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。