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渗透、网络安全的运维的知识,请关注微信公众号:发哥微课堂。


猜你喜欢

转载自blog.csdn.net/fageweiketang/article/details/80794847