b Spring之面向AOP---基础篇:AOP概念

–> go to 总目录


我这里翻译的很模糊,没有认真分析,后面有时间再整理。
很粗糙的文档建议阅读官方文档。

5.1 AOP Concepts

让我们定义一些核心AOP术语和技术开始。这些东西不是Spring特有的… 不幸的是,AOP技术不是特别直观;然而,这将会使你更加困惑如果Spring使用其自身的术语。

  • Aspect : 穿越多个类的关注点的模块。事务管理是一个在Java商业程序中跨越关注点的好例子。在Spring AOP中,使用常规的class来实现切面(基于约束)或用@Aspect注解标注的常规类(@AspectJ风格)

  • Join point:执行一个程序时的点,例如执行一个方法或处理异常。在Spring AOP,一个连接点永远表示一个方法的执行

  • Advice: 一个方面在某个特定连接点上所采取的动作。不同类型的通知包括:“around", "before”, "after"通知(通知类型会在下面被讨论)。许多AOP框架,包括Spring,把一个通知作为拦截器,管理一个包围连接点的拦截器链。

  • Pointcut:断言匹配到连接点。通知与一个切点表达式关联,通过切点通知可以在任何的连接点执行(例如,一个确定名子的方法的执行)由切点表达式匹配连接点是AOP的核心概念,Spring默认使用AspectJ切点表达式语言
    Introduction

  • Target object : 被一个或多个切面通知的对象。也被称作被通知对象。因为Spring AOP是使用运行时代理实现的,此对象将永远是一个被代理的对象。

  • AOP proxy:一个由AOP框架创建的实现切面合同的(通知方法的执行等)对象。在Spring框架,一个AOP代理将会是JDK的动态代理类或CGLIB代理

  • Weaving:把其他的应用类型或对象连接切面来创建一个通知对象。这可以在编译期做到(使用AspectJ编译),加载时间,运行时间。Spring AOP ,像其他的纯Java AOP框架一样,在运行时织入
    通知类型

  • Before advice: 在连接点之前执行的通知,但是没有阻止接下来继续处理连接点的能力(除非他抛出一个异常)
    //这样抛出异常会限制连接点方法的执行
    @Before(value = “execution(public * com4.comp.SayHello3.sayHello(…))”)
    public void sayHello3Before() {
    throw new NullPointerException();
    }

  • After returning advice:在连接点完全正常执行成功之后要执行的通知:例如,如果一个方法正常返回没有抛出异常。

  • After throwing advice: 如果一个方法通过抛出异常的方式退出要执行的通知

  • After(finally)advice : 要被执行的通知忽视方法的退出方式(正常或异常返回)

  • Around advice: 包围一个连接点(例如一个方法的调用)的通知。这是最强大的一种通知之一。环绕通知可以自定义方法调用之前和之后的行为。它也有能力选择是否继承处理连接点或返回该连接点原本返回的值还是抛出一个异常

环绕通知是最普通的通知的一种,自Spring AOP, 像AspectJ, 提供全范围的通知类型,我们建议你使用最小影响的通知类型可以提升请求的性能。例如,如果你仅需要更新方法返回的值的缓存,你最好实现一个after returing通知而不是环绕通知,仅管一个环绕通知可以完成同样的事情。使用最合式的通知可以提供简单的编程模式减少潜在的错误。例如,你不需要调用around advice 的JoinPoint.proceed()方法,因此调用他不会失败。

所有建议参数都是静态类型的,因此您可以使用适当类型的建议参数(例如,方法执行的返回值的类型),而不是对象数组。
切入点匹配的连接点的概念是AOP的关键,它与仅提供拦截功能的旧技术有所不同。切入点使建议的目标独立于面向对象的层次结构。例如,您可以将提供声明性事务管理的环绕建议应用于跨越多个对象(例如服务层中的所有业务操作)的一组方法。

5.2. Spring AOP Capabilities and Goals

AOP的能力和目标
Spring AOP是用纯Java实现的。不需要特殊的编译过程。 Spring AOP不需要控制类加载器的层次结构,因此适合在Servlet容器或应用程序服务器中使用。

Spring AOP当前仅支持方法执行连接点(建议在Spring Bean上执行方法)。尽管可以添加对字段侦听的支持而不破坏核心Spring AOP API,但是字段侦听并未实现。如果需要建议字段访问和更新连接点,请考虑使用诸如AspectJ之类的语言。

Spring AOP的AOP方法不同于大多数其他AOP框架。目的不是提供最完整的AOP实现(尽管Spring AOP相当强大)。相反,其目的是在AOP实现和Spring IoC之间提供紧密的集成,以帮助解决企业应用程序中的常见问题。

因此,例如,通常将Spring Framework的AOP功能与Spring IoC容器结合使用。通过使用常规bean定义语法来配置方面(尽管这允许强大的“自动代理”功能)。这是与其他AOP实现的关键区别。使用Spring AOP无法轻松或高效地完成某些事情,例如建议非常细粒度的对象(通常是域对象)。在这种情况下,AspectJ是最佳选择。但是,我们的经验是,Spring AOP为AOP可以解决的企业Java应用程序中的大多数问题提供了出色的解决方案。

Spring AOP从未努力与AspectJ竞争以提供全面的AOP解决方案。我们认为,基于代理的框架(如Spring AOP)和成熟的框架(如AspectJ)都是有价值的,它们是互补的,而不是竞争。 Spring无缝地将Spring AOP和IoC与AspectJ集成在一起,以在基于Spring的一致应用程序架构中支持AOP的所有使用。这种集成不会影响Spring AOP API或AOP Alliance API。 Spring AOP仍然向后兼容。有关Spring AOP API的讨论,请参见下一章。

Spring框架的中心宗旨之一是非侵入性。这是一个想法,不应强迫您将特定于框架的类和接口引入业务或域模型。但是,在某些地方,Spring Framework确实为您提供了将特定于Spring Framework的依赖项引入代码库的选项。提供此类选项的基本原理是,在某些情况下,以这种方式阅读或编码某些特定功能可能会更加容易。但是,Spring框架(几乎)总是为您提供选择:您可以自由地就哪个选项最适合您的特定用例或场景做出明智的决定。
与本章相关的一种选择是选择哪种AOP框架(以及哪种AOP样式)。您可以选择AspectJ和/或Spring AOP。您也可以选择@AspectJ注释样式方法或Spring XML配置样式方法。本章选择首先介绍@AspectJ样式的方法这一事实不应被视为表明Spring团队比Spring XML配置样式更喜欢@AspectJ注释样式的方法。
有关每种样式的“为什么和为什么”的更完整讨论,请参见选择要使用的AOP声明样式。

Spring AOP默认将标准JDK动态代理用于AOP代理。这使得可以代理任何接口(或一组接口)。

5.3. AOP Proxies

Spring AOP也可以使用CGLIB代理。这对于代理类而不是接口是必需的。默认情况下,如果业务对象未实现接口,则使用CGLIB。由于对接口而不是对类进行编程是一种好习惯,因此业务类通常实现一个或多个业务接口。在那些需要建议在接口上未声明的方法或需要将代理对象作为具体类型传递给方法的情况下(在极少数情况下),可以强制使用CGLIB。

掌握Spring AOP是基于代理的这一事实很重要。有关完全了解此实现细节实际含义的详细信息,请参阅了解AOP代理。

5.4 @AspectJ support

@AspectJ是一种将方面声明为带有注释的常规Java类的样式。 @AspectJ样式是AspectJ项目在AspectJ 5版本中引入的。 Spring使用AspectJ提供的用于切入点解析和匹配的库来解释与AspectJ 5相同的注释。但是,AOP运行时仍然是纯Spring AOP,并且不依赖于AspectJ编译器或编织器。

5.4.1. Enabling @AspectJ Support

开启@AspectJ支持
在这里插入图片描述
等同于xml
在这里插入图片描述

5.4.2. Declaring an Aspect

声明一个切面
在这里插入图片描述
Aspects有filed和method,它也可以有
pointcut, advice, and introduction(inter-type)的声明

5.4.3. Declaring a Pointcut

切入点确定了感兴趣的连接点,从而使我们能够控制执行建议的时间。 Spring AOP仅支持Spring Bean的method execution join points,因此您可以将pointcut视为与Spring Bean上的方法执行匹配。切入点声明由两部分组成:一个包含名称和任何参数的签名,以及一个切入点表达式,该切入点表达式精确地确定我们感兴趣的方法执行。在AOP的@AspectJ批注样式中,常规方法定义提供了切入点签名。 ,并通过使用@Pointcut批注指示切入点表达式(用作切入点签名的方法必须具有void返回类型)。

一个示例可能有助于使切入点签名和切入点表达式之间的区别变得清晰。下面的示例定义一个名为anyOldTransfer的切入点,该切入点与名为transfer的任何方法的执行相匹配:
声明切点
在这里插入图片描述

形成@Pointcut批注的值的切入点表达式是常规的AspectJ 5切入点表达式。有关AspectJ的切入点语言的完整讨论,请参见AspectJ编程指南(以及扩展,请参见AspectJ 5开发人员手册)或有关AspectJ的书籍之一(如Colyer等人的Eclipse AspectJ,或《 AspectJ in Action》 ,由Ramnivas Laddad撰写)。

Supported Pointcut Designators

在这里插入图片描述

Combining Pointcut Expressions

在这里插入图片描述

Sharing Common Pointcut Definitions

在使用企业应用程序时,开发人员通常希望从多个方面引用应用程序的模块和特定的操作集。我们建议为此定义一个“ SystemArchitecture”方面,以捕获常见的切入点表达式。这样的方面通常类似于以下示例:
在这里插入图片描述

Examples

Spring AOP用户可能最常使用执行切入点指示符。执行表达式的格式如下
在这里插入图片描述
参数的匹配声明
除了返回类型模式(前面的代码片段中的ret-type-pattern),名称模式和参数模式以外的所有部分都是可选的。返回类型模式确定该方法的返回类型必须是什么才能使连接点匹配。 *最常用作返回类型模式。它匹配任何返回类型。仅当方法返回给定类型时,完全合格的类型名称才匹配。名称模式与方法名称匹配。您可以将*通配符用作名称模式的全部或一部分。如果您指定一个声明类型模式,请在其后加上尾随。将其加入名称模式组件。参数模式稍微复杂一些:()匹配不带参数的方法,而(…)匹配任意数量(零个或多个)的参数。 (*)模式与采用任何类型的一个参数的方法匹配。 (*,String)匹配一个带有两个参数的方法。第一个可以是任何类型,而第二个必须是字符串。有关更多信息,请查阅AspectJ编程指南的“语言语义”部分。
在这里插入图片描述

5.4.4. Declaring Advice

before

在这里插入图片描述

After Returning Advice

在这里插入图片描述

After Throwing Advice

在这里插入图片描述

After (Finally) Advice

在这里插入图片描述
最后一种建议是围绕建议。围绕建议在匹配方法的执行过程中“围绕”运行。它有机会在方法执行之前和之后进行工作,并确定何时,如何以及什至根本不执行该方法。如果需要以线程安全的方式(例如,启动和停止计时器)在方法执行之前和之后共享状态,则通常使用环绕建议。始终使用最不符合要求的建议形式(即before advice可以使用,就不要使用advice)。
通过使用@Around批注来声明周围建议。咨询方法的第一个参数必须是ProceedingJoinPoint类型。在建议的正文中,在ProceedingJoinPoint上调用proce()会使基础方法执行。进行方法也可以传入Object []。数组中的值用作方法执行时的参数。

procced()

注意pjp承载着真正的被执行方法,使用proceed()可以让方法执行
在这里插入图片描述
around advice的值是该方法的调用者看到的返回值。例如,如果一个简单的缓存方面有一个值,则它可以从缓存中返回一个值,如果没有,则调用proce()。请注意,proc可能会在环境建议的正文中被调用一次,多次或完全不被调用。所有这些都是合法的。就是可以阻止方法执行的原因

Advice Parameters

Spring提供了完全类型化的建议,这意味着您可以在建议签名中声明所需的参数(如我们先前在返回和抛出示例中所见),而不是一直使用Object []数组。我们将在本节的后面部分介绍如何使参数和其他上下文值可用于建议主体。首先,我们看一下如何编写通用建议,以了解该建议当前建议的方法。

Access to the Current JoinPoint

任何建议方法都可以将org.aspectj.lang.JoinPoint类型的参数声明为它的第一个参数(请注意,需要周围建议以声明ProceedingJoinPoint类型的第一个参数,它是JoinPoint的子类。JoinPoint接口提供了一个多种有用的方法:
在这里插入图片描述

Passing Parameters to Advice

我们已经看到了如何绑定返回的值或异常值(在返回和引发建议之后使用)。要使参数值可用于建议正文,可以使用args的绑定形式。如果在args表达式中使用参数名称代替类型名称,则在调用建议时会将相应参数的值作为参数值传递。一个例子应该使这一点更清楚。假设您要建议以Account对象作为第一个参数的DAO操作的执行,并且您需要在建议正文中访问该帐户。您可以编写以下内容
在这里插入图片描述
切入点表达式的args(account,…)部分有两个用途。首先,它将匹配限制为仅方法采用至少一个参数并且传递给该参数的参数为Account实例的那些方法执行。其次,它通过account参数使建议的实际Account对象可用。 编写此内容的另一种方法是声明一个切入点,当切入点与匹配点匹配时“提供” Account对象值,然后从通知中引用命名切入点。如下所示:
在这里插入图片描述

Advice Parameters and Generics

Spring AOP可以处理类声明和方法参数中使用的泛型。假设您具有如下通用类型
在这里插入图片描述
注意第三个是不能的示例

Determining Argument Names

通知调用中的参数绑定依赖于切入点表达式中使用的名称与通知和切入点方法签名中声明的参数名称的匹配。通过Java反射无法获得参数名称,因此Spring AOP使用以下策略来确定参数名称:

  • 如果用户已明确指定参数名称,则使用指定的参数名称。建议和切入点注释都具有可选的argNames属性,您可以使用该属性来指定带注释的方法的参数名称。这些参数名称在运行时可用。以下示例显示如何使用argNames属性
    在这里插入图片描述
    如果第一个参数是JoinPoint,ProceedingJoinPoint或JoinPoint.StaticPart类型,则可以从argNames属性的值中忽略该参数的名称。例如,如果您修改前面的建议以接收连接点对象,则argNames属性不需要包括它:
    在这里插入图片描述
    对JoinPoint,ProceedingJoinPoint和JoinPoint.StaticPart类型的第一个参数给予的特殊处理对于不收集任何其他联接点上下文的建议实例特别方便。在这种情况下,您可以省略argNames属性。例如,以下建议无需声明argNames属性:
    在这里插入图片描述
  • 使用’argNames’属性有点笨拙,因此,如果未指定’argNames’属性,Spring AOP将查看该类的调试信息,并尝试从局部变量表中确定参数名称。只要已使用调试信息(至少是“ -g:vars”)编译了类,此信息就会存在。启用此标志时进行编译的结果是:(1)您的代码稍微易于理解(反向工程),(2)类文件的大小略大(通常无关紧要),(3)优化以删除未使用的本地文件变量不适用于您的编译器。换句话说,通过启用此标志,您应该不会遇到任何困难。
  • 如果在没有必要调试信息的情况下编译了代码,Spring AOP将尝试推断绑定变量与参数的配对(例如,如果切入点表达式中仅绑定了一个变量,并且advice方法仅接受一个参数,则配对很明显)。如果在给定可用信息的情况下变量的绑定是不明确的,则抛出AmbiguousBindingException。
  • 如果以上所有策略均失败,则抛出IllegalArgumentException

Proceeding with Arguments

前面我们提到,我们将描述如何编写一个在Spring AOP和AspectJ上始终有效的参数的proceed调用。解决方案是确保建议签名按顺序绑定每个方法参数。以下示例显示了如何执行此操作
在这里插入图片描述

Advice Ordering

当多条建议都希望在同一连接点上运行时会发生什么? Spring AOP遵循与AspectJ相同的优先级规则来确定建议执行的顺序。优先级最高的建议首先“在途中”运行(因此,考虑到两条先行建议,优先级最高的建议首先运行)。从连接点“出路”时,优先级最高的建议将最后运行(因此,给定两条后置通知,优先级最高的建议将第二次运行)。 当在不同方面定义的两条建议都需要在同一连接点上运行时,除非另行指定,否则执行顺序是不确定的。您可以通过指定优先级来控制执行顺序。通过在方面类中实现org.springframework.core.Ordered接口或使用Order批注对其进行注释,可以通过普通的Spring方法来完成。给定两个方面,从Ordered.getValue()(或注释值)返回较低值的方面具有较高的优先级。 当在同一方面定义的两条建议都需要在同一连接点上运行时,其顺序是未定义的(因为无法通过反射来获取javac编译类的声明顺序)。考虑将这些建议方法折叠为每个方面类中每个连接点的一个建议方法,或将建议重构为可在方面级别订购的单独方面类。

5.4.5 Introductions

就是Aspect中inter-type类型,可以让aspect 声明 advised对象(实现指定的接口),并且提供指定接口的实现实例。

通过@DeclareParents 来声明 Introduction类。此批注用于声明匹配类型具有新的parent(因此而得名)。例如,给定一个名为UsageTracked的接口和该接口名为DefaultUsageTracked的实现,以下方面声明service接口的所有实现者也都实现了UsageTracked接口(例如,通过JMX公开统计信息)
在这里插入图片描述
要实现的接口由带注释的字段的类型确定。 @DeclareParents批注的value属性是AspectJ类型的模式。匹配类型的任何Bean均实现UsageTracked接口。请注意,在前面示例的之前建议中,服务Bean可以直接用作UsageTracked接口的实现。如果以编程方式访问bean,则应编写以下内容
在这里插入图片描述

5.4.6. Aspect Instantiation Models

默认情况下,应用程序上下文中每个方面都有一个实例。 AspectJ将此称为单例实例化模型。可以使用备用生命周期来定义方面。 Spring支持AspectJ的perthis和pertarget实例化模型(当前不支持percflow,percflowbelow和pertypewithin)。 您可以通过在@Aspect批注中指定perthis子句来声明perthis方面。考虑以下示例:
在这里插入图片描述
在前面的示例中,“ perthis”子句的作用是为每个执行业务服务的唯一服务对象(每个与切入点表达式匹配的联接点绑定到“ this”的唯一对象)创建一个方面实例。方面实例是在服务对象上首次调用方法时创建的。当服务对象超出范围时,方面将超出范围。在创建方面实例之前,其中的任何建议都不会执行。创建方面实例后,在其中声明的建议将在匹配的连接点处执行,但仅当服务对象是与此方面相关联的对象时才执行。有关每个子句的更多信息,请参见AspectJ编程指南。 pertarget实例化模型的工作方式与perthis完全相同,但是它在匹配的连接点为每个唯一目标对象创建一个方面实例。

5.4.7. An AOP Example

既然您已经了解了所有组成部分的工作方式,那么我们可以将它们组合在一起以做一些有用的事情。 有时由于并发问题(例如,死锁失败者),业务服务的执行可能会失败。如果重试该操作,则很可能在下一次尝试中成功。对于适合在这种情况下重试的业务(不需要为解决冲突而需要返回给用户的幂等操作),我们希望透明地重试该操作,以避免客户端看到PessimisticLockingFailureException。这项要求明确地跨越了服务层中的多个服务,因此非常适合通过一个方面实施。 因为我们想重试该操作,所以我们需要使用around advice,以便可以多次调用proced。以下清单显示了基本方面的实现:
在这里插入图片描述
请注意,方面实现了Ordered接口,因此我们可以将方面的优先级设置为高于事务建议(每次重试时都希望有新的事务)。 maxRetries和order属性均由Spring配置。建议的主要动作发生在doConcurrentOperation中。请注意,目前,我们将重试逻辑应用于每个businessService()。我们尝试继续,如果失败并出现PessimisticLockingFailureException,则我们将再次尝试,除非我们用尽了所有重试尝试。
相关的xml配置
在这里插入图片描述
为了重新定义这个aspect得次数我们定义如下的注解
在这里插入图片描述
额外支持注解来定义切点
然后,我们可以使用annotation来标注service操作的实现。aspect更改为仅重试幂等操作涉及更改切入点表达式,以便仅@Idempotent操作匹配,如下所示:
在这里插入图片描述

5.5 基于schema-based的注解

5.6 Choosing which AOP Declaration Style to Use

一旦确定方面是实现给定需求的最佳方法,您如何在使用Spring AOP或AspectJ以及在Aspect语言(代码)样式,@AspectJ批注样式或Spring XML样式之间做出选择?这些决定受许多因素影响,包括应用程序需求,开发工具和团队对AOP的熟悉程度。

5.6.1. Spring AOP or Full AspectJ?

使用最简单的方法即可。 Spring AOP比使用完整的AspectJ更简单,因为不需要在开发和构建过程中引入AspectJ编译器/编织器。如果您只需要建议在Spring bean上执行操作,则Spring AOP是正确的选择。如果您需要建议不受Spring容器管理的对象(通常是域对象),则需要使用AspectJ。如果您希望建议除简单方法执行之外的连接点(例如,字段获取或设置连接点等),则还需要使用AspectJ。 使用AspectJ时,可以选择AspectJ语言语法(也称为“代码样式”)或@AspectJ注释样式。显然,如果您不使用Java 5+,则可以为您做出选择:使用代码样式。如果方面在您的设计中起着重要作用,并且您能够使用用于Eclipse的AspectJ开发工具(AJDT)插件,则AspectJ语言语法是首选。它更干净,更简单,因为该语言是专为编写方面而设计的。如果您不使用Eclipse或只有少数几个方面在您的应用程序中不起作用,那么您可能要考虑使用@AspectJ样式,在IDE中坚持常规Java编译,并向其中添加方面编织阶段您的构建脚本。

5.6.2. @AspectJ or XML for Spring AOP?

如果选择使用Spring AOP,则可以选择@AspectJ或XML样式。有各种折衷考虑。 XML样式可能是现有Spring用户最熟悉的,并且得到了真正的POJO的支持。当使用AOP作为配置企业服务的工具时,XML是一个不错的选择(一个很好的测试是您是否将切入点表达式视为配置的一部分,而您可能希望独立更改)。使用XML样式,可以说从您的配置中可以更清楚地了解系统中存在哪些方面。 XML样式有两个缺点。首先,它没有完全将它所解决的需求的实现封装在一个地方。 DRY原则说,系统中的任何知识都应该有单一,明确,权威的表示。当使用XML样式时,关于如何实现需求的知识会在配置文件中的后备bean类的声明和XML中分散。当您使用@AspectJ样式时,此信息将封装在一个模块中:方面。其次,与@AspectJ样式相比,XML样式在表达能力上有更多限制:仅支持“单例”方面实例化模型,并且无法组合以XML声明的命名切入点。例如,使用@AspectJ样式,您可以编写如下内容:
在这里插入图片描述
XML方法的缺点是您无法通过组合这些定义来定义accountPropertyAccess切入点。 @AspectJ样式支持其他实例化模型和更丰富的切入点组合。它具有将方面保持为模块化单元的优势。它还具有的优点是,Spring AOP和AspectJ都可以理解@AspectJ方面。因此,如果您以后决定需要AspectJ的功能来实现其他要求,则可以轻松地迁移到经典的AspectJ设置。总而言之,Spring团队在自定义方面更喜欢@AspectJ样式,而不是简单地配置企业服务。

5.7 混合 Aspect Types类型

通过使用自动代理支持,模式定义的<aop:aspect>方面,<aop:advisor>声明的顾问程序,甚至是同一配置中其他样式的代理和拦截器,完全可以混合@AspectJ样式的方面。所有这些都是通过使用相同的基础支持机制来实现的,并且可以毫无困难地共存。

5.8. Proxying Mechanisms

Spring AOP使用JDK动态代理或CGLIB创建给定目标对象的代理。 JDK内置了JDK动态代理,而CGLIB是常见的开源类定义库(重新包装到spring-core中)。

如果要代理的目标对象实现至少一个接口,则使用JDK动态代理。代理了由目标类型实现的所有接口。如果目标对象未实现任何接口,则将创建CGLIB代理。

如果要强制使用CGLIB代理(例如,代理为目标对象定义的每个方法,而不仅是由其接口实现的方法),都可以这样做。但是,您应该考虑。

  • 使用CGLIB,不能建议final methods,因为不能在运行时生成的子类中覆盖最终方法。
  • 从Spring 4.0开始,由于CGLIB代理实例是通过Objenesis创建的,因此不再调用代理对象的构造函数两次。仅当您的JVM不允许绕过构造函数时,您才可能从Spring的AOP支持中看到两次调用和相应的调试日志条目。

要强制使用CGLIB代理,请将<aop:config>元素的proxy-target-class属性的值设置为true,如下所示:
在这里插入图片描述

5.8.1. Understanding AOP Proxies

Spring AOP是基于代理的。在编写自己的方面或使用Spring框架随附的任何基于Spring AOP的方面之前,掌握最后一条语句实际含义的语义至关重要。 首先考虑您有一个普通的,未经代理的,无特殊要求的,直接的对象引用的场景,如以下代码片段所示
在这里插入图片描述
直接调用
如果在对象引用上调用方法,则直接在该对象引用上调用该方法,如下图和清单所示:
在这里插入图片描述
简单的代理调用
在这里插入图片描述
在这里要了解的关键是Main类的main(…)方法中的客户端代码具有对代理的引用。这意味着该对象引用上的方法调用是代理上的调用。结果,代理可以委派给与该特定方法调用相关的所有拦截器(建议)。但是,一旦调用最终到达目标对象(在本例中为SimplePojo,则为引用),它可能对其自身进行的任何方法调用(例如this.bar()或this.foo())都会被调用(this.bar被嵌套在this.foo),this.bar不会被代理。这具有重要意义。这意味着自调用不会导致与方法调用相关的advice得到执行的机会。

好吧,那该怎么办?最佳方法(在这里宽松地使用术语“最佳”)是重构代码,以免发生自调用。这确实需要您做一些工作,但这是最好的,侵入性最小的方法。下一种方法绝对可怕,我们正要指出这一点,恰恰是因为它是如此可怕。您可以(对我们来说是痛苦的)完全将类中的逻辑与Spring AOP绑定在一起,如以下示例所示:
有用但是错误的例子
在这里插入图片描述
这将您的代码完全耦合到Spring AOP,并且使类本身意识到在AOP上下文中使用它的事实,而AOP上下文却是如此。创建代理时,它还需要一些其他配置,如以下示例所示:
在这里插入图片描述
结论
最后,必须注意,AspectJ没有此自调用问题,因为它不是基于代理的AOP框架

5.9. Programmatic Creation of @AspectJ Proxies

除了使用<aop:config>或<aop:aspectj-autoproxy>声明配置中的各个方面外,还可以通过编程方式创建建议目标对象的代理。有关Spring的AOP API的完整详细信息,请参阅下一章。在这里,我们要重点介绍通过使用@AspectJ方面自动创建代理的功能。 您可以使用org.springframework.aop.aspectj.annotation.AspectJProxyFactory类为一个或多个@AspectJ方面建议的目标对象创建代理。此类的基本用法非常简单,如以下示例所示:
在这里插入图片描述

5.10. Using AspectJ with Spring Applications

到目前为止,本章介绍的所有内容都是纯Spring AOP。在本节中,我们研究了如果您的需求超出了Spring AOP所提供的功能,那么如何使用AspectJ编译器或weaver代替Spring AOP或除Spring AOP之外使用。 Spring附带了一个小的AspectJ方面库,该库在您的发行版中可以作为spring-aspects.jar独立使用。您需要将其添加到您的类路径中才能使用其中的方面。使用AspectJ依赖于Spring和AspectJ的其他Spring方面来注入域对象以及AspectJ讨论了该库的内容以及如何使用它。使用Spring IoC配置AspectJ方面讨论了如何依赖注入使用AspectJ编译器编织的AspectJ方面。最后,Spring Framework中使用AspectJ进行的加载时编织为使用AspectJ的Spring应用程序提供了加载时编织的介绍。

5.10.1. Using AspectJ to Dependency Inject Domain Objects with Spring

Spring容器实例化并配置在您的应用程序上下文中定义的bean。给定包含要应用的配置的Bean定义的名称,也可以要求Bean工厂配置预先存在的对象。 spring-aspects.jar包含一个注释驱动的方面,该方面利用此功能允许依赖项注入任何对象。该支撑旨在用于在任何容器的控制范围之外创建的对象。域对象通常属于此类,因为它们通常是通过数据库查询的结果由新操作员或ORM工具以编程方式创建的。 @Configurable注释将一个类标记为符合Spring驱动的配置。在最简单的情况下,您可以将其纯粹用作标记注释,如以下示例所示:注意是@Configurable不是@Configuration
在这里插入图片描述
当以这种方式用作标记接口时,Spring通过使用具有与完全限定类型名称(com)相同名称的bean定义(通常为原型作用域)来配置带注释类型的新实例(在这种情况下为Account)。 xyz.myapp.domain.Account)。由于Bean的默认名称是其类型的完全限定名称,因此声明原型定义的便捷方法是省略id属性,如以下示例所示:
在这里插入图片描述
机制描述
Spring现在查找名为account的bean定义,并将其用作配置新Account实例的定义。 您也可以使用自动装配来避免完全指定专用的bean定义。要让Spring应用自动装配,请使用@Configurable批注的autowire属性。您可以指定@Configurable(autowire = Autowire.BY_TYPE)或@Configurable(autowire = Autowire.BY_NAME)分别按类型或名称进行自动装配。作为替代方案,最好为您的对象指定显式的,注释驱动的依赖项注入。通过@Autowired或@Inject在字段或方法级别通过@Configurable bean(有关更多详细信息,请参见基于注释的容器配置)。 最后,您可以使用dependencyCheck属性(例如,@Configurable(autowire = Autowire.BY_NAME,dependencyCheck = true))为新创建和配置的对象中的对象引用启用Spring依赖检查。如果此属性设置为true,则Spring在配置后验证是否已设置所有属性(不是基元或集合)。 请注意,单独使用注释不会执行任何操作。 spring-aspects.jar中的AnnotationBeanConfigurerAspect对注释的存在起作用。从本质上讲,方面说:“在从带有@Configurable注释的类型的新对象的初始化返回之后,使用Spring根据注释的属性配置新创建的对象”。在这种情况下,“初始化”是指新实例化的对象(例如,使用new运算符实例化的对象)以及正在进行反序列化(例如,通过readResolve())的Serializable对象。

5.10.2. Other Spring aspects for AspectJ

5.10.3. Configuring AspectJ Aspects by Using Spring IoC

5.10.4. Load-time Weaving with AspectJ in the Spring Framework

5.11. Further Resources

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值