首页 > 其他分享 >手写mybatis之把反射用到出神入化

手写mybatis之把反射用到出神入化

时间:2024-10-12 12:21:02浏览次数:8  
标签:name Object 出神入化 private mybatis new 手写 public String

前言


但在实操上,很多码农根本没法阅读框架源码。首先一个非常大的问题是,面对如此庞大的框架源码,不知道从哪下手。与平常的业务需求开发相比,框架源码中运用了大量的设计原则和设计模式对系统功能进行解耦和实现,也使用了不少如反射、代理、字节码等相关技术。
如果你有阅读 Mybatis 的源码,会发现这里使用了 Mybatis 自己实现的元对象反射工具类,可以完成一个对象的属性的反射填充。这块的工具类叫做 MetaObject 并提供相应的;元对象、对象包装器、对象工厂、对象包装工厂以及 Reflector 反射器的使用。那么本章节我们就来实现一下反射工具包的内容,因为随着我们后续的开发,也会有很多地方都需要使用反射器优雅的处理我们的属性信息。这也能为你添加一些关于反射的强大的使用!
在这里插入图片描述

反射调用者
关于对象类中的属性值获取和设置可以分为 Field 字段的 get/set 还有普通的 Method 的调用,为了减少使用方的过多的处理,这里可以把集中调用者的实现包装成调用策略,统一接口不同策略不同的实现类。

public interface Invoker {

    Object invoke(Object target, Object[] args) throws Exception;

    Class<?> getType();

}

无论任何类型的反射调用,都离不开对象和入参,只要我们把这两个字段和返回结果定义的通用,就可以包住不同策略的实现类了。
MethodInvoker

public class MethodInvoker implements Invoker {

    private Class<?> type;
    private Method method;

    @Override
    public Object invoke(Object target, Object[] args) throws Exception {
        return method.invoke(target, args);
    }

}

GetFieldInvoker

public class GetFieldInvoker implements Invoker {

    private Field field;

    @Override
    public Object invoke(Object target, Object[] args) throws Exception {
        return field.get(target);
    }

}

SetFieldInvoker

public class SetFieldInvoker implements Invoker {

    private Field field;

    @Override
    public Object invoke(Object target, Object[] args) throws Exception {
        field.set(target, args[0]);
        return null;
    }

}

setter 方法的调用者处理,因为set只是设置值,所以这里就只返回一个 null 就可以了。
反射器解耦对象
Reflector 反射器专门用于解耦对象信息的,只有把一个对象信息所含带的属性、方法以及关联的类都以此解析出来,才能满足后续对属性值的设置和获取。

public class Reflector {

    private static boolean classCacheEnabled = true;

    private static final String[] EMPTY_STRING_ARRAY = new String[0];
    // 线程安全的缓存
    private static final Map<Class<?>, Reflector> REFLECTOR_MAP = new ConcurrentHashMap<>();

    private Class<?> type;
    // get 属性列表
    private String[] readablePropertyNames = EMPTY_STRING_ARRAY;
    // set 属性列表
    private String[] writeablePropertyNames = EMPTY_STRING_ARRAY;
    // set 方法列表
    private Map<String, Invoker> setMethods = new HashMap<>();
    // get 方法列表
    private Map<String, Invoker> getMethods = new HashMap<>();
    // set 类型列表
    private Map<String, Class<?>> setTypes = new HashMap<>();
    // get 类型列表
    private Map<String, Class<?>> getTypes = new HashMap<>();
    // 构造函数
    private Constructor<?> defaultConstructor;

    private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

    public Reflector(Class<?> clazz) {
        this.type = clazz;
        // 加入构造函数
        addDefaultConstructor(clazz);
        // 加入 getter
        addGetMethods(clazz);
        // 加入 setter
        addSetMethods(clazz);
        // 加入字段
        addFields(clazz);
        readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
        for (String propName : readablePropertyNames) {
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
        for (String propName : writeablePropertyNames) {
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
    }
    
}

元类包装反射器
Reflector 反射器类提供的是最基础的核心功能,很多方法也都是私有的,为了更加方便的使用,还需要做一层元类的包装。在元类 MetaClass 提供必要的创建反射器以及使用反射器获取 get/set 的 Invoker 反射方法。

public class MetaClass {

    private Reflector reflector;

    private MetaClass(Class<?> type) {
        this.reflector = Reflector.forClass(type);
    }

    public static MetaClass forClass(Class<?> type) {
        return new MetaClass(type);
    }

    public String[] getGetterNames() {
        return reflector.getGetablePropertyNames();
    }

    public String[] getSetterNames() {
        return reflector.getSetablePropertyNames();
    }

    public Invoker getGetInvoker(String name) {
        return reflector.getGetInvoker(name);
    }

    public Invoker getSetInvoker(String name) {
        return reflector.getSetInvoker(name);
    }

		// ... 方法包装
}

对象包装器Wrapper

public interface ObjectWrapper {

    // get
    Object get(PropertyTokenizer prop);

    // set
    void set(PropertyTokenizer prop, Object value);

    // 查找属性
    String findProperty(String name, boolean useCamelCaseMapping);

    // 取得getter的名字列表
    String[] getGetterNames();

    // 取得setter的名字列表
    String[] getSetterNames();

    //取得setter的类型
    Class<?> getSetterType(String name);

    // 取得getter的类型
    Class<?> getGetterType(String name);

    // ... 省略

}

元对象封装

public class MetaObject {
    // 原对象
    private Object originalObject;
    // 对象包装器
    private ObjectWrapper objectWrapper;
    // 对象工厂
    private ObjectFactory objectFactory;
    // 对象包装工厂
    private ObjectWrapperFactory objectWrapperFactory;

    private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
        this.originalObject = object;
        this.objectFactory = objectFactory;
        this.objectWrapperFactory = objectWrapperFactory;

        if (object instanceof ObjectWrapper) {
            // 如果对象本身已经是ObjectWrapper型,则直接赋给objectWrapper
            this.objectWrapper = (ObjectWrapper) object;
        } else if (objectWrapperFactory.hasWrapperFor(object)) {
            // 如果有包装器,调用ObjectWrapperFactory.getWrapperFor
            this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
        } else if (object instanceof Map) {
            // 如果是Map型,返回MapWrapper
            this.objectWrapper = new MapWrapper(this, (Map) object);
        } else if (object instanceof Collection) {
            // 如果是Collection型,返回CollectionWrapper
            this.objectWrapper = new CollectionWrapper(this, (Collection) object);
        } else {
            // 除此以外,返回BeanWrapper
            this.objectWrapper = new BeanWrapper(this, object);
        }
    }

    public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
        if (object == null) {
            // 处理一下null,将null包装起来
            return SystemMetaObject.NULL_META_OBJECT;
        } else {
            return new MetaObject(object, objectFactory, objectWrapperFactory);
        }
    }
    
    // 取得值
    // 如 班级[0].学生.成绩
    public Object getValue(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                // 如果上层就是null了,那就结束,返回null
                return null;
            } else {
                // 否则继续看下一层,递归调用getValue
                return metaValue.getValue(prop.getChildren());
            }
        } else {
            return objectWrapper.get(prop);
        }
    }

    // 设置值
    // 如 班级[0].学生.成绩
    public void setValue(String name, Object value) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                if (value == null && prop.getChildren() != null) {
                    // don't instantiate child path if value is null
                    // 如果上层就是 null 了,还得看有没有儿子,没有那就结束
                    return;
                } else {
                    // 否则还得 new 一个,委派给 ObjectWrapper.instantiatePropertyValue
                    metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
                }
            }
            // 递归调用setValue
            metaValue.setValue(prop.getChildren(), value);
        } else {
            // 到了最后一层了,所以委派给 ObjectWrapper.set
            objectWrapper.set(prop, value);
        }
    }
    
    // ... 省略
}    

测试
配置数据源

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

配置Mapper

<select id="queryUserInfoById" parameterType="java.lang.Long" resultType="cn.bugstack.mybatis.test.po.User">
    SELECT id, userId, userName, userHead
    FROM user
    where id = #{id}
</select>

单元测试

@Test
public void test01() {
    Teacher teacher = new Teacher();
    List<Teacher.Student> list = new ArrayList<>();
    list.add(new Teacher.Student());
    teacher.setName("明哥");
    teacher.setStudents(list);

    MetaObject metaObject = SystemMetaObject.forObject(teacher);

    logger.info("getGetterNames:{}", JSON.toJSONString(metaObject.getGetterNames()));
    logger.info("getSetterNames:{}", JSON.toJSONString(metaObject.getSetterNames()));
    logger.info("name的get方法返回值:{}", JSON.toJSONString(metaObject.getGetterType("name")));
    logger.info("students的set方法参数值:{}", JSON.toJSONString(metaObject.getGetterType("students")));
    logger.info("name的hasGetter:{}", metaObject.hasGetter("name"));
    logger.info("student.id(属性为对象)的hasGetter:{}", metaObject.hasGetter("student.id"));
    logger.info("获取name的属性值:{}", metaObject.getValue("name"));
    // 重新设置属性值
    metaObject.setValue("name", "小白");
    logger.info("设置name的属性值:{}", metaObject.getValue("name"));
    // 设置属性(集合)的元素值
    metaObject.setValue("students[0].id", "001");
    logger.info("获取students集合的第一个元素的属性值:{}", JSON.toJSONString(metaObject.getValue("students[0].id")));
    logger.info("对象的序列化:{}", JSON.toJSONString(teacher));
}

数据源测试

@Test
public void test02() throws IOException {
    // 1. 从SqlSessionFactory中获取SqlSession
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 2. 获取映射器对象
    IUserDao userDao = sqlSession.getMapper(IUserDao.class);

    // 3. 测试验证
    User user = userDao.queryUserInfoById(1L);
    logger.info("测试结果:{}", JSON.toJSONString(user));
}

总结
1:使用了大量的 JDK 所提供的关于反射一些处理操作,也包括可以获取一个 Class 类中的属性、字段、方法的信息。那么再有了这些信息以后就可以按照功能流程进行解耦,把属性、反射、包装,都依次拆分出来,并按照设计原则,逐步包装让外接更少的知道内部的处理。
2:这里的反射也算是小天花板的使用级别了,封装的工具类方式,如果在我们也有类似的场景中,就可以直接拿来使用。因为整个工具类并没有太多的额外关联,直接拿来封装成一个工具包进行使用,处理平常的业务逻辑中组件化的部分,也是非常不错的。技术迁移、学以致用、升职加薪

好了到这里就结束了手写mybatis之把反射用到出神入化的学习,大家一定要跟着动手操作起来。需要源码的 可si我获取;

标签:name,Object,出神入化,private,mybatis,new,手写,public,String
From: https://blog.csdn.net/CSDN_LiMingfly/article/details/142765298

相关文章

  • 手写mybatis之数据源池化技术实现
    前言在上一章节我们解析了XML中数据源配置信息,并使用Druid创建数据源完成数据库的操作。但其实在Mybatis中是有自己的数据源实现的,包括无池化的UnpooledDataSource实现方式和有池化的PooledDataSource实现方式。你可以把池化技术理解为享元模式的具体实现方......
  • MyBatis 原理
    MyBatis原理  概要  MyBatis是一个持久层框架,用于将对象与数据库中的记录进行映射。它通过XML或注解的方式定义SQL语句并将结果映射到Java对象。  一、MyBatis原理  1.配置  MyBatis通过SqlSessionFactory加载配置文件(mybatis-config.xml),初......
  • 【02】手把手教你0基础部署SpringCloud微服务商城教学-Mybatis篇(下)
    上期回顾:【01】手把手教你0基础部署SpringCloud微服务商城教学-Mybatis篇(上)Part1.续接上文Mybatis-plus的批处理功能接下来我们学习一下IService的批量查询,我们用以往的for循环做一个对比这是for循环部分的代码privateUserbuilderUser(inti){Useruser=new......
  • 一款Java CMS 网站管理系统,基于RuoYi-fast二次开发,网站后台采用SpringBoot + MyBati
    一款JavaCMS网站管理系统基于RuoYi-fast二次开发,网站后台采用SpringBoot+MyBatis文章目录前言一、开源地址二、环境要求三、功能亮点3.1扩展功能3.2内置功能四、安装方法4.1、拉取源码4.2、修改数据库链接配置4.3、创建数据库并导入数据4.4、配置资源上传......
  • Java项目: 基于SpringBoot+mybatis+maven+vue健身房管理系统(含源码+数据库+毕业论文)
    一、项目简介本项目是一套基于SpringBoot+mybatis+maven+vue健身房管理系统包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。项目都经过严格调试,eclipse或者idea确保可以运行!该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用......
  • 如何使用MyBatis框架?
    MyBatis的基本信息     MyBatis本是apache的一个开源项目iBatis,后在2010年迁移到了googlecode,并且改名为MyBatis。后面迁移到Github。MyBatis是一个基于Java的数据持久层(ORM)框架。把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现。ORM对象关系映射,是......
  • [MyBatis-Plus]快速入门
    介绍MyBatis-Plus是MyBatis的好朋友,与MyBatis配合,实现开发效率的提高官网:特点:润物细无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑效率自上:只需简单配置,即可快速进行单表CRUD,从而节省大量时间功能丰富:代码生产,自动分页,逻辑删除......
  • 模拟一个微服务架构项目来学习包括Nacos、EMQX、GateWay、RabbitMQ、Canal、Mybatis-P
    前言介绍下最近做的项目:为什么做这个项目?项目的核心用户目标是谁?面向新能源电车用户给目标用户提供了什么价值?方便快捷充电服务团队的作用?需求分析,概要设计,详细设计,开发,测试,部署,上线我的作用?1-2两个核心业务详细设计(业务流程,接口入参,接口出参,表结......
  • 手写X86——第五节——X86实模式下的编程介绍2(寄存器赋初值)
    ......
  • MyBatis的常见面试题
    MyBatis1、什么是MyBatisMyBatis是一款优秀的半自动化的持久层框架。支持自定义SQL、存储过程以及高级映射。2、MyBatis的特点?简单、灵活、解耦、丰富的标签3、MyBatis的核心组件全局配置文件:MyBatis的一些全局信息,包含数据库链接信息和MyBatis运行时所需要的各种特性,以及......