Spring 整合mybaits 底层实现原理

论坛 期权论坛 脚本     
匿名技术用户   2021-1-7 01:40   564   0

众所周知,将对象交给spring管理有三种方法

1. @Bean, 即

public class AppConfig {

    @Bean
    AccDataCountMapper accDataCountMapper(){
        //实例化一个 AccDataCountMapper 对象
        AccDataCountMapper accDataCountMapper = (AccDataCountMapper) Proxy.newProxyInstance(SelfFactoryBean.class.getClassLoader(), new Class[]{AccDataCountMapper.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //先不做实现
                return null;
            }
        });
        return accDataCountMapper;
    }
}

问题来了,如果只有一个两个mapper,还好,一堆的话,不是要写到吐。不便于扩展。

2.注册

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        context.getBeanFactory().registerSingleton("accMapper", AccDataCountMapper.class);

通过这种方式直接注册了,就失去了交给spring 管理的意义了。

3.FactoryBean

public class SelfFactoryBean implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        
        //实例化一个 AccDataCountMapper 对象
        AccDataCountMapper accDataCountMapper = (AccDataCountMapper)Proxy.newProxyInstance(SelfFactoryBean.class.getClassLoader(), new Class[]{AccDataCountMapper.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //先不做实现
                return null;
            }
        });
        return accDataCountMapper;
    }

    @Override
    public Class<?> getObjectType() {
        return AccDataCountMapper.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

当然,目前还是静态的,即通过指定类。

修改下

public class SelfFactoryBean implements FactoryBean {

//动态实例化
    private Class mapper;

    public SelfFactoryBean(Class mapper){
        this.mapper = mapper;
    }

    @Override
    public Object getObject() throws Exception {
//        Proxy.newProxyInstance  jdk 代理对象
        Object o = Proxy.newProxyInstance(SelfFactoryBean.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //判断是否有这个类
                if(proxy.getClass().equals(method.getDeclaringClass())){
                    return method.invoke(this,args);
                }else{
                    // 执行代理逻辑
                    return null;
                }
            }
        });
        return o;

    }

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

    @Override
    public boolean isSingleton() {
        return false;
    }
}

这下子是个动态生成了,比较灵活,不用写死,便于扩展。

但是问题又来了。mapper从哪来呢,在哪里传过来呢。动态生成了但是不能用啊。

这时候就需要了解下 ImportBeanDefinitionRegistrar动态注册bean 了。

我有动态bean,你可以动态注册,两者结合下

public class SelfImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        

         //mapper 集合
         List<Class> maps = new ArrayList<>();
         maps.add(AccDataCountMapper.class);
         maps.add(ProductGoodsMapper.class);


         for(Class mapper : maps){
         //通过spring的  BeanDefinitionBuilder 生成 SelfFactoryBean,并在SelfFactoryBean
         BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
         AbstractBeanDefinition bd = builder.getBeanDefinition();

         //传入要实例化的mapper 类型
         bd.getConstructorArgumentValues().addGenericArgumentValue(mapper);
         bd.setBeanClass(SelfFactoryBean.class);
         beanDefinitionRegistry.registerBeanDefinition("self" + mapper.getSimpleName(),bd);
         }
    }
}

当然,这里也是个静态的注册,不够灵活

这里的静态注册,结合上面的动态bean工厂 ,实现了 静态注册。

接下来实现动态注册

动态注册的话就需要用到扫描pack了,即配置一个mapper路径,自己扫描路径的mapper

@Retention(RetentionPolicy.RUNTIME)
@Import(SelfImportBeanDefinitionRegister.class)
public @interface SelfScan {

    String value() default "";//要扫描的路径
}

创建一个注解,用来配置扫描的pack路径。 我们这个 SelfImportBeanDefinitionRegister 动态注册,要自动扫描的是 SelfScan 注解的路径,所以这里引入下 @Import(SelfImportBeanDefinitionRegister.class) 。

修改下静态注册为动态注册

public class SelfImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        /**
         * ImportBeanDefinitionRegistrar  配合 @Import 使用
         * spring 通过在 SelfScan 引入的使用:这一行 @Import(SelfImportBeanDefinitionRegister.class)  找到  @SelfScan("com.gifmall.mapper") 配置的value值,即: com.gifmall.mapper
         */
        Map<String,Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(SelfScan.class.getName());
        /**
         * 等同于
         * AnnotationAttributes mapAttr = AnnotationAttributes.fromMap(annotationAttributes);
         *         mapAttr.getStringArray("value");
         */
        String o = annotationAttributes.get("value").toString();
        System.out.println(o.toString());


        System.out.println("开始扫描pack ");
        List<String> mapperList = new ArrayList<>();
        for(String s : o.split(",")){
            System.out.println("开始扫描 : "+ s);
            //扫描pack下的bean,并进行添加
            mapperList.addAll(basePackageScan(s));
        }
        System.out.println("扫描结束");
        System.out.println("开始打印pack 并进行注册");
        for(String map : mapperList){
            System.out.println("开始注册 :" + map);
            Class cla = null;

            try {
                //通过类路径和类名转换成类
                cla = Class.forName(map);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            //通过spring的  BeanDefinitionBuilder 生成 SelfFactoryBean,并注册Bean
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
            AbstractBeanDefinition bd = builder.getBeanDefinition();

            //传入要实例化的mapper 类型
            bd.getConstructorArgumentValues().addGenericArgumentValue(cla);
            bd.setBeanClass(SelfFactoryBean.class);
            beanDefinitionRegistry.registerBeanDefinition("self" + cla.getSimpleName(),bd);
        }

        System.out.println("打印、注册结束");

    }

    private List<String> basePackageScan(String packName){
        List<String>classNames = new ArrayList<String>();
        URL url = this.getClass().getClassLoader().getResource(packName.replaceAll("\\.", "/"));
        String fileUrl = url.getFile()+"/";// E:/workspace/com/spring/mvc/
        //由于上面读取中文会URLEncode ,所以这里将中文路径生成的class写死
        fileUrl = "F:\\项目\\gifmall\\out\\production\\gifmall\\com\\gifmall\\mapper\\";
        File file = new File(fileUrl);//把路径转为文件目录
        String[] fileStr = file.list();//列出文件目录所有文件和文件夹的名字
        for (String path : fileStr) {//遍历所有的文件或文件夹的名字
            File pathFile = new File(fileUrl+path);//根据url和名字拼装成完整的url路径E:/workspace/com/spring/mvc
            if(pathFile.isDirectory()){
                basePackageScan(packName+"."+path);
            }else{//class 文件
                //mybatis.mapper.UserMapper
                classNames.add(packName+"."+pathFile.getName().replace(".class", ""));
            }
        }
        return classNames;
    }
}

自此,动态注册,动态代理,注解扫描mapper pack 都已经完成了。注解配置在哪呢

当然是Appconfig 了

@Configuration//加了全配置注解会被cglib增强
@SelfScan("com.gifmall.mapper")
public class AppConfig {
}

告诉他扫描这个pack 下的mapper

测试一下

public class TestMain {

    public static void main(String[] args) {
        //通过注解方式启动spring。   Appconfig 相当于通过spring 的 xml配置
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        //直接注册方式
//        context.getBeanFactory().registerSingleton("accMapper", AccDataCountMapper.class);

        //因为 在 registerBeanDefinitions 中注册的时候加入了  self  所以这里取的时候也需要加下
        context.getBean("self" + "AccDataCountMapper");
    }
}

结果:

至此,spring 整合mybaits 底层实现结束。

当然了,可能不太好理解。很多问号????

首先,spring 已经可以管理很多bean了。而我有一堆mapper ,要怎么交给spring 管理呢。

肯定不能把这一堆mapper直接扔给spring注册的。现在有10个,20个,你可以都写出来,给它。以后来了50个,100个呢。

所以就需要做到自动扫描了。--- SelfImportBeanDefinitionRegister + 注解 SelfScan 实现

然后我有这么多mapper,每个mapper类型是不是不一样,怎么保证我要的是我要的类型呢。这就需要动态代理了 -- SelfFactoryBean

自动扫描+动态代理都完成了,需要告诉下spring,自动扫描谁了,在 AppConfig 配置要扫描的mapper pack。

最后进行测试

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
context.getBean("self" + "AccDataCountMapper");

辅助参考文章:

https://blog.csdn.net/liurenyou/article/details/97025292

ImportBeanDefinitionRegistrar动态注册bean

https://blog.csdn.net/jiachunchun/article/details/94569246

视频

https://www.bilibili.com/video/BV1p5411x7DB?p=1

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP