zoukankan      html  css  js  c++  java
  • 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>
    
  • 相关阅读:
    spring -项目功能介绍
    【HQL】分页查询 、对象导航查询、外置命名查询、连接查询、查询过滤器、统计查询
    【HQL】属性查询、条件查询
    【HQL】hibernate查询语言hql
    Hibernate- 表联系
    struts 配置过程 -一个计算器程序
    【DRP】-JSTL核心库 c:out标签
    .NET 使用sock5做代理(不是搭建服务端)
    重磅新闻!昨日阿里云发布首款云电脑“无影”,到底如何呢?
    C#如何实现获取电脑硬件相关的配置信息呢?
  • 原文地址:https://www.cnblogs.com/ningxinjie/p/14365172.html
Copyright © 2011-2022 走看看