天天看点

【Spring】Spring Framework Reference Documentation中文版11

12. Spring AOP APIs

12.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 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 4.0 is backwards compatible with Spring 1.2 and everything described in this chapter is fully supported in Spring 4.0.

之前章节描述了spring有关aop的支持通过使用@AspectJ和基于schema的方面定义。这一节中我们讨论底层的spring的aop的api和aop的支持在spring1.2应用中的使用。对于新的应用,我们建议使用spring2.0和最新的aop支持在前面章节中描述过,但是对于以前的应用,或读书和文章,你可能碰到spring1.2风格的例子。spring4.0是和spring1.2兼容的并且所有在这章中的描述也支持spring4.0。

12.2 Pointcut API in Spring

spring中的切点api

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

让我们看一下spring是如何处理关键的切点内容的。

12.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的切点模式允许切点拒绝独立于advice类型。对于使用相同切点而目标不同的advice是可行的。

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.Pointcut接口是关键的接口,用于目标advice对于特定的类和方法。完整的接口展示如下:

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).

分解Pointcut接口为两个部分允许拒绝类和方法匹配部分,和细粒度的操作(例如表现为统一另一个方法匹配)

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代理被创建后,为了避免需要测试每个方法的调用。如果两个参数的matchers方法返回true对于给定的方法,并且isRuntime方法对于MethodMatcher返回true,3个参数的matches方法将被调用根据每次方法的执行。这允许切点查找参数直接通过方法调用在目标advice被执行之前。

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方法返回是falses。在这种情况,3个参数matches方法将会被调用。

[Tip]

提示

If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcut evaluation when an AOP proxy is created.

如果可以,试图使得切点为静态,允许aop框架缓存切点表达式的结果当一个aop代理被创建时。

12.2.2 Operations on pointcuts

切点的操作

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

spring支持在切点的操作:取反、并集和交集。

    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切点表达式通常是一个更简单的方式。

12.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切点的讨论。

12.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. org.springframework.aop.support.JdkRegexpMethodPointcut is a generic regular expression pointcut, using the regular expression support in JDK 1.4+.

一个常见的方法定义静态切点是正则表达式。一些aop框架除spring外支持这种方式。org.springframework.aop.support.JdkRegexpMethodPointcut是通用的正则表达式切点表达式,使用JDK1.4支持的正则表达式。

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,你可以提供一个匹配字符串清单。如果任何一个被匹配,切点解析将返回true。(因此结果对于合并的切点有效)

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允许我们引用一个advice(记住一个advice可以是一个拦截器,在advice之前,异常advice等等)。之后,spring将会使用JdkRegexpMethodPointcut。使用RegexpMethodPointcutAdvisor简化处理,作为一个bean可以处理切点和advice,展示如下:

<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也可以作为任意一个Advice类型来使用。

Attribute-driven pointcuts

基于属性的切点

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类。

[Note]

注意

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.

控制流切点是十分耗费资源的当在运行时被解析比动态切点。在java1.4中,大概是动态切点的5倍左右。

12.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.

你可以使用自定义切点并附带任意advice类型在spring1.0RC2或更高的版本。

12.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切点表达式语言是推荐的如果可行的话。

[Note]

注意

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来支持,“所有的方法改变实例变量对于目标objec”

12.3 Advice API in Spring

spring中的Advice的API

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

让我们看一下如果在spring的aop中处理advice

12.3.1 Advice lifecycles

Advice的生命周期

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.

每个advice是一个spring的bean。一个advice实例可以被所有adviced的object共享,或对于每个adviced的object独立。相关于类之前或实例之前的advice。

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.

类之前的advice比较常用。他适合通用的advice例如事务advisor。他们不依赖于代理object的状态或添加新的状态;他们在方法和参数上有效。

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

实例之前的advice适合于介绍,为了支持混合。在这个例子中,advice为代理object添加了状态。

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

可以用于一个混合的共享和实例之前的advice在相同的aop代理中。

12.3.2 Advice types in Spring

spring中的advice的类型

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提供一些advice类型对外,并且可以扩展支持任意的advice类型。让我们看一下advice类型的基本概念和标准。

Interception around advice

围绕拦截advice

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

spring中最基本的advice类型是环绕拦截advice。

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接口对于环绕advice使用方法拦截。MethodInterceptors实现环绕advice应当实现下面的接口:

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!

注意MethodInvocation中proceed方法的调用。这个处理拦截器链对于连接点。大多数拦截器将会调用这个方法并返回他的返回值。然而,MethodInterceptor类似于任意的环绕advice可以返回一个不同的值或抛出一个异常而不是调用proceed方法。然而你不可能毫无理由就这么做了。

[Note]

注意

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.

MethodInterceptors提供和其他aop的Alliance实现的互操作性。其他的advice类型在后续的部分讨论,他们实现了共同的aop内容但是以spring特定的方式。当这也有优点关于使用特定的advice类型,和MethodInterceptor环绕adivce相连如果你希望在另一个aop框架中运行方面。注意切点不能彼此操作在框架见,并且aop的Alliance不支持当前定义的切点接口。

Before advice

前置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.

一个简单的advice类型是前置advice。他不需要MethodInvocation的object,他只需要在进入方法前被调用即可。

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.

前置advice的优点是不需要调用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设计允许属性前置advice,尽管通常object应用于属性拦截并且不像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。前置advice可以插入自定义行为在连接点执行之前,但是不能改变返回值。如果一个前置advice抛出一个异常,将放弃后续拦截器链的执行。异常将会通过拦截器链传播。如果是非检查的或在调用方法签名上,将直接返回给客户端,另外他将会被aop代理处理为非检查异常。

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

在spirng中一个前置advice的例子,将计算所有方法的调用计数:

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;

    }

}

[Tip]

提示

Before advice can be used with any pointcut.

前置advice可以用于任意的切点上。

Throws advice

异常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:

异常advice被调用在连接返回之后如果连接点抛出一个异常。spring提供类型通过advice。注意这意味着org.springframework.aop.ThrowsAdvice接口不包含任何方法,他是一个标签接口定义给定object实现一个或多个类型通过advice方法。他们的形式应该是:

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.

只有最后一个参数是必须的。方法签名可以有一个或四个参数,依赖于advice方法对于方法和参数的处理。下面的类是一个异常advice的例子。

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

下面的advice被调用如果一个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:

下面的advice被调用如果一个ServletException异常被抛出后。不像上面的advice,他声明了四个参数,因此他可以访问被调用的方法、方法参数和目标object:

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异常。任何数目个异常advice都可以合并到一个类中。

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

    }

}

[Note]

注意

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!

如果一个异常advice方法本身抛出了一个异常,他将会覆盖原有的异常(例如,改变异常抛出给用户)。覆盖异常将通常是RuntimeException,这适用于任何方法签名。然而,如果一个异常advice方法抛出了一个检查异常,他将匹配定义异常关于目标方法并在一定程度耦合到特定目标方法签名中。不要抛出未定义的检查异常对于不合适的目标方法签名。

[Tip]

提示

Throws advice can be used with any pointcut.

异常advice可以使用在任何切点上。

After Returning advice

返回后advice

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

一个返回后advice在spring中必须实现org.springframework.aop.AfterReturningAdvice接口,展示如下:

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.

一个返回后advice可以访问返回值(如果没有被修改),调用方法、方法参数和目标。

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

下面的返回后advice统计所有成功方法调用就是没有抛出异常的:

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.

advice不会改变执行路径。如果他抛出异常,将会被抛到拦截器链中代替返回值。

[Tip]

提示

After returning advice can be used with any pointcut.

返回后advice可以用在任意切点上。

Introduction advice

介绍advice

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

spring处理介绍advice作为特定类型的拦截器advice。

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

介绍需要一个IntroductionAdvisor和IntroductionInterceptor实现下面的接口:

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().

invoke方法阶乘子aop的Alliance MethodInterceptor接口必须实现:如果invoked方法在introduced上,introduction拦截器代表处理方法调用————将不会调用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:

介绍advie不会被使用在任何的切点上,只是应用在类上而不是方法级别。你只能使用介绍advice和IntroductionAdvisor一起使用,有如下的方法:

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也没有PointCut与介绍advice相连。只有类过滤是逻辑的。

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方法用于内部查看是否介绍的接口可以实现通过配置的IntroductionInterceptor。

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的测试包。让我们假设我们希望介绍如下的接口给一个或多个object。

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.

这个说明是一个mixin。我们希望可以转换advised的object到Lockable,不管他的类型并且调用lock和unlock方法。如果我们调用lock方法我们希望所有的设置方法抛出一个LockedException异常。我们可以添加一个方面来证明可以使得object不可改变,不需要任何相关的知识:一个好的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.

首先,我们需要一个IntroductionInterceptor来处理问题。在这种情况下,我们扩展了org.springframework.aop.support.DelegatingIntroductionInterceptor这个方便的类。我们可以直接实现IntroductionInterceptor通过使用DelegatingIntroductionInterceptor对于大多数的情况是好的。

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.

DelegatingIntroductionInterceptor被设计来作为一个介绍对于实际介绍接口的实现,隐藏了拦截器来这样做。委派可以设置任何object使用一个构造器参数,默认的委派(使用无参数的构造器)被使用。在下面的例子中,委派是LockMixin子类继承DelegatingIntroductionInterceptor。给定的委托(默认是其本身),一个DelegatingIntroductionInterceptor实例查看所有接口实现通过委派(而不是IntroductionInterceptor)将支持介绍。对于子类例如LockMixin是可能的调用suppressInterface方法来阻止接口本不需要被暴露。然而无论许多接口是IntroductionInterceptor支持的,IntroductionAdvisor将会用于控制实际暴露的接口。一个介绍接口将任何实现有关相同的接口通过目标。

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继承了DelegatingIntroductionInterceptor并实现了Lockable本身。超类自动处理Lockable可以支持介绍,因此我们不需要定义。我们可以以这种方法介绍多个接口。

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

注意锁定实例变量的使用。他有效的添加了额外的状态对于目标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方法:DelegatingIntroductionInterceptor的实现,调用了委托方法如果方法被介绍,此外对于连接点的处理通常是足够的。在之前的例子中,我们需要添加检查:不管set方法可以被调用如果在锁模式下。

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.

介绍advisor的需求是简单的。所有需要做的是保持明显的LockMixin实例并且定义了介绍接口————在这个例子中,类似于Lockable。一个更加复杂的例子可以利用一个引用处理介绍拦截器(可以被定义作为一个原型):在这个例子,不需要有关LockMixin的配置因此我们简单创建他使用。

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:不需要配置。(然而,这是必须的:不能独立使用IntroductionInterceptor不配合IntroductionAdvisor)。和普通的介绍一样,advisor必须是per-instance并且是有状态的。我们需要不同的LockMixinAdvisor的实例并且对于每个advised的object对应的LockMixin。advisor由advised的object的状态的部分组成。

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.

我们可以变成应用advisor,使用Advised.addAdvisor方法或(推荐方式)在xml配置中,类似于其他的advisor。所有代理创建选择讨论如下,包括“自动代理creators”正确地处理介绍和状态的mixin。

12.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是一个方面包括一个单独的advice的object连接一个切点表达式。

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.

和特定的介绍例子分离,任何advisor可以被用于任何advice上。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.

可以混合advisor和advice类型在spring中以相同的aop代理。例如你可以使用一个拦截器环绕advice、异常advice和前置advice在一个代理配置中:spring将会自动创建需要的拦截器链。

12.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)对于你的业务object————并且你应当————你将会希望使用一个spring的aop的FactoryBeans。(记住一个工厂bean介绍一间接层,允许创建不同类型的object)。

[Note]

注意

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.

基本的方法创建一个aop代理在spring中用于使用org.springframework.aop.framework.ProxyFactoryBean。这提供了完整的控制有关切点和将会应用的advice和他们的顺序。然而他们是简单的选项而且如果你不需要这样的控制会更好。

12.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和其他的工厂bean实现一样,介绍一个中间层。如果你定义一个ProxyFactoryBean名字为foo,object指向foo不是ProxyFactoryBean实例本身,但是一个object通过ProxyFactoryBean的实现创建实现了getObject方法。这个方法创建一个aop代理来包裹目标object。

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或另一个感知IOC类来创建AOP代理,也就意味着advice和切点可以通过ioc来管理。这是一个有力的特性,允许特定的方法处理其他AOP框架。例如,一个advice可以引用应用object(除目标之外,应当在任意的aop框架中可用),有利对于所有独立注入的插件。

12.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 Section 12.5.3, “JDK- and CGLIB-based proxies”).

定义是否使用cglib(见下面和12.5.3章节“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 Section 12.5.3, “JDK- and CGLIB-based proxies”).

proxyTargetClass:为true如果目标类被代理而不是目标类的接口。如果这个属性是设置为true,则CGLIB代理将被创建(见12.5.3章节“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.

optimize:控制是否优化被应用于代理通过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.

frozen:如果一个代理配置为forzen,则改变配置是不允许。作为轻微的优化是有意义的并且对于这些情况当你不希望调用者除了代理(通过Advised接口)在代理被创建后。默认值对于这个属性是false,因此对于添加额外的advice默认是允许的。

    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 Section 12.5.3, “JDK- and CGLIB-based proxies”).

proxyInterfaces:字符串接口names的数组。如果没有被提供,一个CGLIB代理对于目标类将会被使用(见12.5.3章节“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:Advisor的字符串数组,拦截器或其他advice的名字。顺序是有意义的,对于首先被服务的基础。也就是说第一个拦截器在list中将首先可以拦截调用。

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.

names是bean的name在当前工厂中,包括bean的name来自祖先工厂。你不可以引用bean的引用如果这么做将导致ProxyFactoryBean忽略单例设置对于advice。

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 Section 12.5.6, “Using 'global' advisors”.

你可以添加一个拦截器名字为asterisk ( *)。这将导致在应用中所有的advisor的bean开始的部分在被应用的asterisk之前。一个例子使用这个特性可以发现在12.5.6节“使用全局advisor”。

    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.

单例:不管工厂是否返回单个object,不管getObject被调用。一些工厂bean实现提供一个方法。默认值是true。如果你希望使用状态化的advice,例如,对于混合状态,使用prototype的advice对于单例值是false。

12.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的代理对于特定的object(被代理)。

[Note]

注意

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.x和2.0之间对于spring。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.)

如果目标object的类被代理(以后简单的引用作为目标类)不需要实现任何接口,一个基于CGLIB的代理将被创建。这是最早的部分,因为JDK代理是基于接口的并且没有接口意味着JDK代理是不可能的。一个简单的插件在目标bean中并且定义拦截器列表通过interceptorNames属性。注意基于CGLIB代理将被创建尽管ProxyFactoryBean的proxyTargetClass属性被设置为false。(明显这毫无意义并且是最好的从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.

如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现一个(或多个)接口,则ProxyFactoryBean将自动探测,目标类实际至少实现一个接口并且基于JDK代理将被创建。接口实际被代理将会是所有目标类实现的接口,事实上和支持一个列表和每个接口是一样简单的,对于proxyInterfaces属性。然而工作量是少的并且避免打字错误。

12.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.

一个Advisor和一个拦截器将用于提供advice。

    An AOP proxy bean definition specifying the target object (the personTarget bean) and the interfaces to proxy, along with the advices to apply.

一个AOP代理bean定义定义了目标object(personTarget的bean)和代理的接口,和advice在一起。

<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的名字或在当前工厂中的advisor。Advisors、拦截器、前后返回和异常advice的object可以被使用。advisor的顺序是重要的。

[Note]

注意

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.

你或许会疑惑为了list没有保存bean的引用。理由是如果ProxyFactoryBean’s的单例属性被设置为false,他必须可以独立返回代理实例。如果任何一个advisor是原型的,一个独立的实例将需要被返回,因此有必要获得一个原型的实例来自工厂,因此保存一个引用是不够的。

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:

其他bean在相同的ioc上下文中可以表达一个强烈的类型依赖对他,作为普通的java的object:

<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代理可以被用于明显的位置代替实际person的实现。然而,他的类将是动态的代理类。可以被转换为Advised接口(下面讨论的)。

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的定义是不同的,含有advice只是为了完整性考虑。

<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类型的object:如果我们希望保护用户的应用上下文从获得引用对于未advised的object或需要避免任何含糊有关spring的ioc自动处理。也可以论证ProxyFactoryBean定义是自包含的。然而获得未advised的目标来自工厂可以实际是一个优势:例如在测试环境。

12.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接口:我们需要advise一个类名字为Person没有实现任何接口。在这个例子中,你可以配置spring来使用CGLIB代理而不是动态代理。简单设置ProxyFactoryBean的proxyTargetClass属性为true。最好的编程对于接口而不是类,用于advicse类不实现任何接口可以很有用当用于以前的代码中。(通常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配置这个生成的子类用于委托方法来调用原始的object:子类被用于实现装饰者模式在advice中。

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.

Final方法不能被advised,因为他们不能被覆盖。

    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到你的classpath中。在spring3.2中,CGLIB已经打包在spring-core的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代理和动态代理之间。在spring1.0中,动态代理是稍快的。然而在以后或许会改变。性能不会再这个例子中占太大的比重。

12.5.6 Using 'global' advisors

使用全局的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:

通过添加一个星号对于拦截器的名字,所有的advisor关于bean的名字的匹配将在星号之前,可以添加到advisor链中。这迟早会有用如果你需要添加标准的全局advisor的集合。

<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"/>

12.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的定义可以导致更加干净和简洁的代理定义。

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不会以任何方式被外部使用。

<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,此外应用上下文将试图提前实例化他。

12.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的aop而不需要依赖于spring的ioc。

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:

下面展示了对于目标object创建代理和一个拦截器和一个advisor。接口实现对于目标object将自动被代理:

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类型的object。你可以以此创建一个目标object,例如上面的例子,或指定接口代理替代构造器。

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.

你可以添加advice(附带拦截器作为特定类型的advice)和/或advisor,并且操纵他们用于ProxyFactory的生命周期。如果你添加一个IntroductionInterceptionAroundAdvisor,你可以使得代理实现额外的接口。

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)允许你添加其他的advice类型例如前置和异常advice。AdvisedSupport是ProxyFactory和ProxyFactoryBean的超类。

[Tip]

提示

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代码进行形象化的配置。

12.8 Manipulating advised objects

处理advised的object

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、拦截器和其他advice类型被添加到工厂中。如果你添加一个Advisor,返回的advisor在这个位置将是你添加的object。如果你添加一个拦截器或其他advice类型,spring将包裹advisor和切点并总是返回true。如果你添加一个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保持切点并且advice将是通常的DefaultPointcutAdvisor,可以被使用在任何advice或切点中(但是不能用于introductions)

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.)

默认情况下,可以添加或移除advisor或拦截器尽管一个代理被创建。唯一的限制是不能添加或移除一个introduction的advisor,作为已经存在的代理来源于工厂见不会展示接口变化。(你可以获得一个新的代理来自工厂用于避免这个问题)

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

一个简单的例子关于转化一个aop代理为Advised接口和检查、控制这个advice:

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

// 添加一个advice像一个没有切点的拦截器

// Will match all proxied methods

// 将匹配所有代理方法

// Can use for interceptors, before, after returning or throws advice

// 可以用于拦截器、前置、后置返回或异常advice

advised.addAdvice(new DebugInterceptor());

// Add selective advice using a pointcut

// 添加使用切点的可选advice

advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));

assertEquals("Added two advisors", oldAdvisorCount + 2, advised.getAdvisors().length);

[Note]

注意

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.)

这是可疑的不管是否是明智的(没有打算双关语)来修改advice对于业务object在生产中,尽管对于使用没有疑问。然而,可以很有用在开发中,例如测试。我有时发现很有用对于添加测试代码以拦截器的形式或其他advice,获得我希望测试的内部的方法调用。(例如,advice可以获得内部的事务创建对于方法:例如,允许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.

依赖于你怎么创建代理,你通常可以设置一个标志位,Advised的isFrozen方法将返回true,并且任何尝试修改advice通过额外或移动将导致一个AopConfigException。冻结状态的能力对于一个advised的object在一些情况下,例如避免调用代码移除一个安全拦截器。也可以使用在spring1.1中允许最优化如果运行时advice修改被知道不需要确认的情况。

12.9 Using the "auto-proxy" facility

使用自动代理功能

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

至此我们考虑了明确的创建方式关于AOP代理使用ProxyFactoryBean或类似的工厂bean。

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.

在这个模式下,你设置一些特定的bean定义在你的xml的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.

一个特定的例子关于自动代理创建值得被分开考虑,自动代理创建由源码级别的元数据属性驱动。

12.9.1 Autoproxy bean definitions

自动代理bean的定义

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

org.springframework.aop.framework.autoproxy包提供了下面标准的自动代理创建器。

BeanNameAutoProxyCreator

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

BeanNameAutoProxyCreator类是一个BeanPostProcessor自动创建AOP代理对于bean的名字匹配字面值或通配符。

<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属性而不是拦截器列表允许正确的行为对于原型的advisor。名字为interceptors可以是advisor或任何advice类型。

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是应用相同的配置对于不同的object,并且是最小化的配置。这是很好的选择对于应用声明的事务对于不同的object。

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定义对于目标类。一个aop代理将被自动创建通过BeanNameAutoProxyCreator。相同的advice将被应用对于所有匹配的bean。注意如果advisor被使用(而不是上面例子中的拦截器),切点可以对于不同的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。将自动应用于符合的advisor在当前的上下文中,而不需要包含特定的bean的名字在自动代理的advisor的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.

定义任意数量的Advisor在相同或相关的上下文中。注意必须是Advisor,不只是拦截器或其他advice。这是必要的因为必须有一个切点需要衡量,检查对于每个advice的符合对于候选的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中,检查什么样的advice将应用于每个业务object(例如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.

这意味着任何数目的advisor可以自动应用于每个业务object。如果没有切点在任何advisor中匹配一个业务object中的任何方法,object将不会被代理。作为bean定义添加到新的业务object,将自动被代理根据需要。

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.)

自动代理通常有优势对于调用者或依赖获得一个非advised的object。调用这个ApplicationContext上的getBean("businessObject1")将返回一个aop代理,不是目标业务object。(内部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是很有用的如果你希望应用相同的advice对于许多业务object。一旦基础设置定义已经就位,你可以简单添加新的业务object不需要包含特定的代理配置。你也可以简单的处理很多方面————例如,跟踪或性能检测方面————使用最小的改变来配置。

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会被处理,允许使用多个不同配置在相同工厂中的AdvisorAutoProxyCreators)和顺序。Advisor可以实现org.springframework.core.Ordered接口用于保证正确的顺序如果这是一个问题。TransactionAttributeSourceAdvisor在上面的例子中被使用有一个可配置的值:默认值是无序的。

AbstractAdvisorAutoProxyCreator

This is the superclass of DefaultAdvisorAutoProxyCreator. You can create your own auto-proxy creators by subclassing this class, in the unlikely event that advisor definitions offer insufficient customization to the behavior of the framework DefaultAdvisorAutoProxyCreator.

这是DefaultAdvisorAutoProxyCreator的超类。你可以创建你自己的自动代理创建器通过继承这个类,在一个不可能事件中advisor定义提供不充足的自定义对于框架DefaultAdvisorAutoProxyCreator的行为。

12.9.2 Using metadata-driven auto-proxying

使用元数据驱动的自动代理

A particularly important type of auto-proxying is driven by metadata. This produces a similar programming model to .NET ServicedComponents. Instead of defining metadata in XML descriptors, configuration for transaction management and other enterprise services is held in source-level attributes.

一个特别重要的类型有关自动代理是使用元数据驱动的。这产生了一个和.NET ServicedComponents相似的编程模型。代替在xml描述符中定义元数据,配置业务管理和其他企业服务在源码级别的属性。

In this case, you use the DefaultAdvisorAutoProxyCreator, in combination with Advisors that understand metadata attributes. The metadata specifics are held in the pointcut part of the candidate advisors, rather than in the auto-proxy creation class itself.

在这个例子中,你使用DefaultAdvisorAutoProxyCreator与Advisor结合在一起除了元数据属性。元数据定义被保持在切点部分对于候选的advisor,而不是在自动代理类本身存储。

This is really a special case of the DefaultAdvisorAutoProxyCreator, but deserves consideration on its own. (The metadata-aware code is in the pointcuts contained in the advisors, not the AOP framework itself.)

这是一个十分特殊的例子对于DefaultAdvisorAutoProxyCreator但是值得考虑其自身。(在切点中意识到元数据包含在advisor中,不是AOP框架本身)。

The /attributes directory of the JPetStore sample application shows the use of attribute-driven auto-proxying. In this case, there’s no need to use the TransactionProxyFactoryBean. Simply defining transactional attributes on business objects is sufficient, because of the use of metadata-aware pointcuts. The bean definitions include the following code, in /WEB-INF/declarativeServices.xml. Note that this is generic, and can be used outside the JPetStore:

JPetStore例子应用的attributes目录展示了基于属性的自动代理的使用。在这个例子中,不需要使用TransactionProxyFactoryBean。简单定义业务属性在业务object上就足够了,因为意识元数据切点的使用。bean定义包含以下代码,在/WEB-INF/declarativeServices.xml中。注意这是一个一般的例子并且可以在JPetStore之外使用。

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">

    <property name="transactionInterceptor" ref="transactionInterceptor"/>

</bean>

<bean id="transactionInterceptor"

        class="org.springframework.transaction.interceptor.TransactionInterceptor">

    <property name="transactionManager" ref="transactionManager"/>

    <property name="transactionAttributeSource">

        <bean class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource">

            <property name="attributes" ref="attributes"/>

        </bean>

    </property>

</bean>

<bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/>

The DefaultAdvisorAutoProxyCreator bean definition (the name is not significant, hence it can even be omitted) will pick up all eligible pointcuts in the current application context. In this case, the "transactionAdvisor" bean definition, of type TransactionAttributeSourceAdvisor, will apply to classes or methods carrying a transaction attribute. The TransactionAttributeSourceAdvisor depends on a TransactionInterceptor, via constructor dependency. The example resolves this via autowiring. The AttributesTransactionAttributeSource depends on an implementation of the org.springframework.metadata.Attributes interface. In this fragment, the "attributes" bean satisfies this, using the Jakarta Commons Attributes API to obtain attribute information. (The application code must have been compiled using the Commons Attributes compilation task.)

DefaultAdvisorAutoProxyCreator的bean定义(名字不是关键,因此可以被忽略)将处理所有符合的切点在当前的应用上下文中。在这个例子中,transactionAdvisor的bean定义,是TransactionAttributeSourceAdvisor类型的,将应用于类和方法并携带一个业务属性。TransactionAttributeSourceAdvisor依赖TransactionInterceptor通过构造器依赖。例子通过自动包装来处理这个问题。AttributesTransactionAttributeSource实现了org.springframework.metadata.Attributes接口。在这个片段中,attributes的bean足以,使用了Jakarta Commons Attributes API来获得属性信息。(应用代码必须被编译使用通用属性编译任务)

The /annotation directory of the JPetStore sample application contains an analogous example for auto-proxying driven by JDK 1.5+ annotations. The following configuration enables automatic detection of Spring’s Transactional annotation, leading to implicit proxies for beans containing that annotation:

JPetStore例子应用中的annotation目录包含一个类似的例子有关自动代理通过JDK1.5的注解。下面的配置允许自动探测spring的业务注解,导致对于bean的代理包含注解:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">

    <property name="transactionInterceptor" ref="transactionInterceptor"/>

</bean>

<bean id="transactionInterceptor"

        class="org.springframework.transaction.interceptor.TransactionInterceptor">

    <property name="transactionManager" ref="transactionManager"/>

    <property name="transactionAttributeSource">

        <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>

    </property>

</bean>

The TransactionInterceptor defined here depends on a PlatformTransactionManager definition, which is not included in this generic file (although it could be) because it will be specific to the application’s transaction requirements (typically JTA, as in this example, or Hibernate, JDO or JDBC):

TransactionInterceptor在这里依赖于PlatformTransactionManager的定义,没有包含在泛型文件中(尽管可以这么做)因为这将被定义对于应用的业务需求(通常JTA在这个例子中或Hibernate、JDO或JDBC):

<bean id="transactionManager"

        class="org.springframework.transaction.jta.JtaTransactionManager"/>

[Tip]

提示

If you require only declarative transaction management, using these generic XML definitions will result in Spring automatically proxying all classes or methods with transaction attributes. You won’t need to work directly with AOP, and the programming model is similar to that of .NET ServicedComponents.

如果你需要声明式事务管理,使用这个通用的xml定义将导致spring自动代理所有类和方法的业务属性。你不需要直接使用AOP,并且编程模型和.NET ServicedComponents很相似。

This mechanism is extensible. It’s possible to do auto-proxying based on custom attributes. You need to:

策略是可以扩展的。可以实现自动代理对于自定义属性。你需要:

    Define your custom attribute.

定义你的自定义属性。

    Specify an Advisor with the necessary advice, including a pointcut that is triggered by the presence of the custom attribute on a class or method. You may be able to use an existing advice, merely implementing a static pointcut that picks up the custom attribute.

定义一个Advisor和相关的advice,包括切点将被触发类和方法上的自定义属性。你可以使用已有的advice,实现一个静态切点处理自定义属性。

It’s possible for such advisors to be unique to each advised class (for example, mixins): they simply need to be defined as prototype, rather than singleton, bean definitions. For example, the LockMixin introduction interceptor from the Spring test suite, shown above, could be used in conjunction with a generic DefaultIntroductionAdvisor:

对于这些advisor可以独立对于advised类(例如,混合):他们简单需要被定义为原型,而不是单例,有关bean的定义。例如,LockMixin介绍拦截器来自spring的测试包,展示如上,可以被使用和一个通用的DefaultIntroductionAdvisor:

<bean id="lockMixin" class="test.mixin.LockMixin" scope="prototype"/>

<bean id="lockableAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor"

        scope="prototype">

    <constructor-arg ref="lockMixin"/>

</bean>

Note that both lockMixin and lockableAdvisor are defined as prototypes.

注意lockMixin和lockableAdvisor应该被定义为原型。

12.10 Using TargetSources

使用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提供了TargetSources的概念,定义在org.springframework.aop.TargetSource接口总。这个接口用于返回目标object实现连接点。TargetSource的实现被要求目标实例每次aop代理处理一个方法调用。

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,一个默认实现被使用用于处理本地object。相同的目标对于每次调用(根据你的需要)。

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

让我们看一下标准target sources由spring提供,并且如何使用他。

[Tip]

提示

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.

当使用一个自定义target source是,你的目标将通常需要是一个原型而不是单例的bean。这允许spring根据需要创建一个新的目标实例。

12.10.1 Hot swappable target sources

热插拔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.

改变target source的目标会立即有效。HotSwappableTargetSource是线程安全的。

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

你可以改变目标通过swap方法在HotSwappableTargetSource中如下:

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调用改变了swappable 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.

尽管这个例子没有添加任何advice————并且他不需要添加advice来使用TargetSource————当然任何TargetSource可以被使用和任意的advice在一起。

12.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.

使用池化的target source提供了一个类似会话EJB的编程模型,在一个池的实例中,方法调用将释放在池中的object。

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 Pool2.2,提供一个相当有效的池化实现。你将需要commons-pool的jar在你的classpath中来使用这个特性。也可以继承org.springframework.aop.target.AbstractPoolingTargetSource来支持任何其他池API。

[Note]

注意

Commons Pool 1.5+ is also supported but deprecated as of Spring Framework 4.2.

Commons Pool 1.5以上的版本也支持但是在spring框架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.

注意目标object————例子中的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上下文中。然而,不需要定义拦截器来用于池化。如果你只希望使用池化并且不需要advice,不要设置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因此可以转化任何池化的object对于org.springframework.aop.target.PoolingConfig接口,暴露了信息有关配置和当前池的容量通过一个introduction。你也需要定义一个advisor如下:

<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类中一个方便的方法,因此使用MethodInvokingFactoryBean。advisor的名字(在这里是poolConfigAdvisor)必须在拦截器名字的列表中,ProxyFactoryBean暴露了池化的object。

The cast will look as follows:

例子如下:

PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");

System.out.println("Max pool size is " + conf.getMaxSize());

[Note]

注意

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.

池化的无状态服务object不是必须的。我们不相信他是默认的选择,作为大部分无状态object都是天然线程安全的,并且实例池化是有问题的如果资源是缓存的。

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

简单的池化可以使用自动代理。可以设置TargetSource通过自动代理创建器来使用。

12.10.3 Prototype target sources

原型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.

设置原型的target source是和池化的TargetSource相似的。在这个例子中,一个新的实例关于目标将被创建对于每个方法调用。尽管创建一个新object的消耗不是很高对于现代的JVM,处理一个新的object(使用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实现用于保证一致的命名。作为池化的target source,目标bean必须是原型bean的定义。

12.10.4 ThreadLocal target sources

线程本地化的target source

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:

线程本地化target source是有用的如果你需要一个object被创建对于每个输入的请求(每个线程)。线程本地化的概念提供了一个JDK范围的便利对于明显的存储资源在一个线程中。

<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">

    <property name="targetBeanName" value="businessObjectTarget"/>

</bean>

[Note]

注意

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.set(null))本地的线程资源。复位在任何情况下应该可行而且不会导致错误的行为。spring的本地线程支持帮你完成了此事并且应当被考虑支持使用ThreadLocal而不是其他适当的手动操作代码。

12.11 Defining new Advice types

定义新的Advice类型

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被设计成可扩展的。当拦截器实现策略可以在内部使用,支持任意数量的advice类型此外对于外部的拦截环绕advice、前置advice、异常advice和后置返回的advice。

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.

org.springframework.aop.framework.adapter包是一个SPI包允许支持新的自定义advice类型被添加而不需要改变核心框架。自定义advice类型唯一的限制是他必须实现org.aopalliance.aop.Advice标记接口。

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

请参考org.springframework.aop.framework.adapter的javadocs来查看更多的信息。

12.12 Further resources

更多资源

Please refer to the Spring sample applications for further examples of Spring AOP:

请参考spring的样例应用有关更多spring的aop的例子:

    The JPetStore’s default configuration illustrates the use of the TransactionProxyFactoryBean for declarative transaction management.

JPetStore的默认配置声明TransactionProxyFactoryBean的使用对于声明式的事务管理。

    The /attributes directory of the JPetStore illustrates the use of attribute-driven declarative transaction management.

JPetStore的/attribute目录声明基于属性的声明式事务管理。