Weaving in Spring AOP 2
Please refer to spring-framework under org.springframework.mylearntest package for code related to this article (from official source spring-test module).
Accelerating the Weaving Automation Process
Spring AOP automatic proxy implementation is built on concept of BeanPostProcessor in IoC container, using a BeanPostProcessor, and then implementing such logic inside BeanPostProcessor: when object is instantiated, generate proxy object for it and return, instead of instantiated target object itself, thus achieving purpose of automatic proxy.
for(bean in IoC container){
// Check if current bean definition satisfies interception condition, intercept if yes
if(isAssistentStatement){
Object proxy = createProxyFor(bean);
return proxy;
} else {
Object instance = createInstance(bean);
return instance;
}
}
Interception conditions
-
Pass in these interception condition information via external configuration files, for example
PointcutandAdvisoretc. we registered in container configuration file include these information; -
Can also make known specific interception conditions via metadata in definition file of specific class, for example via Jakarta Commons Attributes or Java 5 annotations, directly mark
Pointcutand other interception information in code class.
AutoProxy Classes Available in Spring
Spring AOP provides two commonly used AutoProxyCreator under org.springframework.aop.framework.autoproxy package, namely BeanNameAutoProxyCreator and DefaultAdvisorAutoProxyCreator.
BeanNameAutoProxyCreator
Using BeanNameAutoProxyCreator allows applying a specified set of interceptors to these target objects by specifying BeanName corresponding to a set of target objects in container.
XML Configuration Case
<beans>
<bean id="target1" class="..."/>
<bean id="target2" class="..."/>
<bean id="mockTask" class="..."/>
<bean id="fakeTask" class="..."/>
<bean id="taskThrowsAdvice" class="...TaskThrowsAdvice"/>
<bean id="performanceInterceptor" class="...PerformanceInterceptor">
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!--Specify which beans to automatically generate proxy objects for-->
<property name="beanNames">
<list>
<value>target1</value>
<value>target2</value>
</list>
</property>
<!--Specify interceptors, Advice or Advisor etc. to be applied to target objects-->
<property name="interceptorNames">
<list>
<value>taskThrowsAdvice</value>
</list>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<!--Use * for wildcard-->
<list>
<value>mockTask*</value>
<value>fakeTask*</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>performanceInterceptor</value>
</list>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<!--For * wildcard case, can also use comma to separate-->
<list>
<value>target*,*Task,*service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>performanceInterceptor</value>
</list>
</property>
</bean>
</beans>
DefaultAdvisorAutoProxyCreator
Just need to register Bean in ApplicationContext, remaining tasks will be completed by DefaultAdvisorAutoProxyCreator. After injecting it into container, it will automatically search for all Advisor in container, then generate corresponding proxy objects for target objects in container that meet conditions according to interception information provided by each Advisor.
DefaultAdvisorAutoProxyCreator is only effective for Advisor, because only Advisor has both Pointcut information to capture target objects meeting conditions, and corresponding Advice.
XML Configuration Case
<beans>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<!--Set object to use class-based proxy-->
<property name="proxyTargetClass">
<value>true</value>
</property>
</bean>
<bean id="target1" class="..."/>
<bean id="target2" class="..."/>
<bean id="mockTask" class="..."/>
<bean id="fakeTask" class="..."/>
<bean id="logAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
...
</property>
<property name="advice">
<bean id="performanceInterceptor"
class="org.springframework.mylearntest.aop.advice.perclass.PerformanceMethodInterceptor"></bean>
</property>
</bean>
<bean id="logAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
...
</property>
<property name="advice">
<bean id="taskThrowsAdvice" class="...TaskThrowsAdvice"></bean>
</property>
</bean>
</beans>
Extending AutoProxyCreator
Can implement corresponding subclasses based on AbstractAutoProxyCreator or AbstractAdvisorAutoProxyCreator provided by Spring AOP.
Implementation architecture regarding automatic proxy in Spring AOP framework
- All
AutoProxyCreatorareInstantiationAwareBeanPostProcessor, this type ofBeanPostProcessoris different from ordinaryBeanPostProcessor. When Spring IoC container detectsBeanPostProcessorofInstantiationAwareBeanPostProcessortype, it will directly construct object instance via logic inInstantiationAwareBeanPostProcessorand return, instead of going through normal object instantiation flow. I.e. "Short circuit". This wayAutoProxyCreatorwill directly construct proxy object of target object and return, instead of original target object.

AspectJAwareAdvisorAutoProxyCreator is AutoProxyCreator implementation after Spring 2.0, also considered a custom implementation of AutoProxyCreator. It also has a subclass AnnotationAwareAspectJAutoProxyCreator, which can capture information based on Java 5 annotations to complete automatic proxy.
Spring AOP also supports automatic proxy mechanism based on Jakarta Commons Attributes metadata, to provide interception information.

Role of TargetSource
TargetSource is container of target object, when each method call to target object reaches end of call chain after layers of interceptions, it is time to call method defined on target object, at this time it does not directly call method on this target object, but interacts with actual target object via a certain TargetSource, regarding then call corresponding method on target object obtained from TargetSource.
Features of TargetSource
Every method call will trigger getTarget() method of TargetSource, getTarget() method will get specific target object from corresponding TargetSource implementation class, via this, we can control specific object instance that each method call acts on.
- Provide a target object pool, target object obtained from
TargetSourceevery time is obtained from this target object pool. - Let a
TargetSourceimplementation class hold instances of multiple target objects, then return corresponding target object instance during each method call according to certain rules.
Can also let TargetSource hold only one target object, usually ProxyFactory or ProyxFactoryBean processes target object in this way too, they will construct a org.springframework.aop.target.SingletonTargetSource instance internally, and SingletonTargetSource will return instance reference of same target object for every method call.
TargetSource Implementation Classes
SingletonTargetSource
org.springframework.aop.target.SingletonTargetSource is most used TargetSource implementation class, although we may not know. Because after setting target object via setTarget() of ProxyFactory, ProxyFactory internal will automatically use a SingletonTargetSource to encapsulate set target object.

PrototypeTargetSource
PrototypeTargetSource Usage
<beans>
<bean id="target" class="org.springframework.mylearntest.aop.weaver.baseoninterface.MockTask"
scope="prototype"/>
<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
<property name="targetBeanName">
<value>target</value>
</property>
</bean>
<bean id="targetProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource">
<ref bean="prototypeTargetSource"/>
</property>
<property name="interceptorNames">
<list>
<value>anyInterceptor</value>
</list>
</property>
</bean>
</beans>
Bean definition declaration of target object must be prototype. Specify bean definition name of target object via targetBeanName attribute, instead of reference.
HotSwappableTargetSource
Using HotSwappableTargetSource to encapsulate target object allows us to dynamically replace concrete implementation of target object class according to certain specific conditions while application is running, for example, IService has multiple implementation classes, if default IService implementation class has problem after program starts, we can immediately switch to another implementation of Iservice, and all these are transparent to caller.
xml configuration
<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">
<bean id="task" class="org.springframework.mylearntest.aop.weaver.baseoninterface.MockTask">
</bean>
<bean id="hotSwapTargetSource" class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg>
<ref bean="task"/>
</constructor-arg>
</bean>
<bean id="taskProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="hotSwapTargetSource"/>
<property name="interceptorNames">
<list>
<value>performanceMethodInterceptor</value>
</list>
</property>
</bean>
<bean id="performanceMethodInterceptor"
class="org.springframework.mylearntest.aop.advice.perclass.PerformanceMethodInterceptor"/>
</beans>
Test4HotSwappableTargetSource
package org.springframework.mylearntest.aop.weaver.hotswaptargetsource;
import org.junit.Assert;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mylearntest.aop.weaver.baseoninterface.ITask;
import java.util.Date;
/**
* @Author: whalefall
* @Date: 2020/7/26 19:47
*/
public class Test4HotSwappableTargetSource {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("hotswappabletargetsource\\hotSwappableTargetSource.xml");
Object proxy = context.getBean("taskProxy");
Object initTarget = ((Advised)proxy).getTargetSource().getTarget();
HotSwappableTargetSource hotSwappableTargetSource = (HotSwappableTargetSource)context.getBean(
"hotSwapTargetSource");
Object oldTarget = hotSwappableTargetSource.swap(new ITask() {
@Override
public void execute(Date date) {
System.out.println("old target generated by hotSwapTargetSource");
}
});
Object newTarget = ((Advised)proxy).getTargetSource().getTarget();
// initTarget = org.springframework.mylearntest.aop.weaver.baseoninterface.MockTask@72967906
// oldTarget = org.springframework.mylearntest.aop.weaver.baseoninterface.MockTask@72967906
// newTarget = org.springframework.mylearntest.aop.weaver.hotswaptargetsource
// .Test4HotSwappableTargetSource$1@5b8dfcc1
Assert.assertSame(initTarget, oldTarget);
Assert.assertNotSame(initTarget, newTarget);
}
}
CommonsPoolTargetSource
Sometimes, we may want to return limited number of target object instances, these target object instances are equal in status, just like those Connection in database connection pool, we can provide a target object pool, then let a certain TargetSource implementation get target object from this object pool every time.
If Jakarta Commons Pool cannot be used, then can also implement corresponding TargetSource providing object pooling function by extending org.springframework.aop.target.AbstractPoolingTargetSource class.
ThreadLocalTargetSource
If want to provide different target objects for calls of different threads, then can use org.springframework.aop.target.ThreadLocalTargetSource. It can ensure calls to target object on respective threads can be assigned to instance of that target object corresponding to current thread. Actually, ThreadLocalTargetSource is just simple encapsulation of JDK standard ThreadLocal.
Custom TargetSource
AlternativeTargetSource
package org.springframework.mylearntest.aop.weaver.selfdefinetargetsource;
import org.springframework.aop.TargetSource;
import org.springframework.mylearntest.aop.weaver.baseoninterface.ITask;
/**
* @Author: whalefall
* @Date: 2020/7/27 22:27
*/
@SuppressWarnings("rawtypes")
public class AlternativeTargetSource implements TargetSource {
private ITask alternativeTask1;
private ITask alternativeTask2;
private int counter;
public AlternativeTargetSource(ITask task1, ITask task2) {
this.alternativeTask1 = task1;
this.alternativeTask2 = task2;
}
@Override
public Object getTarget() throws Exception {
try {
if (counter % 2 == 0)
return alternativeTask2;
else
return alternativeTask1;
} finally {
counter ++;
}
}
@Override
public Class getTargetClass() {
return ITask.class;
}
@Override
public boolean isStatic() {
return false;
}
@Override
public void releaseTarget(Object arg0) throws Exception {
}
}
Test4AlternativeTargetSource
package org.springframework.mylearntest.aop.weaver.selfdefinetargetsource;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.mylearntest.aop.weaver.baseoninterface.ITask;
import java.util.Date;
/**
* @Author: whalefall
* @Date: 2020/7/27 22:33
*/
public class Test4AlternativeTargetSource {
public static void main(String[] args) {
ITask task1 = new ITask() {
@Override
public void execute(Date date) {
System.out.println("execute in Task1");
}
};
ITask task2 = new ITask() {
@Override
public void execute(Date date) {
System.out.println("execute in Task2");
}
};
ProxyFactory pf = new ProxyFactory();
TargetSource targetSource = new AlternativeTargetSource(task1, task2);
pf.setTargetSource(targetSource);
Object proxy = pf.getProxy();
for (int i = 0; i < 100; i++) {
((ITask)proxy).execute(new Date());
}
}
}
References
- Book Name: Spring Unveiled Author: Wang Fuqiang
- Attribution: Retain the original author's signature and code source information in the original and derivative code.
- Preserve License: Retain the Apache 2.0 license file in the original and derivative code.
- Attribution: Give appropriate credit, provide a link to the license, and indicate if changes were made.
- NonCommercial: You may not use the material for commercial purposes. For commercial use, please contact the author.
- ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.