少年击剑更吹箫,剑气箫心一例消。这篇文章主要讲述Mapper(DAO层)接口如何实例化相关的知识,希望能为你提供帮助。
上篇mybatis 映射文件加载是分析了一下我们所有的xml映射文件如何加载的,但在我学习ssm的过程中,发现dao层的接口在service层会注入一个实例化对象,直接可以使用,但我们并没有做dao层的实现类,
【Mapper(DAO层)接口如何实例化】很好奇的去查了查资料,下面就分析一下
mybatis配置文件
文章图片
mybatis是通过org.mybatis.spring.mapper.MapperScannerConfigurer类来实现dao层扫描的 我们进入MapperScannerConfigurer这个类,有一个postProcessBeanDefinitionRegistry方法
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); }ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.registerFilters(); //扫描basepackage包下的所有dao接口 scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
进入doscan方法
//由父类去找到符合条件的interface类,并转化为bean类 Set< BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { logger.warn("No MyBatis mapper was found in ‘" + Arrays.toString(basePackages) + "‘ package. Please check your configuration."); } else { //处理找到的interface bean类 processBeanDefinitions(beanDefinitions); }return beanDefinitions;
然后看processBeanDefinitions方法
private void processBeanDefinitions(Set< BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name ‘" + holder.getBeanName() + "‘ and ‘" + definition.getBeanClassName() + "‘ mapperInterface"); }// the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//设置beanclass类型为mapperFactoryBean definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; }if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; }if (!explicitFactoryUsed) { if (logger.isDebugEnabled()) { logger.debug("Enabling autowire by type for MapperFactoryBean with name ‘" + holder.getBeanName() + "‘."); } definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } }
最终会把dao层接口包装成MapperFactoryBean
也就是说我们会把所有的dao层文件,封装成和以下配置文件一样的效果,是不是很熟悉了
< bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> < property name="mapperInterface" value="https://www.songbingjia.com/android/com.zsh.dao.UserMapper"> < /property> < property name="sqlSessionFactory" ref="sqlSessionFactory" /> < /bean>
以上是我们扫描dao层的原理,下面我们继续分析如何自动实例化接口
再说自动实例化接口之前我们了解一下什么是动态代理 Java 动态代理机制分析及扩展
上面是通过工厂方法创建Bean
首先我们定位到类 org.mybatis.spring.mapper.MapperFactoryBean
public class MapperFactoryBean< T> extends SqlSessionDaoSupport implements FactoryBean< T> { private Class< T> mapperInterface; private boolean addToConfig;
可以看到该类实现了org.springframework.beans.factory.FactoryBean接口,通过调用
org.mybatis.spring.mapper.MapperFactoryBean.getObject()方法来获得Bean
public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); }
我们通过分析以上的getSqlSession()可以看出getMapper的实现类是SqlSessionTemplate,我们跳转到SqlSessionTemplate看一下getMapper方法
public < T> T getMapper(Class< T> type) { return getConfiguration().getMapper(type, this); }
可以看到,它首先调用了自身的getConfiguration()方法返回一个Configuration对象,然后再调用Configuration对象的getMapper方法
我们直接定位到Configuration的getMapper方法
public < T> T getMapper(Class< T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
直接定位到mapperRegistry.getMapper
public < T> T getMapper(Class< T> type, SqlSession sqlSession) { final MapperProxyFactory< T> mapperProxyFactory = (MapperProxyFactory< T> ) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
在这个方法里,首先获得一个MapperProxyFactory的对象mapperProxyFactory,然后调用该对象的newInstance方法创建我们需要的bean,定位到newInstance方法
public T newInstance(SqlSession sqlSession) { final MapperProxy< T> mapperProxy = new MapperProxy< T> (sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
最后我们进入newInstance这个重载函数
protected T newInstance(MapperProxy< T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
在这里用jdk的动态代理创建了一个代理对象,也就是说,我们最终是对mapper的所有接口分别创建了各自的代理对象,通过代理对象来执行我们的sql
参考并感谢
https://www.cnblogs.com/question-sky/p/6654101.html
http://blog.csdn.net/tanqidong1992/article/details/48026491
https://www.jianshu.com/p/3e619786dd18
推荐阅读
- Android布局设置背景的透明度
- Android AlertDialog 动态更新里面的ListView数据
- 专业又高效的APP外包定制平台
- Ubuntu16.04 appstreamcli错误
- 解决CMake Error: The source directory "*" does not appear to contain CMakeLists.txt.
- 安卓Adapter去item重复使用的方案!
- 安卓电量优化之WakeLock锁机制全面解析
- Android开发之漫漫长途 X——Android序列化
- android模拟器推荐