I had a pretty strange usecase where i wanted to change the target object of the proxy and runtime. Spring makes it so simple to use.
My usecase was as follows
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:META-INF/spring/aop-poc-module-context.xml");
AccountDAO accountDao = (AccountDAO) ctx.getBean("accountDao");
IAccount account = accountDao.getById(123);
System.out.println("account" + account);
account.setName("sudheer");
System.out.println("account" + account);
Is a property is change on the entity , i want to change the entity object itself , that is , in the above use two sysouts will print different objects.
The use case arose because i was using a cache(infinispan) which did not allow modifying POJOs outside a transaction scope.
I did achieve the same using the follwing
public class AccountDAO {
private Advisor advisor;
//This is get methods of the accountDao
public IAccount getById(long id) {
//creating new account - to mock database behavior
IAccount a = new Account(369, "suji");
//Using spring factory
ProxyFactory pf = new ProxyFactory();
pf.setExposeProxy(true);
pf.addInterface(IAccount.class);
//Using by own Target source to change the Target
pf.setTargetSource(new SwappableTargetSource(a));
//advisor which holds the advice and pointcut
pf.addAdvisor(advisor);
return (IAccount) pf.getProxy();
}
public void setAdvisor(Advisor advisor) {
this.advisor = advisor;
}
}
My advice class
public class SwappableBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("Before advice called");
if (!((IAccount) target).isWritable()) {
//Changing the target source based on specific business condition
((SwappableTargetSource) ((Advised) AopContext.currentProxy())
.getTargetSource()).swap(new Account(56, "dan"));
}
}
}
My custom target source
public class SwappableTargetSource implements TargetSource {
private Object target;
public SwappableTargetSource(Object initialTarget) {
this.target = initialTarget;
}
public synchronized Class<?> getTargetClass() {
return this.target.getClass();
}
public final boolean isStatic() {
return false;
}
public synchronized Object getTarget() {
return this.target;
}
public void releaseTarget(Object target) {
// nothing to do
}
public synchronized void swap(Object newTarget)
throws IllegalArgumentException {
this.target = newTarget;
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof SwappableTargetSource && this.target
.equals(((SwappableTargetSource) other).target)));
}
@Override
public int hashCode() {
return SwappableTargetSource.class.hashCode();
}
@Override
public String toString() {
return "SwappableTargetSource for target: " + this.target;
}
}
These are the spring xml configuration.
<bean id="accountDao" class="com.test.dao.account.AccountDAO">
<property name="advisor" ref="settersAdvisor" />
</bean>
<bean id="swapableBeforeAdvice" class="com.test.framework.SwappableBeforeAdvice"/>
This is the advice which advise all setter methods on the bean
<bean id="settersAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="swapableBeforeAdvice" />
</property>
<property name="patterns">
<list>
<value>.*set.*</value>
</list>
</property>
</bean>
Thanks to this post in spring forum which helped me do this.
http://forum.springsource.org/showthread.php?t=102784