kpe*_*hev 6 java spring annotations dependency-injection proxy-classes
我偶然发现了一个案例,其中使用@Cacheable创建的AOP代理在Spring 3.1.1中打破了依赖注入.这是我的场景:
我有一个接口和一个在实现的方法中使用@Cacheable实现此接口的类.
示例界面:
public interface ImgService {
public byte[] getImage(String name);
}
Run Code Online (Sandbox Code Playgroud)
示例实现:
public class ImgServiceImpl implements ImgService {
@Cacheable(cacheName = "someCache")
public byte[] getImage(String name){//TODO};
protected String someOtherMethod(){//};
}
Run Code Online (Sandbox Code Playgroud)
我还需要JUnit测试类 - 一个注入接口,另一个注入实现:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceTest {
@Inject
private ImgService;
}
Run Code Online (Sandbox Code Playgroud)
和
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {
@Inject
private ImgServiceImpl;
}
Run Code Online (Sandbox Code Playgroud)
接口的依赖注入工作正常.但是,当我在第二个测试类中注入实现时,我得到"注入自动连接的依赖项失败".我能够调试它,看起来ClassUtils.isAssignableValue()错误地将所需类型与代理类进行比较.它由DefaultListableBeanFactory调用.更奇怪的是,如果我从已实现的方法中删除@Cacheable注释并将其添加到其他一些受保护/私有方法,依赖注入再次正常工作.这是一个错误,处理这种情况的正确方法是什么?
axt*_*avt 10
这不是一个错误,这是使用JDK动态代理进行AOP实现的预期副作用.
由于对可缓存方法的所有调用都ImgServiceImpl应该通过类型的动态代理ImgService,因此无法将此依赖项注入到字段类型中ImgServiceImpl.
当您移动@Cacheable到private或protected方法时,注入工作因为@Cacheable在这种情况下不起作用 - 只能public使用基于代理的AOP建议方法.
因此,您应该声明要注入的字段ImgService,或者将Spring配置为使用基于目标类的代理proxy-target-class = "true".
另一种选择是将Spring配置为使用基于AspectJ的AOP实现(需要编译时或加载时编织).
它适用于Spring提供的所有基于AOP的功能(事务,安全性,异步执行,缓存,自定义方面等).
也可以看看:
好的,这就是我最后想出的解决方案。我实现了一个简单的方法,尝试根据类的实现从代理中提取目标对象org.springframework.aop.framework.Advised:
@SuppressWarnings({"unchecked"})
public static <T> T getTargetObject(Object proxy, Class<T> targetClass) {
if (AopUtils.isJdkDynamicProxy(proxy)) {
try {
return (T) ((Advised)proxy).getTargetSource().getTarget();
} catch (Exception e) {
return null;
}
} else {
return (T) proxy;
}
}
Run Code Online (Sandbox Code Playgroud)
我的实现测试类现在如下所示:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {
@Inject
private ImgService imgService;
private ImgServiceImpl imgServiceImpl;
@PostConstruct
public void setUp() {
imgServiceImpl = getTargetObject(imgService, ImgServiceImpl.class);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1769 次 |
| 最近记录: |