一、前言

  除了依赖注入(DI),Spring框架提供的另一个核心功能是对面向方面的编程(AOP)的支持。 AOP通常被称为实现横切关注点的工具。横切关注点一词是指应用程序中的逻辑不能与应用程序的其余部分分离并有效模块化的地方,并且可能导致代码重复和紧密耦合。通过使用AOP模块化单个逻辑(即关注点),可以将它们应用于应用程序的许多部分,而无需复制代码或创建硬依赖关系。日志记录和安全性是许多应用程序中横切关注点的典型示例。AOP是对面向对象编程(OOP)的补充,而不是与之竞争。 OOP非常擅长解决我们作为程序员遇到的各种问题。鉴于AOP可以在OOP之上运行,因此几乎不可能单独使用AOP来开发整个应用程序,尽管使用OOP当然可以开发整个应用程序,但是通过使用AOP解决涉及横切逻辑的某些问题,能更自己聪明地进行工作。

  • AOP基础知识

  • AOP的类型

  • Spring AOP体系结构

  • Spring AOP中的代理
  • 使用Spring AOP

  • 切入点的高级用法
  • AOP框架服务

  • 集成AspectJ

二、AOP基础知识

  与大多数技术一样,AOP带有其自己的特定概念和术语集,因此了解它们的含义很重要。以下是AOP的核心概念:

  • 连接点(Joinpoint):连接点是在应用程序执行期间定义明确的点。连接点的典型示例包括对方法的调用,方法调用本身,类初始化和对象实例化。连接点是AOP的核心概念,它定义了应用程序中的点,可以在这些点上使用AOP插入其他逻辑。

  • 增强(Advice):在特定联接点执行的代码是Advice,由类中的方法定义。Advice有多种类型,例如之前,在连接点之前执行,之后,在连接点之后执行。(为什么不是翻译成通知?过AOP将横切关注功能加到原有的业务逻辑上,这就是对原有业务逻辑的一种增强,这种增强可以是前置增强、后置增强、返回后增强、抛异常时增强和包围型增强。)

  • 切点(Pointcut):切入点是用于定义何时执行建议的联接点的集合。通过创建切入点,您可以对如何将建议应用于应用程序中的组件进行精细控制。如前所述,典型的连接点是方法调用,或特定类中所有方法调用的集合。通常,您可以在复杂的关系中编写切入点,以在执行建议时进一步限制。

  • 切面(Aspect):切面是封装在类中的建议和切入点的组合。这种结合产生了应包含在应用程序中以及应在何处执行的逻辑的定义。

  • 织入(Weaving):这是在适当的时候将各切面插入应用程序代码中的过程。对于编译时AOP解决方案,这种编织通常是在构建时完成的。同样,对于运行时AOP解决方案,编织过程在运行时动态执行。 AspectJ支持另一种编织机制,称为加载时间编织(LTW),在该机制中,它拦截底层的JVM类加载器,并在由类加载器加载字节码时提供对字节码的编织。

  • 目标(Target):通过AOP流程修改了执行流程的对象称为目标对象。通常,你会看到目标对象,即建议对象。

  • 引介(Introduction):这是可以通过向对象引入其他方法或字段来修改其结构的过程。可以使用引介AOP使任何对象实现特定的接口,而无需对象的类显式实现该接口。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

三、AOP的类型

  AOP有两种不同的类型:静态和动态。它们之间的区别实际上是织造过程的发生点以及该过程的实现方式。

① 使用静态AOP

  在静态AOP中,编织过程构成了应用程序构建过程中的另一步骤。用Java术语,可以通过修改应用程序的实际字节码,并根据需要更改和扩展应用程序代码,从而在静态AOP实现中实现编织过程。这是完成编织过程的一种很好的方式,因为最终结果只是Java字节码,并且您在运行时不会执行任何特殊技巧来确定何时应执行建议。这种机制的缺点是,即使只是想添加另一个连接点,对切面进行的任何修改都要求你重新编译整个应用程序。 AspectJ的编译时编织是静态AOP实现的一个很好的例子。

② 使用动态AOP

  诸如Spring AOP之类的动态AOP实现与静态AOP实现的不同之处在于,编织过程是在运行时动态执行的。如何实现此目标取决于实现,但是正如你将看到的那样,Spring的方法是为所有建议对象创建代理,并允许根据需要调用建议。动态AOP的缺点是,通常它的性能不如静态AOP,但是性能却在稳步提高。动态AOP实现的主要好处是,因而无需修改​​主应用程序代码即可轻松修改应用程序的整个切面集。

③ 选择AOP类型

  选择使用静态AOP还是动态AOP是一个相当困难的决定。两者都有其自身的优点,并且你不限于仅使用一种类型。通常,静态AOP实现的时间更长,并且趋向于具有更多功能丰富的实现,并且具有更多的可用连接点。通常,如果性能绝对至关重要,或者你需要在Spring中未实现的AOP功能,则需要使用AspectJ。在大多数其他情况下,Spring AOP是理想的选择。请记住,Spring已经提供了许多基于AOP的解决方案,例如事务管理,因此在滚动自己的框架之前,请检查其框架功能!与往常一样,让应用程序需求来决定您对AOP实施的选择,如果技术组合更适合自己的应用程序,则不要将自己局限于一个实施。通常,Spring AOP不如AspectJ复杂,因此它是理想的首选。

四、Spring AOP体系结构

  Spring的AOP实施可以看作是两个逻辑部分。 第一部分是AOP核心,它提供完全解耦的纯编程AOP功能(也称为Spring AOP API)。 AOP实现的第二部分是框架服务集,这些框架服务使AOP易于在您的应用程序中使用。 最重要的是,Spring的其他组件(例如事务管理器和EJB帮助器类)提供了基于AOP的服务,以简化应用程序的开发。

  AOP Alliance(http://aopalliance.sourceforge.net/)是许多开源AOP项目的代表之间的共同努力,旨在为AOP实现定义标准的接口集。只要适用,Spring都会使用AOP Alliance接口,而不是定义自己的接口。这使您可以在支持AOP Alliance接口的多个AOP实现中重用某些建议。

  先看一个AOP的例子,编写一个名为Agent的类来打印“Bond”:

1 package com.apress.prospring5.ch5;
2 public class Agent {
3 public void speak() {
4 System.out.print("Bond");
5 }
6 }

  然后我们增强该方法,按AOP的属于来说,就是想该方法添加Advice,在该方法执行前打印“James ”,在该方法指向后打印“!”

 1 package com.apress.prospring5.ch5;
2 import org.aopalliance.intercept.MethodInterceptor;
3 import org.aopalliance.intercept.MethodInvocation;
4
5 public class AgentDecorator implements MethodInterceptor {
6 public Object invoke(MethodInvocation invocation) throws Throwable {
7 System.out.print("James ");
8 Object retVal = invocation.proceed();
9 System.out.println("!");
10 return retVal;
11 }
12 }

  MethodInterceptor 接口是一个标准的 AOP Alliance接口,用于为方法调用连接点实施环绕增强。MethodInvocation对象表示正在建议的方法调用,并且使用此对象,我们控制何时允许进行方法调用。能够在调用方法之前,之后以及返回之前执行操作。在这段代码中,将“James ”为控制台输出,通过调用 invocation.proceed() 来调用该方法(指 speak()),然后打印"!"。

  下面是一个测试实例,创建目标实例和用代理工厂创建代理进行编织:

 1 package com.apress.prospring5.ch5;
2 import org.springframework.aop.framework.ProxyFactory;
3
4 public class AgentAOPDemo {
5 public static void main(String... args) {
6 Agent target = new Agent();
7 ProxyFactory pf = new ProxyFactory();
8 pf.addAdvice(new AgentDecorator());
9 pf.setTarget(target);
10 Agent proxy = (Agent) pf.getProxy();
11 target.speak();
12 System.out.println("");
13 proxy.speak();
14 }
15 }

  使用 ProxyFactory 类创建目标对象的代理,同时编织advice。通过调用addAdvice() 将 AgentDecorator 建议传递给 ProxyFactory,并通过调用 setTarget() 来指定编织目标,调用getProxy() 生成代理,这里调用了原始目标和代理的speak方法。

输出:

  代理的 speak() 调用会导致 AgentDecorator 中的代码执行,从而输出所需的"James Bond!"。

Spring AOP 架构

  Spring AOP的核心架构基于代理。使用ProxyFactory是创建AOP代理的纯编程方法,还可以依靠Spring提供的声明式 AOP 配置机制(ProxyFactoryBean类,aop命名空间和@AspectJ样式的注释)来利用声明式代理创建。在运行时,Spring分析为 ApplicationContext 中的bean定义的横切关注点,并动态生成代理bean(包装基础目标bean)。代替直接调用目标bean,调用者被注入代理bean。然后,代理bean分析运行状况(即,连接点,切入点或advice),并相应地编织适当的advice。

  当要增强的目标对象实现接口时,Spring将使用JDK动态代理创建目标的代理实例。但是,如果建议的目标对象未实现接口(例如,它是一个具体的类),则CGLIB将用于创建代理实例。

Spring中的连接点

  Spring AOP中最明显的最简化的其中一种是仅支持一种连接点类型:方法调用。方法调用联接点是迄今为止可用的最有用的联接点,使用它可以完成许多使AOP在日常编程中有用的任务。

Spring中的切面

  在Spring AOP中,切面由实现Advisor接口的类的实例表示。Spring提供了便捷的Advisor的实现,可以在应用程序中重复使用它们。从而消除了一定要自己创建自定义Advisor实施的需要。Advisor 有两个子接口:PointcutAdvisor 和 IntroductionAdvisor。 PointcutAdvisor 接口继承了 Advisor 接口并使用使用切入点管理应用于连接点的advice。 在Spring中,引介被视为特殊的增强,通过使用 IntroductionAdvisor 接口,可以操作,引介所适用的类。

关于代理工厂

  ProxyFactory类控制Spring AOP中的编织和代理创建过程。 在创建代理之前,必须指定建议对象或目标对象。 您可以通过使用setTarget() 方法来执行此操作。 在内部,ProxyFactory 将代理创建过程委托给DefaultAopProxyFactory的一个实例,该实例又委托给Cglib2AopProxy或JdkDynamicAopProxy。

 1 public class ProxyFactory extends ProxyCreatorSupport {
2 ……
3 public Object getProxy() {
4 return createAopProxy().getProxy();
5 }
6 ……
7
8 }
9 10
11 public class ProxyCreatorSupport extends AdvisedSupport {
12 ……
13 /**
14 * Create a new ProxyCreatorSupport instance.
15 */
16 public ProxyCreatorSupport() {
17 this.aopProxyFactory = new DefaultAopProxyFactory();
18 }
19 ……
20 /**
21 * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
22 * create an AOP proxy with {@code this} as an argument.
23 */
24 protected final synchronized AopProxy createAopProxy() {
25 if (!this.active) {
26 activate();
27 }
28 return getAopProxyFactory().createAopProxy(this);
29 }
30 ……
31 }
32

DefaultAopProxyFactory.java

 1 public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
2
3 @Override
4 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
5 if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
6 Class<?> targetClass = config.getTargetClass();
7 if (targetClass == null) {
8 throw new AopConfigException("TargetSource cannot determine target class: " +
9 "Either an interface or a target is required for proxy creation.");
10 }
11 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
12 return new JdkDynamicAopProxy(config);
13 }
14 return new ObjenesisCglibAopProxy(config);
15 }
16 else {
17 return new JdkDynamicAopProxy(config);
18 }
19 }
20
21 ……
22
23 }

在 Spring 中的 advice

  Spring支持六种 advice,如下表所述:

  1. Before:org.springframework.aop.MethodBeforeAdvice,使用BeforeAdvice,可以在连接点执行之前执行自定义的处理。于Spring中的联接点始终是方法调用,可以在方法执行之前执行预处理。
  2. After-Returning:org.springframework.aop.AfterReturningAdvice,返回点建议在连接点处的方法调用完成执行并返回值后执行。 AfterReturningAdvice可以访问调用的目标方法,传递给该方法的参数以及返回值。
  3. After(finally):org.springframework.aop.AfterAdvice,仅在增强的方法正常执行后才执行AfterReturningAdvice。 但是,无论所增强方法的结果如何,都将执行之后(最终)建议。 即使被增强的方法失败并且抛出异常,该advice也会被执行。
  4. Around:org.aopalliance.intercept.MethodInterceptor,在Spring中,围绕增强使用方法拦截器的AOP Alliance标准进行建模。你的advice被允许在方法调用之前和之后执行,并且你可以控制允许方法调用进行的时间点。
  5. Throws:org.springframework.aop.ThrowsAdvice,在方法调用返回后,将执行引发advice,但前提是该调用引发了异常。 抛出advice可能仅捕获特定的异常,如果选择这样做,则可以访问引发异常的方法,传递给调用的参数以及调用的目标。
  6. Introduction:org.springframework.aop.IntroductionInterceptor,Spring将引介建模为特殊类型的拦截器。 使用引介拦截器,可以通过该advice给增强的目标指定某种方法实现。

Spring:面向切面编程的AOP的更多相关文章

  1. Spring 面向切面编程(AOP)

    Spring 系列教程 Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of ...

  2. Spring——面向切面编程(AOP)详解

    声明:本博客仅仅是一个初学者的学习记录.心得总结,其中肯定有许多错误,不具有参考价值,欢迎大佬指正,谢谢!想和我交流.一起学习.一起进步的朋友可以加我微信Liu__66666666 这是简单学习一遍之 ...

  3. 详解Spring面向切面编程(AOP)三种实现

    一.什么是AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善. ...

  4. 快速高效掌握企业级项目中的Spring面向切面编程应用,外带讲面试技巧

    Spring面向切面编程(AOP)是企业级应用的基石,可以这样说,如果大家要升级到高级程序员,这部分的知识必不可少. 这里我们将结合一些具体的案例来讲述这部分的知识,并且还将给出AOP部分的一些常见面 ...

  5. Spring(三)面向切面编程(AOP)

    在直系学长曾经的指导下,参考了直系学长的博客(https://www.cnblogs.com/WellHold/p/6655769.html)学习Spring的另一个核心概念--面向切片编程,即AOP ...

  6. 依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦(转good)

    依赖注入(DI)有助于应用对象之间的解耦,而面向切面编程(AOP)有助于横切关注点与所影响的对象之间的解耦.所谓横切关注点,即影响应用多处的功能,这些功能各个应用模块都需要,但又不是其主要关注点,常见 ...

  7. (转存)面向切面编程(AOP)的理解

    面向切面编程(AOP)的理解 标签: aop编程 2010-06-14 20:17 45894人阅读 评论(11) 收藏 举报  分类: Spring(9)  在传统的编写业务逻辑处理代码时,我们通常 ...

  8. 面向切面编程 (AOP )

    什么是面向切面编程? 面向切面编程就是(AOP --- aspect-oriented programming), 在百科上说: 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一 ...

  9. Java中的面向切面编程(AOP)

    一.什么是AOP? Aspect Oriented Programming ,即面向切面编程. AOP是对面向对象编程的一个补充. 它的目的是将复杂的需求分解为不同的切面,将散布在系统中的公共功能集中 ...

  10. 面向切面编程(AOP)及其作用

    在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用. 1.面向切面编程(AOP) 面向切面编程(AOP)就是对软件系统不同关注点的分离,开发者通过拦截方法调用并在方法调用前后添加辅助代码. ...

随机推荐

  1. Xilinx约束学习笔记(三)—— 时序概念

    3. 时序概念 发现对于时序基础的介绍这一块,Intel 的文档竟然要比 Xilinx 的详细,因此引用了很多 Intel 的文档内容. 3.1 术语 发送沿(launch edge),指用来发送数据 ...

  2. Spring Boot学习(一)——Spring Boot介绍

    Spring Boot介绍 Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式 ...

  3. 在excel中,截取电话号码后4位

    在单元格中输入以下 =RIGHT($A3,4) 其中$a3是手机号所在的单元格,4是从右查4个

  4. Docker修改容器中的时间

    Docker修改容器中的时间 前言 在公司开发时使用 Docker 创建数据库(SQL Server)的实例十分方便,还原数据库也只要设置好共享文件夹,在 SQL Server Management ...

  5. xmind使用技巧

    xmind看似每个人都会使用,但是掌握一些小技巧,能够有效提升工作效率. 多行复制粘贴 在xmind中选中多行,复制然后可以直接粘贴到excel.word当中. 在excel.word选中多行,复制然 ...

  6. Fillder抓包配置

    Faillder设置,完成以下设置后重启Fillder Fillder工具配置 设置端口 端口设置 (根据公司限制使用范围内的端口) 设置是否远程连接 勾选Decrypt HTTPS traffic ...

  7. CF802O-April Fools‘ Problem(hard)【wqs二分,优先队列】

    正题 题目链接:https://www.luogu.com.cn/problem/CF802O 题目大意 \(n\)天每条有\(a_i\)和\(b_i\). 每条可以花费\(a_i\)准备至多一道题, ...

  8. Unity 刚体问题 解决相互作用力

    在进行开发过程中,当两个都具有碰撞体和刚体的 游戏物体进行接触之后,或多或少都会出现相互作用力,对于体验有一定的影响. 需要在FixedUpdate(间隔固定的时间调用,不受游戏帧率的影响)  当中  ...

  9. HttpRunner3.X - 全面讲解如何落地项目实战

    一.前言 接触httprunner框架有一段时间了,也一直探索如何更好的落地到项目上,本篇主要讲述如何应用到实际的项目中,达到提升测试效率的目的. 1.项目难题 这个月开始忙起来了,接了个大项目,苦不 ...

  10. 踩坑系列《九》 无法获取实体类xxx对应的表名

    话不多说,直接说明原因 类似于 @MapperScan(basePackages = "com.hyxiao.user.mapper") 启动类的mapper扫描注解的导入包正确的 ...