MyBatis 配置 objectFactory 详解
0x00:前言参考
之前的《MyBatis 中 SqlMapConfig 配置文件详解》记了一下 MyBatis 中的核心配置文件各个标签的作用和使用场景,这篇文章细说一下配置文件中 objectFactory 标签的详细使用。
0x01:标签介绍
在 MyBatis 中,当其 sql 映射配置文件中的 sql 语句所得到的查询结果,被动态映射到 resultType 或其他处理结果集的参数配置对应的 Java 类型,其中就有 JavaBean 等封装类。而 objectFactory 对象工厂就是用来创建实体对象的类。
在 MyBatis 中,默认的 objectFactory 要做的就是实例化查询结果对应的目标类,有两种方式可以将查询结果的值映射到对应的目标类,一种是通过目标类的默认构造方法,另外一种就是通过目标类的有参构造方法。
有时候在 new 一个新对象(构造方法或者有参构造方法),在得到对象之前需要处理一些逻辑,或者在执行该类的有参构造方法时,在传入参数之前,要对参数进行一些处理,这时就可以创建自己的 objectFactory 来加载该类型的对象。
0x02:配置解读
如果要改写默认的对象工厂,可以继承 DefaultObjectFactory 来创建自己的对象工厂,从而改写相关的 4 个方法,如下:
public class MyObjectFactory extends DefaultObjectFactory {
//处理默认构造方法
public Object create(Class type){
return super.create(type);
}
//处理有参构造方法
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs){
return super.create(type, constructorArgTypes, constructorArgs);
}
//处理参数
public void setProperties(Properties properties){
super.setProperties(properties);
}
//判断集合类型参数
public <T> boolean isCollection(Class<T> type){
return Collection<E>.class.isAssignableFrom(type);
}
}
写好自己的对象工厂之后,在 MyBatis 的全局配置文件 SqlMapConfig.xml 中添加配置使其生效,配置代码如下:
<objectFactory type="org.mybatis.example.MyObjectFactory">
<property name="email" value="undefined"/>
</objectFactory>
其子标签 property 会在加载全局配置文件 SqlMapConfig.xml 时通过 setProperties 方法被初始化到 MyObjectFactory 中,作为该类的全局参数使用。(ps:在 SqlMapConfig.xml 中,objectFactory 中的 property 子参数是通过 objectFactory 类的 setProperties 方法设置进去的。)
0x03:代码示例
例如有一个购物车 ShoppingCart 类的对象工厂 CartObjectFactory,它的功能就是在执行购物车 ShoppingCart 类的构造方法之前,去执行 ShoppingCart 类的 init 方法来计算购物车的总金额,ShoppingCart 类代码如下:
package cn.com.mybatis.pojo;
public class ShoppingCart {
private int productId;
private String productName;
private int number;
private double price;
private double totalAmount;
public ShoppingCart(){}
public ShoppingCart(int productId,String productName,int number,double price,double totalAmount){
super();
this.productId = productId;
this.productName = productName;
this.number = number;
this.price = price;
this.totalAmount = totalAmount;
}
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(double totalAmount) {
this.totalAmount = totalAmount;
}
public void init(){
this.totalAmount = this.number * this.price;
}
}
随后定义 CartObjectFactory 对象工厂类,继承 DefaultObjectFactory 类,并重写 create 方法,在该方法中检测如果加载的是 ShoppingCart 类型,就加载其 init 方法,代码如下:
package cn.com.mybatis.test;
import java.util.List;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import cn.com.mybatis.pojo.ShoppingCart;
public class CartObjectFactory extends DefaultObjectFactory {
public <T> T create(Class<T> type){
return super.create(type);
}
//DefaultObjectFactory的create(Class type)方法也会调用此方法,所以只需要在此方法中添加逻辑即可
public <T> T create(Class<T> type,List<Class<?>> constructorArgTypes,List<Object> constructorArgs){
T ret = super.create(type,constructorArgTypes,constructorArgs);
if(ShoppingCart.class.isAssignableFrom(type)){
ShoppingCart entity = (ShoppingCart)ret;
entity.init();
}
return ret;
}
}
最后只要在 SqlMapConfig.xml 全局配置文件中配置该自定义对象工厂即可,代码如下:
<objectFactory type="cn.com.mybatis.test.CartObjectFactory"/>
下面编写一个测试类,在加载 SqlMapConfig.xml 全局配置文件,初始化配置的自定义对象工厂,然后实例化一个 ShoppingCart 类,并给其有参构造函数传入参数类型和参数值,看是否有执行 init 方法,测试代码如下:
package cn.com.mybatis.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import cn.com.mybatis.datasource.DataConnection;
import cn.com.mybatis.pojo.ShoppingCart;
public class ObjectFactoryTest {
public static DataConnection dataConn = new DataConnection();
public static void main(String[] args) throws IOException{
SqlSession sqlSession = dataConn.getSqlSession();
CartObjectFactory e = new CartObjectFactory();
List constructorArgTypes = new ArrayList();
constructorArgTypes.add(int.class);
constructorArgTypes.add(String.class);
constructorArgTypes.add(int.class);
constructorArgTypes.add(double.class);
constructorArgTypes.add(double.class);
List constructorArgs = new ArrayList();
constructorArgs.add(1);
constructorArgs.add("运动鞋");
constructorArgs.add(5);
constructorArgs.add(300);
constructorArgs.add(0.0);
ShoppingCart sCart = (ShoppingCart)e.create(ShoppingCart.class,constructorArgTypes,constructorArgs);
System.out.println(sCart.getTotalAmount());
sqlSession.close();
}
}
测试用例中运动鞋一双 300,共买 5 双,运行后会在控制台打印结果总价。由此可知,对象工厂在执行 ShoppingCart 类的有参构造方法时,执行了 init 方法。结果如下图:
0x04:总结
在 MyBatis 中,objectFactory 自定义对象类被定义在工程中,在全局配置文件 SqlMapConfig.xml 中配置。当 Resource 资源类加载 SqlMapConfig.xml 文件,并创建出 SqlSessionFactory 时,会加载配置文件中自定义的 objectFactory,并设置配置标签中的 property 参数。
更多关于代码审计、WEB渗透、网络安全的运维的知识,请关注微信公众号:发哥微课堂。
上一篇: MyBatis详解(3)--动态SQL