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>
posted @ 2021-02-03 08:47  程序杰杰  阅读(257)  评论(0编辑  收藏  举报