问题 :

  • 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. Java50道经典习题-程序8 输入数字求和

    题目:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字.例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制. 分析:关键是计算出每一项的值. i ...

  2. ClamAV学习【8】——64位Windows7下编译运行实践

    之前用SourceInsight静态分析了ClamAV引擎源码,现在打算开始动态研究下.不过出师不利,一开始就遇到纠结的问题,能力还有待提高. 从官网下了一个VS2005工程的源码包(http://d ...

  3. lamp-linux3

    LAMP编程之Linux(3) 一.权限管理 1.权限介绍(重点) 在Linux中分别有读.写.执行权限: 读权限: 对于文件夹来说,读权限影响用户是否能够列出目录结构 对于文件来说,读权限影响用户是 ...

  4. windows10 pip install MySQL-python mysqlclient

    https://dev.mysql.com/downloads/connector/python/ 到上述地址下载对应系统的驱动程序安装即可. 安装mysqlclient方法如下: https://w ...

  5. day 08 课后作业

    # -*- coding: utf-8 -*-# @Time : 2018/12/27 17:27# @Author : Endless-cloud# @Site : # @File : day 8课 ...

  6. P3622 [APIO2007]动物园

    题目链接 题意分析 这是一道状压\(DP\)的题 一个人只可以欣赏到\(5\)只动物 显然可以状压 我们用\(dp[i][j]\)表示当前\([i,i+4]\)中这\(5\)只动物的状态\(j\) 在 ...

  7. 队列的理解和实现(二) ----- 链队列(java实现)

    什么是链队列 链队是指采用链式存储结构实现的队列,通常链队用单链表俩表示.一个链队显然需要两个分别指示队头和队尾的指针,也称为头指针和尾指针,有了这两个指针才能唯一的确定. package 链队列; ...

  8. JDK源码分析(10) ConcurrentLinkedQueue

    概述 我们要实现一个线程安全的队列有两种实现方法一种是使用阻塞算法,另一种是使用非阻塞算法.使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,而非阻塞 ...

  9. .Net Core 使用NLog记录日志到文件和数据库

    NLog 记录日志是微软官方推荐使用. 接下来,通过配置日志记录到文件和Sql Server数据库. 第一步:首先添加包NLog.Config (可通过微软添加包命令Install-Package 包 ...

  10. 【杂题】[LibreOJ 2541] 【PKUWC2018】猎人杀【生成函数】【概率与期望】

    Description 猎人杀是一款风靡一时的游戏"狼人杀"的民间版本,他的规则是这样的: 一开始有 n个猎人,第 i 个猎人有仇恨度 wi.每个猎人只有一个固定的技能:死亡后必须 ...