If you use a Spring IoC container (ApplicationContext
or BeanFactory
) for your business
objects (and you should!), your the task is to use one of the FactoryBean
implementations from Spring
AOP. (Remember that the factory bean introduces a layer of indirection, which allows you to create other types of
objects.)
The main way to
create an AOP proxy in Spring is to use org.springframework.aop.framework.ProxyFactoryBean
. It gives
you complete control over the slices, any tips that are applied, and their ordering. However, there are simpler
options that are preferable if you don't need such control.
Basics
ProxyFactoryBean
,
like other implementations FactoryBean
from Spring, introduces a level of indirection. If you define a
ProxyFactoryBean
with the name foo
, objects referencing foo
do not see the
ProxyFactoryBean
instance itself, but the object , created by the implementation of the getObject()
method in the ProxyFactoryBean
. This method creates an AOP proxy that wraps the target object.
One of the most important advantages of using a ProxyFactoryBean
or other IoC-compatible class to
create an AOP proxy is that tips and slices also can be controlled via IoC. This is an effective feature that allows
you to take certain approaches that are difficult to implement using other AOP frameworks. For example, the board
itself can reference application objects (in addition to the target, which must be in any AOP framework), taking
full advantage of the modularity that dependency injection brings.
JavaBean Properties
How and most
FactoryBean
implementations provided by Spring, the ProxyFactoryBean
class is itself a
JavaBean class. Its properties are used to:
Specify the target that needs to be proxyed.
Specify whether to use CGLIB (described below, see also section "Proxies based on JDK and CGLIB").
Some key properties are inherited from org.springframework.aop.framework.ProxyConfig
(superclass
for of all AOP proxy factories in Spring). These key properties consist of:
proxyTargetClass
: istrue
if the target class will be proxied rather than the interfaces of the target class. If this property is set totrue
, then CGLIB proxies are created.optimize
: Controls whether aggressive optimization methods for proxies created using CGLIB. This option should not be used lightly unless you understand how the corresponding AOP proxy controls optimization. Currently only used for CGLIB proxy. The parameter does not affect dynamic JDK proxies.frozen
: If the proxy configuration has thefrozen
property, then changes to it no longer allowed. This can be used both for light optimization and in cases where you do not want callers to be able to manipulate the proxy (via theAdvised
interface) after it has been created. The default value of this property isfalse
, so changes (such as adding additional tips) are allowed.exposeProxy
: Defines whether the current proxy must be opened inThreadLocal
so that it can be accessed by the target. If the target needs to obtain a proxy and theexposeProxy
property is set totrue
, the target can use theAopContext.currentProxy()
method.
Other properties specific to ProxyFactoryBean
are:
proxyInterfaces
: ArrayString
interface names. If this property is not specified, then the CGLIB proxy for the target class is used.interceptorNames
:String
array of namesAdvisor
, interceptors or other advice to apply. Ordering plays an important role, everything happens in the order of priority. Thus, the first interceptor in the list will be the first to intercept the call.Names are the names of the beans in the current factory, including the names of beans from parent factories. In this case, you cannot mention bean references, since this will cause the
ProxyFactoryBean
to ignore the singleton object parameter in the tip.You can append the interceptor name with an asterisk (
*
). This will cause all advisor beans with names starting with the part before the asterisk to be applied.singleton: Whether the factory should return a single object, no matter how often The
getObject()
method is called. SeveralFactoryBean
implementations provide such a method. The default value istrue
. If you need to use a stateful tip—for example, for stateful mixins—use prototype tips along with a singleton property value offalse
.
JDK and CGLIB based proxies
This section serves as prescriptive documentation regarding how the ProxyFactoryBean
chooses between creating a JDK based or a CGLIB based proxy for a particular target object (which should be
proxyed).
ProxyFactoryBean
regarding creating a JDK or
CGLIB based proxy has changed between versions 1.2.x and 2.0 Spring. ProxyFactoryBean
now has the same
semantics regarding automatic detection of interfaces as the TransactionProxyFactoryBean
class.
If the class of the target object that is to be proxied (hereinafter simply called the target class), does not
implement any interfaces, a proxy is created based on CGLIB. This is the simplest scenario because JDK proxies are
based on interfaces, and the lack of interfaces means that JDK proxying is not possible at all. You can connect the
target bean and specify a list of interceptors by setting the interceptorNames
property. Note that a
CGLIB-based proxy is created even if the proxyTargetClass
property in the ProxyFactoryBean
was set to false
. (Doing this is pointless and it is better to remove this property from the bean
definition because it is redundant at best and confusing at worst.)
If the target class implements one (or
more) interfaces, then the type of proxy created depends on the configuration of the ProxyFactoryBean
.
If the proxyTargetClass
property in the ProxyFactoryBean
was set to true
,
then a CGLIB-based proxy is created. This is logical and consistent with the principle of least surprise.
Even if the proxyInterfaces
property in the ProxyFactoryBean
was set to one or more fully
qualified interface names, the fact that the proxyTargetClass
property is set to true
causes CGLIB-based proxying to occur.
If the proxyInterfaces
property in the ProxyFactoryBean
was set to one or more fully qualified interface names, a proxy is created based on the JDK. The created proxy
implements all the interfaces that were specified in the proxyInterfaces
property. If the target class
implements many more interfaces than those specified in the proxyInterfaces
property, that's great, but
those additional interfaces will not be implemented by the returned proxy.
If the
proxyInterfaces
in ProxyFactoryBean
was not specified, but the target class implements one
(or more) interfaces, ProxyFactoryBean
automatically detects the fact that the target class does
implement at least one interface, and a proxy is created based on the JDK. The interfaces that are actually proxied
are all the interfaces that the target class implements. This is essentially the same as specifying a list of each
and every interface that the target class implements for the proxyInterfaces
property. However, it is
significantly less labor intensive and less likely to make typos.
Proxying Interfaces
Let's look at
a simple example of ProxyFactoryBean
in action. This example includes:
The target bean to be proxied. This is the definition of the
personTarget
bean in the example.Advisor
andInterceptor
used to provide advice .Define an AOP proxy bean to specify the target object (the
personTarget
bean), the interfaces for the proxy, and the tips that will be applied.
The following listing shows an example:
<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 accepts a list String
that contains names of
interceptor or advisor beans in the current factory. You can use advisors, interceptors, objects with "before",
"after return" and "throw exception" advice. The ordering of the advisors is important.
ProxyFactoryBean
is set to false
, it must
be able to return independent proxy instances. If any of the advisors are themselves prototypes, then an independent
instance will need to be returned, so it is necessary to be able to obtain an instance of the prototype from the
factory. A single reference is not enough.
The person
bean definition shown earlier can be used instead of the Person
implementation as follows:
Person person = (Person) factory.getBean("person");
val person = factory.getBean("person") as Person;
Other beans in the same IoC context can express strongly a typed dependency on it, just like a regular Java object. 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 for the
AOP proxy, it can be used transparently instead of implementing a "real" person type. However, its class will be
dynamic. You could cast it to the Advised
interface (more on that later).
You can hide the
difference between the target and the proxy by using an anonymous internal bean. Only the
ProxyFactoryBean
definition is different. Tips are provided 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 an internal bean rather than a local reference to the 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 advantage of using an anonymous inner bean is that there will only be one Person
object. This
is useful if we want to prevent users of the application context from obtaining a reference to an object that is not
provided with a hint, or if we want to avoid ambiguity in automatic discovery and binding during IoC in Spring. It's
also probably an advantage that the ProxyFactoryBean
definition is self-contained. However, there are
times when being able to retrieve a target from the factory that has not been provided with advice can be an
advantage (for example, in certain testing scenarios).
Proxying classes
What if need to proxy a class rather than one or more interfaces?
Imagine that in our previous example there was no
Person
interface. We needed to provide advice to a class called Person
that did not
implement any business interface. In this case, you can configure Spring to use CGLIB proxying rather than dynamic
proxies. To do this, set the proxyTargetClass
property of the ProxyFactoryBean
demonstrated earlier to true
. While it's best to program in interfaces rather than classes, the ability
to provide advice to classes that don't implement interfaces can be useful when working with legacy code. (In
general, Spring is not prescriptive. While this makes it easier to follow best practices, it can avoid being forced
into a particular approach.)
If you want, you can force CGLIB to be used in any case, even if you have interfaces.
CGLIB proxying works by generating a subclass of the target class during program execution. Spring configures this generated subclass to delegate method calls to the original target. The subclass is used to implement the Decorator pattern by associating tips with it.
CGLIB proxying should generally be transparent to users. However, there are some points that need to be taken into account:
Final
methods cannot be advised because they cannot be overridden.There is no need to add CGLIB to your classpath. As of Spring 3.2, CGLIB has been repackaged and included in the spring-core module JAR. In other words, CGLIB-based AOP works out of the box, just like dynamic JDK proxies.
The performance difference between CGLIB proxying and dynamic proxying is negligible. Performance should not be a deciding factor in this case.
Using "global" advisors
When adding an asterisk to the interceptor name, all advisors with bean names that match the part before the asterisk are added to the chain of advisors. This can be useful 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"/>
GO TO FULL VERSION