Get AOP proxy from the object itself

2019-01-06 14:31发布

问题:

Is possible to get the proxy of a given object in Spring? I need to call a function of a subclass. But, obviously, when I do a direct call, the aspects aren't applied. Here's an example:

public class Parent {

    public doSomething() {
        Parent proxyOfMe = Spring.getProxyOfMe(this); // (please)
        Method method = this.class.getMethod("sayHello");
        method.invoke(proxyOfMe);
    }
}

public class Child extends Parent {

    @Secured("president")
    public void sayHello() {
        System.out.println("Hello Mr. President");
    }
}

I've found a way of achieving this. It works, but I think is not very elegant:

public class Parent implements BeanNameAware {

    @Autowired private ApplicationContext applicationContext;
    private String beanName; // Getter

    public doSomething() {
        Parent proxyOfMe = applicationContext.getBean(beanName, Parent.class);
        Method method = this.class.getMethod("sayHello");
        method.invoke(proxyOfMe);
    }
}

回答1:

This hack is extremely awkward, please consider refactoring your code or using AspectJ weaving. You may feel warned, here is the solution

AopContext.currentProxy()

JavaDoc. I blogged about it here and here.



回答2:

AopContext.currentProxy() as suggested by Tomasz will work. A more generic solution, that will work outside of the proxied class is to cast the object to org.springframework.aop.framework.Advised and get .getTargetSource().getTarget()

The former (getting the real object from the proxied object) is something that you should not really need. On the other hand getting the target proxy might be useful in some utility class that inspects existing beans in order to add some feature.



回答3:

You can use a bean post-processor to set a reference to the proxy on the target bean. It moves the Spring-specifics from your beans to a single class.

Post-Processor

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class SelfReferencingBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof SelfReferencingBean) {
            ((SelfReferencingBean) bean).setProxy(bean);
        }
        return bean;
    }
}

Context

Register the post-processor in applicationContext.xml.

<bean id="srbpp" class="SelfReferencingBeanPostProcessor"/>

Beans

Each bean must implement SelfReferencingBean to tell the post-processor that it needs a reference to the proxy.

public interface SelfReferencingBean {
    void setProxy(Object proxy) ;
}

Now implement setProxy in each bean that needs to call itself through its proxy.

public class MyBean implements SelfReferencingBean {
    MyBean proxy;

    @Override
    public void setProxy(Object proxy) {
        this.proxy = (MyBean) proxy;
    }
}

You could put this last bit of code into a bean base class if you don't mind casting proxy to bean's type when calling methods directly on it. Since you're going through Method.invoke you wouldn't even need the cast.

With a little work I bet this could be converted to an annotation processor a la @Autowired. Come to think of it, I don't recall if I even tried adding a self-reference using @Autowired itself.

public class MyBean implements SelfReferencingBean {
    @Autowired MyBean proxy;
}