Autoproxy in Spring Aspect-Oriented Programming (AOP)

Create dynamic, feature-rich, and robust enterprise applications using the Spring framework

Autoproxy with classic Spring

By using the class ProxyFactoryBean, AOP can be used in a classic way. But writing separately for each bean on which we want to apply an advisor is not a pleasant thing to see, especially if they are many. So let's consider it as a practicable way only if the beans to be configured in that modality are few.

On the other hand, if the beans to which we have to apply AOP are many, in order to avoid finding ourselves with very long configuration files, we adopt another tactic: We use the auto proxy creator's system, which allows us to automatically create proxies for the beans and prevent using ProxyFactoryBean.

There are two classes made available by Spring to allow the auto proxy creator: BeanNameAutoProxyCreator and DefaultAdvisorAutoProxyCreator.

BeanNameAutoProxyCreator

BeanNameAutoProxyCreator just has a list of beans names to which proxy can be created automatically.

The way in which the autoproxy is created is really simple. It implements the BeanPostProcessor interface, which in its implementation replaces the bean (target) with a proxy.

Example:

This is the interface describing an animal.

package org.springaop.chapter.three.autoproxy.domain;

public interface Animal {

public Integer getNumberPaws();
public Boolean hasTail();
public boolean hasFur();
public Boolean hasHotBlood();

}

The (interface) Bird extends Animal.

package org.springaop.chapter.three.autoproxy.domain;

public interface Bird extends Animal{

public Boolean hasBeak();
public Boolean hasFeathers();

}

The class that implements the Animal interface to describe Cat:

package org.springaop.chapter.three.autoproxy.domain;

public class Cat implements Animal{

public boolean hasFur() {
return true;
}

public Integer getNumberPaws() {
return 4;
}

public Boolean hasTail() {
return true;
}

public Boolean hasHotBlood() {
return true;
}

public void setSpecies(String species) {
this.species = species;
}

public String getSpecies() {
return species;
}

public String getColour() {
return colour;
}

public void setColour(String colour) {
this.colour = colour;
}

private String species, colour;
}

The class that implements Animal and Bird to describe a Seabird:

package org.springaop.chapter.three.autoproxy.domain;

public class Seabird implements Animal,Bird{

public Integer getNumberPaws() {
return 2;
}

public Boolean hasTail() {
return false;
}

public Boolean hasBeak() {
return true;
}

public Boolean hasFeathers() {
return true;
}

public boolean hasFur() {
return false;
}

public Boolean hasHotBlood() {
return false;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

private String name;
}

AnimalAdvice containing just the log with the target class, the invoked method, and the result.

package org.springaop.chapter.three.autoproxy;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AnimalAdvice implements MethodInterceptor {

public Object invoke(MethodInvocation invocation) throws Throwable {
Logger log = Logger.getLogger(Constants.LOG_NAME);
StringBuilder sb = new StringBuilder();
sb.append("Target Class:").append(invocation.getThis()).append("n").append(invocation.getMethod()).append("n");

Object retVal = invocation.proceed();

sb.append(" return value:").append(retVal).append("n");
log.info(sb.toString());
return retVal;
}
}

The configuration file applicationContext.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">


<bean id="tiger" class="org.springaop.chapter.three.autoproxy.domain.Cat">
<property name="species" value="tiger"/>
<property name="colour" value="tear stripes"/>
</bean>

<bean id="albatross" class="org.springaop.chapter.three.autoproxy.domain.Seabird">
<property name="name" value="albatross"/>
</bean>


<!-- Pointcut -->
<bean id="methodNamePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>has*</value>
<value>get*</value>
</list>
</property>
</bean>


<!-- Advices -->
<bean id="animalAdvice" class="org.springaop.chapter.three.autoproxy.AnimalAdvice"/>


<!-- Advisor -->
<bean id="animalAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="methodNamePointcut"/>
<property name="advice" ref="animalAdvice"/>
</bean>


<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="proxyTargetClass" value="true"/>
<property name="beanNames">
<list>
<value>tiger</value>
<value>albatross</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>animalAdvisor</value>
</list>
</property>
</bean>

</beans>

Application context contains two beans, tiger and albatross. The methodNamePointcut acts on the methods starting with has and get.

The animalAdvice (around advice) contains the logics to be executed, the animal advisor that links the animalAdvice to the methodNamePointcut, and the autoProxyCreator, where we declare just the beans' names and the list of interceptors' names.

package org.springaop.chapter.three.autoproxy;

public class AutoProxyTest {

public static void main(String[] args) {

String[] paths = { "org/springaop/chapter/three/autoautoproxy/applicationContext.xml" };

ApplicationContext ctx = new ClassPathXmlApplicationContext(paths);

Cat tiger = (Cat)ctx.getBean("tiger");
tiger.hasHotBlood();

Bird albatross = (Bird)ctx.getBean("albatross");
albatros.hasBeak();
}
}

The test class invokes two methods on the beans tiger and albatross.

Output:

DefaultAdvisorAutoProxyCreator

With BeanNameAutoProxyCreator, we've seen that the configuration file's extension has reduced; but we can do better.

Using the previous example, we modify only the configuration file.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="tiger" class="org.springaop.proxies.autoproxy.domain.Cat">
<property name="species" value="tiger"/>
<property name="colour" value="tear stripes"/>
</bean>

<bean id="albatross" class="org.springaop.chapter.three.autoproxy.domain.Seabird">
<property name="name" value="albatross"/>
</bean>

<!-- Pointcut -->
<bean id="methodNamePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>has*</value>
<value>get*</value>
</list>
</property>
</bean>

<!-- Advices -->
<bean id="animalAdvice" class="org.springaop.chapter.three.autoproxy.AnimalAdvice"/>


<!-- Advisor -->
<bean id="animalAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="methodNamePointcut"/>
<property name="advice" ref="animalAdvice"/>
</bean>

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" >
<property name="proxyTargetClass" value="true"/>
</bean>

</beans>

With DefaultAdvisorAutoProxyCreator, we don't need to define anything, apart from declaring it. This is because it applies the proxies' creation for the classes concerned in the advisors' application through pointcuts. It's important to have advisors, and not any other type of interceptor, because the operation is based on advisors.

The result of the advices' application is the same as you can see from the output:

AbstractAdvisorAutoProxyCreator

In case one wants to create his or her own AutoproxyCreator, he or she can employ the DefaultAdvisorAutoProxyCreator superclass, which is the AbstractAdvisorAutoProxyCreator, just by extending it.

AutoProxyCreator with metadata

This autoproxy option concerns the possibility to employ annotations in the classes, for example to define transactions.

In order to be able to employ this type of configuration, we have to use DefaultAdvisorAutoProxyCreator, a CommonsAttributes bean that interprets source level metadata, and another bean that employs those attributes.

In order to employ the CommosAttributes bean, we must have in the classpath the Jakarta library Commons Attributes (http://commons.apache.org/attributes).

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
...

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

<bean id="advisor" 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="metadataAttributes" />
</bean>
</property>
</bean>

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

<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>

</beans>

The autoProxyCreator bean creates proxies according to what advisors in the application context indicate to it, or the advices to apply and the pointcuts where to apply them.

In the example case, it is composed by a transactionInterceptor, the rules of which are defined into classes through annotations. In order to interpret these annotations, which are the beans' attributes property, it uses the metadataAttributes bean, which is of the type CommonsAttributes.

This type of configuration is used to be employed mostly before JDK 1.5, and it requires a particular compilation task.

With the use of del JDK 1.5 or upward, configuration doesn't require the use of the bean CommonsAttributes to interpret annotations.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

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

<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>

</beans>

Autoproxy with AspectJ

We saw autoproxy's devices with the classic configuration of Spring. Now we're going to see a short introduction on proxies with AspectJ.

The use of AspectJ can be connected to two typologies: through code annotations or through XML schema. Here are the rules that define which beans are excluded from the autoproxy:

  • Proxy is not applied to beans that implement the interfaces BeanPostProcessor or BeanFactoryPostProcessor. The class AnnotationAwareAspectJAutoProxyCreator implements the interface BeanPostProcessor, which allows the class to modify the life cycle of beans on which a proxy must be created and applied.
  • Classes with annotations @AspectJ and classes that implement or extend any other AOP component are excluded from the autoproxy. This is because they aren't target classes, and they perform tasks in Spring AOP infrastructure.

Apart from the beans that belong to these two cases, the other ones can be subject to proxies' self-creation. This is so if they are subject to aspects and advisors with the matching rules defined in pointcuts that are defined in the applicationContext.

Autoproxy with annotation

If we use annotations, we will tell Spring that proxies must be created according to them:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
...
</beans>

AnnotationAwareAspectJAutoProxyCreator must be declared only one time. It's used by Spring to configure all the classes that have the annotation @Aspect, in order to create the proxy for the noted class and give it back for the execution of advices when a pointcut is matched.

Autoproxy with XML Schema

If we want automatic generation of proxies, we will use the tag <aop:aspectj-autoproxy/> through the AOP namespaces. Also this tag must be used one time. But in case of error, beans' proxies will be created only once, whereas if we declare AnnotationAwareAspectJAutoProxyCreator in several points of beans, we'll have several times the proxies' creation. The effect is anyway identical to the use of the bean AnnotationAwareAspectJAutoProxyCreator.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
...
<aop:aspectj-autoproxy/>
...
</beans>

If we want to force the use of CGLIB proxies, we will modify the tag aspectj-autoproxy in this way:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<aop:aspectj-autoproxy proxy-target-class="true"/>
...
</beans>

Summary

In this article, we have seen the important role of autoproxy on which Spring AOP is based. We saw how to reduce the configuration with the features provided by Spring for the automatic creation of beans with three types of autoproxy.

Books to Consider

comments powered by Disqus