当前位置: 首页 > news >正文

松江品划做网站公司网站建设的维护范围

松江品划做网站公司,网站建设的维护范围,wordpress侧边栏加载过慢,微信开发者版是什么意思前言这是 mybatis 比较常问到的面试题#xff0c;我自己在以前的面试过程中被问到了2次#xff0c;2次都是非常重要的面试环节#xff0c;因此自己印象很深刻。这个题目我很早就深入学习了#xff0c;但是一直没有整理出来#xff0c;刚好最近一段时间由于工作太忙#x…前言这是 mybatis 比较常问到的面试题我自己在以前的面试过程中被问到了2次2次都是非常重要的面试环节因此自己印象很深刻。这个题目我很早就深入学习了但是一直没有整理出来刚好最近一段时间由于工作太忙大概有半年没有技术文章产出因此趁着五一有点时间整理了下分享给大家。另外估计不少同学应该也注意到了DAO 接口的全路径名和 XML 文件中的 SQL 的 namespace id 是一样的。其实这也是建立关联的根本原因。本文中的源码使用当前最新的版本即mybatis-spring 为 2.0.4mybatis 为 3.5.4引入这2个 jar 包即可查看到本文的所有代码。正文当一个项目中使用了 Spring 和 Mybatis 时通常会有以下配置。当然现在很多项目应该都是 SpringBoot 了可能没有以下配置但是究其底层原理都是类似的无非是将扫描 bean 等一些工作通过注解来实现。!-- DAO接口所在包名Spring会自动查找其下的类 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer!--basePackage指定要扫描的包在此包之下的映射器都会被搜索到。可指定多个包包与包之间用逗号或分号分隔--property namebasePackage valuecom.joonwhee.open.mapper/property namesqlSessionFactoryBeanName valuesqlSessionFactory/ /bean!-- spring和MyBatis完美整合不需要mybatis的配置映射文件 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBeanproperty namedataSource refdataSource/!-- 自动扫描mapping.xml文件 --property namemapperLocations valueclasspath:config/mapper/*.xml/property nameconfigLocation valueclasspath:config/mybatis/mybatis-config.xml/!--Entity package --property nametypeAliasesPackage valuecom.joonwhee.open.po/ /bean!-- dataSource -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodcloseproperty namedriverClassName value${driver}/property nameurl value${url}/property nameusername value${username}/property namepassword value${password}/ /bean通常我们还会有 DAO 类和 对用的 mapper 文件如下。package com.joonwhee.open.mapper;import com.joonwhee.open.po.UserPO;public interface UserPOMapper {UserPO queryByPrimaryKey(Integer id); } ?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.joonwhee.open.mapper.UserPOMapper resultMap idBaseResultMap typecom.joonwhee.open.po.UserPOresult columnid propertyid jdbcTypeINTEGER /result columnname propertyname jdbcTypeVARCHAR //resultMapselect idqueryByPrimaryKey resultMapBaseResultMapparameterTypejava.lang.Integerselect id, namefrom userwhere id #{id,jdbcTypeINTEGER}/select /mapper1、解析 MapperScannerConfigurerMapperScannerConfigurer 是一个 BeanDefinitionRegistryPostProcessor会在 Spring 构建 IoC容器的早期被调用重写的 postProcessBeanDefinitionRegistry 方法参考Spring IoCinvokeBeanFactoryPostProcessors 详解Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders();}// 1.新建一个ClassPathMapperScanner并填充相应属性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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);if (StringUtils.hasText(lazyInitialization)) {// 2.设置mapper bean是否需要懒加载scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));}// 3.注册Filter因为上面构造函数我们没有使用默认的Filter// 有两种FilterincludeFilters要扫描的excludeFilters要排除的scanner.registerFilters();// 4.扫描basePackagebasePackage可通过,; tn来填写多个// ClassPathMapperScanner重写了doScan方法scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); } 3.注册 Filter见代码块1。4.扫描 basePackage这边会走到 ClassPathBeanDefinitionScannerClassPathMapperScanner 的父类然后在执行 “doScan(basePackages)” 时回到 ClassPathMapperScanner 重写的方法见代码块2。代码块1registerFilterspublic void registerFilters() {boolean acceptAllInterfaces true;// if specified, use the given annotation and / or marker interface// 1.如果指定了注解则将注解添加到includeFiltersif (this.annotationClass ! null) {addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));acceptAllInterfaces false;}// override AssignableTypeFilter to ignore matches on the actual marker interface// 2.如果指定了标记接口则将标记接口添加到includeFilters// 但这边重写了matchClassName方法并返回了false// 相当于忽略了标记接口上的匹配项所以该参数目前相当于没有任何作用if (this.markerInterface ! null) {addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {Overrideprotected boolean matchClassName(String className) {return false;}});acceptAllInterfaces false;}// 3.如果没有指定annotationClass和markerInterface则// 添加默认的includeFilters直接返回true接受所有类if (acceptAllInterfaces) {// default include filter that accepts all classesaddIncludeFilter((metadataReader, metadataReaderFactory) - true);}// exclude package-info.java// 4.添加默认的excludeFilters排除以package-info结尾的类addExcludeFilter((metadataReader, metadataReaderFactory) - {String className metadataReader.getClassMetadata().getClassName();return className.endsWith(package-info);}); }通常我们都不会指定 annotationClass 和 markerInterface也就是会添加默认的 Filter相当于会接受除了 package-info 结尾的所有类。因此basePackage 包下的类不需要使用 Component 注解或 XML 中配置 bean 定义也会被添加到 IoC 容器中。代码块2doScanOverride public SetBeanDefinitionHolder doScan(String... basePackages) {// 1.直接使用父类的方法扫描和注册bean定义// 之前在spring中已经介绍过Spring IoC源码学习context:component-scan 节点详解 代码块5SetBeanDefinitionHolder beanDefinitions super.doScan(basePackages);if (beanDefinitions.isEmpty()) {LOGGER.warn(() - No MyBatis mapper was found in Arrays.toString(basePackages) package. Please check your configuration.);} else {// 2.对扫描到的beanDefinitions进行处理主要4件事// 1将bean的真正接口类添加到通用构造函数参数中// 2将beanClass直接设置为MapperFactoryBean.class// 结合1相当于要使用的构造函数是MapperFactoryBean(java.lang.ClassT)// 3添加sqlSessionFactory属性sqlSessionFactoryBeanName和// sqlSessionFactory中优先使用sqlSessionFactoryBeanName// 4添加sqlSessionTemplate属性同样的sqlSessionTemplateBeanName// 优先于sqlSessionTemplateprocessBeanDefinitions(beanDefinitions);}return beanDefinitions; }小结解析 MapperScannerConfigurer 主要是做了几件事1新建扫描器 ClassPathMapperScanner2使用 ClassPathMapperScanner 扫描注册 basePackage 包下的所有 bean3将 basePackage 包下的所有 bean 进行一些特殊处理beanClass 设置为 MapperFactoryBean、bean 的真正接口类作为构造函数参数传入 MapperFactoryBean、为 MapperFactoryBean 添加 sqlSessionFactory 和 sqlSessionTemplate属性。2、解析 SqlSessionFactoryBean对于 SqlSessionFactoryBean 来说实现了2个接口InitializingBean 和 FactoryBean看过我之前 Spring 文章的同学应该对这2个接口不会陌生简单来说1FactoryBean 可以自己定义创建实例对象的方法只需要实现它的 getObject() 方法InitializingBean 则是会在 bean 初始化阶段被调用。SqlSessionFactoryBean 重写这两个接口的部分方法代码如下核心代码就一个方法—— “buildSqlSessionFactory()”。Override public SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory null) {// 如果之前没有构建则这边也会调用afterPropertiesSet进行构建操作afterPropertiesSet();}return this.sqlSessionFactory; }Override public void afterPropertiesSet() throws Exception {// 省略部分代码// 构建sqlSessionFactorythis.sqlSessionFactory buildSqlSessionFactory(); }buildSqlSessionFactory()主要做了几件事1对我们配置的参数进行相应解析2使用配置的参数构建一个 Configuration3使用 Configuration 新建一个 DefaultSqlSessionFactory。这边的核心内容是对于 mapperLocations 的解析如下代码。protected SqlSessionFactory buildSqlSessionFactory() throws Exception {// 省略部分代码// 5.mapper处理最重要if (this.mapperLocations ! null) {if (this.mapperLocations.length 0) {LOGGER.warn(() - Property mapperLocations was specified but matching resources are not found.);} else {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation null) {continue;}try {// 5.1 新建XMLMapperBuilderXMLMapperBuilder xmlMapperBuilder new XMLMapperBuilder(mapperLocation.getInputStream(),targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());// 5.2 解析mapper文件xmlMapperBuilder.parse();} catch (Exception e) {throw new NestedIOException(Failed to parse mapping resource: mapperLocation , e);} finally {ErrorContext.instance().reset();}LOGGER.debug(() - Parsed mapper file: mapperLocation );}}} else {LOGGER.debug(() - Property mapperLocations was not specified.);}// 6.使用targetConfiguration构建DefaultSqlSessionFactoryreturn this.sqlSessionFactoryBuilder.build(targetConfiguration); }5.2 解析mapper文件见代码块3。代码块3parse()public void parse() {// 1.如果resource没被加载过才进行加载if (!configuration.isResourceLoaded(resource)) {// 1.1 解析mapper文件configurationElement(parser.evalNode(/mapper));// 1.2 将resource添加到已加载列表configuration.addLoadedResource(resource);// 1.3 绑定namespace的mapperbindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements(); }1.1 解析mapper文件见代码4。1.3 绑定namespace的mapper见代码块6。代码块4configurationElementprivate void configurationElement(XNode context) {try {// 1.获取namespace属性String namespace context.getStringAttribute(namespace);if (namespace null || namespace.isEmpty()) {throw new BuilderException(Mappers namespace cannot be empty);}// 2.设置currentNamespace属性builderAssistant.setCurrentNamespace(namespace);// 3.解析parameterMap、resultMap、sql等节点cacheRefElement(context.evalNode(cache-ref));cacheElement(context.evalNode(cache));parameterMapElement(context.evalNodes(/mapper/parameterMap));resultMapElements(context.evalNodes(/mapper/resultMap));sqlElement(context.evalNodes(/mapper/sql));// 4.解析增删改查节点封装成StatementbuildStatementFromContext(context.evalNodes(select|insert|update|delete));} catch (Exception e) {throw new BuilderException(Error parsing Mapper XML. The XML location is resource . Cause: e, e);} }private void buildStatementFromContext(ListXNode list) {if (configuration.getDatabaseId() ! null) {buildStatementFromContext(list, configuration.getDatabaseId());}// 解析增删改查节点封装成StatementbuildStatementFromContext(list, null); }private void buildStatementFromContext(ListXNode list, String requiredDatabaseId) {for (XNode context : list) {// 1.构建XMLStatementBuilderfinal XMLStatementBuilder statementParser new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try {// 2.解析节点statementParser.parseStatementNode();} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}} }这边会一直执行到 “statementParser.parseStatementNode();”见代码块5。这边每个 XNode 都相当于如下的一个 SQL下面封装的每个 MappedStatement 可以理解就是每个 SQL。select idqueryByPrimaryKey resultMapBaseResultMapparameterTypejava.lang.Integerselect id, name, password, agefrom userwhere id #{id,jdbcTypeINTEGER} /select代码块5parseStatementNodepublic void parseStatementNode() {// 省略所有的属性解析// 将解析出来的所有参数添加到 mappedStatements 缓存builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }// MapperBuilderAssistant.java public MappedStatement addMappedStatement(String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class? parameterType,String resultMap,Class? resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang,String resultSets) {if (unresolvedCacheRef) {throw new IncompleteElementException(Cache-ref not yet resolved);}// 1.将id填充上namespace例如queryByPrimaryKey变成// com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKeyid applyCurrentNamespace(id, false);boolean isSelect sqlCommandType SqlCommandType.SELECT;// 2.使用参数构建MappedStatement.BuilderMappedStatement.Builder statementBuilder new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);ParameterMap statementParameterMap getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap ! null) {statementBuilder.parameterMap(statementParameterMap);}// 3.使用MappedStatement.Builder构建MappedStatementMappedStatement statement statementBuilder.build();// 4.将MappedStatement 添加到缓存configuration.addMappedStatement(statement);return statement; }该方法会将节点的属性解析后封装成 MappedStatement放到 mappedStatements 缓存中key 为 id例如com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKeyvalue 为 MappedStatement。代码块6bindMapperForNamespaceprivate void bindMapperForNamespace() {String namespace builderAssistant.getCurrentNamespace();if (namespace ! null) {Class? boundType null;try {// 1.解析namespace对应的绑定类型boundType Resources.classForName(namespace);} catch (ClassNotFoundException e) {// ignore, bound type is not required}if (boundType ! null !configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResource// 2.boundType不为空并且configuration还没有添加boundType// 则将namespace添加到已加载列表将boundType添加到knownMappers缓存configuration.addLoadedResource(namespace: namespace);configuration.addMapper(boundType);}} }public T void addMapper(ClassT type) {mapperRegistry.addMapper(type); }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 {// 将type和以该type为参数构建的MapperProxyFactory作为键值对// 放到knownMappers缓存中去knownMappers.put(type, new MapperProxyFactory(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);}}} }主要是将刚刚解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中key 为 namespace 对应的 classvalue 为 MapperProxyFactory。小结解析 SqlSessionFactoryBean 主要做了几件事1解析处理所有属性参数构建 Configuration 使用 Configuration 新建 DefaultSqlSessionFactory2解析 mapperLocations 属性的 mapper 文件将 mapper 文件中的每个 SQL 封装成 MappedStatement放到 mappedStatements 缓存中key 为 id例如com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKeyvalue 为 MappedStatement。3将解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中key 为 namespace 对应的 classvalue 为 MapperProxyFactory。3、解析 DAO 文件DAO 文件也就是 basePackage 指定的包下的文件也就是上文的 interface UserPOMapper 。上文 doScan 中说过basePackage 包下所有 bean 定义的 beanClass 会被设置成 MapperFactoryBean.class而 MapperFactoryBean 也是 FactoryBean因此直接看 MapperFactoryBean 的 getObject 方法。Override public T getObject() throws Exception {// 1.从父类中拿到sqlSessionTemplate这边的sqlSessionTemplate也是doScan中添加的属性// 2.通过mapperInterface获取mapperreturn getSqlSession().getMapper(this.mapperInterface); }// SqlSessionTemplate Override public T T getMapper(ClassT type) {return getConfiguration().getMapper(type, this); }// Configuration.java public T T getMapper(ClassT type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession); }// MapperRegistry.java public T T getMapper(ClassT type, SqlSession sqlSession) {// 1.从knownMappers缓存中获取final MapperProxyFactoryT mapperProxyFactory (MapperProxyFactoryT) knownMappers.get(type);if (mapperProxyFactory null) {throw new BindingException(Type type is not known to the MapperRegistry.);}try {// 2.新建实例return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException(Error getting mapper instance. Cause: e, e);} }// MapperProxyFactory.java public T newInstance(SqlSession sqlSession) {// 1.构造一个MapperProxyfinal MapperProxyT mapperProxy new MapperProxy(sqlSession, mapperInterface, methodCache);// 2.使用MapperProxy来构建实例对象return newInstance(mapperProxy); }protected T newInstance(MapperProxyT mapperProxy) {// 使用JDK动态代理来代理要创建的实例对象InvocationHandler为mapperProxy// 因此当我们真正调用时会走到mapperProxy的invoke方法return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }这边代码用到的 sqlSessionTemplate、mapperInterface 等都是之前添加的属性。小结解析 DAO 文件 主要做了几件事 1通过 mapperInterface 从 knownMappers 缓存中获取到 MapperProxyFactory 对象2通过 JDK 动态代理创建 MapperProxyFactory 实例对象InvocationHandler 为 MapperProxy。4、DAO 接口被调用当 DAO 中的接口被调用时会走到 MapperProxy 的 invoke 方法。Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 1.创建MapperMethodInvoker// 2.将method - MapperMethodInvoker放到methodCache缓存// 3.调用MapperMethodInvoker的invoke方法return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);} }// MapperProxy.java private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {// 1.放到methodCache缓存key为methodvalue为MapperMethodInvokerreturn methodCache.computeIfAbsent(method, m - {if (m.isDefault()) {// 2.方法为默认方法Java8之后接口允许有默认方法try {if (privateLookupInMethod null) {return new DefaultMethodInvoker(getMethodHandleJava8(method));} else {return new DefaultMethodInvoker(getMethodHandleJava9(method));}} catch (IllegalAccessException | InstantiationException | InvocationTargetException| NoSuchMethodException e) {throw new RuntimeException(e);}} else {// 3.正常接口会走这边使用mapperInterface、method、configuration// 构建一个MapperMethod封装成PlainMethodInvokerreturn new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));}});} catch (RuntimeException re) {Throwable cause re.getCause();throw cause null ? re : cause;} }3.调用 MapperMethodInvoker 的 invoke 方法见代码块7。代码块7invokeOverride public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args); }// MapperMethod.java public Object execute(SqlSession sqlSession, Object[] args) {Object result;// 1.根据命令类型执行来进行相应操作switch (command.getType()) {case INSERT: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param method.convertArgsToSqlCommandParam(args);result rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result null;} else if (method.returnsMany()) {result executeForMany(sqlSession, args);} else if (method.returnsMap()) {result executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result executeForCursor(sqlSession, args);} else {Object param method.convertArgsToSqlCommandParam(args);result sqlSession.selectOne(command.getName(), param);if (method.returnsOptional() (result null || !method.getReturnType().equals(result.getClass()))) {result Optional.ofNullable(result);}}break;case FLUSH:result sqlSession.flushStatements();break;default:throw new BindingException(Unknown execution method for: command.getName());}if (result null method.getReturnType().isPrimitive() !method.returnsVoid()) {throw new BindingException(Mapper method command.getName() attempted to return null from a method with a primitive return type ( method.getReturnType() ).);}return result; } 这边就比较简单根据不同的操作类型执行相应的操作最终将结果返回见代码块8。这边的 command 是上文 “new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())” 时创建的。代码块8增删改查// 1.insert Override public int insert(String statement, Object parameter) {return update(statement, parameter); }// 2.update Override public int update(String statement, Object parameter) {try {dirty true;// 从mappedStatements缓存拿到对应的MappedStatement对象执行更新操作MappedStatement ms configuration.getMappedStatement(statement);return executor.update(ms, wrapCollection(parameter));} catch (Exception e) {throw ExceptionFactory.wrapException(Error updating database. Cause: e, e);} finally {ErrorContext.instance().reset();} }// 3.delete Override public int delete(String statement, Object parameter) {return update(statement, parameter); }// 4.select以executeForMany为例 private E Object executeForMany(SqlSession sqlSession, Object[] args) {ListE result;// 1.参数转换成sql命令参数Object param method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds method.extractRowBounds(args);result sqlSession.selectList(command.getName(), param, rowBounds);} else {// 2.执行查询操作result sqlSession.selectList(command.getName(), param);}// 3.处理返回结果// issue #510 Collections arrays supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result; }Override public E ListE selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT); }Override public E ListE selectList(String statement, Object parameter, RowBounds rowBounds) {try {//从mappedStatements缓存中拿到对应的MappedStatement对象执行查询操作MappedStatement ms configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException(Error querying database. Cause: e, e);} finally {ErrorContext.instance().reset();} }可以看出最终都是从 mappedStatements 缓存中拿到对应的 MappedStatement 对象执行相应的操作。这边的增删改查不是直接调用 SqlSession 中的方法而是调用 SqlSessionTemplate 中的方法继而通过 sqlSessionProxy 来调用 SqlSession 中的方法。SqlSessionTemplate 中的方法主要是通过 sqlSessionProxy 做了一层动态代理基本没差别。总结整个流程主要是以下几个核心步骤1扫描注册 basePackage 包下的所有 bean将 basePackage 包下的所有 bean 进行一些特殊处理beanClass 设置为 MapperFactoryBean、bean 的真正接口类作为构造函数参数传入 MapperFactoryBean、为 MapperFactoryBean 添加 sqlSessionFactory 和 sqlSessionTemplate属性。2解析 mapperLocations 属性的 mapper 文件将 mapper 文件中的每个 SQL 封装成 MappedStatement放到 mappedStatements 缓存中key 为 id例如com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKeyvalue 为 MappedStatement。并且将解析过的 mapper 文件的 namespace 放到 knownMappers 缓存中key 为 namespace 对应的 classvalue 为 MapperProxyFactory。3创建 DAO 的 bean 时通过 mapperInterface 从 knownMappers 缓存中获取到 MapperProxyFactory 对象通过 JDK 动态代理创建 MapperProxyFactory 实例对象InvocationHandler 为 MapperProxy。4DAO 中的接口被调用时通过动态代理调用 MapperProxy 的 invoke 方法最终通过 mapperInterface 从 mappedStatements 缓存中拿到对应的 MappedStatement执行相应的操作。推荐阅读程序员囧辉字节、美团、快手核心部门面试总结真题解析​zhuanlan.zhihu.com程序员囧辉如何准备好一场大厂面试​zhuanlan.zhihu.com程序员囧辉跳槽如何选择一家公司​zhuanlan.zhihu.com程序员囧辉如何写一份让 HR 眼前一亮的简历附模板​zhuanlan.zhihu.com程序员囧辉4 年 Java 经验面试总结、心得体会​zhuanlan.zhihu.com程序员囧辉面试阿里HashMap 这一篇就够了​zhuanlan.zhihu.com程序员囧辉面试必问的线程池你懂了吗​zhuanlan.zhihu.com程序员囧辉BATJTMD 面试必问的 MySQL你懂了吗​zhuanlan.zhihu.com程序员囧辉面试题mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立关系的​zhuanlan.zhihu.com
http://www.zqtcl.cn/news/528432/

相关文章:

  • 自己做网站卖什么给个网站好人有好报2020免费
  • 网站源码安装步骤网站开发用c 语言
  • 网站首页是什么产品网络推广方案
  • 网站首页制作方案南通市规划建设局网站
  • 网站建设费用兴田德润团队西宁网站策划公司
  • 手机价格网站建设用别人备案域名做违法网站
  • 成都武侯区建设厅官方网站石家庄住房和城乡建设部网站
  • 前端做网站的步骤酉阳网站建设
  • 湖北省住房与建设厅网站php做网站访问记录
  • 做网站的公司没有技术吉林北京网站建设
  • 产品设计培训机构哪家好贵州整站优化seo平台
  • 天津网站制作推广wordpress 果酱
  • 写给初学网站开发们的一封信企业网站建设 ppt
  • 做装修网站多少钱做网站百度一下
  • 用asp做网站的可行性分析9免费建网站
  • 网站域名注册商查询徐州集团网站建设报价
  • 句容网站设计公司做网站充值犯法吗
  • 网站建设所用系统网站备案目的
  • 苏州做网站优化公司哪家好网站的大小
  • 四川省住房和城乡建设厅官方网站网站建设图标图片
  • 做影视网站侵权吗评论凡科网站建设怎么样
  • 建设个人网站流程建设游戏网站需要哪些设备
  • 四字母net做网站怎么样河南做网站优化
  • 怎样做网站快照网站当前位置怎么做
  • 网站模板移植现在c 做网站用什么框架
  • 国内专业的室内设计网站盐城网站开发代理商
  • 外贸网站建设 评价wordpress 函数调用
  • 广告支持模式的网站二级域名做网站域名
  • 空间 两个网站购物网站建设图标大全
  • 17.zwd一起做网站广州网站制作费用