发现带注释的方法

时间:2015-10-26 23:44:34

标签: java spring proxy annotations

在我的Spring应用程序中,我有使用Spring的缓存机制的组件。每个@Cacheable注释指定要使用的缓存。我想自动发现启动时所需的所有缓存,以便可以自动配置它们。

最简单的方法似乎创建了一个标记接口(例如:CacheUser),供每个缓存组件使用:

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

然后我会让Spring自动发现此接口的所有实现,并将它们自动装配到配置列表中,该列表可在配置缓存管理器时使用。这很有效。

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

我的计划是获取每个已发现的类,并找到所有使用@Cacheable注释的方法。从那里我将访问注释的属性并获取缓存名称。我正在使用AnnotationUtils.findAnnotation()来获取注释声明。

这就是计划崩溃的地方。 Spring实际上是代理而不是原始组件,并且注释不会复制到代理的方法。我发现的唯一解决方法是利用代理实现Advised的事实,它提供了对代理类的访问:

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

从那里我可以获得原始注释,但这种方法显然很脆弱。

真的有两个问题:

  1. 有没有更好的方法来获取代理类定义的注释?
  2. 你能否建议在我的项目中发现@Cacheable的所有用途?我很乐意没有标记界面。
  3. 谢谢!

1 个答案:

答案 0 :(得分:4)

Spring有许多基础设施接口,可以帮助您利用容器和/或bean的生命周期。出于您的目的,您希望使用BeanPostProcessorSmartInitializingSingleton

BeanPostProcessor将获得所有构建的bean的回调,您只需要实现postProcessAfterInitialization方法。您可以在该方法中检测注释并填充缓存列表。

然后在SmartInitializingSingleton 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.
        }
    }
}