2015-10-26 63 views
3

在我的Spring應用程序中,我有使用Spring的緩存機制的組件。每個@Cacheable批註指定要使用的緩存。我想自動發現啓動時需要的所有緩存,以便它們可以自動配置。發現帶註釋的方法

最簡單的方法似乎是創建一個標記接口(例如:CacheUser)由每個緩存組件一起使用:

@Component 
public class ComponentA implements CacheUser { 
    @Cacheable("dictionaryCache") 
    public String getDefinition(String word) { 
    ... 
    } 
} 

我將不得不春自動發現該接口的所有的實現,並將它們自動裝配到在配置緩存管理器時可以使用的配置列表。這工作。

@Autowired 
private Optional<List<CacheUser>> cacheUsers; 

我的計劃是把每發現的一類,並找到@Cacheable註釋的所有方法。從那裏我將訪問註解的屬性並獲取緩存名稱。我正在使用AnnotationUtils.findAnnotation()來獲取註釋聲明。

這就是計劃分崩離析的地方。 Spring實際上是連接代理而不是原始組件,並且註釋不會被複制到代理的方法中。我發現的唯一的解決方法利用了一個事實,即代理實現Advised它提供了訪問代理的類:

((Advised)proxy).getTargetSource().getTargetClass().getMethods() 

從那裏我能得到原來的註釋,但這種做法顯然是脆。

所以兩個問題,真正做到:

  1. 有沒有更好的辦法去通過代理的類中定義的註釋嗎?
  2. 你可以建議任何其他方式來發現我項目中@Cacheable的所有用途嗎?我喜歡沒有標記界面。

謝謝!

+0

爲什麼你需要一個自定義接口。實現'BeanPostProcessor'實現'postProcessAfterInitialization'並使用'AopUtils'來獲取實際的目標類並檢查註釋。你可以看看JMS支持如何工作(https://github.com/spring-projects/spring-framework/blob/master/spring-jms/src/main/java/org/springframework/jms/annotation/ JmsListenerAnnotationBeanPostProcessor.java#L195),然後使用'SmartInitializingSingleton'並在你需要的方法中實現你的緩存。 –

回答

2

Spring有很多基礎設施接口,可以幫助您打入容器和/或bean的生命週期。爲了您的目的,您希望使用BeanPostProcessorSmartInitializingSingleton

BeanPostProcessor將得到所有構建的bean的回調函數,您只需要實現postProcessAfterInitialization方法。您可以在該方法中檢測註釋並填充緩存列表。

然後在SmartInitializingSingleton s afterSingletonsInstantiated方法中使用此列表來引導/初始化緩存。

像下面這樣(這是未經測試,但應該給你一個想法)。

public class CacheInitialingProcessor implements BeanPostProcessor, SmartInitializingSingleton { 

    private final Set<String> caches = new HashSet<String>(); 

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

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
     Class<?> targetClass = AopUtils.getTargetClass(bean); 
     ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() { 
      @Override 
      public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { 
       Cacheable cacheable = AnnotationUtils.getAnnotation(method, Cacheable.class); 
       if (cacheable != null) { 
        caches.addAll(Arrays.asList(cacheable.cacheNames())); 
       } 
      } 
     }); 
     return bean; 
    } 

    @Override 
    public void afterSingletonsInstantiated() { 
     for (String cache : caches) { 
      // inti caches. 
     } 
    } 
} 
+0

謝謝,這似乎是一個相當明智的做法。一個小缺點是,默認情況下處理器將被調用所有的bean,這意味着你可能會爲具有自己的初始化機制的「第三方」組件創建緩存。我認爲將註釋搜索限制到特定的軟件包會有所幫助,但會帶來額外的配置。 – torngat

+0

順便說一句,感謝您對'AopUtils.getTargetClass()'的提示。不知道我是如何錯過它的,但它讓我能夠讓Spring弄清楚如何獲得目標課程。 :) – torngat

+0

您可以根據需要使其變得複雜,例如可以檢查目標類的包,以查看它是否與您的某個匹配,如果不做任何事情。 –