Detailed Explanation of Spring AOP API

The previous chapter introduced Spring's support for AOP, including @AspectJ and schema-based aspect definitions. In this chapter, we will discuss the low-level Spring AOP API. For normal applications, we recommend using Spring AOP with AspectJ pointcuts as described in the previous chapter.

6.1. Pointcut API in Spring

This section describes how Spring handles key pointcut concepts.

6.1.1. Concept

Spring's pointcut model makes pointcut reuse independent of the advice type. You can use the same pointcut for different advice.

The org.springframework.aop.Pointcut interface is the central interface used to provide advice for specific classes and methods. The complete interface is as follows.

public interface Pointcut {

    ClassFilter getClassFilter();

    MethodMatcher getMethodMatcher();
}

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

The ClassFilter interface is used to restrict the pointcut to a given set of target classes. If the matches() method always returns true, all target classes are matched. The following listing shows the definition of the ClassFilter interface.

public interface ClassFilter {

    boolean matches(Class clazz);
}

The MethodMatcher interface is usually more important. Complete interface:

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 ever matches a given method on the target class. This evaluation can be done when creating AOP proxies to avoid testing on every method call. If the two-argument matches method returns true for a given method, and the MethodMatcher's isRuntime() method returns true, then the three-argument matches method will be called on every method call. This lets a pointcut look at the arguments passed to the method call immediately before the target advice begins.

Most MethodMatcher implementations are static, which means their isRuntime() method returns false. In this case, the three-argument matches method is never called.

If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcuts evaluation when creating AOP proxies.

6.1.2. Operations on Pointcut

Spring supports pointcut operations (especially union and intersection).

Union refers to any pointcut matching method. Intersection refers to the method where both pointcuts match. Union is usually more useful. You can compose pointcuts by using the static methods in the org.springframework.aop.support.Pointcuts class or by using the ComposablePointcut class in the same package. However, using AspectJ's pointcut expressions is often an easier approach.

6.1.3. Pointcuts for AspectJ expressions

Since 2.0, the most important pointcut type used by Spring is org.springframework.aop.aspectj.AspectJExpressionPointcut. This is a pointcut that parses AspectJ pointcut expression strings using libraries provided by AspectJ.

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

6.1.4. Convenient Pointcut Implementation

Spring provides several convenient pointcut implementations. You can use some of them directly; others are meant to be subclassed at application-specific pointcuts.

Static Pointcut

Static pointcut is based on method and target class, and cannot take into account the parameters of the method. For most uses, a static pointcut is adequate and best. Spring can only evaluate static pointcuts once, the first time a method is called. After that, there is no need to evaluate that pointcut again every time the method is called.

The remainder of this section describes some of the static pointcut implementations included in Spring.

Regular Expression Pointcut

An obvious way to specify static pointcuts is regular expressions. org.springframework.aop.support.JdkRegexpMethodPointcut is a generic regular expression pointcut that uses the regular expression support in the JDK.

Through the JdkRegexpMethodPointcut class, you can provide a list of pattern strings. If any of them match, then the pointcut is evaluated to true. (Thus, the resulting pointcut is actually a union of the specified patterns.)

The following example shows how to use JdkRegexpMethodPointcut.

<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 called RegexpMethodPointcutAdvisor that allows us to reference an Advice as well (remember, an Advice can be an interceptor, before advice, throws advice, and others). Behind the scenes, Spring uses JdkRegexpMethodPointcut. Using RegexpMethodPointcutAdvisor simplifies assembly because this bean encapsulates both Pointcut and Advice, as shown in the following example.

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

You can use RegexpMethodPointcutAdvisor with any Advice type.

Attribute Driven Pointcuts

An important type of static pointcut is the metadata-driven pointcut. It uses the value of a metadata attribute (usually source-level metadata).

Dynamic pointcuts

Dynamic pointcuts are more expensive to evaluate than static pointcuts. They take into account the parameters of the method as well as static information. This means they have to be evaluated every time the method is called, and the result cannot be cached because the parameters will vary.

The main example is control flow pointcut.

Control Flow Pointcut

Spring control flow pointcut is conceptually similar to AspectJ's cflow pointcut, but less powerful. (There is currently no way to specify that a pointcut run below a joinpoint matched by another pointcut). A control flow pointcut matches the current call stack. For example, if the join point is called by a method in the com.mycompany.web package or by the SomeCaller class, it will start. Control flow pointcut is specified by using org.springframework.aop.support.ControlFlowPointcut class.

Control flow pointcuts are even much more expensive to evaluate at runtime than other dynamic pointcuts. In Java 1.4, its cost is about five times that of other dynamic pointcuts.

6.1.5. Pointcut superclass

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

Since static pointcuts are the most useful, you should probably subclass StaticMethodMatcherPointcut. This only requires implementing one abstract method (although you can override other methods to customize behavior). The following example shows how to subclass StaticMethodMatcherPointcut.

Java

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.

6.1.6. Custom Pointcut

Because pointcuts in Spring AOP are Java classes, not language features (like AspectJ), you can declare custom pointcuts, either static or dynamic. In Spring, custom pointcuts can be of arbitrary complexity. However, we recommend using AspectJ's pointcut expression language if available.

Later versions of Spring may provide support for the "semantic pointcuts" provided by the JAC—for example, "all methods that mutate instance variables in the target object".

6.2. Spring 的 Advice API

Now we can investigate how Spring AOP handles advice.

6.2.1. Advice life cycle

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

By class advice is the most commonly used. It is suitable for general advice, such as transaction advice. These advices do not depend on the state of the proxied object or add new state. They just act on methods and parameters.

The advice for each instance is suitable for introduction to support mashups. In this case, advice adds state to the proxied object.

You can mix shared and per-instance advice in the same AOP proxy.

6.2.2. Spring 的 Advice Type

Spring provides several advice types and can be extended to support arbitrary advice types. This section introduces the basic concepts and standard advice types.

Interception Around Advice

The most basic type of advice in Spring is interception around advice.

Spring conforms to the AOP Alliance interface for around advice using method interception. Classes that implement MethodInterceptor and implement around advice should also implement the following interface.

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;
}

The MethodInvocation parameter of the invoke() method exposes the invoked method, target join point, AOP proxy, and method parameters. The invoke() method should return the result of the call: the return value of the join point.

The following example shows a simple MethodInterceptor implementation.

Java

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 proceed() method of MethodInvocation. This goes up the interceptor chain towards the join point. Most interceptors call this method and return its return value. However, the MethodInterceptor, like any around advice, can return a different value or throw an exception instead of calling the proceed method. However, you don't want to do this without a good reason.

Implementations of MethodInterceptor provide interoperability with other AOP Alliance-compliant AOP implementations. The other advice types discussed in the rest of this section implement common AOP concepts, but in a Spring-specific way. While there are benefits to using the most specific advice type, stick to the MethodInterceptor around advice if you might want to run the aspect in another AOP framework. Please note that currently pointcut is not interoperable between frameworks, and the AOP alliance currently does not define a pointcut interface.

Before Advice

A simpler type of advice is before advice. This does not require a MethodInvocation object, as it is only called before entering the method.

The main advantage of Before advice is that there is no need to call the proceed() method, so it is impossible to inadvertently fail to follow the interceptor chain.

The following listing shows the MethodBeforeAdvice interface.

public interface MethodBeforeAdvice extends BeforeAdvice {

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

(Spring's API design will allow field before advice, although objects in general are suitable for field interception, and Spring is unlikely to implement it).

Note that the void type is invalid. Before advice can insert custom behavior before the join point runs, but cannot change the return value. If a before advice throws an exception, it stops further execution of the interceptor chain. Exceptions are propagated backwards up the interceptor chain. If it is not selected or on the signature of the method being called, it will be passed directly to the client. Otherwise, it will be wrapped in an unchecked exception by the AOP proxy.

The following example shows a before advice in Spring, which counts all method calls.

Java

public class CountingBeforeAdvice implements MethodBeforeAdvice {

    private int count;

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

    public int getCount() {
        return count;
    }
}

Before advice, can be used with any pointcut.

Throws Advice

If the join point throws an exception, Throws advice will be called after the join point returns. Spring provides typed throws advice. Note that this means that the org.springframework.aop.ThrowsAdvice interface does not contain any methods. It is a tagged interface that identifies that a given object implements one or more typed throws advice methods. These methods should be of the following form.

afterThrowing([Method, args, target], subclassOfThrowable)

Only the last parameter is required. The method signature can have one or four parameters, depending on whether the advice method is interested in methods and parameters. The next two listings show classes that are examples of throws advice.

If a RemoteException is thrown (including from subclasses), the following advice will be invoked.

Java

public class RemoteThrowsAdvice implements ThrowsAdvice {

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

Unlike the previous advice, the next example declares four parameters so that it has access to the method being called, the method parameters, and the target object. If a ServletException is thrown, the following advice will be invoked.

Java

public class ServletThrowsAdviceWithArguments implements ThrowsAdvice {

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

The last example shows how to use both methods in a single class that handles both RemoteException and ServletException. Any number of throws advice methods can be combined in a class. The list below shows the last example.

Java

public static class CombinedThrowsAdvice implements ThrowsAdvice {

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

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

If a throws-advice method throws an exception itself, it overrides the original exception (that is, it changes the exception thrown to the user). The overridden exception is usually a RuntimeException that is compatible with any method's signature. However, if a throws-advice method throws a checked exception, it must match the declared exception of the target method and, therefore, be coupled in some way to a specific target method signature. Do not throw an undeclared checked exception that is not compatible with the signature of the target method!

Throws advice can be used with any pointcut.

After Returning Advice

After returning advice in Spring must implement the org.springframework.aop.AfterReturningAdvice interface, as listed below.

public interface AfterReturningAdvice extends Advice {

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

An after return advice has access to the return value (which cannot be modified), the method that was called, the method's parameters, and the target.

The following is after returning advice to count all successful method calls that did not throw an exception.

Java

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 does not change the execution path. If it throws an exception, it will be thrown up the interceptor chain instead of returning a value.

After returning advice can be used with any pointcut.

Introduction Advice

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

Introduction requires an IntroductionAdvisor and an IntroductionInterceptor to implement the following interfaces.

public interface IntroductionInterceptor extends MethodInterceptor {

    boolean implementsInterface(Class intf);
}

The invoke() method inherited from the AOP Alliance MethodInterceptor interface must implement import. That is, if the called method is on an incoming interface, the incoming interceptor is responsible for handling the method call—​it cannot call proceed().

Introduction advice can be used at any pointcut because it only applies to classes, not methods. You can only use introduction advice in IntroductionAdvisor, which has the following methods.

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

    ClassFilter getClassFilter();

    void validateInterfaces() throws IllegalArgumentException;
}

public interface IntroductionInfo {

    Class<?>[] getInterfaces();
}

There is no MethodMatcher and therefore no Pointcut related to the introduction advice. Only class filtering is logical.

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

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

Consider an example from the Spring test suite, suppose we want to introduce the following interface for one or more objects.

Java

public interface Lockable {
    void lock();
    void unlock();
    boolean locked();
}

This illustrates a mixer. We want to be able to cast the advice object to Lockable, regardless of its type, and call the lock and unlock methods. We want all setter methods to throw a LockedException if we call the lock() method. So we can add an aspect that provides the ability to make objects immutable without them knowing about it: this is a good example of AOP.

First, we need an IntroductionInterceptor to do the heavy lifting. In this case we extend the org.springframework.aop.support.DelegatingIntroductionInterceptor convenience class. We could implement IntroductionInterceptor directly, but in most cases, using DelegatingIntroductionInterceptor is best.

The DelegatingIntroductionInterceptor is designed to delegate the introduction to the actual implementation of the introduced (introduced) interface, implicitly using interception to do so. You can set the delegate to any object using the constructor parameter. The default delegate (when using the parameterless constructor) is this. So, in the next example, the delegate is a LockMixin subclass of DelegatingIntroductionInterceptor. Given a delegate (by default, itself), a DelegatingIntroductionInterceptor instance looks for all interfaces implemented by the delegate (except IntroductionInterceptor), and supports introduction against any of them. Subclasses like LockMixin can call the suppressInterface(Class intf) method to suppress interfaces that should not be exposed. However, regardless of how many interfaces an IntroductionInterceptor is intended to support, the IntroductionAdvisor used controls which interfaces are actually exposed. An imported interface hides any implementation of the same interface by the target.

Therefore, LockMixin extends DelegatingIntroductionInterceptor and implements Lockable itself. The superclass automatically catches that Lockable is supported for importing, so we don't need to specify that. We can introduce any number of interfaces in this way.

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

The following example shows an example of the LockMixin class.

Java

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);
    }

}

Normally, you don't need to override the invoke() method. Usually, a DelegatingIntroductionInterceptor implementation (which calls the delegate method if the method is introduced, otherwise proceeds to the join point) is sufficient. In this case we need to add a check: if in locked mode, no setter methods can be called.

The required introduction just needs to hold a unique instance of LockMixin and specify the introduced interface (in this case, only Lockable). A more complex example might require a reference to the import interceptor (which will be defined as a prototype). In this case, there is no configuration related to LockMixin, so we create it with new. The following example shows our LockMixinAdvisor class.

Java

public class LockMixinAdvisor extends DefaultIntroductionAdvisor {

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

We can apply this advice very simply because it requires no configuration. (However, it is not possible to use IntroductionInterceptor without IntroductionAdvisor). Like usual introductions, advisors must be per-instance because they are stateful. We need a different instance of LockMixinAdvisor to provide LockMixin for each advised object. The advisor includes part of the state of the object being advised.

We can apply this advice programmatically like any other advice by using the Advised.addAdvisor() method or (the recommended way) in the XML configuration. All proxy creation options discussed below, including the "automatic proxy creator", correctly handle introduction and stateful mixins.

6.3. Advisor API in Spring

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

Except for the special case of introduction, any advisor can be used with any advice. org.springframework.aop.support.DefaultPointcutAdvisor is the most commonly used advisor class. It can be used with MethodInterceptor, BeforeAdvice or ThrowsAdvice.

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

6.4. Using ProxyFactoryBean to create AOP proxy

If you use a Spring IoC container (ApplicationContext or BeanFactory) for your business objects (and you should!), you want to use one of Spring's AOP FactoryBean implementations. (Remember, the factory bean introduces a layer of intermediary, let it create different types of objects).

Spring's AOP support also uses factory beans.

The basic way to create an AOP proxy in Spring is to use org.springframework.aop.framework.ProxyFactoryBean. This allows full control over pointcuts, any applicable advice, and their ordering. However, if you don't need such control, there are simpler options that are better.

6.4.1. Basic knowledge

ProxyFactoryBean, like other Spring FactoryBean implementations, introduces a certain level of indirection. If you define a ProxyFactoryBean named foo, then objects referencing foo see not the ProxyFactoryBean instance itself, but an object created by the implementation of the getObject() method in the ProxyFactoryBean. This method creates an AOP proxy that wraps a target object.

One of the most important benefits of using ProxyFactoryBean or other IoC aware (aware) classes to create AOP proxies is that advice and pointcuts can also be managed by IoC. This is a powerful feature that enables certain methods that are difficult to achieve with other AOP frameworks. For example, advice itself can reference application objects (in addition to targets, which any AOP framework should have), benefiting from all the pluggability provided by dependency injection.

6.4.2. JavaBean Properties

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

  • Specify the target you want to proxy.
  • Specifies whether to use CGLIB (described later, see also JDK and CGLIB-based proxies).

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

  • proxyTargetClass: true if the target class is to be proxied, rather than the interface of the target class. If this property value is set to true, then a CGLIB proxy will be created (but also see JDK and CGLIB based proxies).
  • optimize: Controls whether aggressive optimizations are applied to proxies created via CGLIB. You should not use this setting lightly unless you fully understand how the relevant AOP proxy handles optimizations. Currently this is only used for CGLIB proxies. It has no effect on JDK dynamic proxies.
  • frozen: If a proxy configuration is frozen (frozen), changes to the configuration are no longer allowed. This is both a slight optimization and for those cases where you don't want the caller 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.
  • exposeProxy: Determines whether the current proxy should be exposed in the ThreadLocal so that it can be accessed by the target. If the target needs to obtain a proxy, and the exposeProxy property is set to true, then the target can use the AopContext.currentProxy() method.

Other specific properties of ProxyFactoryBean include the following.

  • proxyInterfaces: An array of String interface names. If this is not provided, the target class's CGLIB proxy is used (but see also JDK and CGLIB based proxies).
  • interceptorNames: A String array of Advisor, interceptor or other advice names to apply. Sorting is very important, on a first come, first served basis. That is, the first interceptor in the list is the first capable of intercepting the call.
  • These names are the bean names of the current factory, including bean names from ancestor factories. You cannot mention the bean reference here, because doing so will cause the ProxyFactoryBean to ignore the singleton setting of the advice.
  • You can add an asterisk (*) after the interceptor name. The effect of this is that all advisor beans whose names begin with the part before the asterisk will be applied. You can find an example of using this feature in Using the Global Advisor.
  • singleton: Whether the factory should return a singleton object no matter how many times the getObject() method is called. Some FactoryBean implementations provide such methods. The default value is true. If you want to use stateful advice—for example, for a stateful mixin—use prototype advice with a singleton value of false.

6.4.3. JDK- and CGLIB-based proxies

This section is the definitive documentation on how a ProxyFactoryBean chooses to create a JDK-based proxy or a CGLIB-based proxy for a particular target object (the object to be proxied).

The behavior of the ProxyFactoryBean in creating JDK- or CGLIB-based proxies changed between Spring versions 1.2.x and 2.0. ProxyFactoryBean now exhibits similar semantics to the TransactionProxyFactoryBean class in terms of auto-detection interfaces.

If the class of the target object to be proxied (hereinafter referred to as the target class) does not implement any interface, a CGLIB-based proxy is created. This is the simplest case, since JDK proxies are interface-based, and the absence of interfaces means that JDK proxies are not even possible. You can plug in the target bean and specify the list of interceptors by setting the interceptorNames property. Note that CGLIB-based proxies are created even if the proxyTargetClass property of the ProxyFactoryBean is set to false. (There's no point in doing this and it's best removed from the bean definition (definition), since it's redundant at best and confusing at worst.)

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

If the proxyTargetClass property of the ProxyFactoryBean is set to true, a CGLIB-based proxy is created. This makes sense and conforms to the principle of least surprise. Even though the proxyInterfaces property of the ProxyFactoryBean is set to one or more fully qualified interface names, the fact that the proxyTargetClass property is set to true causes CGLIB-based proxies to take effect.

If the proxyInterfaces property of the ProxyFactoryBean is set to one or more fully qualified interface names, a JDK-based proxy is created. The created proxy implements all the interfaces specified in the proxyInterfaces property. This is fine if the target class happens to implement more interfaces than specified in the proxyInterfaces attribute, but those additional interfaces will not be implemented by the returned proxy.

If the proxyInterfaces property of the ProxyFactoryBean is not set, but the target class does implement one (or more) interfaces, the ProxyFactoryBean will automatically detect the fact that the target class does implement at least one interface, and create a JDK-based proxy. The interfaces that are actually proxied are all interfaces implemented by the target class. In effect, this is the same as supplying the proxyInterfaces property with a list of every interface implemented by the target class. However, this significantly reduces the workload and is less prone to typographical errors.

6.4.4. Proxy interface

Consider a simple example of a ProxyFactoryBean in an action. This example involves:

  • A proxied target bean. This is the personTarget Bean definition in the example.
  • An Advisor and an Interceptor are used to provide advice.
  • A definition of an AOP proxy bean, which is used to specify the target object (personTarget Bean), the interface of the proxy, and the advice of the application.

The listing below shows an example of this.

<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 receives a list of Strings holding the bean names of the interceptors or advisors in the current factory. You can use advice objects for advisors, interceptors, before, after returning and throws. The ordering of advisors is important.

You might wonder why the bean reference is not kept in the list. The reason is that if the ProxyFactoryBean's singleton property is set to false, it must be able to return a standalone proxy instance. If any advisor is itself a prototype, it needs to return an independent instance, so it must be possible to obtain an instance of prototype from the factory. Holding a reference is not enough.

The person bean definition shown earlier can be used in place of the Person implementation, as shown below.

Java

Person person = (Person) factory.getBean("person");

Other beans in the same IoC context can express strongly typed dependencies on it, just like ordinary Java objects. The following example shows how to do this.

<bean id="personUser" class="com.mycompany.PersonUser">
    <property name="person"><ref bean="person"/></property>
</bean>

The PersonUser class in this example exposes a property of type Person. As far as it goes, an AOP proxy can be used transparently in place of the "real" person's implementation. However, its class will be a dynamic proxy class. It is possible to cast it to the Advised interface (discussed later).

You can mask the distinction between target and proxy by using an anonymous inner bean. Only the definition of ProxyFactoryBean is different. This suggestion is included for completeness only. The following example shows how to use an anonymous inner 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 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>

The benefit of using an anonymous inner bean is that there is only one object of type Person. This is useful if we want to prevent users of the application context from obtaining references to objects that are not advised, or need to avoid any ambiguity with Spring IoC autowiring. Arguably, there is also the benefit that the definition of the ProxyFactoryBean is self-contained. However, sometimes being able to get an unadvised target from a factory can actually be an advantage (for example, in certain test scenarios).

6.4.5. Proxy class

What if you need to proxy a class, not an interface or interfaces?

Imagine that in our previous example, there was no Person interface. We need to propose a class named Person, which does not implement any business interface. In this case, you can configure Spring to use CGLIB proxies instead of dynamic proxies. To do this, set the proxyTargetClass property on the ProxyFactoryBean shown earlier to true. While it's best to program to interfaces rather than classes, the ability to provide advice to classes that don't implement interfaces can be useful when dealing with legacy code. (In general, Spring is not prescriptive. While it makes it easy to apply good practices, it avoids forcing a particular approach).

If you want, you can force CGLIB to be used in any case, even if you do have interfaces.

CGLIB proxies work by generating a subclass of the target class at runtime. Spring configures this generated subclass to delegate method calls to the original target. This subclass is used to implement the Decorator pattern, in which advice is woven.

CGLIB proxies should generally be transparent to the user. However, there are some issues to consider.

  • Final classes cannot be proxied because they cannot be inherited.
  • final methods cannot be adivce because they cannot be overridden.
  • private methods cannot be adivce because they cannot be overridden.

There is no need to add CGLIB to your classpath. CGLIB is repackaged and included in the spring-core JAR. In other words, CGLIB-based AOP works "out of the box", as does the JDK dynamic proxy.

The performance difference between CGLIB proxies and dynamic proxies is minimal. Performance should not be a decisive consideration in this case.

6.4.6. Using the Global Advisor

By appending an asterisk to the interceptor name, all advisors whose bean names match the part before the asterisk are added to the advisor chain. This is handy if you need to add a standard set of "global" advisors. The following example defines two global advisors.

<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="service"/>
    <property name="interceptorNames">
        <list>
            <value>global*</value>
        </list>
    </property>
</bean>

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

6.5. Concise proxy definition

Especially when defining transactional proxies, you may end up with many similar proxy definitions. Proxy definitions can be made cleaner and more concise with definitions of parent and child beans, as well as definitions of inner beans.

First, we create a parent class, template, bean class definition for the proxy as shown below.

<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 itself is never instantiated, so it can actually be incomplete. Then, each proxy that needs to be created is a child bean definition that wraps the target of the proxy into an inner bean definition, since the target is never used alone anyway. The following example shows such a child bean.

<bean id="myService" parent="txProxyTemplate">
    <property name="target">
        <bean class="org.springframework.samples.MyServiceImpl">
        </bean>
    </property>
</bean>

You can override properties from parent templates. In the example below, we override the settings for transaction propagation.

<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 parent bean example, we explicitly marked the parent bean's definition as abstract by setting the abstract attribute to true, as described earlier, so it may never actually be instantiated. Application contexts (but not simple bean factories), by default, pre-instantiate all singletons. It is therefore important (at least for monadic beans) that if you have a (parent) bean definition that you intend to use only as a template, and this definition specifies a class, you must ensure that the abstract attribute is set to true. Otherwise, the application context will actually try to pre-instantiate it.

6.6. Creating AOP proxies programmatically with ProxyFactory

Creating AOP proxies programmatically with Spring is easy. This lets you use Spring AOP without relying on Spring IoC.

Interfaces implemented by the target object are automatically proxied. The following listing shows creating a proxy for a target object, with an interceptor and an advisor.

Java

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 it with a target object as in the previous example, or specify the interface to proxy to in another constructor.

You can add advice (using interceptors as a kind of specialized advice), advisors, or both, and manipulate them during the life of the ProxyFactory. If you add an IntroductionInterceptionAroundAdvisor you can make the proxy implement additional interfaces.

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

Combining the creation of AOP proxies with an IoC framework is best practice in most applications. We recommend that you use AOP to externalize configuration from your Java code, and generally, you should.

6.7. Manipulating advice objects

Regardless of how you create AOP proxies, you can manipulate them by using the org.springframework.aop.framework.Advised interface. Any AOP proxy can be cast to an interface, regardless of which other interfaces it implements. This interface includes the following methods.

Java

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 returns an Advisor for each advisor, interceptor, or other advisor type that has been added to the factory. If you add an Advisor, the advisor returned at this index is the object you added. If you add an interceptor or other advice type, Spring wraps it in an advisor with a pointer that always returns true. So if you add a MethodInterceptor, the advisor returned by this index is a DefaultPointcutAdvisor that returns your MethodInterceptor and a pointcut that matches all classes and methods.

The addAdvisor() method can be used to add any Advisor. Usually, the advisor that holds pointcut and advice is the generic DefaultPointcutAdvisor, which you can use for any advice or pointcut (but not for introduction).

By default, advisors or interceptors can be added or removed even after the agent has been created. The only limitation is that it is not possible to add or remove import advisors, since existing agents from the factory do not show interface changes. (You can get a new proxy from the factory to avoid this problem).

The following example shows casting an AOP proxy to the Advised interface, and checking and manipulating its advice.

Java

Advised advised = (Advised) myObject;
Advisor[] advisors = advised.getAdvisors();
int oldAdvisorCount = advisors.length;
System.out.println(oldAdvisorCount + " advisors");

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

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

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

It is questionable (no pun intended) whether advice to modify business objects in production is advisable, although there are undoubtedly legitimate use cases. However, it can be very useful in development (for example, in testing). We sometimes find it useful to be able to add test code, in the form of interceptors or other advice, into the method calls we want to test. (For example, advice could step into the transaction created for the method, perhaps run SQL to check that the database was updated correctly, before marking the transaction for rollback).

Depending on how you created your proxy, you can usually set a frozen flag. In this case, the Advised isFrozen() method returns true, and any attempt to modify the advice by adding or removing it will result in an AopConfigException. The ability to freeze the state of an advice object is useful in some situations (for example, to prevent calling code from removing security interceptors).

6.8. Using the "auto-proxy" facility

So far we have considered creating AOP proxies explicitly by using a ProxyFactoryBean or similar factory bean.

Spring also lets us use "auto-proxy" bean definitions, which automatically proxy selected bean definitions. This is built on Spring's "Bean Post Processor" infrastructure, which can modify any bean definition at container load time.

In this mode, you set up some special bean definitions in your XML Bean definition file to configure the autoproxy infrastructure. This allows you to declare targets that are eligible for autoproxying. You don't need to use ProxyFactoryBean.

There are two ways to do this.

  • Reference specific beans in the current context by using an auto-proxy creator.
  • A special case of automatic proxy creation that deserves separate consideration: automatic proxy creation driven by source-level metadata attributes.

6.8.1. Auto-proxy Bean Definition

This section deals with the autoproxy creators provided by the org.springframework.aop.framework.autoproxy package.

BeanNameAutoProxyCreator

The BeanNameAutoProxyCreator class is a BeanPostProcessor that automatically creates AOP proxies for beans whose names match literals or wildcards. The following example shows how to create a BeanNameAutoProxyCreator 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>

Like ProxyFactoryBean, there is an interceptorNames property instead of a list of interceptors to allow correct behavior of the prototype advisor. The named "interceptors" can be advisors or any advice type.

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

Bean definitions with matching names, such as jdkMyBean and onlyJdk in the previous example, are common bean definitions of the target class. An AOP proxy is created automatically by BeanNameAutoProxyCreator. The same advice is applied to all matching beans. Please note that if you use an advisor (instead of an interceptor as in the previous example), then the applicable pointcuts may be different for different beans.

DefaultAdvisorAutoProxyCreator

A more general and extremely powerful auto proxy creator is the DefaultAdvisorAutoProxyCreator. It can automatically apply qualified advisors in the current context without including specific bean names in the bean definition of the autoproxy advisor. It provides the same consistent configuration and avoidance of duplication benefits as BeanNameAutoProxyCreator.

Using this mechanism involves:

  • Specify a DefaultAdvisorAutoProxyCreator bean class definition.
  • Specify any number of advisors under the same or related context. Note that these must be advisors, not interceptors or other advice. This is necessary because there must be a pointcut to evaluate to check the eligibility of each advice against the candidate bean definition.

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

This means that any number of advisors can be automatically applied to each business object. If any point in any advisor does not match any method in the business object, then the object is not proxied. When bean definitions are added to new business objects, they are automatically proxied if necessary.

In general, the benefit of automatic proxying is to make it impossible for callers or dependents to obtain an object that is not advised. Calling getBean("businessObject1") on this ApplicationContext will return an AOP proxy instead of the target business object. (The "inner bean" idiom shown earlier also provides this benefit).

The following example creates a DefaultAdvisorAutoProxyCreator Bean and other elements discussed in this section.

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

DefaultAdvisorAutoProxyCreator is useful if you want to apply the same advice consistently to many business objects. Once the infrastructure definition is in place, you can add new business objects without specific proxy configuration. You can also easily add additional aspects (for example, tracing or performance monitoring aspects) with minimal configuration changes.

DefaultAdvisorAutoProxyCreator provides support for filtering (by using a naming convention so that only certain advisors are evaluated, which allows multiple AdvisorAutoProxyCreators with different configurations to be used in the same factory) and ordering. Advisors can implement the org.springframework.core.Ordered interface to ensure proper ordering if this is a concern. The TransactionAttributeSourceAdvisor used in the previous example has a configurable order value. The default setting is unordered.

6.9. Implementation using TargetSource

Spring provides the concept of TargetSource, represented by org.springframework.aop.TargetSource interface. This interface is responsible for returning the "target object" that implements the join point. The TargetSource implementation is asked to provide a target instance each time the AOP proxy handles a method call.

Developers using Spring AOP generally do not need to use TargetSource implementations directly, but this provides a powerful means of supporting pooling, hotplugging, and other complex targets. For example, a pooled TargetSource can return a different target instance for each invocation by using a pool to manage the instances.

If you do not specify a TargetSource, a default implementation wrapping a local object will be used. Each call returns the same target (as you'd expect).

The remainder of this section describes the standard target sources provided by Spring and how you can use them.

When using a custom target source, your target usually needs to be a prototype, not a singleton bean definition. This allows Spring to create a new target instance when needed.

6.9.1. Hot-pluggable target sources

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

Changing the target source's target takes effect immediately. HotSwappableTargetSource is thread-safe.

You can change the target by using the swap() method of HotSwappableTargetSource, as shown in the following example.

Java

HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);

The following example shows the required XML definition.

<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 previous swap() call changed the target of the swappable bean. Clients holding references to the bean are unaware of the change and immediately start hitting the new target.

Although this example does not add any advice (using a TargetSource does not necessarily add advice), any TargetSource can be used with any advice.

6.9.2. Pooling target sources

Using pooled target sources provides a programming model similar to stateless session EJBs, in which an identical pool of instances is maintained and method calls are sent to free objects in the pool.

An important difference between Spring pool and SLSB pool is that Spring pool can be applied to any POJO. As with Spring, such services can be applied in a non-intrusive manner.

Spring provides support for Commons Pool 2.2, which provides a fairly efficient pooling implementation. You need to have the commons-poo`l Jar installed on your application's classpath to use this feature. You can also subclass `org.springframework.aop.target.AbstractPoolingTargetSource to support any other pooling API.

Commons Pool 1.5+ is also supported, but deprecated since Spring Framework 4.2.

The following listing shows an example configuration.

<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 previous example) must be a prototype. This allows implementations of PoolingTargetSource to create new target instances to grow the pool if necessary. See the javadoc for AbstractPoolingTargetSource and the concrete subclass you wish to use for information on its properties. maxSize is fundamental and is always guaranteed to be present.

In this case, myInterceptor is the name of an interceptor that needs to be defined in the same IoC context. However, you don't need to specify interceptors to use pooling. If you just want to use pooling and nothing else, don't set the interceptorNames property at all.

You can configure Spring to cast any pool object to the org.springframework.aop.target.PoolingConfig interface, which exposes information about the pool's configuration and current size through an introduction. You need to define an advisor similar to the following.

<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, thus using the MethodInvokingFactoryBean. The advisor's name (here poolConfigAdvisor) must be in the list of interceptor names in the ProxyFactoryBean exposing the pool object.

cast is defined as follows.

Java

PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());

Pooling stateless service objects is generally not necessary. We don't think this should be the default choice, since most stateless objects are naturally thread-safe, and if resources are cached, there will be problems with instance pools.

A simpler pool can be obtained by using an autoproxy. You can set any TargetSource implementation used by the auto proxy creator.

6.9.3. Prototype target source

Setting a "prototype" target source is similar to setting a pooled TargetSource. In this case, a new target instance is created with each method call. Although the cost of creating a new object in a modern JVM is not high, the cost of wiring a new object (satisfying its IoC dependencies) can be higher. Therefore, you should not use this method without a very good reason.

To do this, you can modify the poolTargetSource definition shown earlier as follows (we also changed the name for clarity).

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
    <property name="targetBeanName" ref="businessObjectTarget"/>
</bean>

The only property is the name of the target bean. Inheritance is used in the implementation of TargetSource to ensure naming consistency. Like the pooling target source, the target bean must be a prototype bean definition.

6.9.4. ThreadLocal target source

The ThreadLocal target source is useful if you need to create an object per incoming request (i.e. per thread). The concept of ThreadLocal provides a JDK-wide facility to transparently store resources with threads. The method for setting a ThreadLocalTargetSource is basically the same as for other types of target sources, as shown in the following example.

<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
</bean>

Serious problems (possibly memory leaks) can arise when ThreadLocal instances are used incorrectly in a multithreaded and multiclassloader environment. You should always consider wrapping a ThreadLocal in other classes rather than using the ThreadLocal itself directly (except in wrapper classes). Also, you should always remember to properly set and unset (the latter simply requires calling ThreadLocal.set(null) ) the thread's local resources. It should be unset in any case, as failure to unset may result in problematic behavior. Spring's ThreadLocal support does this for you, and when using a ThreadLocal instance you should always take into account that there is no other proper handling code. 

6.10. Define a new Advice Type

Spring AOP is designed to be extensible. Although the interception implementation strategy is currently used internally, in addition to the interception of around advice, before, throws advice, and after returning advice, it can also support arbitrary advice types.

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

Guess you like

Origin blog.csdn.net/leesinbad/article/details/131978078