Mybatis整合Spring思路

Mybatis整合Spring思路

需要解决的问题和解决思路

1、如何将接口注入到容器
这个在Mybatis其实已经做好了,是通过动态代理的方式,将接口由代理类注入;
但是要采取一种合适的方式注入,我们常用的方式有@component注解,或是通过@configuration和@bean注解,但是这里显然都不能满足需求。
采取的做法是通过FactoryBean和ImportBeanDefinitionRegistrar的方式

beanFactoryPostProcessor
bean工厂的后置处理器,只能修改beanDeinition,不能往里面新增

ImportBeanDefinitionRegister 可以实现新增beanDefinition
2、如果扫描到mapper注解
通过spring的工具类方式,代码里体现

核心代码

实现FactoryBean


/**
 * 通过动态代理生成mapper的实体类
 * 实现FactoryBean方法,用于配置到ImportBeanDefinitionRegistrar
 */
public class MapperProxy implements FactoryBean<Object> {

    private Class<?> clazz;

    /**
     * 将需要代理的接口通过参数传递进来
     *
     * @param clazz
     */
    public MapperProxy(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object getObject() throws Exception {
        //动态代理接口
        return Proxy.newProxyInstance(MapperProxy.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) {
                return method.getAnnotation(Select.class).value();
            }
        });
    }

    @Override
    public Class<?> getObjectType() {
        return clazz;
    }
}

实现ImportBeanDefinitionRegistrar

/**
 * 实现ImportBeanDefinitionRegistrar的registerBeanDefinitions方法
 * 可以通过构造beanDefinition来注入bean
 * AnnotationMetadata importingClassMetadata这个变量可以获取主类上的所有注解
 */
@Component
public class BeanRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName());
        String basePackage = (String) annotationAttributes.get("value");

        Map<String, Class> map = ScanUtils.getMap(basePackage, Mapper.class);
        for (Class clazz : map.values()) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
            beanDefinition.setBeanClassName(MapperProxy.class.getName());
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(clazz);
            registry.registerBeanDefinition(clazz.getSimpleName(), beanDefinition);
        }


    }
}

主类

需要使用Import注解

/**
 * 实现mybatis整合spring的大致思路
 * 主要思路为
 * 1、通过jdk动态代理生成接口mapper的代理类
 * 2、将代理类注入到容器
 * 2.1 采用BeanFactory的方式,工厂化生成代理类
 * 2.2 采用ImportBeanDefinitionRegistrar,直接定义BeanDefinition来构造bean
 * 3、指定包扫描注解(ScanUtils)
 * 测试方法在com.lexiaoyao.springmybatis.SpringMybatisApplicationTests
 */
@SpringBootApplication
@Import(BeanRegister.class)//需要通过Import注解来导入BeanRegister
@MapperScan("com.lexiaoyao.springmybatis")
public class SpringMybatisApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringMybatisApplication.class, args);
    }

}

扫包工具类

public class ScanUtils {

    private static final String RESOURCE_PATTERN = "/**/*.class";

    /**
     * 可以获取指定路径下的带有某个注解的全部类
     *
     * @param path      指定路径
     * @param annoClass 指定要寻找的注解
     * @return
     */
    public static Map<String, Class> getMap(String path, Class<? extends Annotation> annoClass) {
        Map<String, Class> handlerMap = new HashMap<String, Class>();

        //spring工具类,可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(path) + RESOURCE_PATTERN;
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = readerfactory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                //判断是否有指定主解
                Annotation annotation = clazz.getAnnotation(annoClass);
                if (annotation != null) {
                    //将注解中的类型值作为key,对应的类作为 value
                    handlerMap.put(classname, clazz);
                }
            }
        } catch (IOException | ClassNotFoundException e) {
        }
        return handlerMap;
    }

}

git

https://github.com/lexiaoyao1995/spring-mybatis

posted @ 2020-11-10 18:27  刃牙  阅读(220)  评论(0编辑  收藏  举报