Live2D

spring官网在线学习文档翻译5

5. Aspect Oriented Programming with Spring(使用Spring进行面向切面编程)

5.1. Introduction(入门)

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns such as transaction management that cut across multiple types and objects. (Such concerns are often termed crosscutting concerns in AOP literature.)

  • 面向方面程序设计(AOP)通过提供另一种考虑程序结构的方法来补充面向对象程序设计(OOP)。OOP中模块性的关键单元是类,而AOP中模块性的单元是方面。方面支持关注点的模块化,例如跨多个类型和对象的事务管理。(在AOP文献中,这样的关注点通常被称为横切关注点。)

One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP, meaning you do not need to use AOP if you don’t want to, AOP complements Spring IoC to provide a very capable middleware solution.

  • Spring的关键组件之一是AOP框架。虽然Spring IoC容器不依赖于AOP,这意味着如果您不想使用AOP,也不需要使用AOP,但AOP对Spring IoC进行了补充,提供了一个非常强大的中间件解决方案。

Spring 2.0+ AOP

Spring 2.0 introduced a simpler and more powerful way of writing custom aspects using either a schema-based approach or the @AspectJ annotation style. Both of these styles offer fully typed advice and use of the AspectJ pointcut language, while still using Spring AOP for weaving.

  • Spring 2.0引入了一种更简单、更强大的方法,可以使用基于模式的方法或@AspectJ注释风格编写自定义方面。这两种风格都提供了完全类型的通知和AspectJ切入点语言的使用,同时仍然使用Spring AOP进行编织。

The Spring 2.0+ schema- and @AspectJ-based AOP support is discussed in this chapter. The lower-level AOP support, as commonly exposed in Spring 1.2 applications, is discussed in the following chapter.

  • 本章将讨论基于Spring 2.0+ schema和@ aspectj的AOP支持。下一章将讨论在Spring 1.2应用程序中常见的低层AOP支持。

AOP is used in the Spring Framework to…(在Spring框架中使用AOP来……)

  • … provide declarative enterprise services, especially as a replacement for EJB declarative services. The most important such service is declarative transaction management.
    • 提供声明性企业服务,特别是作为EJB声明性服务的替代品。此类服务中最重要的是声明式事务管理。
  • … allow users to implement custom aspects, complementing their use of OOP with AOP.
    • 允许用户实现自定义方面,用AOP补充他们对OOP的使用。
If you are interested only in generic declarative services or other pre-packaged declarative middleware services such as pooling, you do not need to work directly with Spring AOP, and can skip most of this chapter.
  • 如果您只对通用的声明性服务或其他预先打包的声明性中间件服务(如池)感兴趣,那么您不需要直接使用Spring AOP,可以跳过本章的大部分内容。

5.1.1. AOP concepts()

Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific… unfortunately, AOP terminology is not particularly intuitive; however, it would be even more confusing if Spring used its own terminology.

  • 让我们从定义一些核心的AOP概念和术语开始。这些术语不是特定于spring的……不幸的是,AOP术语不是特别直观;但是,如果Spring使用自己的术语,就更令人困惑了。

  • Aspect: a modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style).

    • 方面:跨多个类的关注点的模块化。事务管理是企业级Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,方面是使用常规类(基于模式的方法)或使用@Aspect注释的常规类(@AspectJ风格)实现的。
  • Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

    • 连接点:程序执行期间的一个点,例如方法执行或异常处理期间的一个点。在Spring AOP中,连接点总是表示方法执行。
  • Advice: action taken by an aspect at a particular join point. Different types of advice include "around", "before" and "after" advice. (Advice types are discussed below.) Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.

    • 建议:方面在特定连接点上采取的操作。不同类型的建议包括“周围”、“之前”和“之后”的建议。(下面将讨论建议类型。)许多AOP框架(包括Spring)将通知建模为拦截器,在连接点周围维护拦截器链。
  • Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

    • 切入点:匹配连接点的谓词。通知与切入点表达式相关联,并在切入点匹配的任何连接点上运行(例如,具有特定名称的方法的执行)。由切入点表达式匹配的连接点的概念是AOP的核心,Spring在默认情况下使用AspectJ切入点表达式语言。
  • Introduction: declaring additional methods or fields on behalf of a type. Spring AOP allows you to introduce new interfaces (and a corresponding implementation) to any advised object. For example, you could use an introduction to make a bean implement an IsModified interface, to simplify caching. (An introduction is known as an inter-type declaration in the AspectJ community.)

    • 介绍:代表类型声明其他方法或字段。Spring AOP允许您向任何被建议的对象引入新的接口(以及相应的实现)。例如,您可以使用一个介绍来让一个bean实现一个IsModified接口,以简化缓存。(在AspectJ社区中,介绍称为类型间声明。)
  • Target object: object being advised by one or more aspects. Also referred to as the advised object. Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.

    • 目标对象:由一个或多个方面通知的对象。也称为被建议的对象。因为Spring AOP是使用运行时代理实现的,所以这个对象将始终是代理对象。
  • AOP proxy: an object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy.

    • AOP代理:AOP框架为了实现方面契约(通知方法的执行等等)而创建的对象。在Spring框架中,AOP代理将是JDK动态代理或CGLIB代理。
  • Weaving: linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.

    • 编织:将方面与其他应用程序类型或对象链接,以创建建议对象。这可以在编译时(例如使用AspectJ编译器)、加载时或运行时完成。与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。

Types of advice:(类型的建议:)

  • Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
    • Before advice:在连接点之前执行的通知,但是不能阻止执行流继续到连接点(除非它抛出异常)。
  • After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
    • 返回通知后:在连接点正常完成后执行的通知:例如,如果方法返回时没有抛出异常。
  • After throwing advice: Advice to be executed if a method exits by throwing an exception.
    • 抛出通知后:如果方法通过抛出异常而退出,则执行通知。
  • After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).
    • 通知之后(最后):无论连接点以何种方式退出(正常或异常返回),都要执行通知。
  • Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.
    • 环绕通知:围绕连接点(如方法调用)的通知。这是最有力的建议。Around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续到连接点,还是通过返回自己的返回值或抛出异常来简化建议的方法的执行。

Around advice is the most general kind of advice. Since Spring AOP, like AspectJ, provides a full range of advice types, we recommend that you use the least powerful advice type that can implement the required behavior. For example, if you need only to update a cache with the return value of a method, you are better off implementing an after returning advice than an around advice, although an around advice can accomplish the same thing. Using the most specific advice type provides a simpler programming model with less potential for errors. For example, you do not need to invoke the proceed() method on the JoinPoint used for around advice, and hence cannot fail to invoke it.

  • Around advice是最常见的一种建议。由于Spring AOP像AspectJ一样提供了完整的通知类型,所以我们建议您使用功能最弱的通知类型来实现所需的行为。例如,如果您只需要用方法的返回值更新缓存,那么最好实现一个after return advice,而不是around advice,尽管around advice可以完成同样的事情。使用最特定的通知类型可以提供更简单的编程模型,出错的可能性更小。例如,您不需要在用于around通知的JoinPoint上调用proceed()方法,因此调用它不会失败。

In Spring 2.0, all advice parameters are statically typed, so that you work with advice parameters of the appropriate type (the type of the return value from a method execution for example) rather than Object arrays.

  • 在Spring 2.0中,所有通知参数都是静态类型的,因此您可以使用适当类型的通知参数(例如方法执行返回值的类型),而不是使用对象数组。

The concept of join points, matched by pointcuts, is the key to AOP which distinguishes it from older technologies offering only interception. Pointcuts enable advice to be targeted independently of the Object-Oriented hierarchy. For example, an around advice providing declarative transaction management can be applied to a set of methods spanning multiple objects (such as all business operations in the service layer).

  • 由切入点匹配的连接点的概念是AOP区别于只提供拦截的旧技术的关键。切入点使通知能够独立于面向对象的层次结构作为目标。例如,提供声明性事务管理的around通知可以应用于一组跨越多个对象的方法(例如服务层中的所有业务操作)。

5.1.2. Spring AOP capabilities and goals(Spring AOP的功能和目标)

Spring AOP is implemented in pure Java. There is no need for a special compilation process. Spring AOP does not need to control the class loader hierarchy, and is thus suitable for use in a Servlet container or application server.

  • Spring AOP是用纯Java实现的。不需要特殊的编译过程。Spring AOP不需要控制类装入器的层次结构,因此适合在Servlet容器或应用服务器中使用。

Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.

  • Spring AOP目前只支持方法执行连接点(通知方法在Spring bean上的执行)。虽然可以在不破坏Spring AOP核心api的情况下添加对字段拦截的支持,但没有实现字段拦截。如果需要通知字段访问和更新连接点,请考虑使用AspectJ之类的语言

Spring AOP’s approach to AOP differs from that of most other AOP frameworks. The aim is not to provide the most complete AOP implementation (although Spring AOP is quite capable); it is rather to provide a close integration between AOP implementation and Spring IoC to help solve common problems in enterprise applications.

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

Thus, for example, the Spring Framework’s AOP functionality is normally used in conjunction with the Spring IoC container. Aspects are configured using normal bean definition syntax (although this allows powerful "autoproxying" capabilities): this is a crucial difference from other AOP implementations. There are some things you cannot do easily or efficiently with Spring AOP, such as advise very fine-grained objects (such as domain objects typically): AspectJ is the best choice in such cases. However, our experience is that Spring AOP provides an excellent solution to most problems in enterprise Java applications that are amenable to AOP.

  • 因此,例如,Spring框架的AOP功能通常与Spring IoC容器一起使用。方面是使用普通的bean定义语法配置的(尽管这允许强大的“自动代理”功能):这是与其他AOP实现的一个关键区别。使用Spring AOP有一些事情是不能轻松或有效地完成的,比如通知非常细粒度的对象(通常是域对象):在这种情况下,AspectJ是最好的选择。然而,我们的经验是,Spring AOP为企业级Java应用程序中的大多数问题提供了一个优秀的解决方案,而这些问题都是符合AOP的。

Spring AOP will never strive to compete with AspectJ to provide a comprehensive AOP solution. We believe that both proxy-based frameworks like Spring AOP and full-blown frameworks such as AspectJ are valuable, and that they are complementary, rather than in competition. Spring seamlessly integrates Spring AOP and IoC with AspectJ, to enable all uses of AOP to be catered for within a consistent Spring-based application architecture. This integration does not affect the Spring AOP API or the AOP Alliance API: Spring AOP remains backward-compatible. See the following chapter for a discussion of the Spring AOP APIs.

  • Spring AOP永远不会努力与AspectJ竞争来提供全面的AOP解决方案。我们相信基于代理的框架(如Spring AOP)和成熟的框架(如AspectJ)都是有价值的,而且它们是互补的,而不是竞争的。Spring与AspectJ无缝地集成了Spring AOP和IoC,使AOP的所有使用都能在一致的基于Spring的应用程序体系结构中得到满足。这种集成不会影响Spring AOP API或AOP Alliance API: Spring AOP保持向后兼容。有关Spring AOP api的讨论,请参阅下一章。
One of the central tenets of the Spring Framework is that of non-invasiveness; this is the idea that you should not be forced to introduce framework-specific classes and interfaces into your business/domain model. However, in some places the Spring Framework does give you the option to introduce Spring Framework-specific dependencies into your codebase: the rationale in giving you such options is because in certain scenarios it might be just plain easier to read or code some specific piece of functionality in such a way. The Spring Framework (almost) always offers you the choice though: you have the freedom to make an informed decision as to which option best suits your particular use case or scenario.One such choice that is relevant to this chapter is that of which AOP framework (and which AOP style) to choose. You have the choice of AspectJ and/or Spring AOP, and you also have the choice of either the @AspectJ annotation-style approach or the Spring XML configuration-style approach. The fact that this chapter chooses to introduce the @AspectJ-style approach first should not be taken as an indication that the Spring team favors the @AspectJ annotation-style approach over the Spring XML configuration-style.See Choosing which AOP declaration style to use for a more complete discussion of the whys and wherefores of each style.
  • Spring框架的核心原则之一是非侵入性;这就是不应该强迫您将特定于框架的类和接口引入您的业务/领域模型的思想。然而,在一些地方,Spring框架并给你选择春天特定于框架的依赖关系引入到你的代码库:原理等给你选择的是,因为在某些情况下它可能只是普通的容易阅读或某些特定功能的代码的方式。不过,Spring框架(几乎)总是为您提供选择:您可以自由地做出明智的决定,选择哪个选项最适合您的特定用例或场景。与本章相关的一个选择是选择哪个AOP框架(以及哪种AOP风格)。您可以选择AspectJ和/或Spring AOP,也可以选择@AspectJ注释风格的方法或Spring XML配置风格的方法。本章选择首先介绍@AspectJ风格的方法并不意味着Spring团队更喜欢@AspectJ注释风格的方法,而不是Spring XML配置风格的方法。有关每种风格的原因和原因的更完整的讨论,请参见选择使用哪种AOP声明风格。

5.1.3. AOP Proxies(代理)

Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.

  • Spring AOP默认为AOP代理使用标准JDK动态代理。这允许代理任何接口(或一组接口)。

Spring AOP can also use CGLIB proxies. This is necessary to proxy classes rather than interfaces. CGLIB is used by default if a business object does not implement an interface. As it is good practice to program to interfaces rather than classes; business classes normally will implement one or more business interfaces. It is possible to force the use of CGLIB, in those (hopefully rare) cases where you need to advise a method that is not declared on an interface, or where you need to pass a proxied object to a method as a concrete type.

  • Spring AOP也可以使用CGLIB代理。这对于代理类而不是接口是必要的。如果业务对象没有实现接口,则默认使用CGLIB。因为这是一个很好的做法,编程接口而不是类;业务类通常会实现一个或多个业务接口。在需要通知接口上没有声明的方法,或者需要将代理对象作为具体类型传递给方法的情况下(希望很少),可以强制使用CGLIB。

It is important to grasp the fact that Spring AOP is proxy-based. See Understanding AOP proxies for a thorough examination of exactly what this implementation detail actually means.

  • 掌握Spring AOP是基于代理的这一事实很重要。请参阅理解AOP代理,了解这个实现细节的真正含义。

5.2. @AspectJ support(支持)

@AspectJ refers to a style of declaring aspects as regular Java classes annotated with annotations. The @AspectJ style was introduced by the AspectJ project as part of the AspectJ 5 release. Spring interprets the same annotations as AspectJ 5, using a library supplied by AspectJ for pointcut parsing and matching. The AOP runtime is still pure Spring AOP though, and there is no dependency on the AspectJ compiler or weaver.

  • @AspectJ引用了一种将方面声明为带有注释的常规Java类的样式。@AspectJ样式是由AspectJ项目作为AspectJ 5发行版的一部分引入的。Spring使用AspectJ提供的用于切入点解析和匹配的库来解释与AspectJ 5相同的注释。AOP运行时仍然是纯粹的Spring AOP,而且不依赖于AspectJ编译器或weaver。
Using the AspectJ compiler and weaver enables use of the full AspectJ language, and is discussed in Using AspectJ with Spring applications.
  • 使用AspectJ编译器和weaver使使用完整的AspectJ语言成为可能,将在与Spring应用程序一起使用AspectJ中进行讨论。

5.2.1. Enabling @AspectJ Support(支持@ aspectj的支持)

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

  • 要在Spring配置中使用@AspectJ方面,您需要启用Spring支持来配置基于@AspectJ方面的Spring AOP,并根据这些方面是否建议自动代理bean。通过自动代理,我们的意思是,如果Spring确定一个bean被一个或多个方面通知,它将自动为该bean生成一个代理来拦截方法调用,并确保在需要时执行通知。

The @AspectJ support can be enabled with XML or Java style configuration. In either case you will also need to ensure that AspectJ’s aspectjweaver.jar library is on the classpath of your application (version 1.8 or later). This library is available in the 'lib' directory of an AspectJ distribution or via the Maven Central repository.

  • 可以通过XML或Java风格配置启用@AspectJ支持。在这两种情况下,您还需要确保AspectJ的aspectjwever .jar库位于应用程序的类路径中(版本1.8或更高)。这个库可以在AspectJ发行版的“lib”目录中获得,也可以通过Maven中央存储库获得。
Enabling @AspectJ Support with Java configuration(使用Java配置启用@AspectJ支持)

To enable @AspectJ support with Java @Configuration add the @EnableAspectJAutoProxy annotation:

  • 要使用Java @Configuration启用@AspectJ支持,添加@EnableAspectJAutoProxy注释:
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}
Enabling @AspectJ Support with XML configuration(使用XML配置启用@AspectJ支持)

To enable @AspectJ support with XML based configuration use the aop:aspectj-autoproxy element:

  • 使用aop:aspectj-autoproxy元素来启用基于XML的配置@AspectJ支持:
<aop:aspectj-autoproxy/>

This assumes that you are using schema support as described in XML Schema-based configuration. See the AOP schema for how to import the tags in the aop namespace.

  • 这假设您正在使用XML基于模式的配置中描述的模式支持。请参阅AOP模式,了解如何在AOP名称空间中导入标记。

5.2.2. Declaring an aspect(声明一个方面)

With the @AspectJ support enabled, any bean defined in your application context with a class that is an @AspectJ aspect (has the @Aspect annotation) will be automatically detected by Spring and used to configure Spring AOP. The following example shows the minimal definition required for a not-very-useful aspect:

  • 启用了@AspectJ支持后,在您的应用程序上下文中定义的带有@AspectJ方面(具有@Aspect注释)类的任何bean都将被Spring自动检测并用于配置Spring AOP。下面的例子显示了一个不太有用的方面所需的最小定义:

A regular bean definition in the application context, pointing to a bean class that has the @Aspect annotation:

  • 应用程序上下文中的一个常规bean定义,指向一个带有@Aspect注释的bean类:
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
    <!-- configure properties of aspect here as normal -->
</bean>

And the NotVeryUsefulAspect class definition, annotated with org.aspectj.lang.annotation.Aspect annotation;

  • 和NotVeryUsefulAspect类定义,用org.aspectj.lang.annotation注释。注释方面;
package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}

Aspects (classes annotated with @Aspect) may have methods and fields just like any other class. They may also contain pointcut, advice, and introduction (inter-type) declarations.

  • 方面(用@Aspect注释的类)可能像其他类一样有方法和字段。它们还可能包含切入点、通知和引入(类型间)声明。
Autodetecting aspects through component scanningYou may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the @Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate @Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).
Advising aspects with other aspects?In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The @Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.
  • 通过组件扫描自动检测各个方面

您可以在Spring XML配置中将方面类注册为常规bean,或者通过类路径扫描自动检测它们—就像任何其他Spring管理的bean一样。然而,请注意@Aspect注释对于类路径中的自动检测是不够的:为了这个目的,您需要添加一个单独的@Component注释(或者根据Spring的组件扫描器的规则,一个定制的构造型注释)。

  • 对其他方面提出建议?

    在Spring AOP中,不可能让方面本身成为来自其他方面的通知的目标。类上的@Aspect注释将其标记为一个方面,因此将其从自动代理中排除。

5.2.3. Declaring a pointcut(声明一个切入点)

Recall that pointcuts determine join points of interest, and thus enable us to control when advice executes. Spring AOP only supports method execution join points for Spring beans, so you can think of a pointcut as matching the execution of methods on Spring beans. A pointcut declaration has two parts: a signature comprising a name and any parameters, and a pointcut expression that determines exactly which method executions we are interested in. In the @AspectJ annotation-style of AOP, a pointcut signature is provided by a regular method definition, and the pointcut expression is indicated using the @Pointcut annotation (the method serving as the pointcut signature must have a void return type).

  • 回想一下,切入点决定了感兴趣的连接点,从而使我们能够控制通知何时执行。Spring AOP只支持Spring bean的方法执行连接点,所以您可以将切入点看作是匹配Spring bean上方法的执行。切入点声明有两个部分:由名称和任何参数组成的签名,以及准确确定我们感兴趣的方法执行的切入点表达式。在@AspectJ注释风格的AOP中,一个切入点签名是由一个常规方法定义提供的,而切入点表达式是使用@Pointcut注释表示的(作为切入点签名的方法必须有一个void返回类型)。

An example will help make this distinction between a pointcut signature and a pointcut expression clear. The following example defines a pointcut named 'anyOldTransfer' that will match the execution of any method named 'transfer':

  • 一个示例将有助于清晰地区分切入点签名和切入点表达式。下面的例子定义了一个名为“anyOldTransfer”的切入点,它将匹配任何名为“transfer”的方法的执行:
@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature

The pointcut expression that forms the value of the @Pointcut annotation is a regular AspectJ 5 pointcut expression. For a full discussion of AspectJ’s pointcut language, see the AspectJ Programming Guide (and for extensions, the AspectJ 5 Developers Notebook) or one of the books on AspectJ such as "Eclipse AspectJ" by Colyer et. al. or "AspectJ in Action" by Ramnivas Laddad.

  • 构成@Pointcut注释值的切入点表达式是一个常规的AspectJ 5切入点表达式。关于AspectJ的切入点语言的完整讨论,请参阅AspectJ编程指南(关于扩展,请参阅AspectJ 5开发人员笔记)或一本关于AspectJ的书,比如Colyer等人写的“Eclipse AspectJ”或Ramnivas Laddad写的“AspectJ in Action”。
Supported Pointcut Designators(支持切入点指示器)

Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions:

  • Spring AOP支持在切入点表达式中使用以下AspectJ切入点指示器(PCD):

Other pointcut types(其他类型的切入点)

The full AspectJ pointcut language supports additional pointcut designators that are not supported in Spring. These are: call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this, and @withincode. Use of these pointcut designators in pointcut expressions interpreted by Spring AOP will result in an IllegalArgumentException being thrown.

  • 完整的AspectJ切入点语言支持Spring中不支持的附加切入点指示器。这些是:调用,获取,设置,预初始化,静态初始化,初始化,处理器,adviceexecution,内部代码,cflow, cflowbelow, if, @this,和@内thincode。在由Spring AOP解释的切入点表达式中使用这些切入点指示器将导致抛出一个IllegalArgumentException。

The set of pointcut designators supported by Spring AOP may be extended in future releases to support more of the AspectJ pointcut designators.

  • Spring AOP支持的切入点指示器集可能在未来的版本中得到扩展,以支持更多的AspectJ切入点指示器。

  • execution - for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP

    • 执行——为了匹配方法执行连接点,这是在使用Spring AOP时将使用的主要切入点指示器
  • within - limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP)

    • 限定对某些类型内的连接点的匹配(在使用Spring AOP时,简单地执行在匹配类型内声明的方法)
  • this - limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type

    • 这—将匹配限制为连接点(使用Spring AOP时方法的执行),其中bean引用(Spring AOP代理)是给定类型的实例
  • target - limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type

    • 目标——限制对连接点(使用Spring AOP时方法的执行)的匹配,其中目标对象(被代理的应用程序对象)是给定类型的实例
  • args - limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types

    • args—限制对连接点(使用Spring AOP时方法的执行)的匹配,其中参数是给定类型的实例
  • @target - limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type

    • @target—限制对连接点(使用Spring AOP时方法的执行)的匹配,其中执行对象的类具有给定类型的注释
  • @args - limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s)

    • @args—限制对连接点的匹配(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释
  • @within - limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)

    • @within—与具有给定注释的类型中的连接点匹配的限制(使用Spring AOP时使用给定注释执行类型中声明的方法)
  • @annotation - limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation

    • @annotation——限制对连接点主题(在Spring AOP中执行的方法)具有给定注释的连接点的匹配

Because Spring AOP limits matching to only method execution join points, the discussion of the pointcut designators above gives a narrower definition than you will find in the AspectJ programming guide. In addition, AspectJ itself has type-based semantics and at an execution join point both this and target refer to the same object - the object executing the method. Spring AOP is a proxy-based system and differentiates between the proxy object itself (bound to this) and the target object behind the proxy (bound to target).

  • 因为Spring AOP将匹配限制为只匹配方法执行连接点,所以上面关于切入点指示器的讨论给出了一个比AspectJ编程指南中更窄的定义。此外,AspectJ本身具有基于类型的语义,并且在执行连接点上,this和target都引用相同的对象—执行方法的对象。Spring AOP是一个基于代理的系统,区分了代理对象本身(绑定到它)和代理背后的目标对象(绑定到target)。
Due to the proxy-based nature of Spring’s AOP framework, calls within the target object are by definition not intercepted. For JDK proxies, only public interface method calls on the proxy can be intercepted. With CGLIB, public and protected method calls on the proxy will be intercepted, and even package-visible methods if necessary. However, common interactions through proxies should always be designed through public signatures.Note that pointcut definitions are generally matched against any intercepted method. If a pointcut is strictly meant to be public-only, even in a CGLIB proxy scenario with potential non-public interactions through proxies, it needs to be defined accordingly.If your interception needs include method calls or even constructors within the target class, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
  • 由于Spring s AOP框架基于代理的本质,目标对象内的调用根据定义是不被拦截的。对于JDK代理,只能拦截代理上的公共接口方法调用。使用CGLIB,代理上的公共和受保护的方法调用将被拦截,如果需要,甚至可以拦截包可见的方法。但是,通过代理的常见交互应该始终通过公共签名来设计。注意,切入点定义通常与任何截获的方法相匹配。如果严格意义上说切入点是只公开的,即使是在CGLIB代理场景中,通过代理进行潜在的非公开交互,也需要相应地定义它。如果您的拦截需要包括目标类中的方法调用甚至构造函数,请考虑使用Spring驱动的本地AspectJ编织,而不是基于Spring代理的AOP框架。这构成了具有不同特征的不同AOP使用模式,因此在做出决定之前,一定要先熟悉编织。

Spring AOP also supports an additional PCD named bean. This PCD allows you to limit the matching of join points to a particular named Spring bean, or to a set of named Spring beans (when using wildcards). The bean PCD has the following form:

  • Spring AOP还支持另外一个名为bean的PCD。这个PCD允许您将连接点的匹配限制为特定的命名Spring bean或一组命名Spring bean(当使用通配符时)。bean PCD有以下形式:
bean(idOrNameOfBean)

The idOrNameOfBean token can be the name of any Spring bean: limited wildcard support using the * character is provided, so if you establish some naming conventions for your Spring beans you can quite easily write a bean PCD expression to pick them out. As is the case with other pointcut designators, the bean PCD can be &&'ed, ||'ed, and ! (negated) too.

  • id装饰eofbean标记可以是任何Spring bean的名称:提供了使用*字符的有限通配符支持,因此如果您为Spring bean建立了一些命名约定,您可以很容易地编写一个bean PCD表达式来将它们挑选出来。与其他切入点指示器的情况一样,bean PCD可以被&&'ed、||'ed和!也(否定)。
Please note that the bean PCD is only supported in Spring AOP - and not in native AspectJ weaving. It is a Spring-specific extension to the standard PCDs that AspectJ defines and therefore not available for aspects declared in the @Aspect model.The bean PCD operates at the instance level (building on the Spring bean name concept) rather than at the type level only (which is what weaving-based AOP is limited to). Instance-based pointcut designators are a special capability of Spring’s proxy-based AOP framework and its close integration with the Spring bean factory, where it is natural and straightforward to identify specific beans by name.
  • 请注意,bean PCD仅在Spring AOP中得到支持——而在本地AspectJ编织中没有得到支持。它是AspectJ定义的标准pcd的一个特定于spring的扩展,因此对于在@Aspect模型中声明的方面不可用。bean PCD在实例级操作(构建在Spring bean名称概念上),而不是仅在类型级操作(这是基于编织的AOP的限制)。基于实例的切入点指示器是Spring s基于代理的AOP框架及其与Spring bean工厂的紧密集成的一种特殊功能,在Spring bean工厂中,通过名称识别特定的bean是自然而直接的。
Combining pointcut expressions(结合切入点表达式)

Pointcut expressions can be combined using '&&', '||' and '!'. It is also possible to refer to pointcut expressions by name. The following example shows three pointcut expressions: anyPublicOperation (which matches if a method execution join point represents the execution of any public method); inTrading (which matches if a method execution is in the trading module), and tradingOperation (which matches if a method execution represents any public method in the trading module).

  • 切入点表达式可以使用'&&'、'||'和'!'组合起来。还可以通过名称引用切入点表达式。下面的例子显示了三个切入点表达式:anyPublicOperation(如果一个方法执行连接点表示任何公共方法的执行,那么它就匹配);inTrading(如果方法执行在交易模块中,则匹配)和tradingOperation(如果方法执行表示交易模块中的任何公共方法,则匹配)。
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}

@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}

It is a best practice to build more complex pointcut expressions out of smaller named components as shown above. When referring to pointcuts by name, normal Java visibility rules apply (you can see private pointcuts in the same type, protected pointcuts in the hierarchy, public pointcuts anywhere and so on). Visibility does not affect pointcut matching.

  • 从上面所示的较小的已命名组件中构建更复杂的切入点表达式是一种最佳实践。当通过名称引用切入点时,应用普通的Java可见性规则(您可以看到相同类型的私有切入点、层次结构中的受保护切入点、任何地方的公共切入点等等)。可见性不影响切入点匹配。
Sharing common pointcut definitions(共享公共切入点定义)

When working with enterprise applications, you often want to refer to modules of the application and particular sets of operations from within several aspects. We recommend defining a "SystemArchitecture" aspect that captures common pointcut expressions for this purpose. A typical such aspect would look as follows:

  • 在使用企业应用程序时,您经常需要从几个方面引用应用程序的模块和特定的操作集。我们建议定义一个“系统体系结构”方面,它可以捕获为此目的的公共切入点表达式。一个典型的这样的方面看起来如下:
package com.xyz.someapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {

    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.xyz.someapp.web package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.someapp.web..*)")
    public void inWebLayer() {}

    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.xyz.someapp.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.someapp.service..*)")
    public void inServiceLayer() {}

    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.xyz.someapp.dao package or any sub-package
     * under that.
     */
    @Pointcut("within(com.xyz.someapp.dao..*)")
    public void inDataAccessLayer() {}

    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     *
     * If you group service interfaces by functional area (for example,
     * in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then
     * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))"
     * could be used instead.
     *
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
     */
    @Pointcut("execution(* com.xyz.someapp..service.*.*(..))")
    public void businessService() {}

    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     */
    @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
    public void dataAccessOperation() {}

}

The pointcuts defined in such an aspect can be referred to anywhere that you need a pointcut expression. For example, to make the service layer transactional, you could write:

  • 在这样一个方面中定义的切入点可以在需要切入点表达式的任何地方引用。例如,要使服务层具有事务性,您可以编写:
<aop:config>
    <aop:advisor
        pointcut="com.xyz.someapp.SystemArchitecture.businessService()"
        advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

The <aop:config> and <aop:advisor> elements are discussed in Schema-based AOP support. The transaction elements are discussed in Transaction Management.

  • aop:configaop:advisor元素在基于模式的aop支持中被讨论。在事务管理中讨论了事务元素。
Examples(举例说明)

Spring AOP users are likely to use the execution pointcut designator the most often. The format of an execution expression is:

  • Spring AOP用户可能最经常使用执行切入点指示器。执行表达式的格式为:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
            throws-pattern?)

All parts except the returning type pattern (ret-type-pattern in the snippet above), name pattern, and parameters pattern are optional. The returning type pattern determines what the return type of the method must be in order for a join point to be matched. Most frequently you will use * as the returning type pattern, which matches any return type. A fully-qualified type name will match only when the method returns the given type. The name pattern matches the method name. You can use the * wildcard as all or part of a name pattern. If specifying a declaring type pattern then include a trailing . to join it to the name pattern component. The parameters pattern is slightly more complex: () matches a method that takes no parameters, whereas (..) matches any number of parameters (zero or more). The pattern (*) matches a method taking one parameter of any type, (*,String) matches a method taking two parameters, the first can be of any type, the second must be a String. Consult the Language Semantics section of the AspectJ Programming Guide for more information.

  • 除了返回类型模式(上面代码片段中的ret-type-模式)、名称模式和参数模式之外,所有部分都是可选的。返回类型模式确定要匹配连接点,方法的返回类型必须是什么。最常见的是使用作为返回类型模式,它匹配任何返回类型。只有当方法返回给定类型时,全限定类型名称才会匹配。名称模式与方法名称匹配。您可以使用通配符作为名称模式的全部或部分。如果指定声明类型模式,则包括一个尾随。将其连接到名称模式组件。参数模式稍微复杂一些:()匹配不接受参数的方法,而(..)匹配任意数量的参数(零或更多)。模式()匹配具有任意类型一个参数的方法,(,String)匹配具有两个参数的方法,第一个参数可以是任何类型,第二个必须是字符串。有关更多信息,请参阅AspectJ编程指南的语言语义部分。

Some examples of common pointcut expressions are given below.

  • 下面给出了一些常见切入点表达式的示例。

  • the execution of any public method:

    • 任何公共方法的执行:
execution(public * *(..))
  • the execution of any method with a name beginning with "set":
    • 执行任何名称以“set”开头的方法:
execution(* set*(..))
  • the execution of any method defined by the AccountService interface:
    • AccountService接口定义的任何方法的执行:
execution(* com.xyz.service.AccountService.*(..))
  • the execution of any method defined in the service package:
    • 执行服务包中定义的任何方法:
execution(* com.xyz.service.*.*(..))
  • the execution of any method defined in the service package or a sub-package:
    • 执行服务包或子包中定义的任何方法:
execution(* com.xyz.service..*.*(..))
  • any join point (method execution only in Spring AOP) within the service package:
    • 服务包内的任何连接点(仅在Spring AOP中执行方法):
within(com.xyz.service.*)
  • any join point (method execution only in Spring AOP) within the service package or a sub-package:
    • 服务包或子包内的任何连接点(仅在Spring AOP中执行方法):
within(com.xyz.service..*)
  • any join point (method execution only in Spring AOP) where the proxy implements the AccountService interface:
    • 代理实现AccountService接口的任何连接点(仅在Spring AOP中执行方法):
this(com.xyz.service.AccountService)
'this' is more commonly used in a binding form :- see the following section on advice for how to make the proxy object available in the advice body.
  • “this”更常用在绑定形式中:——请参阅下面的建议部分,了解如何在建议正文中提供代理对象。
  • any join point (method execution only in Spring AOP) where the target object implements the AccountService interface:
    • 目标对象实现AccountService接口的任何连接点(仅在Spring AOP中执行方法):
target(com.xyz.service.AccountService)
'target' is more commonly used in a binding form :- see the following section on advice for how to make the target object available in the advice body.
  • 'target'更常用在绑定形式中:-关于如何在通知正文中提供目标对象,请参阅下面的建议部分。
  • any join point (method execution only in Spring AOP) which takes a single parameter, and where the argument passed at runtime is Serializable:
    • 任何具有单个参数的连接点(仅在Spring AOP中执行方法),并且在运行时传递的参数是可序列化的:
args(java.io.Serializable)
'args' is more commonly used in a binding form :- see the following section on advice for how to make the method arguments available in the advice body.
  • 'args'通常以绑定的形式使用:-请参阅下面关于如何在通知正文中提供方法参数的通知部分。

Note that the pointcut given in this example is different to execution(* *(java.io.Serializable)): the args version matches if the argument passed at runtime is Serializable, the execution version matches if the method signature declares a single parameter of type Serializable.

  • 注意,在这个例子中给出的切入点不同于执行(* *(java.io.Serializable)):如果在运行时传递的参数是Serializable,那么args版本就匹配;如果方法签名声明了一个类型为Serializable的参数,那么执行版本就匹配。

  • any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation:

    • 任何目标对象有@Transactional注释的连接点(仅在Spring AOP中执行方法):
@target(org.springframework.transaction.annotation.Transactional)
'@target' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.
  • '@target'也可以以绑定的形式使用:——请参阅下面的通知部分,了解如何在通知正文中提供注释对象。
  • any join point (method execution only in Spring AOP) where the declared type of the target object has an @Transactional annotation:
    • 目标对象的声明类型有@Transactional注释的任何连接点(仅在Spring AOP中执行方法):
@within(org.springframework.transaction.annotation.Transactional)
'@within' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.
'@within'也可以以绑定的形式使用:——请参阅下面的通知部分,了解如何在通知正文中提供注释对象。
  • any join point (method execution only in Spring AOP) where the executing method has an @Transactional annotation:
    • 任何执行方法带有@Transactional注释的连接点(仅在Spring AOP中执行方法):
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation' can also be used in a binding form :- see the following section on advice for how to make the annotation object available in the advice body.
  • '@annotation'也可以以绑定的形式使用:-请参阅下面关于如何使注释对象在通知正文中可用的通知部分。
  • any join point (method execution only in Spring AOP) which takes a single parameter, and where the runtime type of the argument passed has the @Classified annotation:
    • 任何接受单个参数的连接点(仅在Spring AOP中执行方法),其运行时传递的参数类型有@ classid注释:
@args(com.xyz.security.Classified)
'@args' can also be used in a binding form :- see the following section on advice for how to make the annotation object(s) available in the advice body.
    • '@args'也可以以绑定的形式使用:-请参阅下面关于如何在通知正文中提供注释对象的通知部分。
  • any join point (method execution only in Spring AOP) on a Spring bean named tradeService:
    • 在名为tradeService的Spring bean上的任何连接点(仅在Spring AOP中执行方法):
bean(tradeService)
  • any join point (method execution only in Spring AOP) on Spring beans having names that match the wildcard expression *Service:
    • 名称与通配符表达式*Service匹配的Spring bean上的任何连接点(仅在Spring AOP中执行方法):
bean(*Service)
Writing good pointcuts(编写好的切入点)

During compilation, AspectJ processes pointcuts in order to try and optimize matching performance. Examining code and determining if each join point matches (statically or dynamically) a given pointcut is a costly process. (A dynamic match means the match cannot be fully determined from static analysis and a test will be placed in the code to determine if there is an actual match when the code is running). On first encountering a pointcut declaration, AspectJ will rewrite it into an optimal form for the matching process. What does this mean? Basically pointcuts are rewritten in DNF (Disjunctive Normal Form) and the components of the pointcut are sorted such that those components that are cheaper to evaluate are checked first. This means you do not have to worry about understanding the performance of various pointcut designators and may supply them in any order in a pointcut declaration.

  • 在编译期间,AspectJ处理切入点,以便尝试和优化匹配性能。检查代码并确定每个连接点是否(静态或动态)匹配给定的切入点是一个代价高昂的过程。(动态匹配意味着不能从静态分析完全确定匹配,将在代码中放置一个测试,以确定在代码运行时是否存在实际匹配)。在第一次遇到切入点声明时,AspectJ将把它重写为匹配过程所需的最优形式。这是什么意思?基本上,用DNF(析取范式)重写切入点,对切入点的组件进行排序,以便首先检查那些计算成本较低的组件。这意味着您不必担心理解各种切入点指示器的性能,并且可以在切入点声明中以任何顺序提供它们。

However, AspectJ can only work with what it is told, and for optimal performance of matching you should think about what they are trying to achieve and narrow the search space for matches as much as possible in the definition. The existing designators naturally fall into one of three groups: kinded, scoping and context:

  • 但是,AspectJ只能使用它被告知的内容,为了优化匹配性能,您应该考虑它们试图实现什么,并在定义中尽可能地缩小匹配的搜索空间。现有的指示器自然分为三组:kinded, scoping和context:

  • Kinded designators are those which select a particular kind of join point. For example: execution, get, set, call, handler

    • Kinded指示器是那些选择特定类型的连接点的指示器。例如:执行、获取、设置、调用、处理程序
  • Scoping designators are those which select a group of join points of interest (of probably many kinds). For example: within, withincode

    • 作用域指示符是选择一组感兴趣的连接点(可能有很多种类)的指示符。例如:within, within thincode
  • Contextual designators are those that match (and optionally bind) based on context. For example: this, target, @annotation

    • 上下文指示符是基于上下文匹配(和可选绑定)的指示符。例如:this, target, @annotation

A well written pointcut should try and include at least the first two types (kinded and scoping), whilst the contextual designators may be included if wishing to match based on join point context, or bind that context for use in the advice. Supplying either just a kinded designator or just a contextual designator will work but could affect weaving performance (time and memory used) due to all the extra processing and analysis. Scoping designators are very fast to match and their usage means AspectJ can very quickly dismiss groups of join points that should not be further processed - that is why a good pointcut should always include one if possible.

  • 一个编写良好的切入点应该尝试并至少包含前两种类型(kinded和scoping),而如果希望基于连接点上下文进行匹配,或者绑定上下文以便在通知中使用,则上下文指示符可能会被包括在内。只提供kinded指示符或仅提供上下文指示符都可以,但由于所有额外的处理和分析,可能会影响编织性能(时间和内存使用)。范围指示符匹配起来非常快,它们的使用意味着AspectJ可以非常快地删除不应该进一步处理的连接点组——这就是为什么一个好的切入点应该尽可能包含一个连接点。

5.2.4. Declaring advice(声明的建议)

Advice is associated with a pointcut expression, and runs before, after, or around method executions matched by the pointcut. The pointcut expression may be either a simple reference to a named pointcut, or a pointcut expression declared in place.

  • 通知与切入点表达式关联,并在切入点匹配的方法执行之前、之后或周围运行。切入点表达式可以是对命名切入点的简单引用,也可以是在适当位置声明的切入点表达式。
Before advice(前置增强)

Before advice is declared in an aspect using the @Before annotation:

  • 在通知使用@Before注释在方面中声明之前:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

}

If using an in-place pointcut expression we could rewrite the above example as:

  • 如果使用一个就地切入点表达式,我们可以将上面的例子重写为:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

    @Before("execution(* com.xyz.myapp.dao.*.*(..))")
    public void doAccessCheck() {
        // ...
    }

}
After returning advice(后置增强)

After returning advice runs when a matched method execution returns normally. It is declared using the @AfterReturning annotation:

  • 在返回通知后,当匹配的方法执行正常返回时运行。它是使用@ afterreturn注释声明的:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheck() {
        // ...
    }

}
Note: it is of course possible to have multiple advice declarations, and other members as well, all inside the same aspect. We’re just showing a single advice declaration in these examples to focus on the issue under discussion at the time.
  • 注意:当然也可以有多个通知声明和其他成员,它们都在同一个方面内。我们在这些示例中只显示了一个建议声明,以便集中讨论当时正在讨论的问题。

Sometimes you need access in the advice body to the actual value that was returned. You can use the form of @AfterReturning that binds the return value for this:

  • 有时您需要在通知正文中访问返回的实际值。你可以使用@ afterreturn的形式绑定这个返回值:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

    @AfterReturning(
        pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
        returning="retVal")
    public void doAccessCheck(Object retVal) {
        // ...
    }

}

The name used in the returning attribute must correspond to the name of a parameter in the advice method. When a method execution returns, the return value will be passed to the advice method as the corresponding argument value. A returning clause also restricts matching to only those method executions that return a value of the specified type ( Object in this case, which will match any return value).

  • 返回属性中使用的名称必须与通知方法中的参数名称对应。当一个方法执行返回时,返回值将作为相应的参数值传递给通知方法。return子句还将匹配限制为只匹配那些返回指定类型值的方法执行(本例中为Object,它将匹配任何返回值)。

Please note that it is not possible to return a totally different reference when using after-returning advice.

  • 请注意,它是不可能返回一个完全不同的引用时,使用返回后的建议。
After throwing advice(抛出点增强)

After throwing advice runs when a matched method execution exits by throwing an exception. It is declared using the @AfterThrowing annotation:

  • 在抛出通知后,当匹配的方法执行通过抛出异常退出时运行。它是使用@ afterthrow注释声明的:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doRecoveryActions() {
        // ...
    }

}

Often you want the advice to run only when exceptions of a given type are thrown, and you also often need access to the thrown exception in the advice body. Use the throwing attribute to both restrict matching (if desired, use Throwable as the exception type otherwise) and bind the thrown exception to an advice parameter.

  • 通常,您希望仅在抛出给定类型的异常时才运行通知,而且您还经常需要访问通知主体中抛出的异常。使用抛出属性来限制匹配(如果需要,可以使用Throwable作为异常类型),并将抛出的异常绑定到一个通知参数。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

    @AfterThrowing(
        pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
        throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // ...
    }

}

The name used in the throwing attribute must correspond to the name of a parameter in the advice method. When a method execution exits by throwing an exception, the exception will be passed to the advice method as the corresponding argument value. A throwing clause also restricts matching to only those method executions that throw an exception of the specified type ( DataAccessException in this case).

  • 抛出属性中使用的名称必须与通知方法中的参数名称对应。当方法执行通过抛出异常而退出时,异常将作为相应的参数值传递给通知方法。抛出子句还限制只匹配抛出指定类型异常(本例中为DataAccessException)的方法执行。
After (finally) advice(finally增强)

After (finally) advice runs however a matched method execution exits. It is declared using the @After annotation. After advice must be prepared to handle both normal and exception return conditions. It is typically used for releasing resources, etc.

  • 在(最后)通知运行后,匹配的方法执行退出。它是使用@After注释声明的。After advice必须准备好处理正常和异常返回条件。它通常用于释放资源,等等。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

    @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
    public void doReleaseLock() {
        // ...
    }

}
Around advice(环绕增强)

The final kind of advice is around advice. Around advice runs "around" a matched method execution. It has the opportunity to do work both before and after the method executes, and to determine when, how, and even if, the method actually gets to execute at all. Around advice is often used if you need to share state before and after a method execution in a thread-safe manner (starting and stopping a timer for example). Always use the least powerful form of advice that meets your requirements (i.e. don’t use around advice if simple before advice would do).

  • 最后一种建议是关于建议的。Around advice“绕过”匹配的方法执行。它有机会在方法执行之前和之后执行工作,并确定何时、如何以及是否该方法实际执行。如果您需要以线程安全的方式(例如启动和停止计时器)在方法执行之前和之后共享状态,则经常使用Around通知。总是使用最弱的形式的建议来满足你的要求(例如,不要使用around advice,如果简单之前的建议可以的话)。

Around advice is declared using the @Around annotation. The first parameter of the advice method must be of type ProceedingJoinPoint. Within the body of the advice, calling proceed() on the ProceedingJoinPoint causes the underlying method to execute. The proceed method may also be called passing in an Object[] - the values in the array will be used as the arguments to the method execution when it proceeds.

  • Around通知是使用@Around注释声明的。通知方法的第一个参数的类型必须是ProceedingJoinPoint。在通知的主体中,对ProceedingJoinPoint调用proceed()将导致底层方法的执行。通过传入Object[]也可以调用proceed方法——数组中的值将作为方法执行时的参数。
The behavior of proceed when called with an Object[] is a little different than the behavior of proceed for around advice compiled by the AspectJ compiler. For around advice written using the traditional AspectJ language, the number of arguments passed to proceed must match the number of arguments passed to the around advice (not the number of arguments taken by the underlying join point), and the value passed to proceed in a given argument position supplants the original value at the join point for the entity the value was bound to (Don’t worry if this doesn’t make sense right now!). The approach taken by Spring is simpler and a better match to its proxy-based, execution only semantics. You only need to be aware of this difference if you are compiling @AspectJ aspects written for Spring and using proceed with arguments with the AspectJ compiler and weaver. There is a way to write such aspects that is 100% compatible across both Spring AOP and AspectJ, and this is discussed in the following section on advice parameters.
  • 当对对象[]进行调用时,proceed的行为与AspectJ编译器编译的proceed for around通知的行为略有不同。建议使用传统的AspectJ语言编写,左右进行传递的参数的数量必须匹配的参数传递到周围的建议(不是参数由底层连接点的数量),并继续在一个给定的参数传递的价值立场取代原来的价值实体价值的连接点是绑定到(别担心这并t意义吧!)。Spring采用的方法更简单,更符合其基于代理、仅执行的语义。只有在编译为Spring编写的@AspectJ方面并使用AspectJ编译器和weaver的proceed with参数时,您才需要知道这种区别。有一种方法可以编写这种在Spring AOP和AspectJ之间100%兼容的方面,这将在下面的通知参数部分中讨论。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

    @Around("com.xyz.myapp.SystemArchitecture.businessService()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }

}

The value returned by the around advice will be the return value seen by the caller of the method. A simple caching aspect for example could return a value from a cache if it has one, and invoke proceed() if it does not. Note that proceed may be invoked once, many times, or not at all within the body of the around advice, all of these are quite legal.

  • around通知返回的值将是该方法的调用者看到的返回值。例如,一个简单的缓存方面可以从缓存中返回一个值,如果缓存中有一个值,则调用proceed()。注意,在around通知的主体中,proceed可能被调用一次、多次或者根本不被调用,所有这些都是合法的。
Advice parameters(通知参数)

Spring offers fully typed advice - meaning that you declare the parameters you need in the advice signature (as we saw for the returning and throwing examples above) rather than work with Object[] arrays all the time. We’ll see how to make argument and other contextual values available to the advice body in a moment. First let’s take a look at how to write generic advice that can find out about the method the advice is currently advising.

  • Spring提供了完全类型的通知——这意味着您可以在通知签名中声明所需的参数(正如我们在上面的返回和抛出示例中看到的那样),而不是一直使用Object[]数组。稍后我们将看到如何使参数和其他上下文值对advice主体可用。首先,让我们看看如何编写通用的建议,以找出该建议目前正在建议的方法。
Access to the current JoinPoint(访问当前连接点)

Any advice method may declare as its first parameter, a parameter of type org.aspectj.lang.JoinPoint (please note that around advice is required to declare a first parameter of type ProceedingJoinPoint, which is a subclass of JoinPoint. The JoinPoint interface provides a number of useful methods such as getArgs() (returns the method arguments), getThis() (returns the proxy object), getTarget() (returns the target object), getSignature() (returns a description of the method that is being advised) and toString() (prints a useful description of the method being advised). Please do consult the javadocs for full details.

  • 任何通知方法都可以声明为它的第一个参数,一个org.aspectj.lang类型的参数。请注意,around advice需要声明一个类型为ProceedingJoinPoint的第一个参数,它是JoinPoint的一个子类。JoinPoint接口提供了许多有用的方法,比如getArgs()(返回方法参数)、getThis()(返回代理对象)、getTarget()(返回目标对象)、getSignature()(返回被通知方法的描述)和toString()(打印被通知方法的有用描述)。请咨询javadocs以获得详细信息。
Passing parameters to advice(向通知传递参数)

We’ve already seen how to bind the returned value or exception value (using after returning and after throwing advice). To make argument values available to the advice body, you can use the binding form of args. If a parameter name is used in place of a type name in an args expression, then the value of the corresponding argument will be passed as the parameter value when the advice is invoked. An example should make this clearer. Suppose you want to advise the execution of dao operations that take an Account object as the first parameter, and you need access to the account in the advice body. You could write the following:

  • 我们已经看到了如何绑定返回值或异常值(在返回和抛出通知之后使用)。要使通知主体可用参数值,可以使用args的绑定形式。如果在args表达式中使用参数名代替类型名,那么在调用通知时,相应参数的值将作为参数值传递。一个例子应该会使这一点更清楚。假设您希望通知以Account对象作为第一个参数的dao操作的执行,并且需要访问通知主体中的Account。你可以这样写
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account) {
    // ...
}

The args(account,..) part of the pointcut expression serves two purposes: firstly, it restricts matching to only those method executions where the method takes at least one parameter, and the argument passed to that parameter is an instance of Account; secondly, it makes the actual Account object available to the advice via the account parameter.

  • 切入点表达式的args(account,..)部分有两个目的:首先,它将匹配限制在那些方法执行中,该方法至少有一个参数,并且传递给该参数的参数是account的实例;其次,它通过Account参数使通知可以使用实际的Account对象。

Another way of writing this is to declare a pointcut that "provides" the Account object value when it matches a join point, and then just refer to the named pointcut from the advice. This would look as follows:

  • 写这个的另一种方法是声明一个切入点,当它匹配一个连接点时“提供”Account对象值,然后只从通知中引用命名的切入点。这看起来如下:
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
    // ...
}

The interested reader is once more referred to the AspectJ programming guide for more details.

  • 有兴趣的读者可以再一次参考AspectJ编程指南以获得更多细节。

The proxy object ( this), target object ( target), and annotations ( @within, @target, @annotation, @args) can all be bound in a similar fashion. The following example shows how you could match the execution of methods annotated with an @Auditable annotation, and extract the audit code.

  • 代理对象(this)、目标对象(target)和注释(@within、@target、@annotation、@args)都可以以类似的方式绑定。下面的示例展示如何匹配带有@Auditable注释的方法的执行,并提取审计代码。

First the definition of the @Auditable annotation:

  • 首先是@Auditable注释的定义:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
    AuditCode value();
}

And then the advice that matches the execution of @Auditable methods:

  • 然后是匹配@Auditable方法执行的通知:
@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable) {
    AuditCode code = auditable.value();
    // ...
}
Advice parameters and generics(通知参数和泛型)

Spring AOP can handle generics used in class declarations and method parameters. Suppose you have a generic type like this:

  • Spring AOP可以处理类声明和方法参数中使用的泛型。假设你有一个这样的泛型:
public interface Sample<T> {
    void sampleGenericMethod(T param);
    void sampleGenericCollectionMethod(Collection<T> param);
}

You can restrict interception of method types to certain parameter types by simply typing the advice parameter to the parameter type you want to intercept the method for:

  • 你可以将方法类型的拦截限制为某些参数类型,只需将advice参数输入到你想要拦截方法的参数类型:
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param) {
    // Advice implementation
}

That this works is pretty obvious as we already discussed above. However, it’s worth pointing out that this won’t work for generic collections. So you cannot define a pointcut like this:

  • 正如我们在上面所讨论的那样,这是非常明显的。但是,值得指出的是,这不适用于泛型集合。所以不能像这样定义切入点:
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<MyType> param) {
    // Advice implementation
}

To make this work we would have to inspect every element of the collection, which is not reasonable as we also cannot decide how to treat null values in general. To achieve something similar to this you have to type the parameter to Collection<?> and manually check the type of the elements.

  • 要做到这一点,我们必须检查集合的每个元素,这是不合理的,因为我们也不能决定如何处理空值一般。要达到类似的效果,您必须键入参数集合<?并手动检查元素的类型。
Determining argument names(确定参数的名字)

The parameter binding in advice invocations relies on matching names used in pointcut expressions to declared parameter names in (advice and pointcut) method signatures. Parameter names are not available through Java reflection, so Spring AOP uses the following strategies to determine parameter names:

  • 通知调用中的参数绑定依赖于切入点表达式中使用的名称与方法签名(通知和切入点)中声明的参数名称的匹配。Java反射不能提供参数名,所以Spring AOP使用以下策略来确定参数名:

  • If the parameter names have been specified by the user explicitly, then the specified parameter names are used: both the advice and the pointcut annotations have an optional "argNames" attribute which can be used to specify the argument names of the annotated method - these argument names are available at runtime. For example:

    • 如果由用户显式地指定参数名称,然后使用指定的参数名称:建议和切入点的注释都有一个可选的“argNames”属性,可以用来指定带注释的方法的参数名称,在运行时这些参数名称可用。例如:
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code and bean
}

If the first parameter is of the JoinPoint, ProceedingJoinPoint, or JoinPoint.StaticPart type, you may leave out the name of the parameter from the value of the "argNames" attribute. For example, if you modify the preceding advice to receive the join point object, the "argNames" attribute need not include it:

  • 如果第一个参数是JoinPoint的,则为ProceedingJoinPoint或JoinPoint。如果是StaticPart类型,则可以从“argNames”属性的值中去掉参数的名称。例如,如果您修改了前面的通知来接收连接点对象,“argNames”属性就不需要包含它:
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
        argNames="bean,auditable")
public void audit(JoinPoint jp, Object bean, Auditable auditable) {
    AuditCode code = auditable.value();
    // ... use code, bean, and jp
}

The special treatment given to the first parameter of the JoinPoint, ProceedingJoinPoint, and JoinPoint.StaticPart types is particularly convenient for advice that do not collect any other join point context. In such situations, you may simply omit the "argNames" attribute. For example, the following advice need not declare the "argNames" attribute:

  • 对连接点的第一个参数、过程连接点和连接点的特殊处理。对于不收集任何其他连接点上下文的通知,StaticPart类型特别方便。在这种情况下,可以简单地忽略“argNames”属性。例如,下面的建议不需要声明“argNames”属性:
@Before("com.xyz.lib.Pointcuts.anyPublicMethod()")
public void audit(JoinPoint jp) {
    // ... use jp
}
  • Using the 'argNames' attribute is a little clumsy, so if the 'argNames' attribute has not been specified, then Spring AOP will look at the debug information for the class and try to determine the parameter names from the local variable table. This information will be present as long as the classes have been compiled with debug information ( '-g:vars' at a minimum). The consequences of compiling with this flag on are: (1) your code will be slightly easier to understand (reverse engineer), (2) the class file sizes will be very slightly bigger (typically inconsequential), (3) the optimization to remove unused local variables will not be applied by your compiler. In other words, you should encounter no difficulties building with this flag on.
    • 使用'argNames'属性有点笨拙,因此如果'argNames'属性没有被指定,那么Spring AOP将查看类的调试信息,并尝试从本地变量表中确定参数名。只要用调试信息编译类('-g:vars'最少),这个信息就会出现。使用这个标志进行编译的结果是:(1)您的代码将稍微容易理解(反向工程),(2)类文件的大小将会稍微大一点(通常是不重要的),(3)删除未使用的局部变量的优化将不会被编译器应用。换句话说,您在使用这个旗帜时应该不会遇到任何困难。
If an @AspectJ aspect has been compiled by the AspectJ compiler (ajc) even without the debug information then there is no need to add the argNames attribute as the compiler will retain the needed information.
    • 如果AspectJ编译器(ajc)已经编译了一个@AspectJ方面,即使没有调试信息,也不需要添加argNames属性,因为编译器将保留所需的信息。
  • If the code has been compiled without the necessary debug information, then Spring AOP will attempt to deduce the pairing of binding variables to parameters (for example, if only one variable is bound in the pointcut expression, and the advice method only takes one parameter, the pairing is obvious!). If the binding of variables is ambiguous given the available information, then an AmbiguousBindingException will be thrown.
    • 如果编译代码时没有必要的调试信息,那么Spring AOP将尝试推断绑定变量与参数的配对(例如,如果在切入点表达式中只有一个变量被绑定,并且advice方法只接受一个参数,那么这种配对是显而易见的!)如果给定可用信息,变量的绑定是不明确的,那么将抛出一个含糊不清的bindingexception。
  • If all of the above strategies fail then an IllegalArgumentException will be thrown.
    • 如果上述所有策略都失败了,那么将抛出一个IllegalArgumentException。
Proceeding with arguments(继续争论)

We remarked earlier that we would describe how to write a proceed call with arguments that works consistently across Spring AOP and AspectJ. The solution is simply to ensure that the advice signature binds each of the method parameters in order. For example:

  • 我们前面提到过,我们将描述如何使用在Spring AOP和AspectJ中一致工作的参数编写proceed调用。解决方案就是确保通知签名按顺序绑定每个方法参数。例如:
@Around("execution(List<Account> find*(..)) && " +
        "com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +
        "args(accountHolderNamePattern)")
public Object preProcessQueryPattern(ProceedingJoinPoint pjp,
        String accountHolderNamePattern) throws Throwable {
    String newPattern = preProcess(accountHolderNamePattern);
    return pjp.proceed(new Object[] {newPattern});
}

In many cases you will be doing this binding anyway (as in the example above).

  • 在许多情况下,无论如何都要进行绑定(如上面的示例所示)。
Advice ordering(建议订购)

What happens when multiple pieces of advice all want to run at the same join point? Spring AOP follows the same precedence rules as AspectJ to determine the order of advice execution. The highest precedence advice runs first "on the way in" (so given two pieces of before advice, the one with highest precedence runs first). "On the way out" from a join point, the highest precedence advice runs last (so given two pieces of after advice, the one with the highest precedence will run second).

  • 如果多个通知都希望在同一个连接点上运行,会发生什么情况?Spring AOP遵循与AspectJ相同的优先规则来决定通知的执行顺序。优先级最高的建议在“进入时”首先运行(因此,在给出两个before建议时,优先级最高的建议首先运行)。从连接点“在退出的过程中”,优先级最高的通知最后运行(因此,给定两个after通知,优先级最高的那个将排在第二)。

When two pieces of advice defined in different aspects both need to run at the same join point, unless you specify otherwise the order of execution is undefined. You can control the order of execution by specifying precedence. This is done in the normal Spring way by either implementing the org.springframework.core.Ordered interface in the aspect class or annotating it with the Order annotation. Given two aspects, the aspect returning the lower value from Ordered.getValue() (or the annotation value) has the higher precedence.

  • 在不同方面定义的两个通知都需要在同一个连接点上运行时,除非您指定了其他方式,否则执行顺序是未定义的。您可以通过指定优先级来控制执行顺序。这是通过实现org.springframework.core以正常的Spring方式完成的。方面类中的有序接口,或者使用Order注释对其进行注释。对于两个方面,从order . getvalue()(或注释值)返回较低值的方面具有较高的优先级。

When two pieces of advice defined in the same aspect both need to run at the same join point, the ordering is undefined (since there is no way to retrieve the declaration order via reflection for javac-compiled classes). Consider collapsing such advice methods into one advice method per join point in each aspect class, or refactor the pieces of advice into separate aspect classes - which can be ordered at the aspect level.

  • 当在同一个方面中定义的两个通知都需要在同一个连接点上运行时,顺序是未定义的(因为无法通过java编译类的反射检索声明顺序)。考虑将此类通知方法分解为每个方面类中的每个连接点的一个通知方法,或者将通知片段重构为单独的方面类——可以在方面级别对其进行排序。

5.2.5. Introductions(引入)

Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.

  • 引入(在AspectJ中称为类型间声明)使方面能够声明已通知的对象实现给定接口,并代表这些对象提供该接口的实现。

An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name). For example, given an interface UsageTracked, and an implementation of that interface DefaultUsageTracked, the following aspect declares that all implementors of service interfaces also implement the UsageTracked interface. (In order to expose statistics via JMX for example.)

  • 使用@DeclareParents注释进行了介绍。此注释用于声明匹配类型有一个新的父类(因此得名)。例如,给定一个接口UsageTracked和该接口的实现DefaultUsageTracked,下面的方面声明服务接口的所有实现者也实现UsageTracked接口。(例如,为了通过JMX公开统计信息。)
@Aspect
public class UsageTracking {

    @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
    public static UsageTracked mixin;

    @Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
    public void recordUsage(UsageTracked usageTracked) {
        usageTracked.incrementUseCount();
    }

}

The interface to be implemented is determined by the type of the annotated field. The value attribute of the @DeclareParents annotation is an AspectJ type pattern :- any bean of a matching type will implement the UsageTracked interface. Note that in the before advice of the above example, service beans can be directly used as implementations of the UsageTracked interface. If accessing a bean programmatically you would write the following:

  • 要实现的接口由带注释的字段的类型决定。@DeclareParents注释的值属性是一个AspectJ类型模式:-任何匹配类型的bean都将实现UsageTracked接口。注意,在上面示例的before通知中,服务bean可以直接用作UsageTracked接口的实现。如果以编程方式访问一个bean,您将编写以下代码:
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

5.2.6. Aspect instantiation models(实例化模型方面)

(This is an advanced topic, so if you are just starting out with AOP you can safely skip it until later.)
  • (这是一个高级的主题,所以如果您刚刚开始使用AOP,您可以安全地跳过它直到以后。)

By default there will be a single instance of each aspect within the application context. AspectJ calls this the singleton instantiation model. It is possible to define aspects with alternate lifecycles :- Spring supports AspectJ’s perthis and pertarget instantiation models ( percflow, percflowbelow, and pertypewithin are not currently supported).

  • 默认情况下,应用程序上下文中的每个方面都有一个实例。AspectJ将其称为单例实例化模型。Spring支持AspectJ的perthis和pertarget实例化模型(目前不支持percflow、percflowbelow和pertypewithin)。

A "perthis" aspect is declared by specifying a perthis clause in the @Aspect annotation. Let’s look at an example, and then we’ll explain how it works.

  • “perthis”方面是通过在@Aspect注释中指定perthis子句来声明的。让我们看一个例子,然后解释它是如何工作的。
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {

    private int someState;

    @Before(com.xyz.myapp.SystemArchitecture.businessService())
    public void recordServiceUsage() {
        // ...
    }

}

The effect of the 'perthis' clause is that one aspect instance will be created for each unique service object executing a business service (each unique object bound to 'this' at join points matched by the pointcut expression). The aspect instance is created the first time that a method is invoked on the service object. The aspect goes out of scope when the service object goes out of scope. Before the aspect instance is created, none of the advice within it executes. As soon as the aspect instance has been created, the advice declared within it will execute at matched join points, but only when the service object is the one this aspect is associated with. See the AspectJ programming guide for more information on per-clauses.

  • 'perthis'子句的效果是,将为执行业务服务的每个惟一服务对象创建一个方面实例(每个惟一对象在由切入点表达式匹配的连接点上绑定到'this')。方面实例是在服务对象上第一次调用方法时创建的。当服务对象超出范围时,方面就超出了范围。在创建方面实例之前,其中的通知都不会执行。一旦创建了方面实例,在其中声明的通知将在匹配的连接点上执行,但仅当服务对象是与此方面关联的服务对象时才执行。有关每个子句的更多信息,请参阅AspectJ编程指南。

The 'pertarget' instantiation model works in exactly the same way as perthis, but creates one aspect instance for each unique target object at matched join points.

  • pertarget实例化模型的工作方式与perthis完全相同,但是在匹配的连接点上为每个唯一的目标对象创建一个方面实例。

5.2.7. Example(例子)

Now that you have seen how all the constituent parts work, let’s put them together to do something useful!

  • 现在您已经了解了所有组成部分是如何工作的,让我们将它们组合在一起来完成一些有用的工作!

The execution of business services can sometimes fail due to concurrency issues (for example, deadlock loser). If the operation is retried, it is quite likely to succeed next time round. For business services where it is appropriate to retry in such conditions (idempotent operations that don’t need to go back to the user for conflict resolution), we’d like to transparently retry the operation to avoid the client seeing a PessimisticLockingFailureException. This is a requirement that clearly cuts across multiple services in the service layer, and hence is ideal for implementing via an aspect.

  • 业务服务的执行有时会由于并发问题(例如,死锁失败)而失败。如果重试该操作,下一次很可能会成功。对于适合在这种情况下重试的业务服务(幂等操作不需要返回给用户以解决冲突),我们希望透明地重试操作,以避免客户机看到一个悲观lockingfailureexception异常。这是一个明显跨越服务层中的多个服务的需求,因此是通过方面实现的理想需求。

Because we want to retry the operation, we will need to use around advice so that we can call proceed multiple times. Here’s how the basic aspect implementation looks:

  • 因为我们想要重试操作,我们将需要使用around通知,以便我们可以多次调用proceed。下面是基本方面实现的样子:
@Aspect
public class ConcurrentOperationExecutor implements Ordered {

    private static final int DEFAULT_MAX_RETRIES = 2;

    private int maxRetries = DEFAULT_MAX_RETRIES;
    private int order = 1;

    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Around("com.xyz.myapp.SystemArchitecture.businessService()")
    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numAttempts = 0;
        PessimisticLockingFailureException lockFailureException;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            }
            catch(PessimisticLockingFailureException ex) {
                lockFailureException = ex;
            }
        } while(numAttempts <= this.maxRetries);
        throw lockFailureException;
    }

}

Note that the aspect implements the Ordered interface so we can set the precedence of the aspect higher than the transaction advice (we want a fresh transaction each time we retry). The maxRetries and order properties will both be configured by Spring. The main action happens in the doConcurrentOperation around advice. Notice that for the moment we’re applying the retry logic to all businessService()s. We try to proceed, and if we fail with an PessimisticLockingFailureException we simply try again unless we have exhausted all of our retry attempts.

  • 请注意,方面实现了有序接口,因此我们可以将方面的优先级设置为高于事务通知的优先级(每次重试时都需要一个新的事务)。maxRetries和order属性都将由Spring配置。主要操作发生在围绕通知的doConcurrentOperation中。请注意,目前我们正在对所有businessService()应用重试逻辑。我们尝试继续,如果我们失败了一个悲观的锁定失败异常,我们就会简单地再试一次,除非我们已经耗尽所有的重试尝试。

The corresponding Spring configuration is:

  • 对应的Spring配置为:
<aop:aspectj-autoproxy/>

<bean id="concurrentOperationExecutor" class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
    <property name="maxRetries" value="3"/>
    <property name="order" value="100"/>
</bean>

To refine the aspect so that it only retries idempotent operations, we might define an Idempotent annotation:

  • 为了精炼方面,使它只重试幂等运算,我们可以定义一个幂等注释:
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    // marker annotation
}

and use the annotation to annotate the implementation of service operations. The change to the aspect to only retry idempotent operations simply involves refining the pointcut expression so that only @Idempotent operations match:

  • 并使用注释对服务操作的实现进行注释。对只重试幂等操作的方面的改变只涉及细化切入点表达式,以便只有@幂等操作匹配:
@Around("com.xyz.myapp.SystemArchitecture.businessService() && " +
        "@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
    ...
}

5.3. Schema-based AOP support(基于AOP支持)

If you prefer an XML-based format, then Spring also offers support for defining aspects using the new "aop" namespace tags. The exact same pointcut expressions and advice kinds are supported as when using the @AspectJ style, hence in this section we will focus on the new syntax and refer the reader to the discussion in the previous section (@AspectJ support) for an understanding of writing pointcut expressions and the binding of advice parameters.

  • 如果您喜欢基于xml的格式,那么Spring还提供了使用新的“aop”名称空间标记定义方面的支持。相同的切入点表达式支持和建议类型时使用@ aspectj风格,因此在本节中,我们将关注新的语法和参考读者在前一节中讨论(@ aspectj支持)的理解写作切入点表达式和绑定参数的建议。

To use the aop namespace tags described in this section, you need to import the spring-aop schema as described in XML Schema-based configuration. See the AOP schema for how to import the tags in the aop namespace.

  • 要使用本节中描述的aop名称空间标记,需要导入基于XML模式的配置中描述的spring-aop模式。请参阅AOP模式,了解如何在AOP名称空间中导入标记。

Within your Spring configurations, all aspect and advisor elements must be placed within an <aop:config> element (you can have more than one <aop:config> element in an application context configuration). An <aop:config> element can contain pointcut, advisor, and aspect elements (note these must be declared in that order).

  • 在Spring配置中,所有aspect和advisor元素都必须放在aop:config元素中(在应用程序上下文配置中可以有多个aop:config元素)。一个aop:config元素可以包含切入点、顾问和方面元素(注意这些必须按照顺序声明)。
The <aop:config> style of configuration makes heavy use of Spring’s auto-proxying mechanism. This can cause issues (such as advice not being woven) if you are already using explicit auto-proxying via the use of BeanNameAutoProxyCreator or suchlike. The recommended usage pattern is to use either just the <aop:config> style, or just the AutoProxyCreator style.
  • 配置的aop:config风格大量使用了Spring的自动代理机制。如果您已经通过使用BeanNameAutoProxyCreator或类似的工具来使用显式的自动代理,那么这可能会导致问题(比如没有编织通知)。推荐的使用模式是要么仅仅使用aop:config风格,要么仅仅使用AutoProxyCreator风格。

5.3.1. Declaring an aspect(声明一个方面)

Using the schema support, an aspect is simply a regular Java object defined as a bean in your Spring application context. The state and behavior is captured in the fields and methods of the object, and the pointcut and advice information is captured in the XML.

  • 使用模式支持,方面只是在Spring应用程序上下文中定义为bean的常规Java对象。状态和行为在对象的字段和方法中捕获,切入点和通知信息在XML中捕获。

An aspect is declared using the aop:aspect element, and the backing bean is referenced using the ref attribute:

  • 方面是使用aop:aspect元素声明的,后台bean是使用ref属性引用的:
<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
    ...
</bean>

The bean backing the aspect ("aBean" in this case) can of course be configured and dependency injected just like any other Spring bean.

  • 支持方面的bean(本例中为“aBean”)当然可以像其他Spring bean一样配置和注入依赖项。

5.3.2. Declaring a pointcut(声明一个切入点)

A named pointcut can be declared inside an aop:config element, enabling the pointcut definition to be shared across several aspects and advisors.

  • 命名的切入点可以在aop:config元素中声明,从而使切入点定义能够在多个方面和顾问之间共享。

A pointcut representing the execution of any business service in the service layer could be defined as follows:

  • 表示服务层中任何业务服务执行的切入点可以定义如下:
<aop:config>

    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

</aop:config>

Note that the pointcut expression itself is using the same AspectJ pointcut expression language as described in @AspectJ support. If you are using the schema based declaration style, you can refer to named pointcuts defined in types (@Aspects) within the pointcut expression. Another way of defining the above pointcut would be:

  • 注意,切入点表达式本身使用@AspectJ支持中描述的同一种AspectJ切入点表达式语言。如果您正在使用基于模式的声明样式,那么您可以引用在切入点表达式中的类型(@Aspects)中定义的命名切入点。定义上述切入点的另一种方法是:
<aop:config>

    <aop:pointcut id="businessService"
        expression="com.xyz.myapp.SystemArchitecture.businessService()"/>

</aop:config>

Assuming you have a SystemArchitecture aspect as described in Sharing common pointcut definitions.

  • 假设您有一个共享公共切入点定义中描述的系统架构方面。

Declaring a pointcut inside an aspect is very similar to declaring a top-level pointcut:

  • 在方面中声明切入点非常类似于声明顶级切入点:
<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>

        ...

    </aop:aspect>

</aop:config>

Much the same way in an @AspectJ aspect, pointcuts declared using the schema based definition style may collect join point context. For example, the following pointcut collects the 'this' object as the join point context and passes it to advice:

  • 在@AspectJ方面中,使用基于模式定义风格声明的切入点也可以收集连接点上下文。例如,下面的切入点收集“this”对象作为连接点上下文,并将其传递给advice:
<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service.*.*(..)) &amp;&amp; this(service)"/>

        <aop:before pointcut-ref="businessService" method="monitor"/>

        ...

    </aop:aspect>

</aop:config>

The advice must be declared to receive the collected join point context by including parameters of the matching names:

  • 通知必须声明,通过包含匹配名称的参数来接收收集的连接点上下文:
public void monitor(Object service) {
    ...
}

When combining pointcut sub-expressions, && is awkward within an XML document, and so the keywords and, or, and not can be used in place of &&, ||, and ! respectively. For example, the previous pointcut may be better written as:

  • 在组合切入点子表达式时,&&在XML文档中很笨拙,因此关键字and、or和not可以用来代替&&、||和!分别。例如,前面的切入点可能写为:
<aop:config>

    <aop:aspect id="myAspect" ref="aBean">

        <aop:pointcut id="businessService"
            expression="execution(* com.xyz.myapp.service..(..)) and this(service)"/>

        <aop:before pointcut-ref="businessService" method="monitor"/>

        ...
    </aop:aspect>
</aop:config>

Note that pointcuts defined in this way are referred to by their XML id and cannot be used as named pointcuts to form composite pointcuts. The named pointcut support in the schema based definition style is thus more limited than that offered by the @AspectJ style.

  • 注意,以这种方式定义的切入点由其XML id引用,不能用作命名切入点来形成复合切入点。因此,基于模式定义风格中的命名切入点支持比@AspectJ风格所提供的更加有限。

5.3.3. Declaring advice(声明的建议)

The same five advice kinds are supported as for the @AspectJ style, and they have exactly the same semantics.

  • 与@AspectJ风格一样,支持相同的五种通知类型,而且它们具有完全相同的语义。
Before advice(前置增强)

Before advice runs before a matched method execution. It is declared inside an <aop:aspect> using the aop:before element.

  • advice在匹配的方法执行之前运行。它是在aop:aspect中使用aop:在元素之前声明的。
<aop:aspect id="beforeExample" ref="aBean">

    <aop:before
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...

</aop:aspect>

Here dataAccessOperation is the id of a pointcut defined at the top ( <aop:config>) level. To define the pointcut inline instead, replace the pointcut-ref attribute with a pointcut attribute:

  • 这里的dataAccessOperation是在顶部(aop:config)级别定义的切入点的id。要想以内联方式定义切入点,可以用切入点属性替换切入点-ref属性:
<aop:aspect id="beforeExample" ref="aBean">

    <aop:before
        pointcut="execution(* com.xyz.myapp.dao.*.*(..))"
        method="doAccessCheck"/>

    ...

</aop:aspect>

As we noted in the discussion of the @AspectJ style, using named pointcuts can significantly improve the readability of your code.

  • 正如我们在@AspectJ风格的讨论中指出的,使用命名切入点可以显著提高代码的可读性。

The method attribute identifies a method ( doAccessCheck) that provides the body of the advice. This method must be defined for the bean referenced by the aspect element containing the advice. Before a data access operation is executed (a method execution join point matched by the pointcut expression), the "doAccessCheck" method on the aspect bean will be invoked.

  • 方法属性标识提供通知主体的方法(doAccessCheck)。必须为包含通知的方面元素引用的bean定义此方法。在执行数据访问操作(与切入点表达式匹配的方法执行连接点)之前,将调用方面bean上的“doAccessCheck”方法。
After returning advice(返回后置通知)

After returning advice runs when a matched method execution completes normally. It is declared inside an <aop:aspect> in the same way as before advice. For example:

  • 在返回通知后,当匹配的方法执行正常完成时运行。它在aop:aspect中声明,方式与通知之前相同。例如:
<aop:aspect id="afterReturningExample" ref="aBean">

    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        method="doAccessCheck"/>

    ...

</aop:aspect>

Just as in the @AspectJ style, it is possible to get hold of the return value within the advice body. Use the returning attribute to specify the name of the parameter to which the return value should be passed:

  • 就像在@AspectJ风格中一样,可以在通知体中获得返回值。使用返回属性来指定应该传递返回值的参数的名称:
<aop:aspect id="afterReturningExample" ref="aBean">

    <aop:after-returning
        pointcut-ref="dataAccessOperation"
        returning="retVal"
        method="doAccessCheck"/>

    ...

</aop:aspect>

The doAccessCheck method must declare a parameter named retVal. The type of this parameter constrains matching in the same way as described for @AfterReturning. For example, the method signature may be declared as:

  • doAccessCheck方法必须声明一个名为retVal的参数。该参数的类型以与@ afterreturn中描述的相同的方式限制匹配。例如,方法签名可以声明为:
public void doAccessCheck(Object retVal) {...
After throwing advice(抛出后增强)

After throwing advice executes when a matched method execution exits by throwing an exception. It is declared inside an <aop:aspect> using the after-throwing element:

  • 抛出通知后,当匹配的方法执行通过抛出异常退出时执行通知。它是在aop:aspect中使用抛出后的元素声明的:
<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        method="doRecoveryActions"/>

    ...

</aop:aspect>

Just as in the @AspectJ style, it is possible to get hold of the thrown exception within the advice body. Use the throwing attribute to specify the name of the parameter to which the exception should be passed:

  • 就像在@AspectJ风格中一样,可以在通知主体中获得抛出的异常。使用抛出属性指定异常应该传递到的参数的名称:
<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing
        pointcut-ref="dataAccessOperation"
        throwing="dataAccessEx"
        method="doRecoveryActions"/>

    ...

</aop:aspect>

The doRecoveryActions method must declare a parameter named dataAccessEx. The type of this parameter constrains matching in the same way as described for @AfterThrowing. For example, the method signature may be declared as:

  • doRecoveryActions方法必须声明一个名为dataAccessEx的参数。该参数的类型以与@ afterthrow中描述的相同的方式限制匹配。例如,方法签名可以声明为:
public void doRecoveryActions(DataAccessException dataAccessEx) {...
After (finally) advice((最后)后的建议)

After (finally) advice runs however a matched method execution exits. It is declared using the after element:

  • 在(最后)通知运行后,匹配的方法执行退出。它是使用after元素声明的:
<aop:aspect id="afterFinallyExample" ref="aBean">

    <aop:after
        pointcut-ref="dataAccessOperation"
        method="doReleaseLock"/>

    ...

</aop:aspect>
Around advice(环绕增强)

The final kind of advice is around advice. Around advice runs "around" a matched method execution. It has the opportunity to do work both before and after the method executes, and to determine when, how, and even if, the method actually gets to execute at all. Around advice is often used if you need to share state before and after a method execution in a thread-safe manner (starting and stopping a timer for example). Always use the least powerful form of advice that meets your requirements; don’t use around advice if simple before advice would do.

  • 最后一种建议是关于建议的。Around advice“绕过”匹配的方法执行。它有机会在方法执行之前和之后执行工作,并确定何时、如何以及是否该方法实际执行。如果您需要以线程安全的方式(例如启动和停止计时器)在方法执行之前和之后共享状态,则经常使用Around通知。总是用最不有力的方式提出符合你要求的建议;不要使用周围的建议,如果简单之前的建议可以。

Around advice is declared using the aop:around element. The first parameter of the advice method must be of type ProceedingJoinPoint. Within the body of the advice, calling proceed() on the ProceedingJoinPoint causes the underlying method to execute. The proceed method may also be calling passing in an Object[] - the values in the array will be used as the arguments to the method execution when it proceeds. See Around advice for notes on calling proceed with an Object[].

  • Around通知是使用aop: Around元素声明的。通知方法的第一个参数的类型必须是ProceedingJoinPoint。在通知的主体中,对ProceedingJoinPoint调用proceed()将导致底层方法的执行。proceed方法也可以调用传入Object[]—数组中的值将作为方法执行时的参数。有关调用proceed with对象[]的注释,请参阅周围的建议。
<aop:aspect id="aroundExample" ref="aBean">

    <aop:around
        pointcut-ref="businessService"
        method="doBasicProfiling"/>

    ...

</aop:aspect>

The implementation of the doBasicProfiling advice would be exactly the same as in the @AspectJ example (minus the annotation of course):

  • doBasicProfiling通知的实现将与@AspectJ的例子完全相同(当然是减去注释):
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}
Advice parameters(通知参数)

The schema based declaration style supports fully typed advice in the same way as described for the @AspectJ support - by matching pointcut parameters by name against advice method parameters. See Advice parameters for details. If you wish to explicitly specify argument names for the advice methods (not relying on the detection strategies previously described) then this is done using the arg-names attribute of the advice element, which is treated in the same manner to the "argNames" attribute in an advice annotation as described in Determining argument names. For example:

  • 基于模式的声明风格以与@AspectJ支持相同的方式支持全类型通知——通过按名称匹配切入点参数和通知方法参数。有关详细信息,请参阅通知参数。如果你想显式地指定参数名称建议方法(不依赖于先前描述的检测策略)然后使用arg-names这样做是属性元素的建议,即以同样的方式对待“argNames”属性的一个建议注释在确定所述参数名称。例如:
<aop:before
    pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
    method="audit"
    arg-names="auditable"/>

The arg-names attribute accepts a comma-delimited list of parameter names.

  • arg-names属性接受以逗号分隔的参数名称列表。

Find below a slightly more involved example of the XSD-based approach that illustrates some around advice used in conjunction with a number of strongly typed parameters.

  • 下面是一个稍微复杂一些的基于xsd的方法示例,它演示了与许多强类型参数一起使用的通知。
package x.y.service;

public interface FooService {

    Foo getFoo(String fooName, int age);
}

public class DefaultFooService implements FooService {

    public Foo getFoo(String name, int age) {
        return new Foo(name, age);
    }
}

Next up is the aspect. Notice the fact that the profile(..) method accepts a number of strongly-typed parameters, the first of which happens to be the join point used to proceed with the method call: the presence of this parameter is an indication that the profile(..) is to be used as around advice:

  • 下一个是方面。请注意,profile(..)方法接受大量强类型参数,第一个参数恰好是用于继续进行方法调用的连接点:这个参数的出现表明profile(..)将被用作around通知:
package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class SimpleProfiler {

    public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
        StopWatch clock = new StopWatch("Profiling for '" + name + "' and '" + age + "'");
        try {
            clock.start(call.toShortString());
            return call.proceed();
        } finally {
            clock.stop();
            System.out.println(clock.prettyPrint());
        }
    }
}

Finally, here is the XML configuration that is required to effect the execution of the above advice for a particular join point:

  • 最后,下面是对特定连接点执行上述通知所需的XML配置:
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- this is the object that will be proxied by Spring's AOP infrastructure -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this is the actual advice itself -->
    <bean id="profiler" class="x.y.SimpleProfiler"/>

    <aop:config>
        <aop:aspect ref="profiler">

            <aop:pointcut id="theExecutionOfSomeFooServiceMethod"
                expression="execution(* x.y.service.FooService.getFoo(String,int))
                and args(name, age)"/>

            <aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
                method="profile"/>

        </aop:aspect>
    </aop:config>

</beans>

If we had the following driver script, we would get output something like this on standard output:

  • 如果我们有以下驱动程序脚本,我们会得到类似这样的输出在标准输出:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.service.FooService;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");
        FooService foo = (FooService) ctx.getBean("fooService");
        foo.getFoo("Pengo", 12);
    }
}
StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0
-----------------------------------------
ms     %     Task name
-----------------------------------------
00000  ?  execution(getFoo)
Advice ordering(建议订购)

When multiple advice needs to execute at the same join point (executing method) the ordering rules are as described in Advice ordering. The precedence between aspects is determined by either adding the Order annotation to the bean backing the aspect or by having the bean implement the Ordered interface.

  • 当多个通知需要在同一个连接点(执行方法)执行时,顺序规则如通知顺序中所述。方面之间的优先级是通过向支持方面的bean添加Order注释或让bean实现Ordered接口来确定的。

5.3.4. Introductions(引入)

Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects.

  • 引入(在AspectJ中称为类型间声明)使方面能够声明已通知的对象实现给定接口,并代表这些对象提供该接口的实现。

An introduction is made using the aop:declare-parents element inside an aop:aspect This element is used to declare that matching types have a new parent (hence the name). For example, given an interface UsageTracked, and an implementation of that interface DefaultUsageTracked, the following aspect declares that all implementors of service interfaces also implement the UsageTracked interface. (In order to expose statistics via JMX for example.)

  • 在aop:方面中使用aop:declare-parent元素进行了介绍,该元素用于声明匹配的类型有一个新的父元素(因此有了这个名称)。例如,给定一个接口UsageTracked和该接口的实现DefaultUsageTracked,下面的方面声明服务接口的所有实现者也实现UsageTracked接口。(例如,为了通过JMX公开统计信息。)
<aop:aspect id="usageTrackerAspect" ref="usageTracking">

    <aop:declare-parents
        types-matching="com.xzy.myapp.service.*+"
        implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
        default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>

    <aop:before
        pointcut="com.xyz.myapp.SystemArchitecture.businessService()
            and this(usageTracked)"
            method="recordUsage"/>

</aop:aspect>

The class backing the usageTracking bean would contain the method:

  • 支持usageTracking bean的类将包含以下方法:
public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
}

The interface to be implemented is determined by implement-interface attribute. The value of the types-matching attribute is an AspectJ type pattern :- any bean of a matching type will implement the UsageTracked interface. Note that in the before advice of the above example, service beans can be directly used as implementations of the UsageTracked interface. If accessing a bean programmatically you would write the following:

  • 要实现的接口由实现接口属性决定。类型匹配属性的值是一个AspectJ类型模式:-任何匹配类型的bean都将实现UsageTracked接口。注意,在上面示例的before通知中,服务bean可以直接用作UsageTracked接口的实现。如果以编程方式访问一个bean,您将编写以下代码:
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

5.3.5. Aspect instantiation models(实例化模型方面)

The only supported instantiation model for schema-defined aspects is the singleton model. Other instantiation models may be supported in future releases.

  • 模式定义的方面唯一受支持的实例化模型是单例模型。其他实例化模型可能会在将来的版本中得到支持。

5.3.6. Advisors(咨询)

The concept of "advisors" is brought forward from the AOP support defined in Spring and does not have a direct equivalent in AspectJ. An advisor is like a small self-contained aspect that has a single piece of advice. The advice itself is represented by a bean, and must implement one of the advice interfaces described in Advice types in Spring. Advisors can take advantage of AspectJ pointcut expressions though.

  • “顾问”的概念是由Spring中定义的AOP支持提出的,在AspectJ中没有直接的对等物。顾问就像一个小的自包含的方面,只有一条建议。通知本身由bean表示,并且必须实现在Spring的通知类型中描述的通知接口之一。但是,顾问可以利用AspectJ切入点表达式。

Spring supports the advisor concept with the <aop:advisor> element. You will most commonly see it used in conjunction with transactional advice, which also has its own namespace support in Spring. Here’s how it looks:

  • Spring通过aop:advisor元素支持advisor概念。您将经常看到它与事务通知一起使用,事务通知在Spring中也有自己的名称空间支持。下面是它的样子:
<aop:config>

    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

    <aop:advisor
        pointcut-ref="businessService"
        advice-ref="tx-advice"/>

</aop:config>

<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

As well as the pointcut-ref attribute used in the above example, you can also use the pointcut attribute to define a pointcut expression inline.

  • 除了在上面的例子中使用的pointcut-ref属性外,您还可以使用pointcut属性来内联地定义一个切入点表达式。

To define the precedence of an advisor so that the advice can participate in ordering, use the order attribute to define the Ordered value of the advisor.

  • 要定义advisor的优先级以便通知可以参与排序,可以使用order属性来定义advisor的排序值。

5.3.7. Example(例子)

Let’s see how the concurrent locking failure retry example from Example looks when rewritten using the schema support.

  • 让我们看看使用模式支持重写示例中的并发锁定失败重试示例时的情况。

The execution of business services can sometimes fail due to concurrency issues (for example, deadlock loser). If the operation is retried, it is quite likely it will succeed next time round. For business services where it is appropriate to retry in such conditions (idempotent operations that don’t need to go back to the user for conflict resolution), we’d like to transparently retry the operation to avoid the client seeing a PessimisticLockingFailureException. This is a requirement that clearly cuts across multiple services in the service layer, and hence is ideal for implementing via an aspect.

  • 业务服务的执行有时会由于并发问题(例如,死锁失败)而失败。如果操作重试,下一次很可能会成功。对于适合在这种情况下重试的业务服务(幂等操作不需要返回给用户以解决冲突),我们希望透明地重试操作,以避免客户机看到一个悲观lockingfailureexception异常。这是一个明显跨越服务层中的多个服务的需求,因此是通过方面实现的理想需求。

Because we want to retry the operation, we’ll need to use around advice so that we can call proceed multiple times. Here’s how the basic aspect implementation looks (it’s just a regular Java class using the schema support):

  • 因为我们希望重试该操作,所以需要使用around通知,以便多次调用proceed。下面是基本方面实现的样子(它只是一个使用模式支持的常规Java类):
public class ConcurrentOperationExecutor implements Ordered {

    private static final int DEFAULT_MAX_RETRIES = 2;

    private int maxRetries = DEFAULT_MAX_RETRIES;
    private int order = 1;

    public void setMaxRetries(int maxRetries) {
        this.maxRetries = maxRetries;
    }

    public int getOrder() {
        return this.order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numAttempts = 0;
        PessimisticLockingFailureException lockFailureException;
        do {
            numAttempts++;
            try {
                return pjp.proceed();
            }
            catch(PessimisticLockingFailureException ex) {
                lockFailureException = ex;
            }
        } while(numAttempts <= this.maxRetries);
        throw lockFailureException;
    }

}

Note that the aspect implements the Ordered interface so we can set the precedence of the aspect higher than the transaction advice (we want a fresh transaction each time we retry). The maxRetries and order properties will both be configured by Spring. The main action happens in the doConcurrentOperation around advice method. We try to proceed, and if we fail with a PessimisticLockingFailureException we simply try again unless we have exhausted all of our retry attempts.

  • 请注意,方面实现了有序接口,因此我们可以将方面的优先级设置为高于事务通知的优先级(每次重试时都需要一个新的事务)。maxRetries和order属性都将由Spring配置。主要操作发生在围绕通知方法的doConcurrentOperation中。我们尝试继续前进,如果我们失败了一个悲观的lockingfailureexception,我们就会简单地再试一次,除非我们已经耗尽了所有的重试尝试。
This class is identical to the one used in the @AspectJ example, but with the annotations removed.
  • 这个类与@AspectJ示例中使用的类相同,但是去掉了注释。

The corresponding Spring configuration is:

  • 对应的Spring配置为:
<aop:config>

    <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">

        <aop:pointcut id="idempotentOperation"
            expression="execution(* com.xyz.myapp.service.*.*(..))"/>

        <aop:around
            pointcut-ref="idempotentOperation"
            method="doConcurrentOperation"/>

    </aop:aspect>

</aop:config>

<bean id="concurrentOperationExecutor"
    class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
        <property name="maxRetries" value="3"/>
        <property name="order" value="100"/>
</bean>

Notice that for the time being we assume that all business services are idempotent. If this is not the case we can refine the aspect so that it only retries genuinely idempotent operations, by introducing an Idempotent annotation:

  • 请注意,目前我们假设所有业务服务都是幂等的。如果不是这样的情况,我们可以改进方面,使它只尝试真正的幂等运算,通过引入一个幂等注释:
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    // marker annotation
}

and using the annotation to annotate the implementation of service operations. The change to the aspect to retry only idempotent operations simply involves refining the pointcut expression so that only @Idempotent operations match:

  • 以及使用注释对服务操作的实现进行注释。对只重试幂等操作的方面的改变只涉及细化切入点表达式,以便只有@幂等操作匹配:
<aop:pointcut id="idempotentOperation"
        expression="execution(* com.xyz.myapp.service.*.*(..)) and
        @annotation(com.xyz.myapp.service.Idempotent)"/>

5.4. Choosing which AOP declaration style to use(选择要使用的AOP声明风格)

Once you have decided that an aspect is the best approach for implementing a given requirement, how do you decide between using Spring AOP or AspectJ, and between the Aspect language (code) style, @AspectJ annotation style, or the Spring XML style? These decisions are influenced by a number of factors including application requirements, development tools, and team familiarity with AOP.

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

5.4.1. Spring AOP or full AspectJ?(Spring AOP还是full AspectJ?)

Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ as there is no requirement to introduce the AspectJ compiler / weaver into your development and build processes. If you only need to advise the execution of operations on Spring beans, then Spring AOP is the right choice. If you need to advise objects not managed by the Spring container (such as domain objects typically), then you will need to use AspectJ. You will also need to use AspectJ if you wish to advise join points other than simple method executions (for example, field get or set join points, and so on).

  • 使用最简单的方法。Spring AOP比使用完整的AspectJ更简单,因为不需要在开发和构建过程中引入AspectJ编译器/ weaver。如果您只需要通知Spring bean上的操作的执行,那么Spring AOP就是正确的选择。如果您需要通知不是由Spring容器管理的对象(比如通常的域对象),那么您将需要使用AspectJ。如果您希望通知连接点而不是简单的方法执行(例如,字段get或set连接点,等等),那么还需要使用AspectJ。

When using AspectJ, you have the choice of the AspectJ language syntax (also known as the "code style") or the @AspectJ annotation style. Clearly, if you are not using Java 5+ then the choice has been made for you… use the code style. If aspects play a large role in your design, and you are able to use the AspectJ Development Tools (AJDT) plugin for Eclipse, then the AspectJ language syntax is the preferred option: it is cleaner and simpler because the language was purposefully designed for writing aspects. If you are not using Eclipse, or have only a few aspects that do not play a major role in your application, then you may want to consider using the @AspectJ style and sticking with a regular Java compilation in your IDE, and adding an aspect weaving phase to your build script.

  • 在使用AspectJ时,可以选择AspectJ语言语法(也称为“代码风格”)或@AspectJ注释风格。显然,如果您不使用Java 5+,那么就可以选择使用代码样式。如果方面在您的设计中发挥了很大的作用,并且您能够使用用于Eclipse的AspectJ开发工具(AJDT)插件,那么AspectJ语言语法是首选的选择:它更干净、更简单,因为这种语言是专门为编写方面而设计的。如果你不使用Eclipse,或只有几个方面不扮演重要角色在你的应用程序中,那么您可能需要考虑使用@ aspectj风格和坚持常规Java IDE编译,并添加一个方面编织阶段构建脚本。

5.4.2. @AspectJ or XML for Spring AOP?(@AspectJ还是Spring AOP的XML ?)

If you have chosen to use Spring AOP, then you have a choice of @AspectJ or XML style. There are various tradeoffs to consider.

  • 如果选择使用Spring AOP,那么可以选择@AspectJ或XML样式。有各种各样的权衡需要考虑。

The XML style will be most familiar to existing Spring users and it is backed by genuine POJOs. When using AOP as a tool to configure enterprise services then XML can be a good choice (a good test is whether you consider the pointcut expression to be a part of your configuration you might want to change independently). With the XML style arguably it is clearer from your configuration what aspects are present in the system.

  • 现有的Spring用户最熟悉XML风格,而且它由真正的pojo支持。当使用AOP作为配置企业服务的工具时,XML可能是一个不错的选择(一个好的测试是,是否将切入点表达式作为您可能想要单独更改的配置的一部分)。对于XML样式,可以从配置中更清楚地看出系统中存在哪些方面。

The XML style has two disadvantages. Firstly it does not fully encapsulate the implementation of the requirement it addresses in a single place. The DRY principle says that there should be a single, unambiguous, authoritative representation of any piece of knowledge within a system. When using the XML style, the knowledge of how a requirement is implemented is split across the declaration of the backing bean class, and the XML in the configuration file. When using the @AspectJ style there is a single module - the aspect - in which this information is encapsulated. Secondly, the XML style is slightly more limited in what it can express than the @AspectJ style: only the "singleton" aspect instantiation model is supported, and it is not possible to combine named pointcuts declared in XML. For example, in the @AspectJ style you can write something like:

  • XML样式有两个缺点。首先,它没有将它所处理的需求的实现完全封装在一个地方。DRY原则认为系统内的任何知识都应该有一个单一的、明确的、权威的表示。当使用XML样式时,需求是如何实现的知识在支持bean类的声明和配置文件中的XML中被分割开来。当使用@AspectJ样式时,只有一个模块—方面—将此信息封装在其中。其次,XML风格在表达内容上比@AspectJ风格稍微受限一些:只支持“单例”方面实例化模型,而且不可能组合XML中声明的命名切入点。例如,在@AspectJ样式中,您可以编写类似这样的内容
@Pointcut(execution(* get*()))
public void propertyAccess() {}

@Pointcut(execution(org.xyz.Account+ *(..))
public void operationReturningAnAccount() {}

@Pointcut(propertyAccess() && operationReturningAnAccount())
public void accountPropertyAccess() {}

In the XML style I can declare the first two pointcuts:

  • 在XML样式中,我可以声明前两个切入点:
<aop:pointcut id="propertyAccess"
        expression="execution(* get*())"/>

<aop:pointcut id="operationReturningAnAccount"
        expression="execution(org.xyz.Account+ *(..))"/>

The downside of the XML approach is that you cannot define the accountPropertyAccess pointcut by combining these definitions.

  • XML方法的缺点是不能通过组合这些定义来定义accountPropertyAccess切入点。

The @AspectJ style supports additional instantiation models, and richer pointcut composition. It has the advantage of keeping the aspect as a modular unit. It also has the advantage the @AspectJ aspects can be understood (and thus consumed) both by Spring AOP and by AspectJ - so if you later decide you need the capabilities of AspectJ to implement additional requirements then it is very easy to migrate to an AspectJ-based approach. On balance the Spring team prefer the @AspectJ style whenever you have aspects that do more than simple "configuration" of enterprise services.

  • @AspectJ样式支持额外的实例化模型和更丰富的切入点组合。它的优点是将方面保持为模块化单元。它还有一个优点,即@AspectJ方面可以被Spring AOP和AspectJ理解(并因此消费)——因此,如果您后来决定需要AspectJ的功能来实现额外的需求,那么迁移到基于AspectJ的方法是非常容易的。总的来说,当您的方面不仅仅是完成企业服务的简单“配置”时,Spring团队更喜欢使用@AspectJ风格。

5.5. Mixing aspect types(混合类型方面)

It is perfectly possible to mix @AspectJ style aspects using the autoproxying support, schema-defined <aop:aspect> aspects, <aop:advisor> declared advisors and even proxies and interceptors defined using the Spring 1.2 style in the same configuration. All of these are implemented using the same underlying support mechanism and will co-exist without any difficulty.

  • 使用自动代理支持、模式定义的aop:aspect方面、aop:advisor声明的顾问、甚至在相同配置中使用Spring 1.2风格定义的代理和拦截器来混合使用@AspectJ风格的方面是完全可能的。所有这些都是使用相同的底层支持机制实现的,并且将毫无困难地共存。

5.6. Proxying mechanisms(代理机制)

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).

  • Spring AOP使用JDK动态代理或CGLIB为给定的目标对象创建代理。(只要有选择,JDK动态代理是首选)。

If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.

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

If you want to force the use of CGLIB proxying (for example, to proxy every method defined for the target object, not just those implemented by its interfaces) you can do so. However, there are some issues to consider:

  • 如果您想强制使用CGLIB代理(例如,代理为目标对象定义的每个方法,而不仅仅是由其接口实现的方法),您可以这样做。但是,有一些问题需要考虑:

  • final methods cannot be advised, as they cannot be overridden.

    • 最后的方法不能被建议,因为它们不能被覆盖。
  • As of Spring 3.2, it is no longer necessary to add CGLIB to your project classpath, as CGLIB classes are repackaged under org.springframework and included directly in the spring-core JAR. This means that CGLIB-based proxy support 'just works' in the same way that JDK dynamic proxies always have.

    • 从Spring 3.2开始,不再需要将CGLIB添加到项目类路径中,因为CGLIB类是在org下重新打包的。并直接包含在spring-core JAR中。这意味着基于cglib的代理支持与JDK动态代理总是以相同的方式工作。
  • As of Spring 4.0, the constructor of your proxied object will NOT be called twice anymore since the CGLIB proxy instance will be created via Objenesis. Only if your JVM does not allow for constructor bypassing, you might see double invocations and corresponding debug log entries from Spring’s AOP support.

    • v

To force the use of CGLIB proxies set the value of the proxy-target-class attribute of the <aop:config> element to true:

  • 要强制使用CGLIB代理,请将aop:config元素的代理-目标-类属性的值设为true:
<aop:config proxy-target-class="true">
    <!-- other beans defined here... -->
</aop:config>

To force CGLIB proxying when using the @AspectJ autoproxy support, set the 'proxy-target-class' attribute of the <aop:aspectj-autoproxy> element to true:

  • 当使用@AspectJ自动代理支持时,要强制执行CGLIB代理,请将aop:aspectj-autoproxy元素的“代理-目标-类”属性设置为true:
<aop:aspectj-autoproxy proxy-target-class="true"/>
Multiple <aop:config/> sections are collapsed into a single unified auto-proxy creator at runtime, which applies the strongest proxy settings that any of the <aop:config/> sections (typically from different XML bean definition files) specified. This also applies to the <tx:annotation-driven/> and <aop:aspectj-autoproxy/> elements.To be clear: using proxy-target-class="true" on <tx:annotation-driven/>, <aop:aspectj-autoproxy/> or <aop:config/> elements will force the use of CGLIB proxies for all three of them.

需要明确的是:在tx:注释驱动/aop:aspectj-autoproxy/aop:config/元素上使用代理-target-class="true"将会强制这三个元素都使用CGLIB代理。

5.6.1. Understanding AOP proxies(理解AOP代理)

Spring AOP is proxy-based. It is vitally important that you grasp the semantics of what that last statement actually means before you write your own aspects or use any of the Spring AOP-based aspects supplied with the Spring Framework.

  • Spring AOP是基于代理的。在编写自己的方面或使用Spring框架提供的任何基于Spring aop的方面之前,掌握最后一条语句的语义是非常重要的。

Consider first the scenario where you have a plain-vanilla, un-proxied, nothing-special-about-it, straight object reference, as illustrated by the following code snippet.

  • 首先考虑这样一个场景:您有一个普通的、没有代理的、没有特殊的、直接的对象引用,如下面的代码片段所示。
public class SimplePojo implements Pojo {

    public void foo() {
        // this next method invocation is a direct call on the 'this' reference
        this.bar();
    }

    public void bar() {
        // some logic...
    }
}

If you invoke a method on an object reference, the method is invoked directly on that object reference, as can be seen below.

  • 如果您在对象引用上调用一个方法,那么该方法将直接在该对象引用上调用,如下所示。

public class Main {

    public static void main(String[] args) {

        Pojo pojo = new SimplePojo();

        // this is a direct method call on the 'pojo' reference
        pojo.foo();
    }
}

Things change slightly when the reference that client code has is a proxy. Consider the following diagram and code snippet.

  • 当客户端代码的引用是一个代理时,情况会略有变化。考虑下面的图表和代码片段。

public class Main {

    public static void main(String[] args) {

        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());

        Pojo pojo = (Pojo) factory.getProxy();

        // this is a method call on the proxy!
        pojo.foo();
    }
}

The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

  • 这里需要理解的关键是,main类的main(..)内部的客户机代码有一个对代理的引用。这意味着对该对象引用的方法调用将是对代理的调用,因此代理将能够委托给与该特定方法调用相关的所有拦截器(通知)。然而,一旦调用最终到达目标对象(本例中是SimplePojo引用),它可能对自身进行的任何方法调用,比如this.bar()或this.foo(),都将针对this引用而不是代理进行调用。这具有重要的意义。这意味着自调用不会导致与方法调用关联的通知有机会执行。

Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen. For sure, this does entail some work on your part, but it is the best, least-invasive approach. The next approach is absolutely horrendous, and I am almost reticent to point it out precisely because it is so horrendous. You can (choke!) totally tie the logic within your class to Spring AOP by doing this:

  • 好了,我们该怎么做呢?最好的方法(这里使用的术语“最好”不太严格)是重构代码,使其不会发生自调用。当然,这需要您做一些工作,但这是最好的、最不具侵入性的方法。下一个方法绝对是可怕的,我几乎不愿指出它的确切原因,因为它是如此可怕。通过这样做,您可以(窒息!)将类中的逻辑完全绑定到Spring AOP
public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

This totally couples your code to Spring AOP, and it makes the class itself aware of the fact that it is being used in an AOP context, which flies in the face of AOP. It also requires some additional configuration when the proxy is being created:

  • 这完全将您的代码与Spring AOP结合起来,并且使类本身意识到它是在AOP上下文中使用的这一事实,这与AOP迎面而来。它也需要一些额外的配置时,代理创建:
public class Main {

    public static void main(String[] args) {

        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.adddInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());
        factory.setExposeProxy(true);

        Pojo pojo = (Pojo) factory.getProxy();

        // this is a method call on the proxy!
        pojo.foo();
    }
}

Finally, it must be noted that AspectJ does not have this self-invocation issue because it is not a proxy-based AOP framework.

  • 最后,必须注意AspectJ没有这个自调用问题,因为它不是一个基于代理的AOP框架。

5.7. Programmatic creation of @AspectJ Proxies(通过编程创建@AspectJ代理)

In addition to declaring aspects in your configuration using either <aop:config> or <aop:aspectj-autoproxy>, it is also possible programmatically to create proxies that advise target objects. For the full details of Spring’s AOP API, see the next chapter. Here we want to focus on the ability to automatically create proxies using @AspectJ aspects.

  • 除了使用aop:configaop:aspectj-autoproxy在配置中声明方面之外,还可以通过编程方式创建通知目标对象的代理。有关Spring的AOP API的完整细节,请参阅下一章。在这里,我们想关注使用@AspectJ方面自动创建代理的能力。

The class org.springframework.aop.aspectj.annotation.AspectJProxyFactory can be used to create a proxy for a target object that is advised by one or more @AspectJ aspects. Basic usage for this class is very simple, as illustrated below. See the javadocs for full information.

  • 类org.springframework.aop.aspectj.annotation。AspectJProxyFactory可用于为由一个或多个@AspectJ方面通知的目标对象创建代理。这个类的基本用法非常简单,如下所示。有关完整信息,请参阅javadocs。
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);

// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();

5.8. Using AspectJ with Spring applications(在Spring应用程序中使用AspectJ)

Everything we’ve covered so far in this chapter is pure Spring AOP. In this section, we’re going to look at how you can use the AspectJ compiler/weaver instead of, or in addition to, Spring AOP if your needs go beyond the facilities offered by Spring AOP alone.

  • 到目前为止,我们在本章中讨论的都是纯粹的Spring AOP。在这一节中,我们将介绍如果您的需求超出了Spring AOP单独提供的功能,那么如何使用AspectJ编译器/weaver来代替或补充Spring AOP。

Spring ships with a small AspectJ aspect library, which is available standalone in your distribution as spring-aspects.jar; you’ll need to add this to your classpath in order to use the aspects in it. Using AspectJ to dependency inject domain objects with Spring and Other Spring aspects for AspectJ discuss the content of this library and how you can use it. Configuring AspectJ aspects using Spring IoC discusses how to dependency inject AspectJ aspects that are woven using the AspectJ compiler. Finally, Load-time weaving with AspectJ in the Spring Framework provides an introduction to load-time weaving for Spring applications using AspectJ.

  • Spring附带了一个小型的AspectJ方面库,它在发行版中作为独立的Spring -aspect .jar提供;您需要将它添加到类路径中,以便在其中使用方面。使用AspectJ向Spring和AspectJ的其他Spring方面依赖注入域对象将讨论这个库的内容以及如何使用它。使用Spring IoC配置AspectJ方面讨论了如何依赖注入使用AspectJ编译器编织的AspectJ方面。最后,在Spring框架中使用AspectJ进行加载时编织,介绍了使用AspectJ的Spring应用程序的加载时编织。

5.8.1. Using AspectJ to dependency inject domain objects with Spring(使用AspectJ用Spring依赖注入域对象)

The Spring container instantiates and configures beans defined in your application context. It is also possible to ask a bean factory to configure a pre-existing object given the name of a bean definition containing the configuration to be applied. The spring-aspects.jar contains an annotation-driven aspect that exploits this capability to allow dependency injection of any object. The support is intended to be used for objects created outside of the control of any container. Domain objects often fall into this category because they are often created programmatically using the new operator, or by an ORM tool as a result of a database query.

  • Spring容器实例化和配置在应用程序上下文中定义的bean。如果给定包含要应用的配置的bean定义的名称,也可以要求bean工厂配置已存在的对象。jar包含一个注释驱动的方面,它利用这个功能来允许任何对象的依赖项注入。该支持用于在任何容器控制之外创建的对象。领域对象通常属于这一类,因为它们通常是使用new操作符以编程方式创建的,或者是由ORM工具作为数据库查询的结果创建的。

The @Configurable annotation marks a class as eligible for Spring-driven configuration. In the simplest case it can be used just as a marker annotation:

  • @ configure注释将一个类标记为适合spring驱动配置。在最简单的情况下,它可以被用作一个标记注释:
package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class Account {
    // ...
}

When used as a marker interface in this way, Spring will configure new instances of the annotated type ( Account in this case) using a bean definition (typically prototype-scoped) with the same name as the fully-qualified type name ( com.xyz.myapp.domain.Account). Since the default name for a bean is the fully-qualified name of its type, a convenient way to declare the prototype definition is simply to omit the id attribute:

  • 当以这种方式作为标记接口使用时,Spring将使用与完全限定类型名称(com.xyz.myapp.domain.Account)相同的bean定义(通常是原型作用域)配置注释类型的新实例(本例中为Account)。因为bean的默认名称是其类型的全限定名,所以声明原型定义的一种方便的方法就是省略id属性:
<bean class="com.xyz.myapp.domain.Account" scope="prototype">
    <property name="fundsTransferService" ref="fundsTransferService"/>
</bean>

If you want to explicitly specify the name of the prototype bean definition to use, you can do so directly in the annotation:

  • 如果您想显式地指定要使用的原型bean定义的名称,您可以在注释中直接这样做:
package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable("account")
public class Account {
    // ...
}

Spring will now look for a bean definition named "account" and use that as the definition to configure new Account instances.

  • Spring现在将查找名为“account”的bean定义,并使用它作为定义来配置新的account实例。

You can also use autowiring to avoid having to specify a dedicated bean definition at all. To have Spring apply autowiring use the autowire property of the @Configurable annotation: specify either @Configurable(autowire=Autowire.BY_TYPE) or @Configurable(autowire=Autowire.BY_NAME for autowiring by type or by name respectively. As an alternative, as of Spring 2.5 it is preferable to specify explicit, annotation-driven dependency injection for your @Configurable beans by using @Autowired or @Inject at the field or method level (see Annotation-based container configuration for further details).

  • 您还可以使用自动装配来避免指定一个专门的bean定义。要让Spring应用自动装配,请使用@可配置注释的自动装配属性:指定@可配置(autowire= autowire . by_type)或@可配置(autowire= autowire)。BY_NAME分别用于按类型或按名称自动装配。作为一种替代方案,从spring2.5开始,最好在字段或方法级别使用@Autowired或@Inject为@可配置的bean指定显式的注解驱动的依赖注入(请参阅基于注解的容器配置以了解更多细节)。

Finally you can enable Spring dependency checking for the object references in the newly created and configured object by using the dependencyCheck attribute (for example: @Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)). If this attribute is set to true, then Spring will validate after configuration that all properties (which are not primitives or collections) have been set.

  • 最后,通过使用dependencyCheck属性(例如:@可配置(autowire= autowire . by_name,dependencyCheck=true)),您可以为新创建和配置的对象中的对象引用启用Spring依赖项检查。如果该属性设置为true,那么Spring将在配置后验证所有属性(不是原语或集合)都已设置。

Using the annotation on its own does nothing of course. It is the AnnotationBeanConfigurerAspect in spring-aspects.jar that acts on the presence of the annotation. In essence the aspect says "after returning from the initialization of a new object of a type annotated with @Configurable, configure the newly created object using Spring in accordance with the properties of the annotation". In this context, initialization refers to newly instantiated objects (e.g., objects instantiated with the new operator) as well as to Serializable objects that are undergoing deserialization (e.g., via readResolve()).

  • 当然,单独使用注释不会产生任何效果。jar中的AnnotationBeanConfigurerAspect在注释出现时起作用。本质上,方面说的是“在初始化一个带有@可配置注释的类型的新对象之后,使用Spring根据注释的属性配置新创建的对象”。在这个上下文中,初始化指的是新实例化的对象(例如,用new操作符实例化的对象)以及正在进行反序列化的可序列化对象(例如,通过readResolve())。
One of the key phrases in the above paragraph is 'in essence'. For most cases, the exact semantics of 'after returning from the initialization of a new object' will be fine… in this context, 'after initialization' means that the dependencies will be injected after the object has been constructed - this means that the dependencies will not be available for use in the constructor bodies of the class. If you want the dependencies to be injected before the constructor bodies execute, and thus be available for use in the body of the constructors, then you need to define this on the @Configurable declaration like so:@Configurable(preConstruction=true)You can find out more information about the language semantics of the various pointcut types in AspectJ in this appendix of the AspectJ Programming Guide.
  • 上面段落中的一个关键短语是“in essence”。在大多数情况下,精确的语义的回国后初始化一个新对象的会没事的在这种情况下,“后初始化”意味着后将被注入对象的依赖关系构造——这意味着依赖关系将不会被使用在类的构造函数体。如果你想要依赖项注入身体构造函数执行之前,因此可用于人体的构造函数,那么您需要定义这个@Configurable宣言一样:@Configurable(分段装配= true)你可以找到更多的信息在AspectJ切入点语言语义的各种类型的附录AspectJ编程指南。

For this to work the annotated types must be woven with the AspectJ weaver - you can either use a build-time Ant or Maven task to do this (see for example the AspectJ Development Environment Guide) or load-time weaving (see Load-time weaving with AspectJ in the Spring Framework). The AnnotationBeanConfigurerAspect itself needs configuring by Spring (in order to obtain a reference to the bean factory that is to be used to configure new objects). If you are using Java based configuration simply add @EnableSpringConfigured to any @Configuration class.

  • 要做到这一点,注释类型必须与AspectJ编织器一起编织——您可以使用构建时Ant或Maven任务来完成这一点(例如,请参阅AspectJ开发环境指南),也可以使用加载时编织(请参阅Spring框架中使用AspectJ进行加载时编织)。AnnotationBeanConfigurerAspect本身需要Spring进行配置(以便获得一个对bean工厂的引用,该工厂将用于配置新对象)。如果您使用基于Java的配置,只需将@EnableSpringConfigured添加到任何@Configuration类。
@Configuration
@EnableSpringConfigured
public class AppConfig {

}

If you prefer XML based configuration, the Spring context namespace defines a convenient context:spring-configured element:

  • 如果您更喜欢基于XML的配置,Spring上下文名称空间定义了一个方便的上下文:Spring配置元素:
<context:spring-configured/>

Instances of @Configurable objects created before the aspect has been configured will result in a message being issued to the debug log and no configuration of the object taking place. An example might be a bean in the Spring configuration that creates domain objects when it is initialized by Spring. In this case you can use the "depends-on" bean attribute to manually specify that the bean depends on the configuration aspect.

  • 在配置方面之前创建的@可配置对象的实例将导致向调试日志发出一条消息,并且不进行对象的配置。例如,Spring配置中的bean在被Spring初始化时创建域对象。在这种情况下,您可以使用“depends-on”bean属性手动指定bean依赖于配置方面。
<bean id="myService"
        class="com.xzy.myapp.service.MyService"
        depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">

    <!-- ... -->

</bean>
Do not activate @Configurable processing through the bean configurer aspect unless you really mean to rely on its semantics at runtime. In particular, make sure that you do not use @Configurable on bean classes which are registered as regular Spring beans with the container: You would get double initialization otherwise, once through the container and once through the aspect.
  • 不要通过bean配置器方面激活@ configure处理,除非您真的想在运行时依赖于它的语义。特别地,确保不要在容器中注册为常规Spring bean的bean类上使用@ configure:否则将获得两次初始化,一次通过容器,一次通过方面。
Unit testing @Configurable objects(单元测试@可配置对象)

One of the goals of the @Configurable support is to enable independent unit testing of domain objects without the difficulties associated with hard-coded lookups. If @Configurable types have not been woven by AspectJ then the annotation has no affect during unit testing, and you can simply set mock or stub property references in the object under test and proceed as normal. If @Configurable types have been woven by AspectJ then you can still unit test outside of the container as normal, but you will see a warning message each time that you construct an @Configurable object indicating that it has not been configured by Spring.

  • @可配置支持的目标之一是启用域对象的独立单元测试,而不存在与硬编码查找相关的困难。如果AspectJ没有编织@可配置类型,那么在单元测试期间注释不会产生影响,您可以在测试对象中简单地设置mock或存根属性引用,然后正常进行。如果AspectJ已经编织了@可配置类型,那么您仍然可以在容器之外正常地进行单元测试,但是每次构造一个@可配置对象时,您都会看到一条警告消息,指示它没有被Spring配置。
Working with multiple application contexts(使用多个应用程序上下文)

The AnnotationBeanConfigurerAspect used to implement the @Configurable support is an AspectJ singleton aspect. The scope of a singleton aspect is the same as the scope of static members, that is to say there is one aspect instance per classloader that defines the type. This means that if you define multiple application contexts within the same classloader hierarchy you need to consider where to define the @EnableSpringConfigured bean and where to place spring-aspects.jar on the classpath.

  • 用于实现@可配置支持的AnnotationBeanConfigurerAspect是一个AspectJ单例方面。单例方面的作用域与静态成员的作用域相同,也就是说,每个类加载器都有一个方面实例来定义类型。这意味着,如果在同一个类加载器层次结构中定义多个应用程序上下文,则需要考虑在何处定义@EnableSpringConfigured bean,以及在类的何处放置spring-aspect .jar

Consider a typical Spring web-app configuration with a shared parent application context defining common business services and everything needed to support them, and one child application context per servlet containing definitions particular to that servlet. All of these contexts will co-exist within the same classloader hierarchy, and so the AnnotationBeanConfigurerAspect can only hold a reference to one of them. In this case we recommend defining the @EnableSpringConfigured bean in the shared (parent) application context: this defines the services that you are likely to want to inject into domain objects. A consequence is that you cannot configure domain objects with references to beans defined in the child (servlet-specific) contexts using the @Configurable mechanism (probably not something you want to do anyway!).

  • 考虑一个典型的Spring web应用程序配置,其中共享父应用程序上下文定义公共业务服务和支持它们所需的一切,每个servlet有一个子应用程序上下文,其中包含特定于该servlet的定义。所有这些上下文都将共存于同一个类加载器层次结构中,因此AnnotationBeanConfigurerAspect只能保存对其中一个的引用。在这种情况下,我们建议在共享(父)应用程序上下文中定义@EnableSpringConfigured bean:这定义了您可能想要注入到域对象中的服务。其结果是,您不能使用@可配置机制配置域对象,这些对象引用在子(特定于servlet的)上下文中定义的bean(可能无论如何您都不希望这样做!)

When deploying multiple web-apps within the same container, ensure that each web-application loads the types in spring-aspects.jar using its own classloader (for example, by placing spring-aspects.jar in 'WEB-INF/lib'). If spring-aspects.jar is only added to the container wide classpath (and hence loaded by the shared parent classloader), all web applications will share the same aspect instance which is probably not what you want.

  • 当在同一个容器中部署多个web应用程序时,确保每个web应用程序使用自己的类加载器加载spring-aspect .jar中的类型(例如,通过在WEB-INF/lib中放置spring-aspect .jar)。如果spring-aspect .jar只是添加到容器宽类路径(因此由共享的父类加载器加载),那么所有web应用程序将共享相同的方面实例,这可能不是您想要的。

5.8.2. Other Spring aspects for AspectJ(AspectJ的其他Spring方面)

In addition to the @Configurable aspect, spring-aspects.jar contains an AspectJ aspect that can be used to drive Spring’s transaction management for types and methods annotated with the @Transactional annotation. This is primarily intended for users who want to use the Spring Framework’s transaction support outside of the Spring container.

  • 除了@可配置方面之外,Spring - AspectJ .jar还包含一个AspectJ方面,可用于驱动Spring的事务管理,以处理带有@Transactional注释的类型和方法。这主要是为希望在Spring容器之外使用Spring框架事务支持的用户准备的。

The aspect that interprets @Transactional annotations is the AnnotationTransactionAspect. When using this aspect, you must annotate the implementation class (and/or methods within that class), not the interface (if any) that the class implements. AspectJ follows Java’s rule that annotations on interfaces are not inherited.

  • 解释@Transactional注释的方面是AnnotationTransactionAspect。在使用这个方面时,您必须注释实现类(和/或类中的方法),而不是类实现的接口(如果有的话)。AspectJ遵循Java的规则,接口上的注释不被继承。

A @Transactional annotation on a class specifies the default transaction semantics for the execution of any public operation in the class.

  • 类上的@Transactional注释指定了执行类中任何公共操作的默认事务语义。

A @Transactional annotation on a method within the class overrides the default transaction semantics given by the class annotation (if present). Methods of any visibility may be annotated, including private methods. Annotating non-public methods directly is the only way to get transaction demarcation for the execution of such methods.

  • 类中方法上的@Transactional注释覆盖了类注释(如果存在)给出的默认事务语义。任何可见性的方法都可以被注释,包括私有方法。直接注释非公共方法是获得此类方法执行的事务界定的唯一方法。
Since Spring Framework 4.2, spring-aspects provides a similar aspect that offers the exact same features for the standard javax.transaction.Transactional annotation. Check JtaAnnotationTransactionAspect for more details.
  • 自Spring Framework 4.2以来,Spring -aspects提供了一个类似的方面,它为标准javax.transaction提供了完全相同的特性。事务注释。查看JtaAnnotationTransactionAspect以获得更多细节。

For AspectJ programmers that want to use the Spring configuration and transaction management support but don’t want to (or cannot) use annotations, spring-aspects.jar also contains abstract aspects you can extend to provide your own pointcut definitions. See the sources for the AbstractBeanConfigurerAspect and AbstractTransactionAspect aspects for more information. As an example, the following excerpt shows how you could write an aspect to configure all instances of objects defined in the domain model using prototype bean definitions that match the fully-qualified class names:

  • 对于想要使用Spring配置和事务管理支持但不想(或不能)使用注释的AspectJ程序员,Spring -aspect .jar还包含可以扩展以提供自己的切入点定义的抽象方面。有关更多信息,请参阅AbstractBeanConfigurerAspect和AbstractTransactionAspect方面的源代码。作为一个例子,下面的摘录展示了如何编写一个方面来配置在域模型中定义的对象的所有实例,使用原型bean定义来匹配完全限定的类名:
public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {

    public DomainObjectConfiguration() {
        setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
    }

    // the creation of a new bean (any object in the domain model)
    protected pointcut beanCreation(Object beanInstance) :
        initialization(new(..)) &&
        SystemArchitecture.inDomainModel() &&
        this(beanInstance);

}

5.8.3. Configuring AspectJ aspects using Spring IoC(使用Spring IoC配置AspectJ方面)

When using AspectJ aspects with Spring applications, it is natural to both want and expect to be able to configure such aspects using Spring. The AspectJ runtime itself is responsible for aspect creation, and the means of configuring the AspectJ created aspects via Spring depends on the AspectJ instantiation model (the per-xxx clause) used by the aspect.

  • 在Spring应用程序中使用AspectJ方面时,很自然会希望并期望能够使用Spring配置这样的方面。AspectJ运行时本身负责方面的创建,通过Spring配置AspectJ创建的方面的方法取决于方面使用的AspectJ实例化模型(per-xxx子句)。

The majority of AspectJ aspects are singleton aspects. Configuration of these aspects is very easy: simply create a bean definition referencing the aspect type as normal, and include the bean attribute 'factory-method="aspectOf"'. This ensures that Spring obtains the aspect instance by asking AspectJ for it rather than trying to create an instance itself. For example:

  • 大多数AspectJ方面都是单例方面。这些方面的配置非常简单:只需创建一个正常引用方面类型的bean定义,并包含bean属性'factory-method="aspectOf"'。这确保了Spring通过向AspectJ询问方面实例而不是尝试自己创建实例来获得方面实例。例如
<bean id="profiler" class="com.xyz.profiler.Profiler"
        factory-method="aspectOf">

    <property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>

Non-singleton aspects are harder to configure: however it is possible to do so by creating prototype bean definitions and using the @Configurable support from spring-aspects.jar to configure the aspect instances once they have bean created by the AspectJ runtime.

  • 非单例方面更难配置:但是,可以通过创建原型bean定义并在方面实例由AspectJ运行时创建bean后使用spring-aspect .jar中的@可配置支持来配置方面实例。

If you have some @AspectJ aspects that you want to weave with AspectJ (for example, using load-time weaving for domain model types) and other @AspectJ aspects that you want to use with Spring AOP, and these aspects are all configured using Spring, then you will need to tell the Spring AOP @AspectJ autoproxying support which exact subset of the @AspectJ aspects defined in the configuration should be used for autoproxying. You can do this by using one or more <include/> elements inside the <aop:aspectj-autoproxy/> declaration. Each <include/> element specifies a name pattern, and only beans with names matched by at least one of the patterns will be used for Spring AOP autoproxy configuration:

  • 如果你有@ AspectJ方面你想编织与AspectJ域模型(例如,使用装入时编织类型)和其他您想要使用Spring AOP @ AspectJ方面,而这些方面都是使用Spring配置的,那么您将需要告诉Spring AOP @ AspectJ自动代理的支持@ AspectJ方面中定义的精确子集应该用于自动代理配置。你可以使用一个或多个include/>aop内部的元素:aspectj-autoproxy/&gt声明。每个& lt;包括/比;元素指定一个名称模式,并且只有名称与至少一个模式匹配的bean才会用于Spring AOP autoproxy配置
<aop:aspectj-autoproxy>
    <aop:include name="thisBean"/>
    <aop:include name="thatBean"/>
</aop:aspectj-autoproxy>
Do not be misled by the name of the <aop:aspectj-autoproxy/> element: using it will result in the creation of Spring AOP proxies. The @AspectJ style of aspect declaration is just being used here, but the AspectJ runtime is not involved.
  • 不要被aop:aspectj-autoproxy/元素的名称所误导:使用它将导致Spring aop代理的创建。这里只是使用了@AspectJ风格的方面声明,但是没有涉及AspectJ运行时。

5.8.4. Load-time weaving with AspectJ in the Spring Framework(在Spring框架中使用AspectJ进行加载时编织)

Load-time weaving (LTW) refers to the process of weaving AspectJ aspects into an application’s class files as they are being loaded into the Java virtual machine (JVM). The focus of this section is on configuring and using LTW in the specific context of the Spring Framework: this section is not an introduction to LTW though. For full details on the specifics of LTW and configuring LTW with just AspectJ (with Spring not being involved at all), see the LTW section of the AspectJ Development Environment Guide.

  • 加载时编织(LTW)是指在将AspectJ方面装入Java虚拟机(JVM)时,将它们编织到应用程序的类文件中的过程。本节的重点是在Spring框架的特定上下文中配置和使用LTW:但本节并不是LTW的介绍。有关LTW的详细信息以及仅使用AspectJ(完全不涉及Spring)配置LTW的详细信息,请参阅AspectJ开发环境指南的LTW部分。

The value-add that the Spring Framework brings to AspectJ LTW is in enabling much finer-grained control over the weaving process. 'Vanilla' AspectJ LTW is effected using a Java (5+) agent, which is switched on by specifying a VM argument when starting up a JVM. It is thus a JVM-wide setting, which may be fine in some situations, but often is a little too coarse. Spring-enabled LTW enables you to switch on LTW on a per-ClassLoader basis, which obviously is more fine-grained and which can make more sense in a 'single-JVM-multiple-application' environment (such as is found in a typical application server environment).

  • Spring框架给AspectJ LTW带来的增值是支持对编织过程进行更细粒度的控制。“香草的”AspectJ LTW是使用Java(5+)代理实现的,它是通过在启动JVM时指定VM参数来开启的。因此,它是一个jvm范围的设置,在某些情况下可能很好,但通常有点太粗糙了。启用spring的LTW使您能够在每个类加载器的基础上打开LTW,这显然是更细粒度的,在“单jvm -多应用程序”环境中更有意义(比如在典型的应用程序中)

Further, in certain environments, this support enables load-time weaving without making any modifications to the application server’s launch script that will be needed to add -javaagent:path/to/aspectjweaver.jar or (as we describe later in this section) -javaagent:path/to/org.springframework.instrument-{version}.jar (previously named spring-agent.jar). Developers simply modify one or more files that form the application context to enable load-time weaving instead of relying on administrators who typically are in charge of the deployment configuration such as the launch script.

  • 此外,在特定的环境中,不做任何修改的情况下这种支持使装入时编织到应用程序服务器的启动脚本,将需要添加- javaagent:路径/ / aspectjweaver.jar或(正如我们在本节描述后)- javaagent:路径/ / org.springframework.instrument - {version} . jar(以前叫spring-agent.jar)。开发人员只需修改构成应用程序上下文的一个或多个文件,以启用加载时编织,而不是依赖于通常负责部署配置(如启动脚本)的管理员。

Now that the sales pitch is over, let us first walk through a quick example of AspectJ LTW using Spring, followed by detailed specifics about elements introduced in the following example. For a complete example, please see the Petclinic sample application.

  • 现在推销结束了,让我们先看一个使用Spring的AspectJ LTW的快速示例,然后详细介绍下面示例中介绍的元素。要获得完整的示例,请参见Petclinic示例应用程序。
A first example(第一个例子)

Let us assume that you are an application developer who has been tasked with diagnosing the cause of some performance problems in a system. Rather than break out a profiling tool, what we are going to do is switch on a simple profiling aspect that will enable us to very quickly get some performance metrics, so that we can then apply a finer-grained profiling tool to that specific area immediately afterwards.

  • 假设您是一名应用程序开发人员,其任务是诊断系统中某些性能问题的原因。我们要做的不是使用性能分析工具,而是打开一个简单的性能分析方面,这将使我们能够非常快速地获得一些性能指标,这样我们就可以立即对特定区域应用更细粒度的性能分析工具。
The example presented here uses XML style configuration, it is also possible to configure and use @AspectJ with Java Configuration. Specifically the @EnableLoadTimeWeaving annotation can be used as an alternative to <context:load-time-weaver/> (see below for details).
  • 这里给出的示例使用XML风格配置,也可以使用Java配置配置和使用@AspectJ。具体来说,@EnableLoadTimeWeaving注释可以用作context:load-time-weaver/的替代方法(参见下面的详细信息)。

Here is the profiling aspect. Nothing too fancy, just a quick-and-dirty time-based profiler, using the @AspectJ-style of aspect declaration.

  • 下面是分析方面。没有什么特别的,只是一个快速但不完善的基于时间的剖析器,使用了@ aspectj风格的方面声明。
package foo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
import org.springframework.core.annotation.Order;

@Aspect
public class ProfilingAspect {

    @Around("methodsToBeProfiled()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch sw = new StopWatch(getClass().getSimpleName());
        try {
            sw.start(pjp.getSignature().getName());
            return pjp.proceed();
        } finally {
            sw.stop();
            System.out.println(sw.prettyPrint());
        }
    }

    @Pointcut("execution(public * foo..*.*(..))")
    public void methodsToBeProfiled(){}
}

We will also need to create an META-INF/aop.xml file, to inform the AspectJ weaver that we want to weave our ProfilingAspect into our classes. This file convention, namely the presence of a file (or files) on the Java classpath called META-INF/aop.xml is standard AspectJ.

  • 我们还需要创建一个META-INF/aop.xml文件,以通知AspectJ编织器,我们希望将ProfilingAspect编织到类中。这个文件约定,即在Java类路径中出现一个(或多个)名为META-INF/aop.xml的文件是标准的AspectJ。
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver>
        <!-- only weave classes in our application-specific packages -->
        <include within="foo.*"/>
    </weaver>

    <aspects>
        <!-- weave in just this aspect -->
        <aspect name="foo.ProfilingAspect"/>
    </aspects>

</aspectj>

Now to the Spring-specific portion of the configuration. We need to configure a LoadTimeWeaver (all explained later, just take it on trust for now). This load-time weaver is the essential component responsible for weaving the aspect configuration in one or more META-INF/aop.xml files into the classes in your application. The good thing is that it does not require a lot of configuration, as can be seen below (there are some more options that you can specify, but these are detailed later).

  • 现在来看配置中特定于spring的部分。我们需要配置一个LoadTimeWeaver(后面会详细解释,现在只需要信任它)。这个加载时编织器是负责将一个或多个元- inf /aop.xml文件中的方面配置编织到应用程序中的类中的基本组件。好的方面是它不需要大量的配置,如下所示(您可以指定更多的选项,但稍后会详细介绍)。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- a service object; we will be profiling its methods -->
    <bean id="entitlementCalculationService"
            class="foo.StubEntitlementCalculationService"/>

    <!-- this switches on the load-time weaving -->
    <context:load-time-weaver/>
</beans>

Now that all the required artifacts are in place - the aspect, the META-INF/aop.xml file, and the Spring configuration -, let us create a simple driver class with a main(..) method to demonstrate the LTW in action.

  • 现在,所有必需的构件—方面、META-INF/aop.xml文件和Spring配置—都已经就绪,让我们用main(..)方法创建一个简单的驱动程序类来演示LTW的工作。
package foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main {

    public static void main(String[] args) {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);

        EntitlementCalculationService entitlementCalculationService
            = (EntitlementCalculationService) ctx.getBean("entitlementCalculationService");

        // the profiling aspect is 'woven' around this method execution
        entitlementCalculationService.calculateEntitlement();
    }
}

There is one last thing to do. The introduction to this section did say that one could switch on LTW selectively on a per- ClassLoader basis with Spring, and this is true. However, just for this example, we are going to use a Java agent (supplied with Spring) to switch on the LTW. This is the command line we will use to run the above Main class:

  • 还有最后一件事要做。本节的介绍确实说过,可以使用Spring在每个类加载器的基础上有选择地打开LTW,这是真的。但是,就在这个示例中,我们将使用Java代理(随Spring提供)来打开LTW。这是我们用来运行上面主类的命令行:
java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main

The -javaagent is a flag for specifying and enabling agents to instrument programs running on the JVM. The Spring Framework ships with such an agent, the InstrumentationSavingAgent, which is packaged in the spring-instrument.jar that was supplied as the value of the -javaagent argument in the above example.

  • javaagent是一个标志,用于指定和启用代理来检测运行在JVM上的程序。Spring框架附带了这样一个代理,即InstrumentationSavingAgent,它打包在Spring -instrument.jar中,在上面的示例中,Spring -instrument.jar作为-javaagent参数的值提供。

The output from the execution of the Main program will look something like that below. (I have introduced a Thread.sleep(..) statement into the calculateEntitlement() implementation so that the profiler actually captures something other than 0 milliseconds - the 01234 milliseconds is not an overhead introduced by the AOP 😃 )

  • 主程序执行的输出如下所示。(我在calculateEntitlement()实现中引入了一个Thread.sleep(..)语句,这样剖析器实际上捕获的不是0毫秒——01234毫秒不是AOP引入的开销:))
Calculating entitlement

StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms     %     Task name
------ ----- ----------------------------
01234  100%  calculateEntitlement

Since this LTW is effected using full-blown AspectJ, we are not just limited to advising Spring beans; the following slight variation on the Main program will yield the same result.

  • 由于这个LTW是使用成熟的AspectJ实现的,所以我们不仅限于通知Spring bean;下面对主程序进行的细微变化将产生相同的结果。
package foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main {

    public static void main(String[] args) {

        new ClassPathXmlApplicationContext("beans.xml", Main.class);

        EntitlementCalculationService entitlementCalculationService =
            new StubEntitlementCalculationService();

        // the profiling aspect will be 'woven' around this method execution
        entitlementCalculationService.calculateEntitlement();
    }
}

Notice how in the above program we are simply bootstrapping the Spring container, and then creating a new instance of the StubEntitlementCalculationService totally outside the context of Spring… the profiling advice still gets woven in.

  • 注意,在上面的程序中,我们是如何简单地引导Spring容器,然后在完全脱离Spring上下文的情况下创建StubEntitlementCalculationService的新实例的……分析建议仍然会被编织进来。

The example admittedly is simplistic… however the basics of the LTW support in Spring have all been introduced in the above example, and the rest of this section will explain the 'why' behind each bit of configuration and usage in detail.

  • 不可否认,这个例子过于简单了……然而,Spring中LTW支持的基础在上面的例子中已经全部介绍了,本节的其余部分将详细解释每个配置和使用背后的“原因”。
The ProfilingAspect used in this example may be basic, but it is quite useful. It is a nice example of a development-time aspect that developers can use during development (of course), and then quite easily exclude from builds of the application being deployed into UAT or production.
  • 本例中使用的ProfilingAspect可能是基本的,但它非常有用。这是开发人员可以在开发期间(当然)使用的开发时间方面的一个很好的例子,然后可以很容易地从部署到UAT或生产中的应用程序的构建中排除。
Aspects(方面)

The aspects that you use in LTW have to be AspectJ aspects. They can be written in either the AspectJ language itself or you can write your aspects in the @AspectJ-style. It means that your aspects are then both valid AspectJ and Spring AOP aspects. Furthermore, the compiled aspect classes need to be available on the classpath.

  • 在LTW中使用的方面必须是AspectJ方面。它们可以用AspectJ语言本身编写,也可以用@AspectJ风格编写方面。这意味着您的方面是有效的AspectJ和Spring AOP方面。此外,编译后的方面类需要在类路径中可用。
'META-INF/aop.xml'

The AspectJ LTW infrastructure is configured using one or more META-INF/aop.xml files, that are on the Java classpath (either directly, or more typically in jar files).

  • AspectJ LTW基础设施是使用一个或多个元- inf /aop.xml文件配置的,这些文件位于Java类路径中(直接或更典型地位于jar文件中)。

The structure and contents of this file is detailed in the main AspectJ reference documentation, and the interested reader is referred to that resource. (I appreciate that this section is brief, but the aop.xml file is 100% AspectJ - there is no Spring-specific information or semantics that apply to it, and so there is no extra value that I can contribute either as a result), so rather than rehash the quite satisfactory section that the AspectJ developers wrote, I am just directing you there.)

  • 这个文件的结构和内容在主要的AspectJ参考文档中有详细说明,感兴趣的读者可以参考该资源。(我理解,本节是短暂的,但aop.xml文件是AspectJ 100%——没有spring特定信息或语义适用于它,所以没有额外的值,我可以贡献结果),而不是重复相当满意的部分,AspectJ开发人员写道,我只是来引导你。)
Required libraries (JARS)(所需的库(jar))

At a minimum you will need the following libraries to use the Spring Framework’s support for AspectJ LTW:

  • 要使用Spring框架对AspectJ LTW的支持,至少需要以下库:

  • spring-aop.jar (version 2.5 or later, plus all mandatory dependencies)

    • spring-aop.jar(版本2.5或更高,加上所有强制依赖项)
  • aspectjweaver.jar (version 1.6.8 or later)

    • jar(版本1.6.8或更高)

If you are using the Spring-provided agent to enable instrumentation, you will also need:

  • 如果您使用spring提供的代理来启用插装,您还需要:

  • spring-instrument.jar

Spring configuration(Spring配置)

The key component in Spring’s LTW support is the LoadTimeWeaver interface (in the org.springframework.instrument.classloading package), and the numerous implementations of it that ship with the Spring distribution. A LoadTimeWeaver is responsible for adding one or more java.lang.instrument.ClassFileTransformers to a ClassLoader at runtime, which opens the door to all manner of interesting applications, one of which happens to be the LTW of aspects.

  • Spring的LTW支持中的关键组件是LoadTimeWeaver接口(在org.springframework.instrument中)。类加载包),以及Spring发行版附带的众多类加载包实现。LoadTimeWeaver负责添加一个或多个java.lang.instrument。classfiletransformer在运行时转换为类加载器,这为各种有趣的应用程序打开了大门,其中之一就是aspect的LTW。
If you are unfamiliar with the idea of runtime class file transformation, you are encouraged to read the javadoc API documentation for the java.lang.instrument package before continuing. This is not a huge chore because there is - rather annoyingly - precious little documentation there… the key interfaces and classes will at least be laid out in front of you for reference as you read through this section.
  • 如果您不熟悉运行时类文件转换的概念,建议您阅读java.lang的javadoc API文档。继续之前的仪器包。这并不是一件很麻烦的事情,因为这里有(相当恼人的)宝贵的文档……当您阅读本节时,关键的接口和类至少会摆在您面前供您参考。

Configuring a LoadTimeWeaver for a particular ApplicationContext can be as easy as adding one line. (Please note that you almost certainly will need to be using an ApplicationContext as your Spring container - typically a BeanFactory will not be enough because the LTW support makes use of BeanFactoryPostProcessors.)

  • 为特定的ApplicationContext配置LoadTimeWeaver非常简单,只需添加一行代码即可。(请注意,您几乎肯定需要使用ApplicationContext作为Spring容器——通常只有一个BeanFactory是不够的,因为LTW支持使用了beanfactorypostprocessor。)

To enable the Spring Framework’s LTW support, you need to configure a LoadTimeWeaver, which typically is done using the @EnableLoadTimeWeaving annotation.

  • 要启用Spring框架的LTW支持,需要配置LoadTimeWeaver,这通常是使用@EnableLoadTimeWeaving注释完成的。
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {

}

Alternatively, if you prefer XML based configuration, use the <context:load-time-weaver/> element. Note that the element is defined in the context namespace.

  • 另外,如果您更喜欢基于XML的配置,可以使用context:load-time-weaver/元素。注意,元素是在上下文名称空间中定义的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:load-time-weaver/>

</beans>

The above configuration will define and register a number of LTW-specific infrastructure beans for you automatically, such as a LoadTimeWeaver and an AspectJWeavingEnabler. The default LoadTimeWeaver is the DefaultContextLoadTimeWeaver class, which attempts to decorate an automatically detected LoadTimeWeaver: the exact type of LoadTimeWeaver that will be 'automatically detected' is dependent upon your runtime environment (summarized in the following table).

  • 上述配置将为您自动定义和注册许多特定于lt的基础设施bean,例如LoadTimeWeaver和AspectJWeavingEnabler。默认的LoadTimeWeaver是DefaultContextLoadTimeWeaver类,它试图修饰一个自动检测到的LoadTimeWeaver:将被“自动检测到”的LoadTimeWeaver的确切类型取决于您的运行时环境(总结如下表)。
Runtime Environment LoadTimeWeaver implementation
Running in Oracle’s WebLogic WebLogicLoadTimeWeaver
Running in Oracle’s GlassFish GlassFishLoadTimeWeaver
Running in Apache Tomcat TomcatLoadTimeWeaver
Running in Red Hat’s JBoss AS or WildFly JBossLoadTimeWeaver
Running in IBM’s WebSphere WebSphereLoadTimeWeaver
JVM started with Spring InstrumentationSavingAgent (java -javaagent:path/to/spring-instrument.jar) InstrumentationLoadTimeWeaver
Fallback, expecting the underlying ClassLoader to follow common conventions (e.g. applicable to TomcatInstrumentableClassLoader and Resin)【后退,期望基础类加载器遵循通用约定(例如,适用于TomcatInstrumentableClassLoader和Resin)】 ReflectiveLoadTimeWeaver

Note that these are just the LoadTimeWeavers that are autodetected when using the DefaultContextLoadTimeWeaver: it is of course possible to specify exactly which LoadTimeWeaver implementation that you wish to use.

  • 注意,当使用DefaultContextLoadTimeWeaver时,这些只是自动检测到的loadtime编织器:当然,可以指定您希望使用的LoadTimeWeaver实现。

To specify a specific LoadTimeWeaver with Java configuration implement the LoadTimeWeavingConfigurer interface and override the getLoadTimeWeaver() method:

  • 要指定带有Java配置的特定LoadTimeWeaver,请实现LoadTimeWeavingConfigurer接口并覆盖getLoadTimeWeaver()方法:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {

    @Override
    public LoadTimeWeaver getLoadTimeWeaver() {
        return new ReflectiveLoadTimeWeaver();
    }
}

If you are using XML based configuration you can specify the fully-qualified classname as the value of the weaver-class attribute on the <context:load-time-weaver/> element:

  • 如果您使用基于XML的配置,您可以指定完全限定的类名作为context:load-time-weaver/元素上的weavero -class属性的值:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:load-time-weaver
            weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>

</beans>

The LoadTimeWeaver that is defined and registered by the configuration can be later retrieved from the Spring container using the well-known name loadTimeWeaver. Remember that the LoadTimeWeaver exists just as a mechanism for Spring’s LTW infrastructure to add one or more ClassFileTransformers. The actual ClassFileTransformer that does the LTW is the ClassPreProcessorAgentAdapter (from the org.aspectj.weaver.loadtime package) class. See the class-level javadocs of the ClassPreProcessorAgentAdapter class for further details, because the specifics of how the weaving is actually effected is beyond the scope of this section.

  • 由配置定义和注册的LoadTimeWeaver稍后可以使用众所周知的名称LoadTimeWeaver从Spring容器中检索。请记住,LoadTimeWeaver仅作为Spring s LTW基础设施的一种机制存在,用于添加一个或多个类文件转换器。执行LTW的实际ClassFileTransformer是ClassPreProcessorAgentAdapter(来自org.aspectj.w。loadtime包)类。更多细节,请参阅ClassPreProcessorAgentAdapter类的类级javadocs,因为关于编织如何实际影响的细节超出了本节的范围。

There is one final attribute of the configuration left to discuss: the aspectjWeaving attribute (or aspectj-weaving if you are using XML). This is a simple attribute that controls whether LTW is enabled or not; it is as simple as that. It accepts one of three possible values, summarized below, with the default value being autodetect if the attribute is not present.

  • 配置的最后一个属性需要讨论:aspectjWeaving属性(如果使用XML,则称为aspectj-织造)。这是一个简单的属性,用于控制LTW是否启用;事情就这么简单。它接受下面总结的三个可能值中的一个,如果属性不存在,默认值将被自动检测。
Annotation Value XML Value Explanation
ENABLED on AspectJ weaving is on, and aspects will be woven at load-time as appropriate.【v】
DISABLED off LTW is off… no aspect will be woven at load-time.【LTW是关闭的…没有方面将被编织在加载时。】
AUTODETECT autodetect If the Spring LTW infrastructure can find at least one META-INF/aop.xml file, then AspectJ weaving is on, else it is off. This is the default value.【如果Spring LTW基础设施可以找到至少一个META-INF/aop.xml文件,那么就打开AspectJ编织,否则就关闭。这是默认值。】
Environment-specific configuration(特定于环境的配置)

This last section contains any additional settings and configuration that you will need when using Spring’s LTW support in environments such as application servers and web containers.

  • 最后一节包含在应用程序服务器和web容器等环境中使用Spring的LTW支持时需要的任何其他设置和配置。
Tomcat(到处找女人鬼混的男子)

Historically, Apache Tomcat's default class loader did not support class transformation which is why Spring provides an enhanced implementation that addresses this need. Named TomcatInstrumentableClassLoader, the loader works on Tomcat 6.0 and above.

  • 历史上,Apache Tomcat的默认类加载器不支持类转换,这就是为什么Spring提供了一个增强的实现来满足这一需求。这个加载器名为TomcatInstrumentableClassLoader,可以在Tomcat 6.0及以上版本上工作。
Do not define TomcatInstrumentableClassLoader anymore on Tomcat 8.0 and higher. Instead, let Spring automatically use Tomcat’s new native InstrumentableClassLoader facility through the TomcatLoadTimeWeaver strategy.
  • 在Tomcat 8.0或更高版本上不要再定义TomcatInstrumentableClassLoader了。相反,让Spring通过TomcatLoadTimeWeaver策略自动使用Tomcat新的本机InstrumentableClassLoader工具。

If you still need to use TomcatInstrumentableClassLoader, it can be registered individually for each web application as follows:

  • 如果您仍然需要使用TomcatInstrumentableClassLoader,它可以为每个web应用程序分别注册,如下所示:

  • Copy org.springframework.instrument.tomcat.jar into \(CATALINA_HOME*/lib, where *\)CATALINA_HOME represents the root of the Tomcat installation)

    • org.springframework.instrument.tomcat.jar复制到\(CATALINA_HOME/lib中,其中\)CATALINA_HOME表示Tomcat安装的根目录)
  • Instruct Tomcat to use the custom class loader (instead of the default) by editing the web application context file:

    • 通过编辑web应用程序上下文文件,指示Tomcat使用自定义类加载器(而不是默认的):
<Context path="/myWebApp" docBase="/my/webApp/location">
    <Loader
        loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Apache Tomcat (6.0+) supports several context locations:

  • Apache Tomcat(6.0+)支持几个上下文位置:

  • server configuration file - $CATALINA_HOME/conf/server.xml

    • 服务器配置文件- $CATALINA_HOME/conf/server.xml
  • default context configuration - $CATALINA_HOME/conf/context.xml - that affects all deployed web applications

    • 默认上下文配置—$CATALINA_HOME/conf/context.xml—它影响所有已部署的web应用程序
  • per-web application configuration which can be deployed either on the server-side at $CATALINA_HOME/conf/[enginename]/[hostname]/[webapp]-context.xml or embedded inside the web-app archive at META-INF/context.xml

    • 每个web应用程序配置,可以部署在服务器端$CATALINA_HOME/conf/[enginename]/[hostname]/[webapp]-context.xml,也可以嵌入在web应用存档中的META-INF/context.xml

For efficiency, the embedded per-web-app configuration style is recommended because it will impact only applications that use the custom class loader and does not require any changes to the server configuration. See the Tomcat 6.0.x documentation for more details about available context locations.

  • 为了提高效率,建议采用嵌入的每个web应用程序配置样式,因为它只会影响使用自定义类加载器且不需要对服务器配置进行任何更改的应用程序。参见Tomcat 6.0。有关可用上下文位置的详细信息。

Alternatively, consider the use of the Spring-provided generic VM agent, to be specified in Tomcat’s launch script (see above). This will make instrumentation available to all deployed web applications, no matter what ClassLoader they happen to run on.

  • 或者,考虑使用spring提供的通用VM代理,在Tomcat的启动脚本中指定(参见上面)。这将使检测对所有已部署的web应用程序可用,而不管它们运行在什么类加载器上。
WebLogic, WebSphere, Resin, GlassFish, JBoss

Recent versions of WebLogic Server (version 10 and above), IBM WebSphere Application Server (version 7 and above), Resin (3.1 and above) and JBoss (6.x or above) provide a ClassLoader that is capable of local instrumentation. Spring’s native LTW leverages such ClassLoaders to enable AspectJ weaving. You can enable LTW by simply activating load-time weaving as described earlier. Specifically, you do not need to modify the launch script to add -javaagent:path/to/spring-instrument.jar.

  • WebLogic Server(版本10及以上)、IBM WebSphere Application Server(版本7及以上)、Resin(3.1及以上)和JBoss(6)的最新版本。)提供一个能够进行本地插装的类加载器。Spring的本机LTW利用这种类加载器来支持AspectJ编织。您可以像前面描述的那样通过激活加载时编织来启用LTW。具体来说,您不需要修改启动脚本来添加-javaagent:path/to/spring-instrument.jar。

Note that GlassFish instrumentation-capable ClassLoader is available only in its EAR environment. For GlassFish web applications, follow the Tomcat setup instructions as outlined above.

  • 请注意,只在其EAR环境中可用带有GlassFish工具功能的类加载器。对于GlassFish web应用程序,请遵循上面列出的Tomcat设置说明。

Note that on JBoss 6.x, the app server scanning needs to be disabled to prevent it from loading the classes before the application actually starts. A quick workaround is to add to your artifact a file named WEB-INF/jboss-scanning.xml with the following content:

  • 注意在JBoss 6上。x,应用服务器扫描需要被禁用,以防止它在应用程序实际开始之前加载类。一个快速的解决方法是在您的工件中添加一个名为WEB-INF/jbos -scan .xml的文件,其中包含以下内容:
<scanning xmlns="urn:jboss:scanning:1.0"/>
Generic Java applications(泛型Java应用程序)

When class instrumentation is required in environments that do not support or are not supported by the existing LoadTimeWeaver implementations, a JDK agent can be the only solution. For such cases, Spring provides InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general) VM agent, org.springframework.instrument-{version}.jar (previously named spring-agent.jar).

  • 当在不支持或现有LoadTimeWeaver实现不支持的环境中需要类插装时,JDK代理可能是唯一的解决方案。对于这种情况,Spring提供了InstrumentationLoadTimeWeaver,它需要一个特定于Spring(但非常通用)的VM代理,org.springframework.instrument-{version}.jar(以前称为Spring- agent.jar)。

To use it, you must start the virtual machine with the Spring agent, by supplying the following JVM options:

  • 要使用它,您必须使用Spring代理启动虚拟机,通过提供以下JVM选项:
-javaagent:/path/to/org.springframework.instrument-{version}.jar

Note that this requires modification of the VM launch script which may prevent you from using this in application server environments (depending on your operation policies). Additionally, the JDK agent will instrument the entire VM which can prove expensive.

  • 注意,这需要修改VM启动脚本,这可能会阻止您在应用服务器环境中使用它(取决于您的操作策略)。此外,JDK代理将检测整个VM,这可能是昂贵的。

For performance reasons, it is recommended to use this configuration only if your target environment (such as Jetty) does not have (or does not support) a dedicated LTW.

  • 出于性能原因,仅当您的目标环境(如Jetty)没有(或不支持)专用LTW时,建议使用此配置。

5.9. Further Resources(更多资源)

More information on AspectJ can be found on the AspectJ website.

  • 有关AspectJ的更多信息可以在AspectJ网站上找到。

The book Eclipse AspectJ by Adrian Colyer et. al. (Addison-Wesley, 2005) provides a comprehensive introduction and reference for the AspectJ language.

  • Adrian Colyer等人写的《Eclipse AspectJ》(Addison-Wesley, 2005)一书对AspectJ语言进行了全面的介绍和参考。

The book AspectJ in Action, Second Edition by Ramnivas Laddad (Manning, 2009) comes highly recommended; the focus of the book is on AspectJ, but a lot of general AOP themes are explored (in some depth).

  • 本书AspectJ在行动,第二版由Ramnivas Laddad(曼宁,2009)高度推荐;这本书的重点是AspectJ,但是也探讨了许多一般的AOP主题(有一定的深度)。

后续内容戳我^^

posted @ 2020-09-29 10:48  六爻呈乾  阅读(292)  评论(0编辑  收藏  举报