在開發的過程中,一個接口往往有多個實作類。但根據需求,不一定會使用到所有的實作類。
以本人目前遇到的需求為例,一個系統不同的國家部署時,需要使用不同的實作類。在此給出基于注解的實作方法。
1.注解定義
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Adapter {
/** 環境value */
AdapterEnv[] value();
/** bean 名稱 */
String name();
enum AdapterEnv{
// 不同國家的枚舉
TH("th"), CN("cn"), ID("id");
String value;
AdapterEnv(String value) {
this.value = value;
}
public static AdapterEnv parse(String value) {
for (AdapterEnv adapterEnv : AdapterEnv.values()) {
if (adapterEnv.value.equals(value)) {
return adapterEnv;
}
}
throw new RuntimeException("error adapter env");
}
}
}
2. 注解實作
public class I18nAdapterBeanDefinitionRegistry implements ResourceLoaderAware, BeanFactoryPostProcessor {
// 掃描路徑
private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
// 通過配置檔案注入
private String i18nAdapterValue;
// bean初始化處理
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
if (configurableListableBeanFactory instanceof BeanDefinitionRegistry) {
postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) configurableListableBeanFactory);
}
}
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// bean定義map
Map<String, AdapterBeanDefinitionInfo> definitionMap = new LinkedHashMap<String, AdapterBeanDefinitionInfo>();
// 注解中的值不符合目前環境的bean定義map
Map<String, AdapterBeanDefinitionInfo> nullDefinitionMap = new LinkedHashMap<String, AdapterBeanDefinitionInfo>();
// 掃描指定包
String basePackage = "com.xx.service.adapter";
Set<Class<?>> classes = scannerPackages(basePackage);
Adapter.AdapterEnv i18nAdapterEnv = Adapter.AdapterEnv.parse(i18nAdapterValue);
for (Class<?> adapterClass : classes) {
// 檢查注解
Adapter annotation = adapterClass.getAnnotation(Adapter.class);
if (annotation == null) {
continue;
}
// bean 名稱
String beanName = annotation.name();
// bean 環境
Adapter.AdapterEnv[] values = annotation.value();
// bean 實作的接口
Class<?>[] interfaces = adapterClass.getInterfaces();
if (StringUtils.isBlank(beanName) || ArrayUtils.isEmpty(values) || ArrayUtils.isEmpty(interfaces)) {
continue;
}
//判斷是否是目前環境需要加載的bean
// 校驗bean 接口
AdapterBeanDefinitionInfo definitionInfo;
if (ArrayUtils.contains(values, i18nAdapterEnv)) {
if (definitionMap.containsKey(beanName)) {
definitionInfo = definitionMap.get(beanName);
} else {
definitionInfo = new AdapterBeanDefinitionInfo(beanName);
definitionMap.put(beanName, definitionInfo);
}
definitionInfo.setInterface(interfaces);
RootBeanDefinition beanDefinition = new RootBeanDefinition(adapterClass);
beanDefinition.setAutowireMode(AUTOWIRE_BY_NAME);
definitionInfo.setAdapterClass(adapterClass);
definitionInfo.setBeanDefinition(beanDefinition);
}else {
if (nullDefinitionMap.containsKey(beanName)) {
definitionInfo = nullDefinitionMap.get(beanName);
} else {
definitionInfo = new AdapterBeanDefinitionInfo(beanName);
nullDefinitionMap.put(beanName, definitionInfo);
}
definitionInfo.setAdapterClass(adapterClass);
definitionInfo.setInterface(interfaces);
}
}
Map<String, AdapterBeanDefinitionInfo> checkNullDefinitionMap = new LinkedHashMap<String, AdapterBeanDefinitionInfo>();
if(MapUtils.isNotEmpty(nullDefinitionMap)) {
// 循環判斷,不符合目前環境的bean是否已經在定義map中
// 如果對應的名稱已有bean,則忽略
for (Entry<String, AdapterBeanDefinitionInfo> adapterBeanDefinitionEntry : nullDefinitionMap.entrySet()) {
// 生成定義
String beanNameTemp=adapterBeanDefinitionEntry.getKey();
//已有執行個體 不需要在做空的 bean
if(!definitionMap.containsKey(beanNameTemp)) {
AdapterBeanDefinitionInfo adapterBeanDefinition =adapterBeanDefinitionEntry.getValue();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(adapterBeanDefinition.getAdapterClass());
GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
definition.getConstructorArgumentValues().addGenericArgumentValue(adapterBeanDefinition.getAdapterClass());
definition.setBeanClass(I18nNullAdapterFactoryBean.class);
definition.setAutowireMode(AUTOWIRE_BY_NAME);
adapterBeanDefinition.setBeanDefinition(definition);
checkNullDefinitionMap.put(beanNameTemp, adapterBeanDefinition);
}
}
}
definitionMap.putAll(checkNullDefinitionMap);
// 注冊bean
for (AdapterBeanDefinitionInfo definitionInfo : definitionMap.values()) {
beanDefinitionRegistry.registerBeanDefinition(definitionInfo.getBeanName(), definitionInfo.getBeanDefinition());
}
}
// 掃描包class
private Set<Class<?>> scannerPackages(String basePackage) {
Set<Class<?>> set = new LinkedHashSet<Class<?>>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + DEFAULT_RESOURCE_PATTERN;
try {
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
String className = metadataReader.getClassMetadata().getClassName();
Class<?> clazz;
try {
clazz = Class.forName(className);
set.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return set;
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
}
public String getI18nAdapterValue() {
return i18nAdapterValue;
}
public void setI18nAdapterValue(String i18nAdapterValue) {
this.i18nAdapterValue = i18nAdapterValue;
}
}
3.配置檔案
<bean class="com.xx.adapter.I18nAdapterBeanDefinitionRegistry">
<property name="i18nAdapterValue" value="cn"/>
</bean>