objectFactory解析、
那么,接下来,就简单介绍一下这几个配置的作用吧:
1、objectFactory是干什么的? 需要配置吗?
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。默认情况下,我们不需要配置,mybatis会调用默认实现的objectFactory。 除非我们要自定义ObjectFactory的实现, 那么我们才需要去手动配置。
简单点就是说,这个类其实是为了在对查询结果集中获取数据被封装成所希望的Java实体类型时用到的,使用这个工厂类通过反射的方式来进行实例对象的创建。所有的工厂类都必须实现ObjectFactory接口。我们来看看这个接口的定义:
public interface ObjectFactory {
/**
* Sets configuration properties.
* @param properties configuration properties
*/
void setProperties(Properties properties);
/**
* Creates a new object with default constructor.
* @param type Object type
* @return
*/
<T> T create(Class<T> type);
/**
* Creates a new object with the specified constructor and params.
* @param type Object type
* @param constructorArgTypes Constructor argument types
* @param constructorArgs Constructor argument values
* @return
*/
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
/**
* Returns true if this object can have a set of other objects.
* It's main purpose is to support non-java.util.Collection objects like Scala collections.
*
* @param type Object type
* @return whether it is a collection or not
* @since 3.1.0
*/
<T> boolean isCollection(Class<T> type);
}
从这个接口定义可以看出,它包含了两种通过反射机制构造实体类对象的方法,一种是通过无参构造函数,一种是通过带参数的构造函数。另外,为了使的这个工厂类能接收设置的附带属性,还提供了setProperties()方法。mybatis为我们实现了一个默认实现,那就是DefaultObjectFactory,我们来看看默认是怎么实现的。
public class DefaultObjectFactory implements ObjectFactory, Serializable {
private static final long serialVersionUID = -8855120656740914948L;
@Override
public <T> T create(Class<T> type) {
return create(type, null, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
// no props for default
}
<T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
for (Class<?> argType : constructorArgTypes) {
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
for (Object argValue : constructorArgs) {
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
}
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
protected Class<?> resolveInterface(Class<?> type) {
Class<?> classToCreate;
if (type == List.class || type == Collection.class || type == Iterable.class) {
classToCreate = ArrayList.class;
} else if (type == Map.class) {
classToCreate = HashMap.class;
} else if (type == SortedSet.class) { // issue #510 Collections Support
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
return classToCreate;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
从上面的源码来看,创建实例对象最终都是通过instantiateClass()方法能实现的,在这个方法中,获取实体类的无参构造函数或者带参构造函数,然后采用反射的机制来实例化实体类对象出来。
如果我们想定义一个自定义的对象工厂类,我们可以实现ObjectFactory这个接口,但是这样我们就需要自己去实现一些在DefaultObjectFactory已经实现好了的东西,因此如果想自定义一个,可以继承这个DefaultObjectFactory类,这样可以使得实现起来更为简单。
下面我们来举个示例,我们假设需要自定义一个对象工厂类ExampleObjectFactory,在要创建我之前定义的User对象时会给属性字段author赋值为我的名字,接下来我们看看怎么实现。
首先我们定义实现这个对象工厂类:
public class ExampleObjectFactory extends DefaultObjectFactory{
private static final long serialVersionUID = 3608715667301891724L;
@Override
public <T> T create(Class<T> type) {
T result = super.create(type);
if(type.equals(Mail.class)){
((Mail)result).setUseFor("曾涛平");
}
return result;
}
}
在这个工厂类中,我们对Mail类中无参构造函数构造出来的对象做了个特殊处理,加了个我的名字。接下来,我们在配置文件中配置这个类为我们想要的对象工厂类。
写好了ObjectFactory, 仅需做如下配置:
<objectFactory type="Factory.ExampleObjectFactory">
<property name="someProperty" value="100"/>
</objectFactory>
然后我们执行测试代码,查询一条用户记录出来,发现返回的对象中的确设置了我的名字,如下所示:
log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
log4j:WARN Please initialize the log4j system properly.
SQL:[select id, create_time, modify_time, web_id, mail, use_for from mail where id = 67;]执行耗时[5644ms]
Mail [id=67, createTime=Wed Jan 17 14:39:04 CST 2018, modifyTime=Wed Jan 17 14:39:04 CST 2018, webId=2, mail=Address [province=Address [province=null, city= city=7]], useFor=曾涛平]
至此,关于mybatis中的对象工厂相关的知识点便讲解的差不多了。