Mybatis常用工具类

MetaObject

MetaObject是MyBatis中的反射工具类,使用MetaObject工具类,我们可以很优雅地获取和设置对象的属性值。

	@Data
  @AllArgsConstructor
  private static class User {
    List<Order> orders;
    String name;
    Integer age;
  }

  @Data
  @AllArgsConstructor
  private static class Order {
    String orderNo;
    String goodsName;
  }


  @Test
  public void metaObjectTest() {
    List<Order> orders = new ArrayList() {
      {
        add(new Order("order20171024010246", "《Mybatis源码深度解析》图书"));
        add(new Order("order20171024010248", "《AngularJS入门与进阶》图书"));
      }
    };
    User user = new User(orders, "江荣波", 3);
    MetaObject metaObject = SystemMetaObject.forObject(user);
    // 获取第一笔订单的商品名称
    System.out.println(metaObject.getValue("orders[0].goodsName"));
    // 获取第二笔订单的商品名称
    System.out.println(metaObject.getValue("orders[1].goodsName"));
    // 为属性设置值
    metaObject.setValue("orders[1].orderNo","order20181113010139");
    // 判断User对象是否有orderNo属性
    System.out.println("是否有orderNo属性且orderNo属性有对应的Getter方法:" + metaObject.hasGetter("orderNo"));
    // 判断User对象是否有name属性
    System.out.println("是否有name属性且orderNo属性有对应的name方法:" + metaObject.hasGetter("name"));
  }

如上面的代码所示,我们创建了一个User对象并初始化User对象的属性值,接着调用SystemMetaObject类的forObject()静态方法创建一个与User对象关联的MetaObject对象。我们可以通过MetaObject对象的getValue()方法以表达式的方式获取User对象的属性值。我们还可以使用MetaObject对象的setValue()方法以表达式的方式为User对象的属性设置值。当类的层级比较深时,使用MetaObject工具能够很方便地获取和设置对象的属性值。除此之外,我们还可以使用MetaObject工具类的hasSetter()和hasGetter()方法通过名称判断对象是否有某个属性且该属性有对应的Getter/Setter方法。

MetaClass

MetaClass是MyBatis中的反射工具类,与MetaOjbect不同的是,MetaObject用于获取和设置对象的属性值,而MetaClass则用于获取类相关的信息。

	@Data
  @AllArgsConstructor
  private static class Order {
    String orderNo;
    String goodsName;
  }
  
	@Test
  public void testMetaClass() {
    MetaClass metaClass = MetaClass.forClass(Order.class, new DefaultReflectorFactory());
    // 获取所有有Getter方法的属性名
    String[] getterNames = metaClass.getGetterNames();
    System.out.println(JSON.toJSONString(getterNames));
    // 是否有默认构造方法
    System.out.println("是否有默认构造方法:" + metaClass.hasDefaultConstructor());
    // 某属性是否有对应的Getter/Setter方法
    System.out.println("orderNo属性是否有对应的Getter方法:" + metaClass.hasGetter("orderNo"));
    System.out.println("orderNo属性是否有对应的Setter方法:" + metaClass.hasSetter("orderNo"));

    System.out.println("orderNo属性类型:" + metaClass.getGetterType("orderNo"));

    // 获取属性Getter方法
    Invoker invoker = metaClass.getGetInvoker("orderNo");
    try {
      // 通过Invoker对象调用Getter方法获取属性值
      Object orderNo = invoker.invoke(new Order("order20171024010248", "《Mybatis源码深度解析》图书"), null);
      System.out.println(orderNo);
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
  }

在上面的案例中,我们通过MetaClass获取了Java类的基本信息,包括Java类中所有的Getter方法对应的属性名称、Java类是否有默认的构造方法等信息。除此之外,我们还可以通过MetaClass获取Getter/Setter方法对应的Invoker对象,然后通过Invoker对象调用Getter/Setter方法。Invoker接口有3个不同的实现,分别为GetFieldInvoker、SetFieldInvoker、MethodInvoker,对应Java类的Getter方法、Setter方法和普通方法。

ObjectFactory

ObjectFactory是MyBatis中的对象工厂,MyBatis每次创建Mapper映射结果对象的新实例时,都会使用一个对象工厂(ObjectFactory)实例来完成。ObjectFactory接口只有一个默认的实现,即DefaultObjectFactory,默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。

	@Test
  public void testObjectFactory() {
    ObjectFactory objectFactory = new DefaultObjectFactory();
    List<Integer> list = objectFactory.create(List.class);
    Map<String, String> map = objectFactory.create(Map.class);
    list.addAll(Arrays.asList(1, 2, 3));
    map.put("test", "test");
    System.out.println(list);
    System.out.println(map);
  }

MyBatis中使用ObjectFactory实例创建Mapper映射结果对象的目的是什么呢?

实际上,这是MyBatis提供的一种扩展机制。有些情况下,在得到映射结果之前我们需要处理一些逻辑,或者在执行该类的有参构造方法时,在传入参数之前,要对参数进行一些处理,这时我们可以通过自定义ObjectFactory来实现

public class CustomObjectFactory extends DefaultObjectFactory {
  @Override
  public <T> T create(Class<T> type) {
    if(type.equals(User.class)){
      //实例化User类
      User user = (User)super.create(type);
      user.setUuid(UUID.randomUUID().toString());
      return (T) user;
    }
    return super.create(type);
  }

  @Data
  @AllArgsConstructor
  private static class User {
    String name;
    String uuid;
  }
}

ProxyFactory

ProxyFactory是MyBatis中的代理工厂,主要用于创建动态代理对象,ProxyFactory接口有两个不同的实现,分别为CglibProxyFactoryJavassistProxyFactory。从实现类的名称可以看出,MyBatis支持两种动态代理策略,分别为Cglib和Javassist动态代理。

ProxyFactory主要用于实现MyBatis的懒加载功能。当开启懒加载后,MyBatis创建Mapper映射结果对象后,会通过ProxyFactory创建映射结果对象的代理对象。当我们调用代理对象的Getter方法获取数据时,会执行CglibProxyFactory或JavassistProxyFactory中定义的拦截逻辑,然后执行一次额外的查询。

	@Test
  public void testProxyFactory() {
    // 创建ProxyFactory对象
    ProxyFactory proxyFactory = new JavassistProxyFactory();
    Order order = new Order("gn20170123","《Mybatis源码深度解析》图书");
    ObjectFactory objectFactory = new DefaultObjectFactory();
    // 调用ProxyFactory对象的createProxy()方法创建代理对象
    Object proxyOrder = proxyFactory.createProxy(order
      ,mock(ResultLoaderMap.class)
      ,mock(Configuration.class)
      ,objectFactory
      ,Arrays.asList(String.class,String.class)
      ,Arrays.asList(order.getOrderNo(),order.getGoodsName())
    );
    System.out.println(proxyOrder.getClass());
    System.out.println(((Order)proxyOrder).getGoodsName());
  }

注意:摘要于《mybatis3源码深度解析》

posted @   围观的小妖g  阅读(166)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示