ip开源网站fpga可以做点什么用,设计公司口号,wordpress开发官网,长春网站优化文章目录 前言MapperFactoryBean的工作原理底层实现剖析MapperFactoryBean的checkDaoConfig()方法总结 MapperFactoryBean的getObject()方法 思考联想后续 系列相关相关文章究竟FactoryBean是什么#xff1f;深入理解Spring的工厂神器超硬核解析Mybatis动态代理原理#xff0… 文章目录 前言MapperFactoryBean的工作原理底层实现剖析MapperFactoryBean的checkDaoConfig()方法总结 MapperFactoryBean的getObject()方法 思考联想后续 系列相关相关文章究竟FactoryBean是什么深入理解Spring的工厂神器超硬核解析Mybatis动态代理原理只有接口没实现也能跑Mybatis与Spring结合深探——MapperFactoryBean的奥秘后续TODOMapperScannerConfigurer
前言 在没有Spring单独使用Mybatis的时候我在之前的文章超硬核解析Mybatis动态代理原理只有接口没实现也能跑 讲解到了调用链路new SqlSessionFactoryBuilder().build(xml)--XMLConfigBuilder#parse--XMLConfigBuilder#parseConfiguration---XMLConfigBuilder#mapperElement--XMLMapperBuilder#mapperParser.parse()--XMLMapperBuilder#configurationElement--XMLMapperBuilder#bindMapperForNamespace--Configuration#MapperRegistry#addMappper()
在SqlSessionFactoryBuilder().build方法 中最终调用Configuration对象的addMappper()方法(实际上是委托给MapperRegistry的addMapper)添加对应的MapperProxyFactory代理工厂类最终通过这个工厂类生成对应的代理对象MapperProxy 。
也就是MapperRegistry内部维护一个映射关系每个接口对应一个MapperProxyFactory生成动态代理工厂类
private final MapClass?, MapperProxyFactory? knownMappers new HashMap();这样便于在后面调用MapperRegistry的getMapper()时直接从Map中获取某个接口对应的动态代理工厂类然后再利用工厂类针对其接口生成真正的动态代理类。 如果想了解什么是FactoryBean是什么可以查看前文究竟FactoryBean是什么深入理解Spring的工厂神器
更详细的内容可以查看我之前的文章超硬核解析Mybatis动态代理原理只有接口没实现也能跑 而我们现在在Spring框架中整合Mybatis时我们通常会使用MapperFactoryBean来生成Mapper的代理实例也就是不需要再通过new SqlSessionFactoryBuilder().build(xml)的方式去注册动态代理接口。这是一种更简单且易于配置的方式可让我们以Spring的形式操作Mybatis的持久层。本文将深入探索MapperFactoryBean的工作原理并说明如何将Mybatis和Spring框架结合起来以构建一个响应迅速而又易于维护的数据访问层。
MapperFactoryBean的工作原理
当应用启动时Spring容器会为每个MapperFactoryBean生成一个相应的Bean实例。这个过程包含了几个关键步骤
Bean的定义在Spring配置文件中定义MapperFactoryBean这包括指定其sqlSessionFactory或sqlSessionTemplate。Bean的实例化Spring容器将调用MapperFactoryBean的getObject()方法这个方法内部又会调用Mybatis的SqlSession.getMapper()。生成Mapper代理正如前面提到的Mybatis使用动态代理技术生成代理对象。这个过程由Mybatis内部的MapperProxyFactory完成。Bean的使用最终创建的Mapper被注入到其他组件中这样业务代码就可以通过普通的Java方法调用来执行SQL操作了。
下面我们看看实际的配置代码示例
!-- Mybatis的SqlSessionFactory配置 --
bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBeanproperty namedataSource refdataSource /property namemapperLocations valueclasspath*:mapper/*.xml /
/bean!-- Mapper接口对应的FactoryBean配置 --
bean iduserMapper classorg.mybatis.spring.mapper.MapperFactoryBeanproperty namemapperInterface valuecom.example.mapper.UserMapper /property namesqlSessionFactory refsqlSessionFactory /
/bean
当然在Spring的Java配置中我们通常用注解来代替上述XML配置得益于Spring的MapperScan可以大幅简化这个配置
Configuration
MapperScan(com.example.mapper)
public class AppConfig {Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);// ...其他配置...return sessionFactory.getObject();}
}
底层实现剖析
MapperFactoryBean的checkDaoConfig()方法
MapperFactoryBean本身extend自SqlSessionDaoSupportSqlSessionDaoSupport又extend自DaoSupport接口DaoSupport接口实现了InitializingBean接口在对象初始化的时候会调用它的afterPropertiesSet方法该方法中首先调用了checkDaoConfig()方法MapperFactoryBean重载的checkDaoConfig()如下面所示—这里最终会将调用configuration.addMapper(this.mapperInterface)实际也是委托给MapperRegistry public class MapperFactoryBeanT extends SqlSessionDaoSupport implements FactoryBeanT {private ClassT mapperInterface;public void setMapperInterface(ClassT mapperInterface) {this.mapperInterface mapperInterface;}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();}}}Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}// ...
}
在checkDaoConfig方法中会检查mapperInterface是否已设置符合Spring管理Bean生命周期的要求。
接着通过configuration.addMapper(this.mapperInterface)方法重点关注最终实现是在MapperRegistry中 到这里以后跟我之前文章超硬核解析Mybatis动态代理原理只有接口没实现也能跑解析的步骤又是一样的了
new SqlSessionFactoryBuilder().build(xml)--XMLConfigBuilder#parse--XMLConfigBuilder#parseConfiguration---XMLConfigBuilder#mapperElement--XMLMapperBuilder#mapperParser.parse()--XMLMapperBuilder#configurationElement--XMLMapperBuilder#bindMapperForNamespace--Configuration#MapperRegistry#addMappper() public T void addMapper(ClassT type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException(Type type is already known to the MapperRegistry.);}boolean loadCompleted false;try {knownMappers.put(type, new MapperProxyFactoryT(type));// Its important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it wont try.MapperAnnotationBuilder parser new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}parser.parse()完成了对mapper对应xml的解析成MappedStatement并添加到了configuration对象中,这里的configuration也就是我们上面提到的new Configuration()创建的那个对象非常重要。
总结
mapper接口的定义在bean加载阶段会被替换成MapperFactoryBean类型在spring容器初始化的时候会给我们生成MapperFactoryBean类型的对象在该对象生成的过程中调用afterPropertiesSet()方法为我们生成了一个MapperProxyFactory类型的对象存放于Configuration里的MapperRegistry对象中同时解析了mapper接口对应的xml文件把每一个方法解析成一个MappedStatement对象存放于Configuration里mappedStatements这个Map集合中。
MapperFactoryBean的getObject()方法
MapperFactoryBean实现了FactoryBean接口实现了FactoryBean接口的类型在调用getBean(beanName)既通过名称获取对象时返回的对象不是本身类型的对象而是通过实现接口中的getObject()方法返回的对象。
这里需要对spring的声明周期有一定的了解下面是简化版的MapperFactoryBean的链路调用
getObject()---doGetBean()---getObjectForBeanInstance()---getObjectFromFactoryBean()---doGetObjectFromFactoryBean---MapperFactoryBean.getObject()方法
protected T T doGetBean(final String name, final Object[] args) {Object sharedInstance getSingleton(name);if (sharedInstance ! null) {// 如果是 FactoryBean则需要调用 FactoryBean#getObjectreturn (T) getObjectForBeanInstance(sharedInstance, name);}BeanDefinition beanDefinition getBeanDefinition(name);//这里如果是MapperFactoryBean对象初始化完成以后会进入下面的判断Object bean createBean(name, beanDefinition, args);//这里如果是MapperFactoryBean对象初始化完成以后会进入下面的判断return (T) getObjectForBeanInstance(bean, name);}private Object getObjectForBeanInstance(Object beanInstance, String beanName) {if (!(beanInstance instanceof FactoryBean)) {return beanInstance;}Object object getCachedObjectForFactoryBean(beanName);if (object null) {FactoryBean? factoryBean (FactoryBean?) beanInstance;//这里如果是MapperFactoryBean对象初始化完成以后会进入这里的逻辑object getObjectFromFactoryBean(factoryBean, beanName);}return object;}FactoryBeanRegistrySupport的方法getObjectFromFactoryBean---doGetObjectFromFactoryBean()---factory.getObject()方法 protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {if (factory.isSingleton()) {Object object this.factoryBeanObjectCache.get(beanName);if (object null) {//这里如果是MapperFactoryBean对象初始化完成以后会进入这里的逻辑object doGetObjectFromFactoryBean(factory, beanName);this.factoryBeanObjectCache.put(beanName, (object ! null ? object : NULL_OBJECT));}return (object ! NULL_OBJECT ? object : null);} else {return doGetObjectFromFactoryBean(factory, beanName);}}private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName){try {//最终调用MapperFactoryBean的getObject方法获取实际的对象return factory.getObject();} catch (Exception e) {throw new BeansException(FactoryBean threw exception on object[ beanName ] creation, e);}}MapperFactoryBean的getObject()方法
public T getObject() throws Exception {return this.getSqlSession().getMapper(this.mapperInterface);
}走到这里是不是也跟我之前文章超硬核解析Mybatis动态代理原理只有接口没实现也能跑解析的步骤又是一样的了
getMapper方法的大致调用逻辑链是 SqlSession#getMapper() —— Configuration#getMapper() —— MapperRegistry#getMapper() —— MapperProxyFactory#newInstance() —— Proxy#newProxyInstance()–MapperProxy#invoke–MapperMethod#execute
思考联想 在之前的文章中超硬核解析Mybatis动态代理原理只有接口没实现也能跑我们通过new SqlSessionFactoryBuilder().build(xml)最终调用委托给Configuration#MapperRegistry#addMappper() 方法进行mapper接口的注册而在Spring结合mybatis的过程中我们通过MapperFactoryBean的checkDaoConfig()最终调用委托给Configuration#MapperRegistry#addMappper() 方法进行mapper接口的注册方法实现本质上是一样的。 在之前的文章中超硬核解析Mybatis动态代理原理只有接口没实现也能跑,我们又通过SqlSessionFactory的openSession()新建一个SqlSession然后通过session#getMapper()最终调用委托给MapperRegistry#getMapper()—— MapperProxyFactory#newInstance() 方法实现代理对象的生成而在Spring结合mybatis的过程中我们通过MapperFactoryBean的getObject()调用this.getSqlSession().getMapper(this.mapperInterface)最终也是委托给MapperRegistry#getMapper()—— MapperProxyFactory#newInstance() 方法实现代理对象的生成本质也是一样的道理。
后续
刚刚上面的例子我们可以发现每配置一个mapper都需要写一个对应的MapperFactoryBean如果mapper多了这样是很繁琐的。
!-- Mapper接口对应的FactoryBean配置 --
bean iduserMapper classorg.mybatis.spring.mapper.MapperFactoryBeanproperty namemapperInterface valuecom.example.mapper.UserMapper /property namesqlSessionFactory refsqlSessionFactory /
/bean为了解决这个问题我们可以使用MapperScannerConfigurer让它扫描特定的包自动帮我们成批的创建映射器。这样一来就能大大减少配置的工作量。具体的实现原理我们后面再进行讲解。
!-- 配置扫描Dao接口包动态实现Dao接口注入到spring容器中 --bean classorg.mybatis.spring.mapper.MapperScannerConfigurer!-- 注入sqlSessionFactory --property namesqlSessionFactoryBeanName valuesqlSessionFactory/!-- 给出需要扫描Dao接口包 --property namebasePackage valuecom.joe.dao//bean