Mybatis整合Spring之MapperFactoryBean简单分析
MapperFactoryBean分析:
MapperFactoryBean的运作原理到底是什么?
继承关系如下图所示:
分析:
1.FactoryBean可以整合到Spring中,其中内部 getObject() 就是实际注册到容器中的对象,getObjectType()就是注册到容器中的类型
2.另外层层集成直到实现InitializingBean,内部具有 afterPropertiesSet() 也是Spring组件在创建的,设置好相关的属性之后就会调用这个方法。
其中 afterPropertiesSet() 在Spring的执行时机
// 前方调用
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
// 后方调用
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
其中DaoSupport重写了
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
发现内部调用了 checkDaoConfig() 与 initDao()方法
其中 checkDaoConfig() 被SqlSessionDaoSupport重写,但是又被其父类MapperFactoryBean重写了如下:
protected void checkDaoConfig() {
super.checkDaoConfig();
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
}
}
其中 initDao() 没有被父类重写,且自己这是个空方法,什么也不做
其中 getObject() 干了什么?
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
其中mapperInterface就是我们bean组件显示赋值我们mapper的接口的全路径,是Class
点进 getMapper(this.mapperInterface)方法,里面任何一个实现类大致的写法都是如下
return this.configuration.getMapper(type, this);
点进 getMapper(type, this)方法
return this.mapperRegistry.getMapper(type, sqlSession);
点进 getMapper(type, sqlSession)方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
看到了,将sqlSession传进去准备构建代理对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
显然用的是Jdk动态代理的模式
个人模仿这种模式来编写一个自己的工厂:
仅仅用于测试,不能用于实际使用,因为我将其中好多判别都取消了。测试也是ok的!
package com.custom.nxj.bean;
import lombok.Data;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author ningxinjie
* @date 2021/2/3
* MapperFactoryBean传的是SqlSessionFactory,这里我传SqlSession试试【缩减版,仅仅用于个人测试】
*/
@Data
public class NxjFactoryBean<T> implements FactoryBean<T>, InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
private Class<T> mapperInterface;
private SqlSessionTemplate sqlSessionTemplate;
@Override
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
// Spring创建这个组件的时候会调用(内部动态代理)
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = this.getSqlSession().getConfiguration();
if (!configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception var6) {
this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
throw new IllegalArgumentException(var6);
} finally {
ErrorContext.instance().reset();
}
}
}
private SqlSession getSqlSession() {
return sqlSessionTemplate;
}
}
然后注册到容器中,如下,也是可以获取到连接的!
<!--自己测试,舍弃了很多判别操作,也是可以的-->
<bean id="personMapper2" class="com.custom.nxj.bean.NxjFactoryBean">
<!-- 关联Mapper接口 -->
<property name="mapperInterface" value="com.custom.nxj.mapper.PersonMapper"/>
<!-- 关联SqlSessionFactory -->
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>