【问题标题】:Discovering annotated methods发现带注释的方法
【发布时间】:2015-10-26 23:44:34
【问题描述】:

在我的 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 的所有用途?我希望没有标记界面。

谢谢!

【问题讨论】:

  • 为什么需要自定义界面。实现BeanPostProcessor 实现postProcessAfterInitialization 并使用AopUtils 获取实际的目标类并检查注释。您可以查看 JMS 支持的工作原理 (github.com/spring-projects/spring-framework/blob/master/…),然后还可以使用 SmartInitializingSingleton 并在您需要实现初始化缓存的方法中。

标签: java spring proxy annotations


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢,这似乎是一个非常明智的方法。一个小缺点是默认情况下将为所有 bean 调用处理器,这意味着您可能会为具有自己初始化机制的“第三方”组件创建缓存。我认为将注释搜索限制为特定的包会有所帮助,但会带来额外的配置。
  • 顺便说一句,感谢AopUtils.getTargetClass() 的提示。不知道我是怎么错过的,但它让我可以让 Spring 弄清楚如何获取目标类。 :)
  • 您可以根据需要将其复杂化,例如,您可以检查目标类的包,如果不执行任何操作,它是否与您的包匹配。
  • 是否存在 bean 可能被延迟加载的风险,因此不会在启动时进行后处理,而只是稍后处理?一些缓存管理器不允许在创建后添加缓存(例如 Simple Spring Memcached 缓存管理器)。
  • 不,因为afterSingletonsInstantiated 只在所有单例实例化后被调用一次。 postProcesAfterInitialization 可能会为其他 bean 调用,但不会触及缓存,因为这将在 afterSingletonsInstantiated 方法中发生。您可以在类中添加一个字段,说明它已经初始化以获得非单例 bean 的一些性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-25
  • 1970-01-01
  • 1970-01-01
  • 2012-02-27
  • 2014-06-09
  • 1970-01-01
相关资源
最近更新 更多