问题 :

  • 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. GO语言官方中文教程!

    官方中文教程网址:https://tour.go-zh.org/basics/1 推荐理由:简洁,一句废话没有,对于初学者可以让大家快速掌握GO语言! 注意问题:如果不能访问,你懂的! 教程截图:

  2. Python面向对象(定义类和创建对象)

    day24 http://www.cnblogs.com/wupeiqi/p/4493506.html Python:函数式+面向对象,函数式编程可以做所有事,但是不一定合适. 小明,10岁,男,上山 ...

  3. 基于openresty配置https访问

    安装方法:http://openresty.org/cn/linux-packages.html 1. openssl的版本信息 [root@localhost conf]# openssl vers ...

  4. python-----读写操作

    1. 文件的读取 注意:在windows中文件的路径是这样:C:\Users\name\mystuff .由于\u在python中表示转义如果使用此文件路径就会报错.解决方法: a.把斜杠\改为反斜杠 ...

  5. c语言求方阵的行列式、伴随矩阵算法

    #include<stdio.h> #include<math.h> #define N 100 //N比输入的阶数大即可 int main() {   int n,a[N][ ...

  6. 【转载】基于Redis实现分布式锁

    背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端 ...

  7. FlowPortal-BPM——数据库交互:创建新接口(类库)—将数据提交给其他程序使用

    使用到的是“流程设计”→“自定义插件” 一.创建新类库 (1)新建类库→引用文件 (2)新建ado.net数据访问类(要操作的数据库) (3)右键类库名称→属性→生成→输出→路径:安装目录下UserD ...

  8. C#-WebForm-设置div边框为内边框:box-sizing:border-box;

    设置div边框为内边框:box-sizing:border-box;

  9. keycloak ssl-required报错问题处理

       两台主机,网段不同,第一台129.30.108.179/24    第二台172.16.160.92/24 都安装keycloak :    docker run -d --name keycl ...

  10. Android NDK开发及OpenCV初步学习笔记

    https://www.jianshu.com/p/c29bb20908da Android NDK开发及OpenCV初步学习笔记 Super_圣代 关注 2017.08.19 00:55* 字数 6 ...