2015-10-26 65 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

您可以根据需要使其变得复杂,例如可以检查目标类的包,以查看它是否与您的某个匹配,如果不做任何事情。 –