我需要为类构建映射(字面意思为Map<Class<?>, String>
),它在运行时不会发生变化,并且保持解耦的优先级。由于我在Spring应用程序中,我认为我会使用注释,而ClassPathScanningCandidateComponentProvider
或多或少都是这样:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Mapping {
String value();
}
和
public class MappingLookUp {
private static final Map<Class<?>, String> MAPPING_LOOK_UP;
static {
Map<Class<?>, String> lookUp = new HashMap<>();
ClassPathScanningCandidateComponentProvider scanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false);
scanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Mapping.class));
for (BeanDefinition beanDefinition : scanningCandidateComponentProvider.findCandidateComponents("blah")) {
Class<?> clazz;
try {
clazz = Class.forName(beanDefinition.getBeanClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Mapping mapping = AnnotationUtils.getAnnotation(clazz, Mapping.class);
if (mapping == null) {
throw new IllegalStateException("This should never be null");
}
lookUp.put(clazz, mapping.value());
}
MAPPING_LOOK_UP = Collections.unmodifiableMap(lookUp);
}
public static String getMapping(Class<?> clazz) {
...
}
}
虽然我相信这会奏效,但感觉就像:
BeanDefinition
使它听起来像是用于查找Spring bean而不是一般的类定义。要明确的是,带注释的值是数据类 - 而不是Spring管理的bean - 因此BeanPostProcessor
模式不合适,事实上,这就是为什么它感到尴尬的原因使用扫描组件提供程序,对我来说,似乎是为了发现Spring托管bean。
这是实施此模式的正确方法吗?它是提供者的正确应用吗?是否有可行的替代方案而不引入其他类路径扫描实现?
答案 0 :(得分:4)
我会建议这看起来不像是以春天的方式完成。
如果我要这样做,我会使用Spring的BeanPostProcessor或BeanFactoryPostProcessor。这两个都允许对Spring的BeanFactory中的所有Bean进行内省,并允许你摆脱当前设置的静态,因为PostProcessors本身就是Spring Bean。
class MappingLookup implements BeanPostProcessor {
private final Map<Class<?>, String> lookup = new HashMap<>();
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// check bean's class for annotation...
// add to lookup map as necessary...
// make sure to return bean (javadoc explains why)
return bean;
}
public String getMapping(Class<?> clazz) {
// ...
}
// omitted other methods...
}
答案 1 :(得分:0)
我最近提出了一个非常类似的问题How to get list of Interfaces from @ComponentScan packages,最后实施了第一个建议的方法。
您可以看到代码https://github.com/StanislavLapitsky/SpringSOAProxy看到https://github.com/StanislavLapitsky/SpringSOAProxy/blob/master/core/src/main/java/org/proxysoa/spring/service/ProxyableScanRegistrar.java,当然还有初始化注释https://github.com/StanislavLapitsky/SpringSOAProxy/blob/master/core/src/main/java/org/proxysoa/spring/annotation/ProxyableScan.java关键是添加@Import({ProxyableScanRegistrar.class})
关键代码是
public class ProxyableScanRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// Get the ProxyableScan annotation attributes
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ProxyableScan.class.getCanonicalName());
if (annotationAttributes != null) {
String[] basePackages = (String[]) annotationAttributes.get("value");
if (basePackages.length == 0) {
// If value attribute is not set, fallback to the package of the annotated class
basePackages = new String[]{((StandardAnnotationMetadata) metadata).getIntrospectedClass().getPackage().getName()};
}