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接口有两个不同的实现,分别为CglibProxyFactory和JavassistProxyFactory。从实现类的名称可以看出,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源码深度解析》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构