我想基于标记ApplicationConfig.class
的自定义注释的存在启用应用程序功能,如下所示:
@FooBar(enabled = true)
@Configuration
@ComponentScan(basePackageClasses = Application.class, excludeFilters = @Filter({Controller.class, Configuration.class}))
@EnableJpaRepositories("com.package.repository")
class ApplicationConfig {
// Application specific configs and bean defs.
}
自定义注释名为FooBar
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FooBar {
boolean enabled() default true;
}
在应用程序启动期间,我想检测此类(或任何其他类/ bean)是否使用此批注进行批注。
这是我的尝试,部分基于此similar question,它包括两种确定正在使用注释的方法。
@Component
public class MyClassWithEventListeners implements ApplicationContextAware {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
@Autowired
ApplicationContext applicationContext;
@EventListener
void contextRefreshedEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
applicationContext.getClassLoader();
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
String[] names = event.getApplicationContext().getBeanDefinitionNames();
for (String name : names) {
Object o = autowireCapableBeanFactory.getBean(name);
if (AopProxyUtils.ultimateTargetClass(o).isAnnotationPresent(FooBar.class)) {
System.out.println("Found class annotated with FooBar");
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
List<String> beanNames = getBeansWithAnnotation(FooBar.class);
if(beanNames !=null){
System.out.println("Found class annotated with FooBar");
}
}
public List<String> getBeansWithAnnotation(Class<? extends Annotation> type) {
Predicate<Map<String, Object>> filter = Predicates.alwaysTrue();
return getBeansWithAnnotation(type, filter);
}
public List<String> getBeansWithAnnotation(Class<? extends Annotation> type, Predicate<Map<String, Object>> attributeFilter) {
List<String> result = Lists.newArrayList();
ConfigurableListableBeanFactory factory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
for (String name : factory.getBeanDefinitionNames()) {
BeanDefinition bd = factory.getBeanDefinition(name);
if (bd.getSource() instanceof StandardMethodMetadata) {
StandardMethodMetadata metadata = (StandardMethodMetadata) bd.getSource();
Map<String, Object> attributes = metadata.getAnnotationAttributes(type.getName());
if (null == attributes) {
continue;
}
if (attributeFilter.apply(attributes)) {
result.add(name);
}
}
}
return result;
}
}
调用contextRefreshedEvent()
和setApplicationContext()
方法,并且都无法检测到自定义注释。
我观察到的是我的ApplicationConfig.class
出现在bean / classes列表中,但显示如下:
com.package.config.ApplicationConfig$$EnhancerBySpringCGLIB$$15073fb3@196887
EnhancedBySpring
是什么?@EnableJpaRepositories
添加了存储库。我想为自己的目的复制这个功能。答案 0 :(得分:2)
核心容器实际上支持这些东西,因此不需要破解上下文。当您在配置类上添加@EnableXYZ
时,您可以阅读正在发生的事情。例如,@EnableCaching
或@EnableJms
的工作方式几乎相同:
该接口使用@Import
进行元注释,这会导致更多bean被上下文加载。 enabled
部分是有点无用的IMO。注释的存在与否具有相同的作用,并且更加明确。
答案 1 :(得分:0)
也许您可以使用Class提供的实用程序而不是AoP提供的util?
@Test
public void testAccess(){
SomeAnnotatedClass annotatedClassInstance = new SomeAnnotatedClass();
Assert.assertNotNull(annotatedClassInstance.getClass().getAnnotation(FooBar.class));
}
答案 2 :(得分:0)
我进一步研究了这一点,找到了两种可能的解决方案。第一个只是检测是否存在自定义注释:
@Component
public class MyClassWithEventListeners {
@EventListener
void contextRefreshedEvent(ContextRefreshedEvent event) throws NoSuchFieldException, IllegalAccessException {
ApplicationContext applicationContext = event.getApplicationContext();
String[] annotations = applicationContext.getBeanNamesForAnnotation(FooBar.class);
if (annotations != null && annotations.length > 0) {
System.out.println("Annotation found");
} else {
System.out.println("Annotation not found");
}
}
}
第二个使用反射来获取注释中设置的值。在这种情况下,enabled
。
@Component
public class MyClassWithEventListeners {
@EventListener
void contextRefreshedEvent(ContextRefreshedEvent event) throws NoSuchFieldException, IllegalAccessException {
ApplicationContext applicationContext = event.getApplicationContext();
AnnotationConfigWebApplicationContext annotationContext = ((AnnotationConfigWebApplicationContext) applicationContext);
for (Field field : annotationContext.getClass().getDeclaredFields()) {
if ("annotatedClasses".equals(field.getName())) {
field.setAccessible(true);
Set<Class<?>> classes = (Set<Class<?>>) field.get(annotationContext);
for (Class clazz : classes) {
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
if (annotation.annotationType().isAssignableFrom(FooBar.class)) {
enabled = ((ServiceRegistration) annotation).enabled();
System.out.println("Enabled: " + enabled);
}
}
}
}
}
}
}
这看起来有点笨拙。我想知道是否有更优雅的方法来解决这个问题。