Reputation: 116908
We are using Spring's TransactionInterceptor
to set some database partition information using ThreadLocal
whenever a DAO method marked with the @Transactional
annotation is executed. We need this to be able to route our queries to different database partitions.
This works fine for most DAO methods:
// this causes the invoke method to set a thread-local with the host name of
// the database server the partition is on
@Transactional
public int deleteAll() throws LocalDataException {
The problem is when we need to reference the DAO proxy object itself inside of the DAO. Typically we have to have the caller pass in the proxy-dao:
public Pager<Foo, Long> getPager(FooDao proxyDao) {
This looks like the following in code which is obviously gross.
fooDao.getPager(fooDao);
The problem is that when we are inside of FooDao, the this
is not the proxy DAO that we need.
Is there a better mechanism for a bean to discover that it has a proxy wrapper around it? I've looked at the Spring AOPUtils but I see no way to find the proxy for an object. I don't want isAopProxy(...)
for example. I've also read the Spring AOP docs but I can't see a solution there unless I implement my own AOP native code which I was hoping to avoid.
I suspect that I might be able to inject the DAO into itself with a ApplicationContextAware
utility bean and a setProxyDao(...)
method, but that seems like a hack as well. Any other ideas how I can detect the proxy so I can make use of it from within the bean itself? Thanks for any help.
Upvotes: 7
Views: 3811
Reputation: 49935
A hacky solution along the lines of what you have suggested, considering that AspectJ compile time or load time weaving will not work for you:
Create an interface along these lines:
public interface ProxyAware<T> {
void setProxy(T proxy);
}
Let your Dao's implement the ProxyAware implementation, now create a BeanPostProcessor with an Ordered interface to run last, along these lines:
public class ProxyInjectingBeanPostProcessor implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (AopUtils.isAopProxy((bean))){
try {
Object target = ((Advised)bean).getTargetSource().getTarget();
if (target instanceof ProxyAware){
((ProxyAware) target).setProxy(bean);
}
} catch (Exception e) {
// ignore
}
}
return bean;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
It is ugly, but works.
Upvotes: 5
Reputation: 340873
There is a handy static utility AopContext.currentProxy()
method provided by Spring which returns a proxy to object from which it was called.
Although using it is considered a bad practice, semantically the same method exists in Java EE as well: SessionContext.getBusinessObject()
.
I wrote few articles about this utility method and various pitfalls: 1, 2, 3.
Upvotes: 4
Reputation: 336
Use Spring to inject a bean reference into the bean, even the same bean, just as you would for any other bean reference. No special action required.
The presence of such a variable explicitly acknowledges in the class design that the class expects to be proxied in some manner. This is not necessarily a bad thing, as aop can change behavior that breaks the class contract.
The bean reference would typically be for an interface, and that interface could even be a different one for the self-referenced internal methods.
Keep it simple. That way lies madness. :-)
More importantly, be sure that the semantics make sense. The need to do this may be a code smell that the class is mixing in multiple responsibilities best decomposed into separate beans.
Upvotes: 3