【问题标题】:@Cacheable breaks DependencyInjection@Cacheable 中断 DependencyInjection
【发布时间】:2012-08-13 15:22:08
【问题描述】:

我偶然发现使用@Cacheable 创建的AOP 代理破坏了Spring 3.1.1 中的依赖注入。这是我的场景:

我有一个接口和一个实现这个接口的类,在实现的方法中使用@Cacheable。

示例界面:

public interface ImgService {
    public byte[] getImage(String name);
}

示例实现:

public class ImgServiceImpl implements ImgService {

    @Cacheable(cacheName = "someCache")
    public byte[] getImage(String name){//TODO};

    protected String someOtherMethod(){//};
}

我还必须对 JUnit 测试类 - 一个注入接口,一个注入实现:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceTest {

    @Inject
    private ImgService;
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {

    @Inject
    private ImgServiceImpl;
}

接口的依赖注入工作正常。然而,当我在第二个测试类中注入实现时,我得到一个“自动装配依赖注入失败”。我能够对其进行调试,并且似乎 ClassUtils.isAssignableValue() 错误地将所需类型与代理类进行了比较。它由 DefaultListableBeanFactory 调用。更奇怪的是,如果我从实现的方法中删除 @Cacheable 注释并将其添加到其他一些受保护/私有方法中,依赖注入再次正常工作。这是一个错误吗?处理这种情况的正确方法是什么?

【问题讨论】:

标签: java spring dependency-injection annotations proxy-classes


【解决方案1】:

这不是错误,这是使用 JDK 动态代理实现 AOP 的预期副作用。

由于对ImgServiceImpl 的可缓存方法的所有调用都应通过ImgService 类型的动态代理,因此无法将此依赖项注入ImgServiceImpl 类型的字段中。

当您将 @Cacheable 移动到 privateprotected 方法时,注入会起作用,因为在这种情况下 @Cacheable 不会生效 - 只有 public 方法可以使用基于代理的 AOP 提供建议。

因此,您应该将要注入的字段声明为 ImgService,或者将 Spring 配置为使用基于目标类的代理,而不是使用 proxy-target-class = "true"

另一种选择是将 Spring 配置为使用 AspectJ-based AOP implementation(需要编译时或加载时编织)。

它适用于 Spring 提供的所有基于 AOP 的特性(事务、安全、异步执行、缓存、自定义方面等)。

另请参阅:

【讨论】:

  • 感谢您的回答。我仍然觉得 ClassUtils 方法不能处理这个问题似乎很奇怪,因为它似乎只需要一个 if 语句。
【解决方案2】:

好的,这就是我最终提出的解决方案。我实现了一个简单的方法,该方法尝试根据其对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;
  }
}

我的实现测试类现在看起来像这样:

@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);
    }


}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-29
    • 2019-02-07
    • 1970-01-01
    • 2021-05-18
    • 2018-01-30
    • 2019-10-12
    • 2019-01-25
    • 1970-01-01
    相关资源
    最近更新 更多