Live2D

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

6. Spring AOP APIs

6.1. Introduction(入门)

The previous chapter described the Spring’s support for AOP using @AspectJ and schema-based aspect definitions. In this chapter we discuss the lower-level Spring AOP APIs and the AOP support typically used in Spring 1.2 applications. For new applications, we recommend the use of the Spring 2.0 and later AOP support described in the previous chapter, but when working with existing applications, or when reading books and articles, you may come across Spring 1.2 style examples. Spring 5 remains backwards compatible with Spring 1.2 and everything described in this chapter is fully supported in Spring 5.

  • 上一章使用@AspectJ和基于模式的方面定义描述了Spring对AOP的支持。在本章中,我们将讨论底层Spring AOP api和通常在Spring 1.2应用程序中使用的AOP支持。对于新的应用程序,我们建议使用前一章中描述的Spring 2.0和以后的AOP支持,但是在使用现有的应用程序时,或者在阅读书籍和文章时,您可能会遇到Spring 1.2风格的例子。Spring 5仍然向后兼容Spring 1.2,并且在Spring 5中完全支持本章中描述的所有内容。

6.2. Pointcut API in Spring(Spring中的切入点API)

Let’s look at how Spring handles the crucial pointcut concept.

  • 让我们看看Spring是如何处理关键的切入点概念的。

6.2.1. Concepts(概念)

Spring’s pointcut model enables pointcut reuse independent of advice types. It’s possible to target different advice using the same pointcut.

  • Spring的切入点模型支持独立于通知类型的切入点重用。可以使用相同的切入点针对不同的通知。hgj `

The org.springframework.aop.Pointcut interface is the central interface, used to target advices to particular classes and methods. The complete interface is shown below:

  • org.springframework.aop。切入点接口是中心接口,用于针对特定类和方法的通知。完整界面如下图所示:
public interface Pointcut {

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();

}

Splitting the Pointcut interface into two parts allows reuse of class and method matching parts, and fine-grained composition operations (such as performing a "union" with another method matcher).

  • 将切入点接口分割为两个部分允许重用类和方法匹配部分,以及细粒度的组合操作(比如与另一个方法匹配器执行“联合”)。

The ClassFilter interface is used to restrict the pointcut to a given set of target classes. If the matches() method always returns true, all target classes will be matched:

  • ClassFilter接口用于将切入点限制为给定的一组目标类。如果matches()方法总是返回true,那么所有的目标类都会被匹配:
public interface ClassFilter {

    boolean matches(Class clazz);
}

The MethodMatcher interface is normally more important. The complete interface is shown below:

  • MethodMatcher接口通常更重要。完整界面如下图所示:
public interface MethodMatcher {

    boolean matches(Method m, Class targetClass);

    boolean isRuntime();

    boolean matches(Method m, Class targetClass, Object[] args);
}

The matches(Method, Class) method is used to test whether this pointcut will ever match a given method on a target class. This evaluation can be performed when an AOP proxy is created, to avoid the need for a test on every method invocation. If the 2-argument matches method returns true for a given method, and the isRuntime() method for the MethodMatcher returns true, the 3-argument matches method will be invoked on every method invocation. This enables a pointcut to look at the arguments passed to the method invocation immediately before the target advice is to execute.

  • matches(方法,类)方法用于测试这个切入点是否会匹配目标类上的给定方法。这种评估可以在创建AOP代理时执行,以避免在每个方法调用上都需要测试。如果2个参数的matches方法对于给定的方法返回true,并且MethodMatcher的isRuntime()方法返回true,那么在每次方法调用时都会调用3个参数的matches方法。这使得切入点能够在目标通知即将执行之前查看传递给方法调用的参数。

Most MethodMatchers are static, meaning that their isRuntime() method returns false. In this case, the 3-argument matches method will never be invoked.

  • 大多数MethodMatchers是静态的,这意味着它们的isRuntime()方法返回false。在这种情况下,3个参数的matches方法将永远不会被调用
If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcut evaluation when an AOP proxy is created.
  • If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcut evaluation when an AOP proxy is created.

6.2.2. Operations on pointcuts(操作切入点)

Spring supports operations on pointcuts: notably, union and intersection.

  • Spring支持切入点上的操作:特别是联合和交集。

    • Union意味着两个切入点都匹配的方法。
  • Union means the methods that either pointcut matches.

    • 交集意味着两个切入点都匹配的方法。
  • Intersection means the methods that both pointcuts match.

    • 联合通常更有用。
  • Union is usually more useful.

  • Pointcuts can be composed using the static methods in the org.springframework.aop.support.Pointcuts class, or using the ComposablePointcut class in the same package. However, using AspectJ pointcut expressions is usually a simpler approach.

    • 切入点可以使用org.springframework.aop.support中的静态方法组合。Pointcuts类,或者使用同一个包中的ComposablePointcut类。但是,使用AspectJ切入点表达式通常是一种更简单的方法。

6.2.3. AspectJ expression pointcuts(AspectJ切入点表达式)

Since 2.0, the most important type of pointcut used by Spring is org.springframework.aop.aspectj.AspectJExpressionPointcut. This is a pointcut that uses an AspectJ supplied library to parse an AspectJ pointcut expression string.

  • 从2.0开始,Spring使用的最重要的切入点类型是org.springframework.aop.aspectj.AspectJExpressionPointcut。这是一个切入点,它使用AspectJ提供的库来解析AspectJ切入点表达式字符串。

See the previous chapter for a discussion of supported AspectJ pointcut primitives.

  • 有关受支持的AspectJ切入点原语的讨论,请参阅前一章。

6.2.4. Convenience pointcut implementations(方便的切入点实现)

Spring provides several convenient pointcut implementations. Some can be used out of the box; others are intended to be subclassed in application-specific pointcuts.

  • Spring提供了几个方便的切入点实现。有些可以开箱即用;其他的则打算在特定于应用程序的切入点中被子类化。
Static pointcuts(静态的切入点)

Static pointcuts are based on method and target class, and cannot take into account the method’s arguments. Static pointcuts are sufficient - and best - for most usages. It’s possible for Spring to evaluate a static pointcut only once, when a method is first invoked: after that, there is no need to evaluate the pointcut again with each method invocation.

  • 静态切入点基于方法和目标类,不能考虑方法的参数。对于大多数用法来说,静态切入点已经足够而且是最好的了。当方法首次被调用时,Spring可能只对静态切入点计算一次:在此之后,就不需要在每次方法调用时再次计算切入点。

Let’s consider some static pointcut implementations included with Spring.

  • 让我们考虑一些包含在Spring中的静态切入点实现。
Regular expression pointcuts(正则表达式的切入点)

One obvious way to specify static pointcuts is regular expressions. Several AOP frameworks besides Spring make this possible.

  • 指定静态切入点的一种明显方法是正则表达式。除了Spring之外,还有几个AOP框架使这成为可能。org.springframework.aop.support。JdkRegexpMethodPointcut是一个通用的正则表达式切入点,使用了JDK中的正则表达式支持。org.springframework.aop.support.JdkRegexpMethodPointcut is a generic regular expression pointcut, using the regular expression support in the JDK.

Using the JdkRegexpMethodPointcut class, you can provide a list of pattern Strings. If any of these is a match, the pointcut will evaluate to true. (So the result is effectively the union of these pointcuts.)

  • 使用JdkRegexpMethodPointcut类,您可以提供一个模式字符串列表。如果其中任何一个匹配,切入点的值将为真。(因此结果实际上是这些切入点的联合。)

The usage is shown below:(使用方法如下:)

<bean id="settersAndAbsquatulatePointcut"
        class="org.springframework.aop.support.JdkRegexpMethodPointcut">
    <property name="patterns">
        <list>
            <value>.*set.*</value>
            <value>.*absquatulate</value>
        </list>
    </property>
</bean>

Spring provides a convenience class, RegexpMethodPointcutAdvisor, that allows us to also reference an Advice (remember that an Advice can be an interceptor, before advice, throws advice etc.). Behind the scenes, Spring will use a JdkRegexpMethodPointcut. Using RegexpMethodPointcutAdvisor simplifies wiring, as the one bean encapsulates both pointcut and advice, as shown below:

  • Spring提供了一个方便的类RegexpMethodPointcutAdvisor,它允许我们也引用一个通知(记住,通知可以是一个拦截器,在通知之前,抛出通知等等)。在幕后,Spring将使用JdkRegexpMethodPointcut。使用RegexpMethodPointcutAdvisor简化了连接,因为一个bean同时封装了切入点和通知,如下所示:
<bean id="settersAndAbsquatulateAdvisor"
        class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice">
        <ref bean="beanNameOfAopAllianceInterceptor"/>
    </property>
    <property name="patterns">
        <list>
            <value>.*set.*</value>
            <value>.*absquatulate</value>
        </list>
    </property>
</bean>

RegexpMethodPointcutAdvisor can be used with any Advice type.

  • RegexpMethodPointcutAdvisor可以与任何通知类型一起使用。
Attribute-driven pointcuts(Attribute-driven切入点)

An important type of static pointcut is a metadata-driven pointcut. This uses the values of metadata attributes: typically, source-level metadata.

  • 静态切入点的一种重要类型是元数据驱动的切入点。这使用元数据属性的值:通常是源级元数据。
Dynamic pointcuts(动态的切入点)

Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account method arguments, as well as static information. This means that they must be evaluated with every method invocation; the result cannot be cached, as arguments will vary.

  • 动态切入点比静态切入点的计算成本更高。它们考虑了方法参数和静态信息。这意味着它们必须在每次方法调用时进行计算;不能缓存结果,因为参数会发生变化。

The main example is the control flow pointcut.

  • 主要的例子是控制流切入点。
Control flow pointcuts(控制流的切入点)

Spring control flow pointcuts are conceptually similar to AspectJ cflow pointcuts, although less powerful. (There is currently no way to specify that a pointcut executes below a join point matched by another pointcut.) A control flow pointcut matches the current call stack. For example, it might fire if the join point was invoked by a method in the com.mycompany.web package, or by the SomeCaller class. Control flow pointcuts are specified using the org.springframework.aop.support.ControlFlowPointcut class.

  • Spring控制流切入点在概念上类似于AspectJ cflow切入点,尽管功能不那么强大。(目前还没有办法指定一个切入点在与另一个切入点匹配的连接点下面执行。)控制流切入点与当前调用堆栈匹配。例如,如果连接点是由com.mycompany中的方法调用的,则它可能触发。web包,或由SomeCaller类。控制流切入点是使用org.springframework.aop.support指定的。ControlFlowPointcut类。
Control flow pointcuts are significantly more expensive to evaluate at runtime than even other dynamic pointcuts. In Java 1.4, the cost is about 5 times that of other dynamic pointcuts.
  • 控制流切入点在运行时计算的开销甚至比其他动态切入点都要大得多。在Java 1.4中,其代价大约是其他动态切入点的5倍。

6.2.5. Pointcut superclasses(切入点超类)

Spring provides useful pointcut superclasses to help you to implement your own pointcuts.

  • Spring提供了有用的切入点超类来帮助您实现自己的切入点。

Because static pointcuts are most useful, you’ll probably subclass StaticMethodMatcherPointcut, as shown below. This requires implementing just one abstract method (although it’s possible to override other methods to customize behavior):

  • 因为静态切入点最有用,所以您可能会子类化StaticMethodMatcherPointcut,如下所示。这只需要实现一个抽象方法(尽管有可能覆盖其他方法来定制行为):
class TestStaticPointcut extends StaticMethodMatcherPointcut {

    public boolean matches(Method m, Class targetClass) {
        // return true if custom criteria match
    }
}

There are also superclasses for dynamic pointcuts.

  • 还有用于动态切入点的超类。

You can use custom pointcuts with any advice type in Spring 1.0 RC2 and above.

  • 在Spring 1.0 RC2及以上版本中,您可以对任何通知类型使用定制切入点。

6.2.6. Custom pointcuts(自定义切入点)

Because pointcuts in Spring AOP are Java classes, rather than language features (as in AspectJ) it’s possible to declare custom pointcuts, whether static or dynamic. Custom pointcuts in Spring can be arbitrarily complex. However, using the AspectJ pointcut expression language is recommended if possible.

  • 因为Spring AOP中的切入点是Java类,而不是语言特性(就像AspectJ中那样),所以可以声明定制的切入点,无论是静态的还是动态的。Spring中的自定义切入点可以是任意复杂的。但是,如果可能的话,建议使用AspectJ切入点表达式语言。
Later versions of Spring may offer support for "semantic pointcuts" as offered by JAC: for example, "all methods that change instance variables in the target object."
  • Spring的后续版本可能会提供对JAC所提供的“语义切入点”的支持:例如,“所有改变目标对象中的实例变量的方法”。

6.3. Advice API in Spring(Spring中的通知API)

Let’s now look at how Spring AOP handles advice.

  • 现在让我们看看Spring AOP是如何处理通知的。

6.3.1. Advice lifecycles(建议的生命周期)

Each advice is a Spring bean. An advice instance can be shared across all advised objects, or unique to each advised object. This corresponds to per-class or per-instance advice.

  • 每个建议都是一个春天的bean。一个通知实例可以在所有被通知的对象之间共享,或者对每个被通知的对象是唯一的。这对应于每个类或每个实例的通知。

Per-class advice is used most often. It is appropriate for generic advice such as transaction advisors. These do not depend on the state of the proxied object or add new state; they merely act on the method and arguments.

  • 最常用的是每节课的建议。它适用于一般的建议,比如事务顾问。这些不依赖于代理对象的状态或添加新状态;它们仅仅作用于方法和参数。

Per-instance advice is appropriate for introductions, to support mixins. In this case, the advice adds state to the proxied object.

  • 每个实例建议适合于介绍,以支持mixin。在这种情况下,通知将状态添加到代理对象。

It’s possible to use a mix of shared and per-instance advice in the same AOP proxy.

  • 可以在同一个AOP代理中混合使用共享通知和每个实例通知。

6.3.2. Advice types in Spring(Spring中的通知类型)

Spring provides several advice types out of the box, and is extensible to support arbitrary advice types. Let us look at the basic concepts and standard advice types.

  • Spring提供了几种开箱即用的通知类型,并且可扩展以支持任意的通知类型。让我们看看基本概念和标准通知类型。
Interception around advice(拦截around通知)

The most fundamental advice type in Spring is interception around advice.

  • Spring中最基本的通知类型是围绕通知的拦截。

Spring is compliant with the AOP Alliance interface for around advice using method interception. MethodInterceptors implementing around advice should implement the following interface:

  • Spring遵循AOP Alliance接口,使用方法拦截来规避通知。实现around通知的MethodInterceptors应该实现以下接口:
public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;
}

The MethodInvocation argument to the invoke() method exposes the method being invoked; the target join point; the AOP proxy; and the arguments to the method. The invoke() method should return the invocation’s result: the return value of the join point.

  • invoke()方法的MethodInvocation参数公开了被调用的方法;目标连接点;AOP代理;以及方法的参数。invoke()方法应该返回调用的结果:连接点的返回值。

A simple MethodInterceptor implementation looks as follows:

  • 一个简单的MethodInterceptor实现如下:
public class DebugInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before: invocation=[" + invocation + "]");
        Object rval = invocation.proceed();
        System.out.println("Invocation returned");
        return rval;
    }
}

Note the call to the MethodInvocation’s proceed() method. This proceeds down the interceptor chain towards the join point. Most interceptors will invoke this method, and return its return value. However, a MethodInterceptor, like any around advice, can return a different value or throw an exception rather than invoke the proceed method. However, you don’t want to do this without good reason!

  • 注意对方法调用的proceed()方法的调用。这沿着拦截器链一直到连接点。大多数拦截器都会调用这个方法,并返回它的返回值。然而,MethodInterceptor,像任何around通知一样,可以返回不同的值或抛出异常,而不是调用proceed方法。但是,如果没有充分的理由,您不会希望这样做!
MethodInterceptors offer interoperability with other AOP Alliance-compliant AOP implementations. The other advice types discussed in the remainder of this section implement common AOP concepts, but in a Spring-specific way. While there is an advantage in using the most specific advice type, stick with MethodInterceptor around advice if you are likely to want to run the aspect in another AOP framework. Note that pointcuts are not currently interoperable between frameworks, and the AOP Alliance does not currently define pointcut interfaces.
  • 方法拦截器提供了与其他符合AOP联盟的AOP实现的互操作性。在本节剩下的部分中讨论的其他通知类型实现了常见的AOP概念,但是以一种特定于spring的方式。虽然使用最特定的通知类型有一个优势,但是如果您可能想要在另一个AOP框架中运行方面,请围绕通知使用MethodInterceptor。注意,切入点目前不能在框架之间互操作,而且AOP联盟目前没有定义切入点接口。
Before advice(前置增强)

A simpler advice type is a before advice. This does not need a MethodInvocation object, since it will only be called before entering the method.

  • 一个更简单的通知类型是before通知。这不需要MethodInvocation对象,因为它只会在进入方法之前被调用。

The main advantage of a before advice is that there is no need to invoke the proceed() method, and therefore no possibility of inadvertently failing to proceed down the interceptor chain.

  • before通知的主要优点是不需要调用proceed()方法,因此不会无意中导致沿着拦截器链继续下去。

The MethodBeforeAdvice interface is shown below. (Spring’s API design would allow for field before advice, although the usual objects apply to field interception and it’s unlikely that Spring will ever implement it).

  • MethodBeforeAdvice接口如下所示。(Spring的API设计允许字段先于通知,尽管通常的对象适用于字段拦截,而且Spring不太可能实现它)。
public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;
}

Note the return type is void. Before advice can insert custom behavior before the join point executes, but cannot change the return value. If a before advice throws an exception, this will abort further execution of the interceptor chain. The exception will propagate back up the interceptor chain. If it is unchecked, or on the signature of the invoked method, it will be passed directly to the client; otherwise it will be wrapped in an unchecked exception by the AOP proxy.

  • 注意返回类型为void。通知可以在连接点执行之前插入自定义行为,但不能更改返回值。如果before通知抛出异常,这将中止拦截器链的进一步执行。异常将向上传播回拦截器链。如果未选中它,或者在被调用方法的签名上,它将直接传递给客户端;否则,它将被AOP代理包装在一个未检查的异常中。

An example of a before advice in Spring, which counts all method invocations:

  • 下面是Spring中的before通知的一个例子,它统计了所有的方法调用:
public class CountingBeforeAdvice implements MethodBeforeAdvice {

    private int count;

    public void before(Method m, Object[] args, Object target) throws Throwable {
        ++count;
    }

    public int getCount() {
        return count;
    }
}
Before advice can be used with any pointcut.(在通知可以用于任何切入点之前。)
Throws advice(抛出通知)

Throws advice is invoked after the return of the join point if the join point threw an exception. Spring offers typed throws advice. Note that this means that the org.springframework.aop.ThrowsAdvice interface does not contain any methods: It is a tag interface identifying that the given object implements one or more typed throws advice methods. These should be in the form of:

  • 如果连接点抛出异常,则在连接点返回后调用抛出通知。Spring提供了输入的抛出建议。请注意,这意味着org.springframework.aop。ThrowsAdvice接口不包含任何方法:它是一个标记接口,标识给定对象实现一个或多个类型的抛出通知方法。这些措施的形式应是:
afterThrowing([Method, args, target], subclassOfThrowable)

Only the last argument is required. The method signatures may have either one or four arguments, depending on whether the advice method is interested in the method and arguments. The following classes are examples of throws advice.

  • 只有最后一个参数是必需的。方法签名可以有一个或四个参数,这取决于通知方法是否对方法和参数感兴趣。下面的类是抛出建议的示例。

The advice below is invoked if a RemoteException is thrown (including subclasses):

  • 如果抛出一个RemoteException(包括子类),将调用下面的通知:
public class RemoteThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }
}

The following advice is invoked if a ServletException is thrown. Unlike the above advice, it declares 4 arguments, so that it has access to the invoked method, method arguments and target object:

  • 如果抛出ServletException,将调用以下通知。与上面的通知不同,它声明了4个参数,这样它就可以访问被调用的方法、方法参数和目标对象:
public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something with all arguments
    }
}

The final example illustrates how these two methods could be used in a single class, which handles both RemoteException and ServletException. Any number of throws advice methods can be combined in a single class.

  • 最后一个示例演示了如何在一个类中使用这两个方法,该类同时处理RemoteException和ServletException。可以在一个类中组合任意数量的抛出通知方法。
public static class CombinedThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(RemoteException ex) throws Throwable {
        // Do something with remote exception
    }

    public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
        // Do something with all arguments
    }
}
If a throws-advice method throws an exception itself, it will override the original exception (i.e. change the exception thrown to the user). The overriding exception will typically be a RuntimeException; this is compatible with any method signature. However, if a throws-advice method throws a checked exception, it will have to match the declared exceptions of the target method and is hence to some degree coupled to specific target method signatures. Do not throw an undeclared checked exception that is incompatible with the target method’s signature!
Throws advice can be used with any pointcut.
  • 如果throws-advice方法本身抛出异常,它将覆盖原始异常(即更改抛出给用户的异常)。覆盖的异常通常是一个RuntimeException;这与任何方法签名都兼容。但是,如果throw -advice方法抛出检查异常,它必须匹配目标方法声明的异常,因此在某种程度上与特定的目标方法签名耦合。不要抛出一个未声明的检查异常,与目标方法的签名不兼容

  • 抛出通知可以用于任何切入点。

After Returning advice(后置增强)

An after returning advice in Spring must implement the org.springframework.aop.AfterReturningAdvice interface, shown below:

  • 在Spring中返回通知后必须实现org.springframework.aop。售后咨询界面,如下图所示:
public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable;
}

An after returning advice has access to the return value (which it cannot modify), invoked method, methods arguments and target.

  • after return通知可以访问返回值(它不能修改它)、被调用的方法、方法参数和目标。

The following after returning advice counts all successful method invocations that have not thrown exceptions:

  • 下面在返回通知后计算所有没有抛出异常的成功方法调用:
public class CountingAfterReturningAdvice implements AfterReturningAdvice {

    private int count;

    public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable {
        ++count;
    }

    public int getCount() {
        return count;
    }
}

This advice doesn’t change the execution path. If it throws an exception, this will be thrown up the interceptor chain instead of the return value.

  • 这个通知不会改变执行路径。如果它抛出异常,它将被抛出到拦截器链,而不是返回值。
After returning advice can be used with any pointcut.
  • 返回后,通知可以用于任何切入点。
Introduction(入门) advice

Spring treats introduction advice as a special kind of interception advice.

  • Spring将引入通知视为一种特殊的侦听通知。

Introduction requires an IntroductionAdvisor, and an IntroductionInterceptor, implementing the following interface:

  • 引入需要一个介绍顾问,和一个介绍拦截器,实现以下接口:
public interface IntroductionInterceptor extends MethodInterceptor {

    boolean implementsInterface(Class intf);
}

The invoke() method inherited from the AOP Alliance MethodInterceptor interface must implement the introduction: that is, if the invoked method is on an introduced interface, the introduction interceptor is responsible for handling the method call - it cannot invoke proceed().

  • 从AOP Alliance MethodInterceptor接口继承的invoke()方法必须实现引入:也就是说,如果被调用的方法在引入的接口上,引入拦截器负责处理方法调用——它不能调用proceed()。

Introduction advice cannot be used with any pointcut, as it applies only at class, rather than method, level. You can only use introduction advice with the IntroductionAdvisor, which has the following methods:

  • 引入通知不能与任何切入点一起使用,因为它只应用于类级别,而不是方法级别。你只能在介绍顾问那里使用介绍建议,它有以下方法:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

    ClassFilter getClassFilter();

    void validateInterfaces() throws IllegalArgumentException;
}

public interface IntroductionInfo {

    Class[] getInterfaces();
}

There is no MethodMatcher, and hence no Pointcut, associated with introduction advice. Only class filtering is logical.

  • 没有MethodMatcher,因此也没有切入点,与引入通知相关联。只有类过滤是合乎逻辑的。**

The getInterfaces() method returns the interfaces introduced by this advisor.

  • getInterfaces()方法返回这个advisor引入的接口。

The validateInterfaces() method is used internally to see whether or not the introduced interfaces can be implemented by the configured IntroductionInterceptor.

  • 在内部使用validateInterfaces()方法来查看引入的接口是否可以由已配置的导入拦截器实现。

Let’s look at a simple example from the Spring test suite. Let’s suppose we want to introduce the following interface to one or more objects:

  • 让我们来看一个来自Spring测试套件的简单示例。假设我们想要向一个或多个对象引入以下接口:
public interface Lockable {
    void lock();
    void unlock();
    boolean locked();
}

This illustrates a mixin. We want to be able to cast advised objects to Lockable, whatever their type, and call lock and unlock methods. If we call the lock() method, we want all setter methods to throw a LockedException. Thus we can add an aspect that provides the ability to make objects immutable, without them having any knowledge of it: a good example of AOP.

  • 这说明了混合。我们希望能够将建议的对象转换为可锁定的,无论它们的类型是什么,并调用lock和unlock方法。如果我们调用lock()方法,我们希望所有的setter方法都抛出一个LockedException。因此,我们可以添加一个方面,它提供了使对象不可变的能力,而对象不需要了解它:AOP的一个好例子。

Firstly, we’ll need an IntroductionInterceptor that does the heavy lifting. In this case, we extend the org.springframework.aop.support.DelegatingIntroductionInterceptor convenience class. We could implement IntroductionInterceptor directly, but using DelegatingIntroductionInterceptor is best for most cases.

  • 首先,我们将需要一个执行繁重任务的前言拦截器。在本例中,我们扩展了org.springframework.aop.support。DelegatingIntroductionInterceptor便利类。我们可以直接实现前言拦截器,但是使用delegating前言拦截器在大多数情况下是最好的。

The DelegatingIntroductionInterceptor is designed to delegate an introduction to an actual implementation of the introduced interface(s), concealing the use of interception to do so. The delegate can be set to any object using a constructor argument; the default delegate (when the no-arg constructor is used) is this. Thus in the example below, the delegate is the LockMixin subclass of DelegatingIntroductionInterceptor. Given a delegate (by default itself), a DelegatingIntroductionInterceptor instance looks for all interfaces implemented by the delegate (other than IntroductionInterceptor), and will support introductions against any of them. It’s possible for subclasses such as LockMixin to call the suppressInterface(Class intf) method to suppress interfaces that should not be exposed. However, no matter how many interfaces an IntroductionInterceptor is prepared to support, the IntroductionAdvisor used will control which interfaces are actually exposed. An introduced interface will conceal any implementation of the same interface by the target.

  • delegating前言拦截器的设计目的是将引入委托给所引入接口的实际实现,而隐藏了侦听的使用。可以使用构造函数参数将委托设置为任何对象;默认的委托(当使用无参数构造函数时)是这样的。因此在下面的示例中,委托是delegating前言拦截器的LockMixin子类。给定一个委托(默认情况下本身),delegating前言拦截器实例将查找由该委托实现的所有接口(而不是前言拦截器),并将支持针对其中任何接口的引入。LockMixin等子类可以调用suppressInterface(intf类)方法来抑制不应该公开的接口。然而,不管准备支持多少个接口,使用的前言拦截器都将控制实际公开的接口。引入的接口将隐藏目标对同一接口的任何实现。

Thus LockMixin extends DelegatingIntroductionInterceptor and implements Lockable itself. The superclass automatically picks up that Lockable can be supported for introduction, so we don’t need to specify that. We could introduce any number of interfaces in this way.

  • 因此,LockMixin扩展了delegating前言拦截器并实现了Lockable本身。超类自动获取可锁定的可支持引入,所以我们不需要指定它。我们可以通过这种方式引入任意数量的接口。

Note the use of the locked instance variable. This effectively adds additional state to that held in the target object.

  • 注意锁定实例变量的使用。这有效地向目标对象中持有的状态添加了额外的状态。
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {

    private boolean locked;

    public void lock() {
        this.locked = true;
    }

    public void unlock() {
        this.locked = false;
    }

    public boolean locked() {
        return this.locked;
    }

    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
            throw new LockedException();
        }
        return super.invoke(invocation);
    }

}

Often it isn’t necessary to override the invoke() method: the DelegatingIntroductionInterceptor implementation - which calls the delegate method if the method is introduced, otherwise proceeds towards the join point - is usually sufficient. In the present case, we need to add a check: no setter method can be invoked if in locked mode.

  • 通常没有必要重写invoke()方法:delegatingisttioninterceptor实现—如果引入了该方法,它将调用该委托方法,否则将继续前往连接点—通常就足够了。在本例中,我们需要添加一个check:如果处于锁定模式,则不能调用setter方法。

The introduction advisor required is simple. All it needs to do is hold a distinct LockMixin instance, and specify the introduced interfaces - in this case, just Lockable. A more complex example might take a reference to the introduction interceptor (which would be defined as a prototype): in this case, there’s no configuration relevant for a LockMixin, so we simply create it using new.

  • 所需的介绍顾问非常简单。它所需要做的只是持有一个不同的LockMixin实例,并指定引入的接口—在本例中,仅为可锁定的。更复杂的示例可能引用引入拦截器(它将被定义为原型):在本例中,没有与LockMixin相关的配置,因此我们只需使用new创建它。
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

    public LockMixinAdvisor() {
        super(new LockMixin(), Lockable.class);
    }
}

We can apply this advisor very simply: it requires no configuration. (However, it is necessary: It’s impossible to use an IntroductionInterceptor without an IntroductionAdvisor.) As usual with introductions, the advisor must be per-instance, as it is stateful. We need a different instance of LockMixinAdvisor, and hence LockMixin, for each advised object. The advisor comprises part of the advised object’s state.

  • 我们可以非常简单地应用这个advisor:它不需要配置。(然而,这是必要的:如果没有一个介绍性顾问,就不可能使用一个介绍性拦截器。)和介绍一样,advisor必须是每个实例的,因为它是有状态的。对于每个被建议的对象,我们需要一个LockMixinAdvisor的不同实例,因此也需要LockMixin的不同实例。advisor包含被建议对象的部分状态。

We can apply this advisor programmatically, using the Advised.addAdvisor() method, or (the recommended way) in XML configuration, like any other advisor. All proxy creation choices discussed below, including "auto proxy creators," correctly handle introductions and stateful mixins.

  • 我们可以使用advise . addadvisor()方法,或在XML配置中(推荐的方法)以编程方式应用这个advisor,就像其他advisor一样。下面讨论的所有代理创建选择,包括“自动代理创建器”,都正确地处理引入和有状态混合。

6.4. Advisor API in Spring(Spring中的Advisor API)

In Spring, an Advisor is an aspect that contains just a single advice object associated with a pointcut expression.

  • 在Spring中,Advisor是只包含一个与切入点表达式关联的通知对象的方面。

Apart from the special case of introductions, any advisor can be used with any advice. org.springframework.aop.support.DefaultPointcutAdvisor is the most commonly used advisor class. For example, it can be used with a MethodInterceptor, BeforeAdvice or ThrowsAdvice.

  • 除了介绍的特殊情况外,任何顾问都可以与任何建议一起使用。org.springframework.aop.support。DefaultPointcutAdvisor是最常用的advisor类。例如,它可以与MethodInterceptor、BeforeAdvice或ThrowsAdvice一起使用。

It is possible to mix advisor and advice types in Spring in the same AOP proxy. For example, you could use a interception around advice, throws advice and before advice in one proxy configuration: Spring will automatically create the necessary interceptor chain.

  • 可以在Spring中将advisor和advice类型混合在同一个AOP代理中。例如,您可以在一个代理配置中使用对通知的拦截、抛出通知和在通知之前的拦截:Spring将自动创建必要的拦截器链。

6.5. Using the ProxyFactoryBean to create AOP proxies(使用ProxyFactoryBean创建AOP代理)

If you’re using the Spring IoC container (an ApplicationContext or BeanFactory) for your business objects - and you should be! - you will want to use one of Spring’s AOP FactoryBeans. (Remember that a factory bean introduces a layer of indirection, enabling it to create objects of a different type.)

  • 如果您正在为您的业务对象使用Spring IoC容器(一个ApplicationContext或BeanFactory)—您应该这样做!-您将需要使用Spring的AOP factorybean之一。(请记住,工厂bean引入了一个间接层,使它能够创建不同类型的对象。)
The Spring AOP support also uses factory beans under the covers.
  • Spring AOP支持也在幕后使用工厂bean。

The basic way to create an AOP proxy in Spring is to use the org.springframework.aop.framework.ProxyFactoryBean. This gives complete control over the pointcuts and advice that will apply, and their ordering. However, there are simpler options that are preferable if you don’t need such control.

  • 在Spring中创建AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean。这提供了对将要应用的切入点和通知及其顺序的完全控制。但是,如果您不需要这样的控制,那么有一些更简单的选项是可取的。

6.5.1. Basics(基础)

The ProxyFactoryBean, like other Spring FactoryBean implementations, introduces a level of indirection. If you define a ProxyFactoryBean with name foo, what objects referencing foo see is not the ProxyFactoryBean instance itself, but an object created by the ProxyFactoryBean's implementation of the getObject() method. This method will create an AOP proxy wrapping a target object.

  • ProxyFactoryBean与其他Spring FactoryBean实现一样,引入了一个间接级别。如果您定义了一个名为foo的ProxyFactoryBean,引用foo的对象看到的不是ProxyFactoryBean实例本身,而是由ProxyFactoryBean的getObject()方法的实现创建的对象。这个方法将创建一个封装目标对象的AOP代理。

One of the most important benefits of using a ProxyFactoryBean or another IoC-aware class to create AOP proxies, is that it means that advices and pointcuts can also be managed by IoC. This is a powerful feature, enabling certain approaches that are hard to achieve with other AOP frameworks. For example, an advice may itself reference application objects (besides the target, which should be available in any AOP framework), benefiting from all the pluggability provided by Dependency Injection.

  • 使用ProxyFactoryBean或另一个支持iocaware的类来创建AOP代理的最重要的好处之一是,它意味着通知和切入点也可以由IoC管理。这是一个强大的特性,它支持某些难以用其他AOP框架实现的方法。例如,通知本身可以引用应用程序对象(除了目标对象,它应该在任何AOP框架中都可用),这得益于依赖注入提供的所有可插入性。

6.5.2. JavaBean properties(JavaBean属性)

In common with most FactoryBean implementations provided with Spring, the ProxyFactoryBean class is itself a JavaBean. Its properties are used to:

  • 与Spring提供的大多数FactoryBean实现一样,ProxyFactoryBean类本身也是一个JavaBean。其属性用于:

  • Specify the target you want to proxy.

    • 指定要代理的目标。
  • Specify whether to use CGLIB (see below and also JDK- and CGLIB-based proxies).

    • 指定是否使用CGLIB(请参阅下面以及基于JDK和CGLIB的代理)。

Some key properties are inherited from org.springframework.aop.framework.ProxyConfig (the superclass for all AOP proxy factories in Spring). These key properties include:

  • 一些关键属性继承自org.springframework.aop.framework.ProxyConfig (Spring中所有AOP代理工厂的超类)。这些关键属性包括:

  • proxyTargetClass: true if the target class is to be proxied, rather than the target class' interfaces. If this property value is set to true, then CGLIB proxies will be created (but see also JDK- and CGLIB-based proxies).

    • proxyTargetClass:如果要代理的是目标类而不是目标类的接口,则为真。如果该属性值设置为true,那么将创建CGLIB代理(请参阅基于JDK和基于CGLIB的代理)。
  • optimize: controls whether or not aggressive optimizations are applied to proxies created via CGLIB. One should not blithely use this setting unless one fully understands how the relevant AOP proxy handles optimization. This is currently used only for CGLIB proxies; it has no effect with JDK dynamic proxies.

    • 优化:控制主动优化是否应用于通过CGLIB创建的代理。除非完全理解相关的AOP代理如何处理优化,否则不应该轻率地使用此设置。目前仅用于CGLIB代理;它对JDK动态代理没有影响。
  • frozen: if a proxy configuration is frozen, then changes to the configuration are no longer allowed. This is useful both as a slight optimization and for those cases when you don’t want callers to be able to manipulate the proxy (via the Advised interface) after the proxy has been created. The default value of this property is false, so changes such as adding additional advice are allowed.

    • 冻结:如果代理配置被冻结,则不再允许更改配置。当您不希望调用者在创建代理后(通过已通知接口)能够操作代理时,这对于进行轻微优化非常有用。此属性的默认值为false,因此允许添加其他通知等更改。
  • exposeProxy: determines whether or not the current proxy should be exposed in a ThreadLocal so that it can be accessed by the target. If a target needs to obtain the proxy and the exposeProxy property is set to true, the target can use the AopContext.currentProxy() method.

    • exposeProxy:确定是否应该在ThreadLocal中公开当前代理,以便目标可以访问它。如果一个目标需要获得代理,并且exposeProxy属性被设置为true,那么该目标可以使用AopContext.currentProxy()方法。

Other properties specific to ProxyFactoryBean include:(其他特定于ProxyFactoryBean的属性包括:)

  • proxyInterfaces: array of String interface names. If this isn’t supplied, a CGLIB proxy for the target class will be used (but see also JDK- and CGLIB-based proxies).
    • proxyInterfaces:字符串接口名称的数组。如果没有提供这个代理,将使用目标类的CGLIB代理(但也请参阅基于JDK和CGLIB的代理)。
  • interceptorNames: String array of Advisor, interceptor or other advice names to apply. Ordering is significant, on a first come-first served basis. That is to say that the first interceptor in the list will be the first to be able to intercept the invocation.
    • interceptorNames:要应用的顾问、拦截器或其他通知名称的字符串数组。订餐很重要,先到先得。也就是说,列表中的第一个拦截器将是第一个能够拦截调用的拦截器。

The names are bean names in the current factory, including bean names from ancestor factories. You can’t mention bean references here since doing so would result in the ProxyFactoryBean ignoring the singleton setting of the advice.

  • 这些名称是当前工厂中的bean名称,包括来自祖先工厂的bean名称。您不能在这里提到bean引用,因为这样做会导致ProxyFactoryBean忽略通知的单例设置。

You can append an interceptor name with an asterisk ( *). This will result in the application of all advisor beans with names starting with the part before the asterisk to be applied. An example of using this feature can be found in Using 'global' advisors.

  • 可以用星号(*)附加拦截器名称。这将导致所有advisor bean的应用程序的名称都以要应用的星号之前的部分开始。使用此功能的一个例子可以在使用“global”顾问中找到。

  • singleton: whether or not the factory should return a single object, no matter how often the getObject() method is called. Several FactoryBean implementations offer such a method. The default value is true. If you want to use stateful advice - for example, for stateful mixins - use prototype advices along with a singleton value of false.

    • singleton:无论getObject()方法被调用多少次,工厂是否应该返回单个对象。有几个FactoryBean实现提供了这样的方法。默认值为true。如果你想使用有状态通知—例如,有状态混合—使用原型通知和一个单例值false。

6.5.3. JDK- and CGLIB-based proxies(基于JDK和基于cglib的代理)

This section serves as the definitive documentation on how the ProxyFactoryBean chooses to create one of either a JDK- and CGLIB-based proxy for a particular target object (that is to be proxied).

  • 本节提供了关于ProxyFactoryBean如何选择为特定目标对象(即被代理)创建基于JDK和基于cglib的代理的权威性文档。
The behavior of the ProxyFactoryBean with regard to creating JDK- or CGLIB-based proxies changed between versions 1.2.x and 2.0 of Spring. The ProxyFactoryBean now exhibits similar semantics with regard to auto-detecting interfaces as those of the TransactionProxyFactoryBean class.
  • ProxyFactoryBean在创建基于JDK或基于cglib的代理时的行为在1.2版本之间发生了变化。Spring的x和2.0。现在,ProxyFactoryBean在自动检测接口方面展示了与TransactionProxyFactoryBean类类似的语义。

If the class of a target object that is to be proxied (hereafter simply referred to as the target class) doesn’t implement any interfaces, then a CGLIB-based proxy will be created. This is the easiest scenario, because JDK proxies are interface based, and no interfaces means JDK proxying isn’t even possible. One simply plugs in the target bean, and specifies the list of interceptors via the interceptorNames property. Note that a CGLIB-based proxy will be created even if the proxyTargetClass property of the ProxyFactoryBean has been set to false. (Obviously this makes no sense, and is best removed from the bean definition because it is at best redundant, and at worst confusing.)

  • 如果要代理的目标对象的类(以下简称为目标类)没有实现任何接口,那么将创建一个基于cglib的代理。这是最简单的场景,因为JDK代理是基于接口的,没有接口意味着JDK代理根本不可能。一种方法只是插入目标bean,并通过interceptorNames属性指定拦截器列表。注意,即使ProxyFactoryBean的proxyTargetClass属性被设置为false,也会创建一个基于cglib的代理。(显然这没有任何意义,最好从bean定义中删除,因为往好了说是冗余的,往坏了说是混乱的。)

If the target class implements one (or more) interfaces, then the type of proxy that is created depends on the configuration of the ProxyFactoryBean.

  • 如果目标类实现了一个(或多个)接口,那么创建的代理的类型取决于ProxyFactoryBean的配置。

If the proxyTargetClass property of the ProxyFactoryBean has been set to true, then a CGLIB-based proxy will be created. This makes sense, and is in keeping with the principle of least surprise. Even if the proxyInterfaces property of the ProxyFactoryBean has been set to one or more fully qualified interface names, the fact that the proxyTargetClass property is set to true will cause CGLIB-based proxying to be in effect.

  • 如果ProxyFactoryBean的proxyTargetClass属性被设置为true,那么将创建一个基于cglib的代理。这是有道理的,并且符合最小意外的原则。即使ProxyFactoryBean的proxyInterfaces属性被设置为一个或多个完全限定的接口名称,proxyTargetClass属性被设置为true也会导致基于cglib的代理生效。

If the proxyInterfaces property of the ProxyFactoryBean has been set to one or more fully qualified interface names, then a JDK-based proxy will be created. The created proxy will implement all of the interfaces that were specified in the proxyInterfaces property; if the target class happens to implement a whole lot more interfaces than those specified in the proxyInterfaces property, that is all well and good but those additional interfaces will not be implemented by the returned proxy.

  • 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或多个完全限定的接口名称,那么将创建一个基于jdk的代理。创建的代理将实现在proxyInterfaces属性中指定的所有接口;如果目标类碰巧实现了比proxyInterfaces属性中指定的更多的接口,这很好,但是那些额外的接口将不会被返回的代理实现。

If the proxyInterfaces property of the ProxyFactoryBean has not been set, but the target class does implement one (or more) interfaces, then the ProxyFactoryBean will auto-detect the fact that the target class does actually implement at least one interface, and a JDK-based proxy will be created. The interfaces that are actually proxied will be all of the interfaces that the target class implements; in effect, this is the same as simply supplying a list of each and every interface that the target class implements to the proxyInterfaces property. However, it is significantly less work, and less prone to typos.

  • 如果proxyInterfaces ProxyFactoryBean没有设置的属性,但目标类实现一个(或多个)接口,那么ProxyFactoryBean将自动检测的目标类实际上实现至少一个接口,以及一个JDK-based代理将被创建。被代理的接口是目标类实现的所有接口;实际上,这与简单地将目标类实现的每个接口的列表提供给proxyInterfaces属性是一样的。然而,这大大减少了工作量,也不容易出现打字错误。

6.5.4. Proxying interfaces(代理接口)

Let’s look at a simple example of ProxyFactoryBean in action. This example involves:

  • 让我们看一个ProxyFactoryBean的运行示例。这个例子包括:

  • A target bean that will be proxied. This is the "personTarget" bean definition in the example below.

    • 将被代理的目标bean。这就是下面示例中的“personTarget”bean定义。
  • An Advisor and an Interceptor used to provide advice.

    • 一个顾问和一个拦截器用来提供建议。
  • An AOP proxy bean definition specifying the target object (the personTarget bean) and the interfaces to proxy, along with the advices to apply.

    • 一个指定目标对象(personTarget bean)和代理接口的AOP代理bean定义,以及要应用的建议。
<bean id="personTarget" class="com.mycompany.PersonImpl">
    <property name="name" value="Tony"/>
    <property name="age" value="51"/>
</bean>

<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
    <property name="someProperty" value="Custom string property value"/>
</bean>

<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
</bean>

<bean id="person"
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="com.mycompany.Person"/>

    <property name="target" ref="personTarget"/>
    <property name="interceptorNames">
        <list>
            <value>myAdvisor</value>
            <value>debugInterceptor</value>
        </list>
    </property>
</bean>

Note that the interceptorNames property takes a list of String: the bean names of the interceptor or advisors in the current factory. Advisors, interceptors, before, after returning and throws advice objects can be used. The ordering of advisors is significant.

  • 注意,interceptorNames属性接受一个字符串列表:当前工厂中的拦截器或顾问的bean名称。顾问,拦截器,在返回和抛出建议对象之前,之后都可以使用。顾问的顺序很重要。
You might be wondering why the list doesn’t hold bean references. The reason for this is that if the ProxyFactoryBean’s singleton property is set to false, it must be able to return independent proxy instances. If any of the advisors is itself a prototype, an independent instance would need to be returned, so it’s necessary to be able to obtain an instance of the prototype from the factory; holding a reference isn’t sufficient.
  • 您可能想知道为什么列表不保存bean引用。这样做的原因是,如果ProxyFactoryBean的singleton属性被设置为false,那么它必须能够返回独立的代理实例。如果任何一个顾问本身是一个原型,就需要返回一个独立的实例,所以必须能够从工厂获得原型的实例;拥有引用是不够的。

The "person" bean definition above can be used in place of a Person implementation, as follows:

  • 上面的“person”bean定义可以用来代替person实现,如下所示:
Person person = (Person) factory.getBean("person");

Other beans in the same IoC context can express a strongly typed dependency on it, as with an ordinary Java object:

  • 在相同IoC上下文中的其他bean可以表达对它的强类型依赖,就像普通Java对象一样:
<bean id="personUser" class="com.mycompany.PersonUser">
    <property name="person"><ref bean="person"/></property>
</bean>

The PersonUser class in this example would expose a property of type Person. As far as it’s concerned, the AOP proxy can be used transparently in place of a "real" person implementation. However, its class would be a dynamic proxy class. It would be possible to cast it to the Advised interface (discussed below).

  • 本例中的PersonUser类将公开Person类型的属性。就其本身而言,AOP代理可以透明地用于“真实的”人员实现。但是,它的类将是一个动态代理类。可以将其强制转换为已建议的接口(下面将讨论)。

It’s possible to conceal the distinction between target and proxy using an anonymous inner bean, as follows. Only the ProxyFactoryBean definition is different; the advice is included only for completeness:

  • 使用匿名内部bean可以隐藏目标和代理之间的区别,如下所示。只有ProxyFactoryBean的定义不同;建议只是为了完整性而包含:
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
    <property name="someProperty" value="Custom string property value"/>
</bean>

<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>

<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="com.mycompany.Person"/>
    <!-- Use inner bean, not local reference to target -->
    <property name="target">
        <bean class="com.mycompany.PersonImpl">
            <property name="name" value="Tony"/>
            <property name="age" value="51"/>
        </bean>
    </property>
    <property name="interceptorNames">
        <list>
            <value>myAdvisor</value>
            <value>debugInterceptor</value>
        </list>
    </property>
</bean>

This has the advantage that there’s only one object of type Person: useful if we want to prevent users of the application context from obtaining a reference to the un-advised object, or need to avoid any ambiguity with Spring IoC autowiring. There’s also arguably an advantage in that the ProxyFactoryBean definition is self-contained. However, there are times when being able to obtain the un-advised target from the factory might actually be an advantage: for example, in certain test scenarios.

  • 这样做的好处是只有一个Person类型的对象:如果我们想防止应用程序上下文的用户获得对未建议对象的引用,或者需要避免Spring IoC自动装配产生歧义,那么这个对象很有用。ProxyFactoryBean定义是自包含的,这一点也有争议。然而,有时候,能够从工厂获得未建议的目标实际上可能是一种优势:例如,在某些测试场景中。

6.5.5. Proxying classes(代理类)

What if you need to proxy a class, rather than one or more interfaces?

  • 如果需要代理一个类而不是一个或多个接口怎么办?

Imagine that in our example above, there was no Person interface: we needed to advise a class called Person that didn’t implement any business interface. In this case, you can configure Spring to use CGLIB proxying, rather than dynamic proxies. Simply set the proxyTargetClass property on the ProxyFactoryBean above to true. While it’s best to program to interfaces, rather than classes, the ability to advise classes that don’t implement interfaces can be useful when working with legacy code. (In general, Spring isn’t prescriptive. While it makes it easy to apply good practices, it avoids forcing a particular approach.)

  • 假设在上面的示例中,没有Person接口:我们需要通知一个名为Person的类,该类没有实现任何业务接口。在这种情况下,您可以配置Spring使用CGLIB代理,而不是动态代理。只需将上面的ProxyFactoryBean上的proxyTargetClass属性设置为true。虽然最好是根据接口编程,而不是根据类,但在处理遗留代码时,通知没有实现接口的类的能力可能很有用。(一般来说,Spring不是说明性的。虽然它使应用良好实践变得容易,但它避免了强制使用特定的方法。)

If you want to, you can force the use of CGLIB in any case, even if you do have interfaces.

  • 如果愿意,可以在任何情况下强制使用CGLIB,即使您有接口。

CGLIB proxying works by generating a subclass of the target class at runtime. Spring configures this generated subclass to delegate method calls to the original target: the subclass is used to implement the Decorator pattern, weaving in the advice.

  • CGLIB代理通过在运行时生成目标类的子类来工作。Spring将这个生成的子类配置为将方法调用委托给原始目标:子类用于实现装饰器模式,编织在通知中。

CGLIB proxying should generally be transparent to users. However, there are some issues to consider:

  • CGLIB代理通常应该对用户透明。但是,有一些问题需要考虑:

  • Final methods can’t be advised, as they can’t be overridden.

    • 最后的方法不能被建议,因为它们不能被覆盖。
  • There is no need to add CGLIB to your classpath. As of Spring 3.2, CGLIB is repackaged and included in the spring-core JAR. In other words, CGLIB-based AOP will work "out of the box" just as do JDK dynamic proxies.

    • 没有必要将CGLIB添加到类路径中。在Spring 3.2中,CGLIB被重新打包并包含在Spring核心JAR中。换句话说,基于cglib的AOP将像JDK动态代理一样“开箱即用”。

There’s little performance difference between CGLIB proxying and dynamic proxies. As of Spring 1.0, dynamic proxies are slightly faster. However, this may change in the future. Performance should not be a decisive consideration in this case.

  • CGLIB代理和动态代理在性能上几乎没有差别。从Spring 1.0开始,动态代理稍微快一些。然而,这种情况在将来可能会改变。在这种情况下,性能不应该是决定性的考虑因素。

6.5.6. Using 'global' advisors(使用“全球”顾问)

By appending an asterisk to an interceptor name, all advisors with bean names matching the part before the asterisk, will be added to the advisor chain. This can come in handy if you need to add a standard set of 'global' advisors:

  • 通过向拦截器名称附加星号,所有具有与星号之前的部分相匹配的bean名称的顾问都将被添加到顾问链中。如果你需要添加一套标准的“全球”顾问,这将派上用场:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="service"/>
    <property name="interceptorNames">
        <list>
            <value>global*</value>
        </list>
    </property>
</bean>

<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>

6.6. Concise proxy definitions(简洁的代理的定义)

Especially when defining transactional proxies, you may end up with many similar proxy definitions. The use of parent and child bean definitions, along with inner bean definitions, can result in much cleaner and more concise proxy definitions.

  • 特别是在定义事务代理时,您可能会得到许多类似的代理定义。使用父bean和子bean定义以及内部bean定义可以产生更干净、更简洁的代理定义。

First a parent, template, bean definition is created for the proxy:

  • 首先为代理创建父、模板、bean定义:
<bean id="txProxyTemplate" abstract="true"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
    <property name="transactionAttributes">
        <props>
            <prop key="*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

This will never be instantiated itself, so may actually be incomplete. Then each proxy which needs to be created is just a child bean definition, which wraps the target of the proxy as an inner bean definition, since the target will never be used on its own anyway.

  • 它本身永远不会实例化,因此实际上可能是不完整的。然后需要创建的每个代理都只是一个子bean定义,它将代理的目标包装为一个内部bean定义,因为无论如何目标都不会单独使用。
<bean id="myService" parent="txProxyTemplate">
    <property name="target">
        <bean class="org.springframework.samples.MyServiceImpl">
        </bean>
    </property>
</bean>

It is of course possible to override properties from the parent template, such as in this case, the transaction propagation settings:

  • 当然,可以覆盖父模板的属性,例如在本例中,事务传播设置:
<bean id="mySpecialService" parent="txProxyTemplate">
    <property name="target">
        <bean class="org.springframework.samples.MySpecialServiceImpl">
        </bean>
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
            <prop key="store*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

Note that in the example above, we have explicitly marked the parent bean definition as abstract by using the abstract attribute, as described previously, so that it may not actually ever be instantiated. Application contexts (but not simple bean factories) will by default pre-instantiate all singletons. It is therefore important (at least for singleton beans) that if you have a (parent) bean definition which you intend to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to true, otherwise the application context will actually try to pre-instantiate it.

  • 请注意,在上面的示例中,我们通过使用抽象属性显式地将父bean定义标记为抽象(如前面所述),因此它可能不会实际实例化。应用程序上下文(但不是简单的bean工厂)默认情况下会预实例化所有单例。因此重要的(至少对单例bean),如果你有一个(父)bean定义你只打算使用作为模板,这个定义指定了一个类,您必须确保设置抽象属性为true,否则应用程序上下文会试图pre-instantiate它。

6.7. Creating AOP proxies programmatically with the ProxyFactory(用ProxyFactory编程地创建AOP代理)

It’s easy to create AOP proxies programmatically using Spring. This enables you to use Spring AOP without dependency on Spring IoC.

  • 使用Spring以编程方式创建AOP代理很容易。这使您能够在不依赖Spring IoC的情况下使用Spring AOP。

The following listing shows creation of a proxy for a target object, with one interceptor and one advisor. The interfaces implemented by the target object will automatically be proxied:

  • 面的清单显示了为目标对象创建代理的过程,其中包含一个拦截器和一个顾问。由目标对象实现的接口将被自动代理:
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();

The first step is to construct an object of type org.springframework.aop.framework.ProxyFactory. You can create this with a target object, as in the above example, or specify the interfaces to be proxied in an alternate constructor.

  • 第一步是构造一个org.springframework.aop.framework.ProxyFactory类型的对象。您可以使用目标对象(如上面的示例所示)来创建它,也可以在替代构造函数中指定要代理的接口。

You can add advices (with interceptors as a specialized kind of advice) and/or advisors, and manipulate them for the life of the ProxyFactory. If you add an IntroductionInterceptionAroundAdvisor, you can cause the proxy to implement additional interfaces.

  • 您可以添加通知(使用拦截器作为一种特殊的通知)和/或建议,并在ProxyFactory的生命周期中操作它们。如果您添加了一个介绍性的interceptionaroundadvisor,您可以使代理实现其他接口。

There are also convenience methods on ProxyFactory (inherited from AdvisedSupport) which allow you to add other advice types such as before and throws advice. AdvisedSupport is the superclass of both ProxyFactory and ProxyFactoryBean.

  • ProxyFactory上还有一些方便的方法(继承自AdvisedSupport),允许您添加其他通知类型,比如before和throw通知。AdvisedSupport是ProxyFactory和ProxyFactoryBean的超类。
Integrating AOP proxy creation with the IoC framework is best practice in most applications. We recommend that you externalize configuration from Java code with AOP, as in general.
  • 将AOP代理创建与IoC框架集成是大多数应用程序中的最佳实践。我们建议您使用AOP将配置从Java代码外部化,就像一般情况一样。

6.8. Manipulating advised objects(操纵建议对象)

However you create AOP proxies, you can manipulate them using the org.springframework.aop.framework.Advised interface. Any AOP proxy can be cast to this interface, whichever other interfaces it implements. This interface includes the following methods:

  • 无论您如何创建AOP代理,您都可以使用org.springframework.aop.framework.Advised接口来操作它们。任何AOP代理都可以强制转换到这个接口,无论它实现了哪个接口。该接口包括以下方法:
Advisor[] getAdvisors();

void addAdvice(Advice advice) throws AopConfigException;

void addAdvice(int pos, Advice advice) throws AopConfigException;

void addAdvisor(Advisor advisor) throws AopConfigException;

void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

int indexOf(Advisor advisor);

boolean removeAdvisor(Advisor advisor) throws AopConfigException;

void removeAdvisor(int index) throws AopConfigException;

boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

boolean isFrozen();

The getAdvisors() method will return an Advisor for every advisor, interceptor or other advice type that has been added to the factory. If you added an Advisor, the returned advisor at this index will be the object that you added. If you added an interceptor or other advice type, Spring will have wrapped this in an advisor with a pointcut that always returns true. Thus if you added a MethodInterceptor, the advisor returned for this index will be an DefaultPointcutAdvisor returning your MethodInterceptor and a pointcut that matches all classes and methods.

  • getAdvisors()方法将为添加到工厂的每个顾问、拦截器或其他通知类型返回一个顾问。如果您添加了一个Advisor,则该索引处返回的Advisor将是您添加的对象。如果您添加了一个拦截器或其他通知类型,Spring将用一个总是返回true的切入点将其包装在advisor中。因此,如果您添加了一个MethodInterceptor,为这个索引返回的advisor将是一个DefaultPointcutAdvisor,它返回您的MethodInterceptor和一个匹配所有类和方法的切入点。

The addAdvisor() methods can be used to add any Advisor. Usually the advisor holding pointcut and advice will be the generic DefaultPointcutAdvisor, which can be used with any advice or pointcut (but not for introductions).

  • addAdvisor()方法可用于添加任何Advisor。通常,持有切入点和通知的advisor工具是通用的DefaultPointcutAdvisor,它可以与任何通知或切入点一起使用(但不用于引入)。

By default, it’s possible to add or remove advisors or interceptors even once a proxy has been created. The only restriction is that it’s impossible to add or remove an introduction advisor, as existing proxies from the factory will not show the interface change. (You can obtain a new proxy from the factory to avoid this problem.)

  • 默认情况下,即使代理已经创建,也可以添加或删除顾问或拦截器。唯一的限制是不可能添加或删除一个introduction advisor,因为工厂中的现有代理不会显示接口更改。(您可以从工厂获得一个新的代理来避免这个问题。)

A simple example of casting an AOP proxy to the Advised interface and examining and manipulating its advice:

  • 简单的例子,它将一个AOP代理转换到已通知的接口,并检查和操作它的通知:
Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + " advisors");

// Add an advice like an interceptor without a pointcut
// Will match all proxied methods
// Can use for interceptors, before, after returning or throws advice
advised.addAdvice(new DebugInterceptor());

// Add selective advice using a pointcut
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));

assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);
It’s questionable whether it’s advisable (no pun intended) to modify advice on a business object in production, although there are no doubt legitimate usage cases. However, it can be very useful in development: for example, in tests. I have sometimes found it very useful to be able to add test code in the form of an interceptor or other advice, getting inside a method invocation I want to test. (For example, the advice can get inside a transaction created for that method: for example, to run SQL to check that a database was correctly updated, before marking the transaction for roll back.)
  • 在生产中修改业务对象上的通知是否可取(没有双关语)值得怀疑,尽管毫无疑问存在合法的使用案例。但是,它在开发中非常有用:例如,在测试中。我有时发现,以拦截器或其他通知的形式添加测试代码非常有用,可以进入我想要测试的方法调用内部。(例如,通知可以进入为该方法创建的事务中:例如,在标记回滚事务之前,运行SQL来检查数据库是否被正确更新。)

Depending on how you created the proxy, you can usually set a frozen flag, in which case the Advised isFrozen() method will return true, and any attempts to modify advice through addition or removal will result in an AopConfigException. The ability to freeze the state of an advised object is useful in some cases, for example, to prevent calling code removing a security interceptor. It may also be used in Spring 1.1 to allow aggressive optimization if runtime advice modification is known not to be required.

  • 根据您创建代理的方式,您通常可以设置一个冻结标志,在这种情况下,建议的isFrozen()方法将返回true,并且任何通过添加或删除修改通知的尝试都将导致AopConfigException。冻结已通知对象状态的能力在某些情况下非常有用,例如,用于防止调用代码删除安全拦截器。如果已知不需要修改运行时通知,Spring 1.1也可以使用它来允许主动优化。

6.9. Using the "auto-proxy" facility(使用“自动代理”工具)

So far we’ve considered explicit creation of AOP proxies using a ProxyFactoryBean or similar factory bean.

  • 到目前为止,我们已经考虑了使用ProxyFactoryBean或类似的工厂bean显式地创建AOP代理。

Spring also allows us to use "auto-proxy" bean definitions, which can automatically proxy selected bean definitions. This is built on Spring "bean post processor" infrastructure, which enables modification of any bean definition as the container loads.

  • Spring还允许我们使用“自动代理”bean定义,它可以自动代理选定的bean定义。它构建在Spring“bean后处理器”基础设施上,该基础设施允许在装载容器时修改任何bean定义。

In this model, you set up some special bean definitions in your XML bean definition file to configure the auto proxy infrastructure. This allows you just to declare the targets eligible for auto-proxying: you don’t need to use ProxyFactoryBean.

  • 在这个模型中,您在XML bean定义文件中设置了一些特殊的bean定义来配置自动代理基础设施。这允许你只声明适合自动代理的目标:你不需要使用ProxyFactoryBean。

There are two ways to do this:(有两种方法可以做到这一点:)

  • Using an auto-proxy creator that refers to specific beans in the current context.
    • 使用自动代理创建器引用当前上下文中的特定bean。
  • A special case of auto-proxy creation that deserves to be considered separately; auto-proxy creation driven by source-level metadata attributes.
    • 一个需要单独考虑的自动代理创建的特殊情况;由源级元数据属性驱动的自动代理创建。

6.9.1. Autoproxy bean definitions(狐的一个插件bean定义)

The org.springframework.aop.framework.autoproxy package provides the following standard auto-proxy creators.

  • autoproxy包提供了以下标准的自动代理创建程序。
BeanNameAutoProxyCreator

The BeanNameAutoProxyCreator class is a BeanPostProcessor that automatically creates AOP proxies for beans with names matching literal values or wildcards.

  • BeanNameAutoProxyCreator类是一个BeanPostProcessor,它自动为名称匹配文字值或通配符的bean创建AOP代理。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="jdk*,onlyJdk"/>
    <property name="interceptorNames">
        <list>
            <value>myInterceptor</value>
        </list>
    </property>
</bean>

As with ProxyFactoryBean, there is an interceptorNames property rather than a list of interceptors, to allow correct behavior for prototype advisors. Named "interceptors" can be advisors or any advice type.

  • 与ProxyFactoryBean一样,有一个interceptorNames属性而不是一列拦截器,以允许原型顾问的正确行为。命名为“拦截器”的可以是建议器或任何建议类型。

As with auto proxying in general, the main point of using BeanNameAutoProxyCreator is to apply the same configuration consistently to multiple objects, with minimal volume of configuration. It is a popular choice for applying declarative transactions to multiple objects.

  • 与一般的自动代理一样,使用BeanNameAutoProxyCreator的主要目的是将相同的配置一致地应用于多个对象,并且配置量最少。它是将声明性事务应用于多个对象的流行选择。

Bean definitions whose names match, such as "jdkMyBean" and "onlyJdk" in the above example, are plain old bean definitions with the target class. An AOP proxy will be created automatically by the BeanNameAutoProxyCreator. The same advice will be applied to all matching beans. Note that if advisors are used (rather than the interceptor in the above example), the pointcuts may apply differently to different beans.

  • 名称匹配的Bean定义,如上面示例中的“jdkMyBean”和“onlyJdk”,是带有目标类的普通旧Bean定义。BeanNameAutoProxyCreator将自动创建一个AOP代理。相同的通知将应用于所有匹配的bean。注意,如果使用了顾问(而不是上面示例中的拦截器),切入点可能会以不同的方式应用于不同的bean。
DefaultAdvisorAutoProxyCreator

A more general and extremely powerful auto proxy creator is DefaultAdvisorAutoProxyCreator. This will automagically apply eligible advisors in the current context, without the need to include specific bean names in the auto-proxy advisor’s bean definition. It offers the same merit of consistent configuration and avoidance of duplication as BeanNameAutoProxyCreator.

  • 一个更通用和非常强大的自动代理创建器是DefaultAdvisorAutoProxyCreator。这将自动在当前上下文中应用合格的顾问,而不需要在自动代理顾问的bean定义中包含特定的bean名称。它提供了与BeanNameAutoProxyCreator相同的优点,即一致的配置和避免重复。**

Using this mechanism involves:(用这种机制包括:)

  • Specifying a DefaultAdvisorAutoProxyCreator bean definition.
    • 指定DefaultAdvisorAutoProxyCreator bean定义。
  • Specifying any number of Advisors in the same or related contexts. Note that these must be Advisors, not just interceptors or other advices. This is necessary because there must be a pointcut to evaluate, to check the eligibility of each advice to candidate bean definitions.
    • 在相同或相关上下文中指定任意数量的顾问。注意,这些必须是顾问,而不仅仅是拦截器或其他建议。这是必要的,因为必须有一个要评估的切入点,以检查每个通知到候选bean定义的资格。

The DefaultAdvisorAutoProxyCreator will automatically evaluate the pointcut contained in each advisor, to see what (if any) advice it should apply to each business object (such as "businessObject1" and "businessObject2" in the example).

  • DefaultAdvisorAutoProxyCreator将自动计算每个advisor中包含的切入点,以查看应该将什么建议(如果有的话)应用到每个业务对象(例如示例中的“businessObject1”和“businessObject2”)。

This means that any number of advisors can be applied automatically to each business object. If no pointcut in any of the advisors matches any method in a business object, the object will not be proxied. As bean definitions are added for new business objects, they will automatically be proxied if necessary.

  • 这意味着可以将任意数量的顾问自动应用到每个业务对象。如果任何顾问中没有切入点匹配业务对象中的任何方法,则该对象将不会被代理。在为新业务对象添加bean定义时,如果需要,它们将自动被代理。

Autoproxying in general has the advantage of making it impossible for callers or dependencies to obtain an un-advised object. Calling getBean("businessObject1") on this ApplicationContext will return an AOP proxy, not the target business object. (The "inner bean" idiom shown earlier also offers this benefit.)

  • 一般来说,自动代理的优点是使调用者或依赖项不可能获得未通知的对象。在这个ApplicationContext上调用getBean(“businessObject1”)将返回一个AOP代理,而不是目标业务对象。(前面显示的“内部bean”习语也提供了这个好处。)
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
    <property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>

<bean id="customAdvisor" class="com.mycompany.MyAdvisor"/>

<bean id="businessObject1" class="com.mycompany.BusinessObject1">
    <!-- Properties omitted -->
</bean>

<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>

The DefaultAdvisorAutoProxyCreator is very useful if you want to apply the same advice consistently to many business objects. Once the infrastructure definitions are in place, you can simply add new business objects without including specific proxy configuration. You can also drop in additional aspects very easily - for example, tracing or performance monitoring aspects - with minimal change to configuration.

  • 如果您想要将相同的通知一致地应用到许多业务对象,DefaultAdvisorAutoProxyCreator非常有用。一旦基础设施定义就位,您就可以简单地添加新的业务对象,而不包括特定的代理配置。您还可以非常容易地删除其他方面—例如,跟踪或性能监视方面—只需对配置进行最小的更改。

The DefaultAdvisorAutoProxyCreator offers support for filtering (using a naming convention so that only certain advisors are evaluated, allowing use of multiple, differently configured, AdvisorAutoProxyCreators in the same factory) and ordering. Advisors can implement the org.springframework.core.Ordered interface to ensure correct ordering if this is an issue. The TransactionAttributeSourceAdvisor used in the above example has a configurable order value; the default setting is unordered.

  • DefaultAdvisorAutoProxyCreator支持过滤(使用命名约定,只有特定的advisor才会被评估,允许在同一个工厂中使用多个不同配置的advisorautoproxycreator)和排序。顾问可以实现org.springframework.core。有序接口,以确保正确的顺序,如果这是一个问题。上面例子中使用的TransactionAttributeSourceAdvisor有一个可配置的订单值;默认设置是无序的

6.10. Using TargetSources(使用目标源)

Spring offers the concept of a TargetSource, expressed in the org.springframework.aop.TargetSource interface. This interface is responsible for returning the "target object" implementing the join point. The TargetSource implementation is asked for a target instance each time the AOP proxy handles a method invocation.

  • Spring提供了目标源的概念,在org.springframework.aop中表示。TargetSource接口。此接口负责返回实现连接点的“目标对象”。每当AOP代理处理方法调用时,就会向TargetSource实现询问目标实例。

Developers using Spring AOP don’t normally need to work directly with TargetSources, but this provides a powerful means of supporting pooling, hot swappable and other sophisticated targets. For example, a pooling TargetSource can return a different target instance for each invocation, using a pool to manage instances.

  • 使用Spring AOP的开发人员通常不需要直接使用targetsource,但是这提供了一种支持池、热切换和其他复杂目标的强大方法。例如,池TargetSource可以使用池来管理实例,为每次调用返回不同的目标实例。

If you do not specify a TargetSource, a default implementation is used that wraps a local object. The same target is returned for each invocation (as you would expect).

  • 如果没有指定TargetSource,则使用包装本地对象的默认实现。每次调用都返回相同的目标(如您所料)。

Let’s look at the standard target sources provided with Spring, and how you can use them.

  • 让我们看看Spring提供的标准目标源,以及如何使用它们。
When using a custom target source, your target will usually need to be a prototype rather than a singleton bean definition. This allows Spring to create a new target instance when required.
  • 在使用自定义目标源时,您的目标通常需要是原型而不是单例bean定义。这允许Spring在需要时创建新的目标实例。

6.10.1. Hot swappable target sources(可热切换的目标源)

The org.springframework.aop.target.HotSwappableTargetSource exists to allow the target of an AOP proxy to be switched while allowing callers to keep their references to it.

-org.springframework.aop.target。存在HotSwappableTargetSource是为了允许切换AOP代理的目标,同时允许调用者保持对它的引用。

Changing the target source’s target takes effect immediately. The HotSwappableTargetSource is threadsafe.

  • 更改目标源的目标将立即生效。HotSwappableTargetSource是线程安全的。

You can change the target via the swap() method on HotSwappableTargetSource as follows:

  • 您可以通过HotSwappableTargetSource上的swap()方法更改目标,如下所示:
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);

The XML definitions required look as follows:

  • 所需的XML定义如下:
<bean id="initialTarget" class="mycompany.OldTarget"/>

<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
    <constructor-arg ref="initialTarget"/>
</bean>

<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="swapper"/>
</bean>

The above swap() call changes the target of the swappable bean. Clients who hold a reference to that bean will be unaware of the change, but will immediately start hitting the new target.

  • 上面的swap()调用改变了可切换bean的目标。持有对该bean的引用的客户机将不知道更改,但将立即开始达到新目标。

Although this example doesn’t add any advice - and it’s not necessary to add advice to use a TargetSource - of course any TargetSource can be used in conjunction with arbitrary advice.

-尽管这个例子没有添加任何通知——而且使用TargetSource也不需要添加通知——当然,任何TargetSource都可以与任意的通知一起使用。

6.10.2. Pooling target sources(池目标来源)

Using a pooling target source provides a similar programming model to stateless session EJBs, in which a pool of identical instances is maintained, with method invocations going to free objects in the pool.

  • 使用池目标源提供了与无状态会话ejb类似的编程模型,其中维护了相同实例的池,方法调用将释放池中的对象。

A crucial difference between Spring pooling and SLSB pooling is that Spring pooling can be applied to any POJO. As with Spring in general, this service can be applied in a non-invasive way.

  • Spring池和SLSB池之间的一个关键区别是,Spring池可以应用于任何POJO。与Spring一般情况一样,可以以非侵入性的方式应用此服务。

Spring provides out-of-the-box support for Commons Pool 2.2, which provides a fairly efficient pooling implementation. You’ll need the commons-pool Jar on your application’s classpath to use this feature. It’s also possible to subclass org.springframework.aop.target.AbstractPoolingTargetSource to support any other pooling API.

  • Spring为Commons Pool 2.2提供了开箱即用的支持,它提供了一个相当有效的池实现。您需要在应用程序的类路径上使用commonpool Jar来使用这个特性。也可以子类化org.springframework.aop.target。AbstractPoolingTargetSource来支持任何其他池API。
Commons Pool 1.5+ is also supported but deprecated as of Spring Framework 4.2.
  • Commons Pool 1.5+也受到支持,但在Spring Framework 4.2时已被弃用。

Sample configuration is shown below:

  • 配置示例如下所示:
<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
        scope="prototype">
    ... properties omitted
</bean>

<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPool2TargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
    <property name="maxSize" value="25"/>
</bean>

<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="poolTargetSource"/>
    <property name="interceptorNames" value="myInterceptor"/>
</bean>

Note that the target object - "businessObjectTarget" in the example - must be a prototype. This allows the PoolingTargetSource implementation to create new instances of the target to grow the pool as necessary. See the javadocs of AbstractPoolingTargetSource and the concrete subclass you wish to use for information about its properties: "maxSize" is the most basic, and always guaranteed to be present.

  • 注意,目标对象——示例中的“businessObjectTarget”——必须是一个原型。这允许PoolingTargetSource实现创建目标的新实例,以根据需要增长池。请参阅AbstractPoolingTargetSource的javadocs和您希望使用的具体子类,以获得关于其属性的信息:“maxSize”是最基本的,并且始终保证存在。

In this case, "myInterceptor" is the name of an interceptor that would need to be defined in the same IoC context. However, it isn’t necessary to specify interceptors to use pooling. If you want only pooling, and no other advice, don’t set the interceptorNames property at all.

  • 在本例中,“myInterceptor”是需要在相同的IoC上下文中定义的拦截器的名称。但是,没有必要指定拦截器来使用池。如果您只需要池,而不需要其他通知,那么完全不要设置interceptorNames属性。

It’s possible to configure Spring so as to be able to cast any pooled object to the org.springframework.aop.target.PoolingConfig interface, which exposes information about the configuration and current size of the pool through an introduction. You’ll need to define an advisor like this:

  • 可以配置Spring,以便能够将任何池中的对象强制转换为org.springframework.aop.target。PoolingConfig接口,它通过介绍公开有关池的配置和当前大小的信息。你需要像这样定义一个顾问:
<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="poolTargetSource"/>
    <property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>

This advisor is obtained by calling a convenience method on the AbstractPoolingTargetSource class, hence the use of MethodInvokingFactoryBean. This advisor’s name ("poolConfigAdvisor" here) must be in the list of interceptors names in the ProxyFactoryBean exposing the pooled object.

  • 这个advisor工具是通过调用AbstractPoolingTargetSource类上的一个方便方法获得的,因此使用方法invokingfactorybean。这个顾问的名字(这里是“poolConfigAdvisor”)必须在ProxyFactoryBean中暴露池中的对象的拦截器名字列表中。

The cast will look as follows:

  • cast的样子如下:
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
Pooling stateless service objects is not usually necessary. We don’t believe it should be the default choice, as most stateless objects are naturally thread safe, and instance pooling is problematic if resources are cached.
  • 池化无状态的服务对象通常是不必要的。我们不认为它应该是默认的选择,因为大多数无状态对象自然是线程安全的,而且如果资源被缓存,实例池是有问题的。

Simpler pooling is available using auto-proxying. It’s possible to set the TargetSources used by any auto-proxy creator.

  • 使用自动代理可以使用更简单的池。可以设置任何自动代理创建器使用的目标源。

6.10.3. Prototype target sources(原型目标的来源)

Setting up a "prototype" target source is similar to a pooling TargetSource. In this case, a new instance of the target will be created on every method invocation. Although the cost of creating a new object isn’t high in a modern JVM, the cost of wiring up the new object (satisfying its IoC dependencies) may be more expensive. Thus you shouldn’t use this approach without very good reason.

  • 建立“原型”目标源类似于池TargetSource。在这种情况下,将在每次方法调用时创建目标的一个新实例。尽管在现代JVM中创建新对象的成本并不高,但连接新对象(满足其IoC依赖项)的成本可能会更高。因此,如果没有很好的理由,您不应该使用这种方法。

To do this, you could modify the poolTargetSource definition shown above as follows. (I’ve also changed the name, for clarity.)

  • 为此,您可以修改上面的poolTargetSource定义,如下所示。(为了清楚起见,我还改了名字。)
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
    <property name="targetBeanName" ref="businessObjectTarget"/>
</bean>

There’s only one property: the name of the target bean. Inheritance is used in the TargetSource implementations to ensure consistent naming. As with the pooling target source, the target bean must be a prototype bean definition.

  • 只有一个属性:目标bean的名称。TargetSource实现中使用继承来确保命名一致。与池目标源一样,目标bean必须是原型bean定义。

6.10.4. ThreadLocal target sources(ThreadLocal目标来源)

ThreadLocal target sources are useful if you need an object to be created for each incoming request (per thread that is). The concept of a ThreadLocal provide a JDK-wide facility to transparently store resource alongside a thread. Setting up a ThreadLocalTargetSource is pretty much the same as was explained for the other types of target source:

  • 如果您需要为每个传入请求(即每个线程)创建一个对象,那么ThreadLocal目标源非常有用。ThreadLocal的概念提供了一个jdk范围的工具来透明地存储线程旁边的资源。设置ThreadLocalTargetSource与解释其他类型的目标源非常相似:
<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
</bean>
ThreadLocals come with serious issues (potentially resulting in memory leaks) when incorrectly using them in a multi-threaded and multi-classloader environments. One should always consider wrapping a threadlocal in some other class and never directly use the ThreadLocal itself (except of course in the wrapper class). Also, one should always remember to correctly set and unset (where the latter simply involved a call to ThreadLocal.set(null)) the resource local to the thread. Unsetting should be done in any case since not unsetting it might result in problematic behavior. Spring’s ThreadLocal support does this for you and should always be considered in favor of using ThreadLocals without other proper handling code.
  • 在多线程和多类加载器环境中不正确地使用线程局部变量会带来严重的问题(可能导致内存泄漏)。应该始终考虑在其他类中包装threadlocal,永远不要直接使用threadlocal本身(当然,在包装类中除外)。另外,应该始终记住正确地设置和取消设置(后者只是调用thread. set(null))线程本地的资源。取消设置在任何情况下都应该进行,因为不取消设置可能会导致有问题的行为。Spring的ThreadLocal支持为您做到了这一点,并且应该始终考虑使用线程局部变量,而不需要其他适当的处理代码。

6.11. Defining new Advice types(定义新的通知类型)

Spring AOP is designed to be extensible. While the interception implementation strategy is presently used internally, it is possible to support arbitrary advice types in addition to the out-of-the-box interception around advice, before, throws advice and after returning advice.

  • Spring AOP被设计为可扩展的。虽然目前在内部使用拦截实现策略,但是除了对通知进行开箱即用的拦截外,还可以支持任意的通知类型,包括在通知之前、抛出通知之前和返回通知之后进行拦截。

The org.springframework.aop.framework.adapter package is an SPI package allowing support for new custom advice types to be added without changing the core framework. The only constraint on a custom Advice type is that it must implement the org.aopalliance.aop.Advice marker interface.

  • 适配器包是一个SPI包,允许在不更改核心框架的情况下添加新的自定义通知类型。定制通知类型的唯一约束是它必须实现org.aopalliance.aop。建议标记接口。

Please refer to the org.springframework.aop.framework.adapter javadocs for further information.

  • 有关进一步信息,请参考org.springframework.aop.framework.adapter javadocs。

后续内容戳我^^

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