Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a Spring bean detect if it itself has been wrapped in an AOP proxy?

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.

like image 732
Gray Avatar asked Jul 16 '12 18:07

Gray


People also ask

How does Spring AOP proxy work?

Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice). If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used.

Are Spring beans proxies?

Whenever the beanB is requested from the container a new instance will be created. To solve these types of problem, Java spring framework provides the concept called proxy beans. For dependencies with less scope than a parent, the framework will create the proxies rather than creating actual objects.

Which of the following enables the detection of @aspect Bean?

Annotation @EnableAspectJAutoProxy enables detection of @Aspect classes and creates a proxy object for beans subject to aspects. Internally process of creating proxies is done by AnnotationAwareAspectJAutoProxyCreator .

What is Spring AOP proxy pattern?

This pattern is particularly used heavily in Spring AOP. As an example, In Spring AOP you create proxies of the objects that handle the cross cutting concern code. The Proxy pattern also forms the core foundation of remoting technologies that Spring supports, such as RMI, Spring's HTTP Invoker, Hessian, and Burlap.


2 Answers

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.

like image 51
Biju Kunjummen Avatar answered Sep 26 '22 17:09

Biju Kunjummen


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.

like image 34
Tomasz Nurkiewicz Avatar answered Sep 22 '22 17:09

Tomasz Nurkiewicz