背景
常用的数据库有mysql,oracle,sql server还有非关系型数据库Redis等,如果希望一套框架能在不同的数据库平台上都能正常运行,而且希望不用做多大的改动.
设计思路
可以将数据库操作抽象成接口,又具体的实现类去完成数据库操作,对用不同的数据库调用不同的实现类,为了兼容mybatis框架,我的设计如下。在传统的框架基础上多加一层daoService,只用于对数据库操作。
后面实现需要知道的知识
1 Autowired注解:最常用的注解之一,该注解根据type注入,如果一个类有多个实现时,将报错;
2 Qualifier注解:与Autowired配合使用,当出现多个实现时,使用该注解指定具体实现类,指定的实现类的BeanDifition的AutowireCandidate必须为true,否则会报错,找不到实现类,当然该属性默认是true,只是在我们这个系统中,将改变这个属性
2-1 在实际使用时,发现这样会有bug,列:如果db_type=mysql ,@Qualifier("xmlUserService")时,由于指定的实例不存在,系统启动时就会报错,对拦截器重新修改为设置Primary属性,当系统中用户没有指定时,我们就将与db_type相等的实现类的Primary设为
3 Primary注解:当有多个实现时,标记有该注解的实现类为主要类,会将有该注解的类注入该需要的地方
4 DBtype注解,这是我们自定义的注解,用于自动匹配数据库类型,注入对应的实现
5 优先级:Qualifier > Primary > DBtype
实现方式
1 在配置文件中增加一个db_type来标识当前系统所使用的数据库
2 增加一个自定义注解,用于标识每个实现类属于哪个数据库的
3 在spring完成BeanDefination解析后,根据db_type对BeanDefination的AutowireCandidate进行修改,以便保证spring属性注入时不会出错和注入正确的实现类
4 实现方式就是如果使用了Qualifier或者Primary,则有spring实现,否则使用根据DBtype将所有不匹配的类的AutowireCandidate设为false
理论以叙述完毕,接下来是实现
具体实现
1 配置文件中设置db_type
# 数据库类型 db_type: xml
2 自定义注解
/** * 该注解用于标注实现类属于哪种库的实现 */ @Documented @Retention(RUNTIME) @Target({TYPE}) public @interface DBtype { String value() ; }
3 创建获取配置文件的方法,后面需要使用
public class DButil { private static String DB_TYPE; private static String[] scanPaths; private static ClassPathResource resource = new ClassPathResource("application.yml"); private static EncodedResource encodedResource = new EncodedResource(resource, "UTF-8"); private static Properties properties; static { try { properties = PropertiesLoaderUtils.loadProperties(encodedResource); } catch (IOException e) { e.printStackTrace(); } } /** * 获取数据库的类型,没发用@value注解,因为应用还未启动 */ public static String getDbType() { if (DB_TYPE == null) { DB_TYPE = properties.getProperty("db_type"); } return DB_TYPE; } }
4 之前实现的BeanFactoryPostProcessor,处理BeanDefination,
@Component public class DBBeanFactoryPostProcessor implements BeanFactoryPostProcessor { /** * 如果与数据库类型相同,这将该beanDefinition的设为AutowireCandidate, * 其余都设为false,但是如果该类是主要的,则不作处理 */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { try { // 设置与数据库匹配的实现类,如果有实现类设置了Primary,则返回该类的实现接口 Set<Class> parentInterfaces = autoMatchDbtype(beanFactory); // 回滚夫接口下对应类的操作,即如果有实现类设置了Primary,则将之前兄弟类的AutowireCandidate改回true if (parentInterfaces != null && parentInterfaces.size() > 0) { rollback(beanFactory, parentInterfaces); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 如果parentInterFaceSet不为空,则再循环一个beanDefinition,将所有该接口的子类的AutowireCandidate还原为true */ private void rollback(ConfigurableListableBeanFactory beanFactory, Set<Class> parentInterFaceSet) throws ClassNotFoundException { String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); for (String definitionName : beanDefinitionNames) { // 获取被代理类的注解 是否有DBtype注解 Class<?> beanClass = getOriginalClassOfBeanDefination(definitionName, beanFactory); if (beanClass == null) { continue; } Class<?>[] interfaces = beanClass.getInterfaces(); for (Class<?> faceInDefinition : interfaces) { for (Class faceInSet : parentInterFaceSet) { if (faceInDefinition.equals(faceInSet)) { // 获取装载bean的信息 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(definitionName); beanDefinition.setAutowireCandidate(true); } } } } } /** * 为definition自动匹配数据库类型 {@link DBtype} */ private Set<Class> autoMatchDbtype(ConfigurableListableBeanFactory beanFactory) throws ClassNotFoundException { String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); // 如果有实现类设置了@Primary,则将它父接口放到这里面,在循环结束后将该接口的实现类的AutowireCandidate还原为true, // 因为有了主要的了 Set<Class> parentInterFaceSet = new HashSet<>(); for (String definitionName : beanDefinitionNames) { // 获取被代理类的注解 是否有DBtype注解 Class<?> beanClass = getOriginalClassOfBeanDefination(definitionName, beanFactory); if (beanClass == null) { continue; } // 获取该类是否有DBtype注解 DBtype annotation = AnnotationUtils.getAnnotation(beanClass, DBtype.class); if (annotation == null) { continue; } // 获取装载bean的信息 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(definitionName); boolean isPrimary = beanDefinition.isPrimary(); // 设置了主要的实现,则不行需要将AutowireCandidate设为false if (isPrimary) { parentInterFaceSet.addAll(Arrays.asList(beanClass.getInterfaces())); } else { // 如果与数据库类型相同,这将该 beanDefinition 的设为 AutowireCandidate, // 其余都设为false,但是如果该类是主要的,则不作处理 由于默认为true,所以这里只设置false的情况 if (!annotation.value().equals(DButil.getDbType())) { beanDefinition.setAutowireCandidate(false); } } } return parentInterFaceSet; } /** * 获取BeanDefination中代理的类名称 */ private Class getOriginalClassOfBeanDefination(String definitionName, ConfigurableListableBeanFactory beanFactory) throws ClassNotFoundException { boolean isOtherFrameName = definitionName.contains("."); if (isOtherFrameName) { return null; } // 获取装载bean的信息 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(definitionName); String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName == null) { return null; } return Class.forName(beanClassName); } }
5 修改后实现BeanFactoryPostProcessor,处理BeanDefination
/** * 根据数据库类型推断bean中该注入的实体 * 1 如果使用了 @Autowired + @Qualifier 则注入指定的实现 * 2 如果使用了 @Primary 则注入拥有该注解的实现 * 3 否则根据数据库类型获取对应的实现 */
@Component public class DBBeanFactoryPostProcessor implements BeanFactoryPostProcessor { /** * 如果与数据库类型相同,这将该beanDefinition的设为AutowireCandidate, * 其余都设为false,但是如果该类是主要的,则不作处理 */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 如果有实现类设置了@Primary,则将它父接口放到这里面,在循环结束后将该接口的实现类的AutowireCandidate还原为true, // 因为有了主要的了 Set<Class> parentInterfaces = new HashSet<>() ; List<BeanDefinition> primaryClass = new ArrayList<>(); try { // 设置与数据库匹配的实现类,如果有实现类设置了Primary,则返回该类的实现接口 autoMatchDbtype(beanFactory,parentInterfaces,primaryClass); // 回滚夫接口下对应类的操作,即如果有实现类设置了Primary,则将之前兄弟类的AutowireCandidate改回true if ( parentInterfaces.size() > 0) { rollback( parentInterfaces,primaryClass); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 如果parentInterFaceSet不为空,则再循环一个beanDefinition,将所有该接口的子类的AutowireCandidate还原为true */ private void rollback( Set<Class> parentInterFaceSet, List<BeanDefinition> primaryClass) throws ClassNotFoundException { for (BeanDefinition definition : primaryClass) { String beanClassName = definition.getBeanClassName(); Class<?> beanClass = Class.forName(beanClassName); // 找到同一个接口的所有实现类,将刚才设置的primary还原为false Class<?>[] interfaces = beanClass.getInterfaces(); for (Class<?> faceInDefinition : interfaces) { if(parentInterFaceSet.contains(faceInDefinition)) { // 获取装载bean的信息 definition.setPrimary(false); break; } } } }
/** * 为definition自动匹配数据库类型 {@link DBtype} */ private void autoMatchDbtype(ConfigurableListableBeanFactory beanFactory, Set<Class> parentInterfaces, List<BeanDefinition> primaryClass) throws ClassNotFoundException { String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); for (String definitionName : beanDefinitionNames) { // 获取被代理类的注解 是否有DBtype注解 Class<?> beanClass = getOriginalClassOfBeanDefination(definitionName, beanFactory); if (beanClass == null) { continue; } // 获取该类是否有DBtype注解 DBtype annotation = AnnotationUtils.getAnnotation(beanClass, DBtype.class); if (annotation == null) { continue; } // 获取装载bean的信息 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(definitionName); boolean isPrimary = beanDefinition.isPrimary(); // 设置了主要的实现,则不行需要将AutowireCandidate设为false if (isPrimary) { parentInterfaces.addAll(Arrays.asList(beanClass.getInterfaces())); } else { // 如果与数据库类型相同,这将该 beanDefinition 的设为Primary设为true if (annotation.value().equals(DButil.getDbType())) { beanDefinition.setPrimary(true); primaryClass.add(beanDefinition); } } } } /** * 获取BeanDefination中代理的类名称 */ private Class getOriginalClassOfBeanDefination(String definitionName, ConfigurableListableBeanFactory beanFactory) throws ClassNotFoundException { boolean isOtherFrameName = definitionName.contains("."); if (isOtherFrameName) { return null; } // 获取装载bean的信息 BeanDefinition beanDefinition = beanFactory.getBeanDefinition(definitionName); String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName == null) { return null; } return Class.forName(beanClassName); } }
完整代码
来源:oschina
链接:https://my.oschina.net/u/2828310/blog/4332619