Accessing Spring Proxies - Reflection and Advised
a reoccuring issue that i've struck when using Spring is trying to work with a proxied object in reflection. it's not something that occurs all the time, and there's very smart people doing very smart things with this, but here's what i've found works… Advised
basically, when you retrieve an object that's been proxied by Spring, it can be cast to an Advised object that can then be used to retrieve the underlying object.
if you execute the underlying object though, you don't get the advantage of whatever aspect you had added so beware of that.
in any event, here's the code to get a quick sample up and running.
here's our holder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.incompletecode.proxy; | |
public class ProxyHolder { | |
private ProxiedClass proxiedClass; | |
public void useProxiedClass() { | |
proxiedClass.doSomething(); | |
} | |
public void setProxiedClass(ProxiedClass proxiedClass) { | |
this.proxiedClass = proxiedClass; | |
} | |
} |
here's the proxied class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.incompletecode.proxy; | |
public class ProxiedClass { | |
public void doSomething() { | |
System.out.println("hello world"); | |
} | |
} |
here's our aspect
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.incompletecode.proxy.aspect; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
public class ProxiedClassAspect { | |
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { | |
//add something to the visibility | |
System.out.println("before executing..."); | |
Object result = proceedingJoinPoint.proceed();//let it continue | |
System.out.println("... after executing"); | |
//carry on | |
return result; | |
} | |
} |
and here's our context XML
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<beans xmlns="http://www.springframework.org/schema/beans" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xmlns:aop="http://www.springframework.org/schema/aop" | |
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd | |
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> | |
<aop:config> | |
<aop:aspect ref="proxiedClassAspect"> | |
<aop:around method="around" pointcut-ref="aroundRef"/> | |
<aop:pointcut expression="execution(* org.incompletecode.proxy.ProxiedClass.doSomething())" id="aroundRef"/> | |
</aop:aspect> | |
</aop:config> | |
<bean id="proxyHolder" | |
class="org.incompletecode.proxy.ProxyHolder"> | |
<property name="proxiedClass" ref="proxiedClass"/> | |
</bean> | |
<bean id="proxiedClass" | |
class="org.incompletecode.proxy.ProxiedClass"> | |
</bean> | |
<bean id="proxiedClassAspect" | |
class="org.incompletecode.proxy.aspect.ProxiedClassAspect"> | |
</bean> | |
</beans> |
now, finally, the test that shows what can be done
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.incompletecode.proxy; | |
import java.lang.reflect.Field; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.springframework.aop.framework.Advised; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.test.context.ContextConfiguration; | |
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; | |
@RunWith(SpringJUnit4ClassRunner.class) | |
@ContextConfiguration({"classpath:/META-INF/spring/application-context.xml"}) | |
public class ProxyHolderTest { | |
@Autowired | |
private ProxyHolder holder; | |
@Test | |
public void test() throws Exception { | |
//lets do a plain run first | |
holder.useProxiedClass(); | |
//now lets use reflection to retrieve the object | |
for (Field field : holder.getClass().getDeclaredFields()) { | |
if (field.getType().equals(ProxiedClass.class)) { | |
//got our field, lets get the object | |
field.setAccessible(true); | |
//show it first to see that it's proxied | |
Object object = field.get(holder); | |
System.out.println(object.getClass().getName()); | |
//now cast to get the underlying object | |
ProxiedClass proxiedClass = (ProxiedClass)((Advised)object).getTargetSource().getTarget(); | |
System.out.println(proxiedClass.getClass().getName()); | |
//now execute directly | |
proxiedClass.doSomething(); | |
} | |
} | |
} | |
} |
No comments:
Post a Comment