天天看點

利用注解指定Spring啟動時加載的bean1.注解定義2. 注解實作3.配置檔案4.使用

在開發的過程中,一個接口往往有多個實作類。但根據需求,不一定會使用到所有的實作類。

以本人目前遇到的需求為例,一個系統不同的國家部署時,需要使用不同的實作類。在此給出基于注解的實作方法。

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>
           

4.使用

繼續閱讀