6

@Cacheableを使用して作成されたAOPプロキシがSpring3.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テストクラスも必要です。1つはインターフェイスを挿入し、もう1つは実装です。

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

インターフェイスの依存性注入は正常に機能します。ただし、2番目のテストクラスに実装を挿入すると、「自動配線された依存関係の挿入に失敗しました」というメッセージが表示されます。デバッグできましたが、ClassUtils.isAssignableValue()が目的のタイプをプロキシクラスと誤って比較しているようです。DefaultListableBeanFactoryによって呼び出されます。さらに奇妙なことに、実装されたメソッドから@Cacheableアノテーションを削除し、それを他の保護された/プライベートなメソッドに追加すると、依存性注入が再び正常に機能します。これはバグですか?この状況を処理するための正しいアプローチは何でしょうか?

4

2 に答える 2

10

これはバグではなく、AOP 実装に JDK 動的プロキシを使用することで予期される副作用です。

のキャッシュ可能なメソッドへのすべての呼び出しImgServiceImplは type の動的プロキシを経由する必要があるため、ImgServiceこの依存関係を type のフィールドに注入する方法はありませんImgServiceImpl

またはメソッドに移動@Cacheableすると、この場合は有効にならないため、インジェクションが機能します。プロキシベースの AOP を使用してアドバイスできるのはメソッドのみです。privateprotected@Cacheablepublic

そのため、注入されるフィールドを として宣言するかImgService、または を使用する代わりにターゲット クラスベースのプロキシを使用するように Spring を構成する必要がありますproxy-target-class = "true"

さらに別のオプションは、AspectJ ベースの AOP 実装を使用するように Spring を構成することです(コンパイル時またはロード時のウィービングが必要です)。

Spring が提供するすべての AOP ベースの機能 (トランザクション、セキュリティ、非同期実行、キャッシュ、カスタム アスペクトなど) に適用できます。

以下も参照してください。

于 2012-08-13T15:40:45.190 に答える
3

OK、これが私が最終的に思いついた解決策です。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);
    }


}
于 2012-08-14T07:26:56.370 に答える