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目錄聲明基于屬性的聲明式事務管理。
新聞熱點
疑難解答