Spring 数据库接口多实现类,根据数据库类型自动匹配对应的实体

扶醉桌前 提交于 2020-08-15 14:39:04

背景

    常用的数据库有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);
    }
}

完整代码

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!