一.业务背景
ToB的产品主要的不确定性来自用户的需求,同样是医院的业务,不同的地区、不同的医院业务流程和需求可能有所不同,比如:上海的医院对医保的校验和北京的医院对医保的校验就有所不同(医保政策不一样)。这就导致在做HIS系统时,需要针对同样的业务要有不同的实现
二.具体实现
为此,无论是业务还是代码设计都要考虑不同场景不同实现的问题,技术上如何解决?根据医院选择不同的实现。
上代码。
public class BeanFactoryOperation implements BeanFactoryPostProcessor,ApplicationContextAware { private static Map<String,Class> mapClass = new HashMap<>(); static{ mapClass.put("com.mysql.jdbc.Driver",IDaoMysql.class); mapClass.put("oracle.jdbc.driver.OracleDriver",IDaoOracle.class); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory)configurableListableBeanFactory; String driver = SpringContextUtil.getProperty("spring.datasource.driverClassName"); Iterator<String> itClass = mapClass.keySet().iterator(); while(itClass.hasNext()){ String keyClass = itClass.next(); if(!keyClass.equals(driver)){//如果不是当前驱动 Map<String,IDaoBase> map = configurableListableBeanFactory.getBeansOfType(mapClass.get(keyClass)); Iterator<String> it = map.keySet().iterator(); while(it.hasNext()){ String key = it.next(); dlbf.removeBeanDefinition(key); /*boolean b = configurableListableBeanFactory.containsSingleton(key); System.out.println(b);*/ } } } /** * 多实现按医院注入处理 */ String globalFlag = SpringContextUtil.getProperty("hospital.flag"); Map<Class<?>,List<TmpBean>> map = new HashMap<>(); String[] beanNames = configurableListableBeanFactory.getBeanNamesForType(IHospitalExtensionActionBase.class); for(String beanName:beanNames){ BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanName); String beanClassName = beanDefinition.getBeanClassName(); Object o = ReflectUtil.newInstance(beanClassName); Class<?> aClass = o.getClass(); HospitalConditional annotation = aClass.getAnnotation(HospitalConditional.class); Map<String, Object> annotationAttributesMap = AnnotationUtils.getAnnotationAttributes(annotation, false); String hospitalFlag = (String)annotationAttributesMap.get("hospitalFlag"); Class inheritClass = (Class)annotationAttributesMap.get("inherit"); TmpBean tb = new TmpBean(); tb.setBeanName(beanName); tb.setImplFlag(hospitalFlag); if(map.containsKey(inheritClass)){ List<TmpBean> list = map.get(inheritClass); list.add(tb); }else{ List<TmpBean> list = new ArrayList<>(5); list.add(tb); map.put(inheritClass,list); } } Iterator<Class<?>> it = map.keySet().iterator(); while(it.hasNext()){ Class<?> clazz = it.next(); List<TmpBean> tbList = map.get(clazz); /** * 1.存在当前院区实现 */ List<TmpBean> list = tbList.stream().filter(tb -> globalFlag.equals(tb.getImplFlag())).collect(Collectors.toList()); if(list.size() > 1){ throw new IllegalStateException("clazz = ["+clazz+"],repeat......"); } if(list.size() == 1){ List<TmpBean> rmList = tbList.stream().filter(tb -> !globalFlag.equals(tb.getImplFlag())).collect(Collectors.toList()); rmList.stream().forEach(tb->dlbf.removeBeanDefinition(tb.getBeanName())); } /** * 2.不存在当前院区实现,使用默认实现 */ if(list.size() == 0){ List<TmpBean> defaultList = tbList.stream().filter(tb -> "default".equals(tb.getImplFlag())).collect(Collectors.toList()); if(defaultList.size() == 1){ List<TmpBean> rmList = tbList.stream().filter(tb -> !"default".equals(tb.getImplFlag())).collect(Collectors.toList()); rmList.stream().forEach(tb->dlbf.removeBeanDefinition(tb.getBeanName())); } if(defaultList.size() > 1){ throw new IllegalStateException("clazz = ["+clazz+"],repeat......"); } if(defaultList.size() == 0){ throw new IllegalStateException("clazz = ["+clazz+"],no default impl......"); } } } map.clear(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.setApplicationContext(applicationContext); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface HospitalConditional { String hospitalFlag() default "default"; /** * 上层类或接口 * @return */ Class<?> inherit() ;
主要就是使用BeanFactoryPostProcessor扩展点,对beanDefinition对象进行指定移除,最后容器在实例化bean时只能找到一个实现类,当然,系统要知道当前是运行在什么环境,这需要配置中心指定环境,根据环境移除不必要的实现,这样在注入接口时Spring IOC中只能找到一个实现类