问题 :

  • AOP 解决的问题是什么
  • Spring AOP 的底层实现是什么
  • Spring AOP 和 AspectJ 的区别是什么

概述

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

上面的概述可以知道,切面编程的实现可以在编译期或是动态代理的时候(即是运行时期)。我们需要知道的是 Spring 使用的是动态代理,即是在runtime进行切面编程,而 AspectJ 既可以在编译期就完成切面植入,也可以在运行期才完成植入。我们先确定下面几个叙述 :

  • Spring AOP 并不是完成的AOP 解决方案,它只作用在被 Ioc Container 管理的 bean
  • AspectJ 是完整的 AOP 解决方案
  • AOP 的目的就是各个模块分离,降低耦合度

AspectJ   AOP  切面编程的方式

AspectJ makes use of three different types of weaving:

  1. Compile-time weaving: The AspectJ compiler takes as input both the source code of our aspect and our application and produces a woven class files as output
  2. Post-compile weaving: This is also known as binary weaving. It is used to weave existing class files and JAR files with our aspects
  3. Load-time weaving: This is exactly like the former binary weaving, with a difference that weaving is postponed until a class loader loads the class files to the JVM

For more in-depth information on AspectJ itself, head on over to this article.

上面讲的是 AspectJ 可以使用的三种类型的植入。

Spring 方式下的 AOP

Spring 的AOP 用两种方式来实现,下面的图表示过程。

    1. JDK dynamic proxy – the preferred way for Spring AOP. Whenever the targeted object implements even one interface, then JDK dynamic proxy will be used
    2. CGLIB proxy – if the target object doesn’t implement an interface, then CGLIB proxy can be used

JDK 动态代理的方式下,需要targetObject 是一个接口,如果不是接口的话,那么就使用 CGLIB proxy

对比

植入的节点对比

看下图。在某些节点的植入中,Spring 是无法做到的,例如方法调用调用,对象初始化等。

易用性对比

很显然 Spring 更加简单易用,因为它不用引入额外的编译器去编译代码,但是缺点也是明显,只使用它所管理的bean对象,而AspectJ需要引入编译器(ajc)和相关的包,除非AspectJ 使用的 post-compile 或是 load-time 植入。

性能对比

compile-time weaving is much faster than runtime weaving.

编译时期完成植入的方式比运行时期完成植入的方式快得多了,可以理解为但我们生产一个class 的时候已经完成了植入,Spring 则是运行时完成植入,那么就有代理的创建等开销。

对比总结

下图为两者的对比。

Spring AOP 的使用

AOP 切面编程,那么我们必须要知道切哪里,切进去做什么,这是我们先要了解的几个名词。

  • advice  (通知) : 切进去做了什么动作
  • PointCut(切点) :  在哪里切
  • Introduction :   引入允许我们向现有的类添加新的方法或属性例如, 我们可以创建一个Auditable通知
    类, 该类记录了对象最后一次修改时的状态。 这很简单, 只需一个方
    法, setLastModified(Date), 和一个实例变量来保存这个状态。 然后, 这个新方法和
    实例变量就可以被引入到现有的类中, 从而可以在无需修改这些现有的类的情况下, 让它们
    具有新的行为和状态。

Spring切面可以应用5种类型的advice :

  • 前置通知(Before) : 在目标方法被调用之前调用通知功能;
  • 后置通知(After) : 在目标方法完成之后调用通知, 此时不会关心方法的输出是什么;
  • 返回通知(After-returning) : 在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing) : 在目标方法抛出异常后调用通知;
  • 环绕通知(Around) : 通知包裹了被通知的方法, 在被通知的方法调用之前和调用之后
    执行自定义的行为。

Spring AOP 需要了解以下几点 :

  • Spring通知是Java编写的
  • Spring在运行时通知对象
  • Spring只支持方法级别的连接点

Spring  AOP 可以通过两种方式来实现 : chema-based configuration 和  @AspectJ style 注解 ,当使用@AspectJ注解时,spring使用了 AspectJ库的注解并且使用 AspectJ库对切点表达式进行解析和匹配,但AOP运行时并不使用 AspectJ的编译器和织入,仍然是使用纯粹的springAOP实现。

Spring 非常聪明,从上面知道使用Aspect需要特定的编译器和库,Spring使用了和它一样的注解,但是运行还是使用 springAOP 实现,为了可以使用@AspectJ 的一样的注解需要引入两个库(看下面的例子)

下面我们使用 @AspectJ style 注解的方式来使用 AOP .

开启@AspectJ支持,以便支持自动代理。关于自动代理看官方文档的叙述 :

To use @AspectJ aspects in a Spring configuration, you need to enable Spring support for configuring Spring AOP based on @AspectJ aspects and auto-proxying beans based on whether or not they are advised by those aspects. By auto-proxying, we mean that, if Spring determines that a bean is advised by one or more aspects, it automatically generates a proxy for that bean to intercept method invocations and ensures that advice is executed as needed.

例子使用的是spring boot ,下面为 build.gradle 文件 :

plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
} apply plugin: 'io.spring.dependency-management' group = 'com.benjious'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8' repositories {
mavenCentral()
} dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web' //AspectJ AOP 功能的支持
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.2'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

定义切面

@Aspect
public class Audience { @Pointcut(value = "within(com..aop..*)")
public void withInPointCut() {
} @Pointcut(value = "execution(* *perform(..))")
public void methodPointCut() {
} @Pointcut("withInPointCut() && methodPointCut()")
public void finalPointCut() {
} @Before(value = "finalPointCut()")
public void beforeWatch(){
System.out.println("观看之前!");
} @AfterReturning(value = "finalPointCut()")
public void afterWatch(){
System.out.println("观看之后把垃圾拿走");
} @AfterThrowing(value = "finalPointCut()")
public void someThingTrouble(){
System.out.println("运行出现异常!!插入纪录");
} @Around(value = "finalPointCut()")
public void aroundDo(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("观看之前!!");
joinPoint.proceed();
System.out.println("观看之后把垃圾拿走");
}
}

目标被切对象。

public interface Performance {
void perform() throws Exception;
}
@Service
public class PerformanceImpl implements Performance {
@Override
public void perform() throws Exception {
System.out.println("进行表演当中!!!");
// System.out.println("我要扔出异常啦!!");
// throw new Exception();
}
}

这里开启@Aspect 注解支持和使其成为一个bean.

@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class ConcertConfig { @Bean
public Audience getAudience(){
return new Audience();
}
}
 

总结

  • AOP 表示切面编程,AOP的作用就是尽量减少侵入代码,使业务逻辑各个模块分离,Spring 中使用 动态代理和 CGLIB 来实现
  • Spring 中如果需要代理的类继承了接口就使用动态代理,如果没使用接口,就使用CGLIB,Spring 在两者之间切换
  • 动态代理的原理是使用反射生成一个继承Proxy 的类,利用这个类来调用实际类的方法,CGLIB 原理是生成一个继承该类的子类来完成代理工作,Cglib代理需要为每个目标类生成相应的子类。
     

参考资料

Spring 学习(四)--- AOP的更多相关文章

  1. Spring学习之AOP的实现方式

    Spring学习之AOP的三种实现方式 一.介绍AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能 ...

  2. spring学习(二) ———— AOP之AspectJ框架的使用

    前面讲解了spring的特性之一,IOC(控制反转),因为有了IOC,所以我们都不需要自己new对象了,想要什么,spring就给什么.而今天要学习spring的第二个重点,AOP.一篇讲解不完,所以 ...

  3. Spring学习之AOP总结帖

    AOP(面向方面编程),也可称为面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组 ...

  4. spring学习(四) ———— 整合web项目(SSH)

    清楚了spring的IOC 和 AOP,最后一篇就来整合SSH框架把,记录下来,以后应该会用的到. --WH 一.web项目中如何使用spring? 当tomcat启动时,就应该加载spring的配置 ...

  5. Spring基础学习(四)—AOP

    一.AOP基础 1.基本需求      需求: 日志功能,在程序执行期间记录发生的活动. ArithmeticCalculate.java public interface ArithmeticCal ...

  6. Spring学习之AOP

    Spring-AOP(Aspect-orented programming) 在业务流程中插入与业务无关的逻辑,这样的逻辑称为Cross-cutting concerns,将Crossing-cutt ...

  7. Spring学习之AOP与事务

      一.概述 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续, ...

  8. Spring学习之==>AOP

    一.概述 AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等,Struts2的拦截器设计就是基于A ...

  9. Spring学习(四)--面向切面的Spring

    一.Spring--面向切面 在软件开发中,散布于应用中多处的功能被称为横切关注点(cross- cutting concern).通常来讲,这些横切关注点从概念上是与应用的业 务逻辑相分离的(但是往 ...

  10. Spring学习之Aop的各种增强方法

    AspectJ允许使用注解用于定义切面.切入点和增强处理,而Spring框架则可以识别并根据这些注解来生成AOP代理.Spring只是使用了和AspectJ 5一样的注解,但并没有使用AspectJ的 ...

随机推荐

  1. Ajax请求的参数

    post请求和get请求存放参数位置 post请求和get请求存放参数位置是不同的: post方式参数存放在请求数据包的消息体中. get方式参数存放在请求数据包的请求行的URI字段中,以?开始以pa ...

  2. Linux下安装pip(遇到了python2.6升级为python2.7道路上的坑,原因已经找到,只差临门一脚了,以后补上)

    1.先说一下什么是pippip 是“A tool for installing and managing Python packages.”,也就是说pip是python的软件安装工具2.下面介绍怎么 ...

  3. [JS] 四角度旋转特效

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name ...

  4. centos和ubuntu配置路由的三种方式

    本篇总结三种修改路由的方式:route, ip route, 以及通过修改文件来配置路由,前2种命令行形式适用于ubuntu和centos,重启失效,最后一种永久有效.     一. route命令 ...

  5. springMVC请求注解@RequestMapping各个属性值

    最近遇到了一个采用fastJson传输数据的方式,搞了半天,总是感觉模糊,觉得自己有必要在这里做一个系统的总结,今天先从@RequestMapping的属性开始,采用REST 风格的 URL 请求,R ...

  6. leetcode-849-到最近的人的最大距离

    题目描述: 在一排座位( seats)中,1 代表有人坐在座位上,0 代表座位上是空的. 至少有一个空座位,且至少有一人坐在座位上. 亚历克斯希望坐在一个能够使他与离他最近的人之间的距离达到最大化的座 ...

  7. word中手动添加endnote的加载项

    用Endnote管理文献,在写作的同时插入引文,这对于写文章的朋友们来说太重要了.我今天遇到这个问题,花时间钻研了,觉得应该记录下来,相信也会方便大家.查了网上许多帖子依然不得解,可能是Word版本变 ...

  8. "[Vue warn]: Failed to mount component: template or render function not defined"错误的解决

    VUE中动态路由出错: vue.esm.js?a026: [Vue warn]: Failed to mount component: template or render function not ...

  9. 关于 unsigned long long 于 long long

    long long 最大只有19位 : unsigned long long  最大有20位 ,原因牺牲了符合位来换取更大的记录

  10. xshell本地上传文件至服务器

    今天本地写了个项目,想传到服务器部署起来.就上网百度了一下挺多的,一个个记录下,如有雷同,纯属抄袭. lrzsz方法 rz # 检查是否安装 yum -y install lrzsz # 安装 rpm ...