当前位置: 首页 > article >正文

手写@MapperScan

  • 定义一个EnableMapperScan注解
    @Import(MapperProxyHandlerRegister.class) 标注将MapperProxyHandlerRegister导入到容器中。
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(MapperProxyHandlerRegister.class)
public @interface EnableMapperScan {

    String baskPackages();

}
  • MapperProxyHandlerRegister

ImportBeanDefinitionRegistrar 是一个特殊组件,实现了这个接口的组件registry会动态给容器中批量注册组件,这里的关键就是获取到baskPackages 指定的路径,然后批量扫描classpath下的所有的接口,然后定义BeanDefinition,注册到容器中。
注意:AnnotationMetadata metadata封装的是标注了@Import注解的那个类。这样就可以拿到最终因哪个类扫描的这个配置类。

/**
 * Mapper组件注册器
 */
public class MapperProxyHandlerRegister implements ImportBeanDefinitionRegistrar {

    /**
     * @param metadata  注意:metadata是Spring解析@Import注解标注的那个类的元信息,而不是导入的配置类的元信息
     * @param registry  BeanDefinition注册器
     * @param generator BeanName生成器
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
                                        BeanDefinitionRegistry registry,
                                        BeanNameGenerator generator) {
        //这里的这个metadata封装的是标注了@Import注解的那个类,如果标注@Import注解因其他类似@Enablexxx注解进一步解析到的
        //那这里的metadata封装的就是解析@Enablexxx那个类的元数据信息,
        //总之,metadata封装的就是Spring原始解析类上关联的@Import注解的类
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(EnableMapperScan.class.getName());
        String baskPackages = annotationAttributes.get("baskPackages").toString();
        //获取此包下所有的类
        try {
            Set<Class<?>> scan = scan(baskPackages);
            if (CollUtil.isNotEmpty(scan)){
                //遍历接口
                for (Class<?> mapperClass : scan) {
                    //注册到容器
                    RootBeanDefinition definition = new RootBeanDefinition();
                    definition.setBeanClass(MapperFactoryBean.class);
                    //设置构造方法参数值
                    ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
                    constructorArgumentValues.addIndexedArgumentValue(0,mapperClass);
                    definition.setConstructorArgumentValues(constructorArgumentValues);
                    //definition.setFactoryMethodName("getObject");
                    //注册Bean
                    registry.registerBeanDefinition(mapperClass.getName(),definition);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /*
        1. 创建A   --> 依赖B --> 创建B(依赖A) -> 为A创建代理对象 ,并从缓存中拿,后注入A的是一个代理对象。


    * */
    public static Set<Class<?>> scan(String basePackage) throws IOException, ClassNotFoundException {
        // 创建一个扫描器
        //ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        // 可以添加过滤器来筛选特定类型的类,这里先不添加任何过滤器,获取所有类
        // 例如,如果只想获取带有特定注解的类,可以添加如下代码
        // scanner.addIncludeFilter(new AnnotationTypeFilter(YourAnnotation.class));
        // 如果只想获取特定接口的实现类,可以添加如下代码
        // scanner.addIncludeFilter(new AssignableTypeFilter(YourInterface.class));
        // 构建要扫描的资源路径模式
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + "/**/*.class";
        Resource[] resources = resolver.getResources(packageSearchPath);
        MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
        Set<Class<?>> classes = new HashSet<>();
        for (Resource resource : resources) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            String sourceClassName = metadataReader.getClassMetadata().getClassName();
            if (metadataReader.getClassMetadata().isInterface()){
                //只扫描mapper接口
                classes.add(Class.forName(sourceClassName));
            }
            //System.out.println("sourceClassName = " + sourceClassName);
            // 排除接口和抽象类,只获取具体类
            //if (!metadataReader.getClassMetadata().isInterface() &&!metadataReader.getClassMetadata().isAbstract()) {
            //    String className = metadataReader.getClassMetadata().getClassName();
            //    classes.add(Class.forName(className));
            //}
        }
        return classes;
    }

    private static String resolveBasePackage(String basePackage) {
        return basePackage.replace('.', '/');
    }

  • MapperProxy

这个类封装了代理对象的核心方法,主要就是获取mapper接口上标注了@Select @Update @Delete注解的方法,解析sql参数,获取sqlsessionfactory,然后执行sql。这里简单模拟输出获取到的sql。

/**
    mapper组件动态代理方法拦截实现
 */
public class MapperProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class){
            return method.invoke(this,args);
        }
        String name = method.getDeclaringClass().getName();
        String methodName = method.getName();
        System.out.println("mapperProxy 执行方法,被代理对象拦截 ===》 " + name + "." +methodName + "()");
        Select select = method.getAnnotation(Select.class);
        if (select != null){
            String[] value = select.value();
            //拦截方法
            System.out.println("拦截到的SQL:===> " + Arrays.toString(value));
        }

        Class<?> returnType = method.getReturnType();
        if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()){
            return 1;
        }
        return null;
    }
}
  • MapperFactoryBean

这个类实现了FactoryBean 接口,实现了这个接口的类Spring会进一步调用getObject方法创建Bean对象,这个Bean对象的类型是getObjectType返回的数据类型,由此,这个类型就是真正的Mapper接口类型,可以实现基于Mapper接口的自动装配。

public class MapperFactoryBean implements FactoryBean {

    private Class mapperInterFaceClazz;

    public MapperFactoryBean(Class mapperInterFaceClazz){
        this.mapperInterFaceClazz = mapperInterFaceClazz;
    }

    @Override
    public Object getObject() throws Exception {
        /**
            在创建代理对象时,传入需要实现的接口mapperInterFaceClazz
            如果这个接口还继承了其他接口,在基于实现类调方法时,父接口的方法也会被代理对象拦截。
            原理很简单,面向对象三大特征之一: 继承,子类继承父类,就把父类所有的内容全都继承
            JVM在判断时,如果满足继承结构以及安全检查
         */
        return Proxy.newProxyInstance(mapperInterFaceClazz.getClassLoader(),new Class[]{mapperInterFaceClazz},new MapperProxy());
    }

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

}
  • MainApp
@SpringBootApplication
@EnableMapperScan(baskPackages = "com.example.ssm3.mapper")

http://www.kler.cn/a/473203.html

相关文章:

  • Linux(上):基本知识篇
  • docker minio镜像arm64架构
  • 使用Llama 3.1创建合成数据集以调优你的大型语言模型
  • 【信息系统项目管理师】第15章:项目风险管理过程详解
  • Arduino IDE刷微控制器并下载对应固件的原由
  • 【银河麒麟高级服务器操作系统实例】tcp半链接数溢出分析及处理全过程
  • 【Linux系列】如何使用 nohup 命令在后台运行脚本
  • PostgreSQL学习笔记(二):PostgreSQL基本操作
  • 算法5--位运算
  • 网络安全-kail linux 网络配置(基础篇)
  • NRF24L01模块STM32通信-发送端
  • OA系统如何做好DDOS防护
  • 【Spring Boot】Spring AOP 快速上手指南:开启面向切面编程新旅程
  • 解决Docker冲突问题
  • RabbitMQ高级篇之MQ可靠性 数据持久化
  • 模式识别-Ch2-高斯下判别函数
  • vue.js 路由的基本使用
  • ChatGPT API快速搭建自己的第一个应用—文章摘要(单轮对话应用)
  • Idea日志乱码
  • 进程件通信——网络通信——TCP
  • Flink维表方案选型
  • IOS开发如何从入门进阶到高级
  • 更改IP地址能提高網路速度嗎?
  • conda 批量安装requirements.txt文件
  • MacBook Linux 树莓派raspberrypi安装Golang环境
  • Huawei Cloud EulerOS上安装sshpass