CodeGym /Java Course /Module 5. Spring /Using ProxyFactoryBean to create an AOP proxy

Using ProxyFactoryBean to create an AOP proxy

Module 5. Spring
Level 5 , Lesson 10
Available

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

Spring AOP helpers also use hidden factory beans.

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: is true if the target class will be proxied rather than the interfaces of the target class. If this property is set to true, 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 the frozen 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 the Advised interface) after it has been created. The default value of this property is false, so changes (such as adding additional tips) are allowed.

  • exposeProxy: Defines whether the current proxy must be opened in 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, the target can use the AopContext.currentProxy() method.

Other properties specific to ProxyFactoryBean are:

  • proxyInterfaces: Array String interface names. If this property is not specified, then the CGLIB proxy for the target class is used.

  • interceptorNames: String array of names Advisor, 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. Several FactoryBean implementations provide such a method. The default value is true. If you need to use a stateful tip—for example, for stateful mixins—use prototype tips along with a singleton property value of false.

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

The logic behind 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 and Interceptor 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.

You may be wondering why there are no links to beans in the list. The reason is that if the singleton object property on the 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:

Java
Person person = (Person) factory.getBean("person");
Kotlin
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"/>
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION