我们都知道Mybatis的Mapper接口可以直接来依赖注入,它本没有实现类,又是如何实例化的呢。先看看Mybatis没有集成Spring是如何实例化的。
上面的MapperFactoryBean就可以实例化出mapperInterface类型的Bean,因为MapperFactoryBean实现了FactoryBean接口的getObject方法,可以实例化出我们想要的Bean,实际上是通过Jdk动态代理得到的Bean。
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
调用方式:
SqlSession session = sqlSessionFactory.openSession();
try {
User user= (User) session.selectOne("org.mybatis.example.UserMapper.selectBlog", 1);
} finally {
session.close();
}
上面的UserMapper接口是一个个的配置来实例化的,每次需要openSession,close Session,重复工作太多,这样肯定比较麻烦,下面看看项目中常用的Mybatis与Spring集成的配置方式。
这个SqlSessionFactoryBean会做哪些事呢,主要是把*Mapper*.xml文件与*Mapper*.java加载进来,根据namespace加载对应的接口类到MapperRegistry,把方法名与*Mapper*.xml里的Select id对应起来等等。MapperRegistry相当于是一个缓存,后面创建代理对象是会用到。
//这里的值设置,只要是注解就行,不一定是Autowird,它代表只对有注解的接口类创建代理对象,否则,对basePackage下所有接口创建代理对象
MapperScannerConfigurer可以对basePackage下所有Mapper接口创建代理对象,而不是像上面一个个配置。因为他实现了接口BeanDefinitionRegistryPostProcessor,它就是用来自定义Bean的,看看源码它是如何实现的。
上面设置mapperInterface,BeanClass其实就是上面讲的Mybatis没有集成Spring的配置方式。
我们知道了代理对象是通过MapperFactoryBean创建的。具体看看是如何创建的。
根据接口类创建代理对象
/**
* {@inheritDoc}
*/
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
这里就用到了之前说的MapperRegistry,只是用来校验该接口是否存在。这里又用到了MapperProxy,是一个代理类,实现了InvocationHandler接口。
public static T newMapperProxy(Class mapperInterface, SqlSession sqlSession) {
ClassLoader classLoader = mapperInterface.getClassLoader();
Class>[] interfaces = new Class[]{mapperInterface};
MapperProxy proxy = new MapperProxy(sqlSession);
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
final Class> declaringInterface = findDeclaringInterface(proxy, method);
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive() && !method.getReturnType().equals(Void.TYPE)) {
throw new BindingException("Mapper method '" + method.getName() + "' (" + method.getDeclaringClass() + ") attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
当我们调用Mpper接口方法时,会被MapperProxy代理类拦截调用,主要调用实现就是mapperMethod.execute(args),看看
底层实现
public Object execute(Object[] args) {
Object result = null;
if (SqlCommandType.INSERT == type) {
Object param = getParam(args);
result = sqlSession.insert(commandName, param);
} else if (SqlCommandType.UPDATE == type) {
Object param = getParam(args);
result = sqlSession.update(commandName, param);
} else if (SqlCommandType.DELETE == type) {
Object param = getParam(args);
result = sqlSession.delete(commandName, param);
} else if (SqlCommandType.SELECT == type) {
if (returnsVoid && resultHandlerIndex != null) {
executeWithResultHandler(args);
} else if (returnsMany) {
result = executeForMany(args);
} else if (returnsMap) {
result = executeForMap(args);
} else {
Object param = getParam(args);
result = sqlSession.selectOne(commandName, param); //跟上面没有集成Spring调用方式一样的。
}
} else {
throw new BindingException("Unknown execution method for: " + commandName);
}
return result;
}