在上一篇文章(MyBatis框架的使用及源码分析(一))的demo中看到了SessionFactory的创建过程:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

那么我们就从SqlSessionFactoryBuilder开始,看看Mybatis的加载过程。

SqlSessionFactoryBuilder的核心源码:

package org.apache.ibatis.session;import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;/*
该类的主要功能是。
1、通过读取字符流(Reader)的方式构件SqlSessionFactory。
2、通过字节流(InputStream)的方式构件SqlSessionFacotry。
3、通过Configuration对象构建SqlSessionFactory。
*/
public class SqlSessionFactoryBuilder {public SqlSessionFactory build(Reader reader) {return build(reader, null, null);}public SqlSessionFactory build(Reader reader, String environment) {return build(reader, environment, null);}public SqlSessionFactory build(Reader reader, Properties properties) {return build(reader, null, properties);}//解析inputStream配置。public SqlSessionFactory build(InputStream inputStream) {return build(inputStream, null, null);}public SqlSessionFactory build(InputStream inputStream, String environment) {return build(inputStream, environment, null);}public SqlSessionFactory build(InputStream inputStream, Properties properties) {return build(inputStream, null, properties);}//和下面的build功能是一样的public SqlSessionFactory build(Reader reader, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {reader.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}/*** 通过Reader读取Mybatis配置,构建SqlSession工厂* @param inputStream xml配置文件* @param environment 默认是空的, 后面会去读xml配置文件中的environment标签,environment存的是事务管理,数据源* @param properties 默认是空的,后面会去读xml配置文件中的properties标签,存的是数据库连接配置。* @return*/public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {//创建XML解析器,用来解析配置文件,包括初始化Configuration对象。XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);//configuration是配置类对象。上一步只是初始化了。开始解析,主要过程是把mybatis的标签内容转到configuration对象中。Configuration configuration = parser.parse();//创建 session 工厂,没做什么,你就理解configuration<==>sqlSessionFactorySqlSessionFactory sqlSessionFactory = build(configuration);return sqlSessionFactory;} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}//以上2个方法最终调用的是build(Configuration config)public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}}

上面代码主要功能:1、通过读取字符流(Reader)的方式构件SqlSessionFactory。
2、通过字节流(InputStream)的方式构件SqlSessionFacotry。
3、通过Configuration对象构建SqlSessionFactory。

流程:通过源码,我们可以看到SqlSessionFactoryBuilder 通过XMLConfigBuilder 去解析我们传入的mybatis的配置文件,构造出Configuration,最终返回new DefaultSqlSessionFactory(config)的SqlSessionFactory实例。

 

接下来我们看看 XMLConfigBuilder 是怎样解析Mybatis的配置文件的,下面是部分源码:

package org.apache.ibatis.builder.xml;/*** 解析Mybatis配置文件*/
public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private XPathParser parser;private String environment;public XMLConfigBuilder(Reader reader) {this(reader, null, null);}public XMLConfigBuilder(Reader reader, String environment) {this(reader, environment, null);}public XMLConfigBuilder(Reader reader, String environment, Properties props) {this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);}public XMLConfigBuilder(InputStream inputStream) {this(inputStream, null, null);}public XMLConfigBuilder(InputStream inputStream, String environment) {this(inputStream, environment, null);}public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {super(new Configuration());ErrorContext.instance().resource("SQL Mapper Configuration");this.configuration.setVariables(props);this.parsed = false;this.environment = environment;this.parser = parser;}//调用此方法对mybatis配置文件进行解析,返回Configuration对象public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;//从根节点configuration,开始解析parseConfiguration(parser.evalNode("/configuration"));return configuration;}//解析configuration节点下的10个子节点。private void parseConfiguration(XNode root) {try {      //解析子节点properties propertiesElement(root.evalNode("properties")); //issue #117 read properties first      //解析子节点typeAliases 别名typeAliasesElement(root.evalNode("typeAliases"));      //解析子节点plugins 插件 pluginElement(root.evalNode("plugins"));      //解析子节点objectFactory mybatis为结果创建对象时都会用到objectFactoryobjectFactoryElement(root.evalNode("objectFactory"));      //解析子节点objectWrapperFactoryobjectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));      //解析settings定义一些全局性的配置settingsElement(root.evalNode("settings"));      //解析environments 可以配置多个运行环境,但是每个SqlSessionFactory 实例只能选择一个运行环境environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631      //解析databaseIdProvider MyBatis能够执行不同的语句取决于你提供的数据库供应商。许多数据库供应商的支持是基于databaseId映射 databaseIdProviderElement(root.evalNode("databaseIdProvider"));      //解析typeHandlers 当MyBatis设置参数到PreparedStatement 或者从ResultSet 结果集中取得值时,就会使用TypeHandler  来处理数据库类型与java 类型之间转换typeHandlerElement(root.evalNode("typeHandlers"));      //解析mappers 主要的crud操作都是在mappers中定义的mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}}

从上面可以看出可以配置10个子节点, 分别为:properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers。

 

再看SqlSessionFactoryBuilder的这行代码

Configuration configuration = parser.parse();

XMLConfigBuilder调用parse()方法解析Mybatis配置文件,生成Configuration对象。

Configuration类主要是用来存储对Mybatis的配置文件及mapper文件解析后的数据,Configuration对象会贯穿整个Mybatis的执行流程,为Mybatis的执行过程提供必要的配置信息。

 

我们先贴出一个包含了所有属性的mybatis-Config.xml实例:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><properties resource="org/apache/ibatis/databases/blog/blog-derby.properties" /><settings><setting name="cacheEnabled" value="true" /><setting name="lazyLoadingEnabled" value="false" /><setting name="multipleResultSetsEnabled" value="true" /><setting name="useColumnLabel" value="true" /><setting name="useGeneratedKeys" value="false" /><setting name="defaultExecutorType" value="SIMPLE" /><setting name="defaultStatementTimeout" value="25" /></settings><typeAliases><typeAlias alias="Author" type="org.apache.ibatis.domain.blog.Author" /><typeAlias alias="Blog" type="org.apache.ibatis.domain.blog.Blog" /></typeAliases><typeHandlers><typeHandler javaType="String" jdbcType="VARCHAR"handler="org.apache.ibatis.builder.CustomStringTypeHandler" /></typeHandlers><objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory"><property name="objectFactoryProperty" value="100" /></objectFactory><plugins><plugin interceptor="org.apache.ibatis.builder.ExamplePlugin"><property name="pluginProperty" value="100" /></plugin></plugins><environments default="development"><environment id="development"><transactionManager type="JDBC"><property name="" value="" /></transactionManager><dataSource type="UNPOOLED"><property name="driver" value="${driver}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></dataSource></environment></environments><databaseIdProvider type="DB_VENDOR"><property name="SQL Server" value="sqlserver" /><property name="DB2" value="db2" /><property name="Oracle" value="oracle" /></databaseIdProvider><mappers><mapper resource="org/apache/ibatis/builder/AuthorMapper.xml" /><mapper resource="org/apache/ibatis/builder/BlogMapper.xml" /></mappers>
</configuration>

再对照看一看Configuration类的成员变量和构造方法:

package org.apache.ibatis.session;public class Configuration {/**   * MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,   * 比如设置不同的开发、测试、线上配置,在每个配置中可以配置事务管理器和数据源对象.   */protected Environment environment;//允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。  protected boolean safeRowBoundsEnabled = false;   //允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false  protected boolean safeResultHandlerEnabled = true;  //是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。  protected boolean mapUnderscoreToCamelCase = false; //当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).protected boolean aggressiveLazyLoading = true;  /是否允许单一语句返回多结果集(需要兼容驱动)protected boolean multipleResultSetsEnabled = true;   //允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。  protected boolean useGeneratedKeys = false;//使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。protected boolean useColumnLabel = true;  //配置全局性的cache开关protected boolean cacheEnabled = true;  /*指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。    注意基本类型(int、boolean等)是不能设置成 null 的。*/protected boolean callSettersOnNulls = false;  //指定 MyBatis 增加到日志名称的前缀。protected String logPrefix;  //指定 MyBatis 所用日志的具体实现,未指定时将自动查找protected Class <? extends Log> logImpl;  /*MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。     默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。     若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。*/protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;  /*当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。     某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。*/protected JdbcType jdbcTypeForNull = JdbcType.OTHER;  //指定哪个对象的方法触发一次延迟加载。protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));  //设置超时时间,它决定驱动等待数据库响应的秒数。protected Integer defaultStatementTimeout;   /*配置默认的执行器。    SIMPLE 就是普通的执行器;    REUSE 执行器会重用预处理语句(prepared statements);     BATCH 执行器将重用语句并执行批量更新。*/  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;  /**   * 指定 MyBatis 应如何自动映射列到字段或属性。    * NONE 表示取消自动映射;   * PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。   * FULL 会自动映射任意复杂的结果集(无论是否嵌套)。   */protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;//这里配置的属性可以在整个配置文件中使用来替换需要动态配置的属性值    protected Properties variables = new Properties();//对象创建工厂,默认的实现类DefaultObjectFactory,用来创建对象,比如传入List.class,利用反射返回ArrayList的实例protected ObjectFactory objectFactory = new DefaultObjectFactory();  //对象包装工厂,默认实现类是DefaultObjectWrapperFactory,包装Object实例protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();  //注册Mapperprotected MapperRegistry mapperRegistry = new MapperRegistry(this);//延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。protected boolean lazyLoadingEnabled = false;  //指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具protected ProxyFactory proxyFactory;//数据库类型id,MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性protected String databaseId;/**      * 指定一个提供Configuration实例的类. 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. * 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)*/    protected Class<?> configurationFactory;//拦截器链protected final InterceptorChain interceptorChain = new InterceptorChain();  //TypeHandler注册protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();  //别名和具体类注册protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();  //这个是指定解析的驱动,比如你可以使用velocity模板引擎来替代xml文件,默认是XMLLanguageDriver,也就是使用xml文件来写sql语句protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();//对应Mapper.xml里配置的Statementprotected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");  //对应Mapper.xml里配置的cacheprotected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");  //对应Mapper.xml里的ResultMapprotected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");  //对应Mapper.xml里的ParameterMapprotected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");  //主键生成器protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");//存储已经加载过的mapper xml资源,见MapperAnnotationBuilder#loadXmlResourceprotected final Set<String> loadedResources = new HashSet<String>();  //存储已经解析过的mapper对应的xml节点protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");//存储所有未处理的protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();  //存储所有未处理的缓存信息protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();  //存储所有未处理ResultMap的映射信息protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();/*      * A map holds cache-ref relationship. The key is the namespace that      * references a cache bound to another namespace and the value is the      * namespace which the actual cache is bound to.      */    protected final Map<String, String> cacheRefMap = new HashMap<String, String>();public Configuration(Environment environment) {this();this.environment = environment;}public Configuration() {//通过使用TypeAliasRegistry来注册一些类的别名typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);typeAliasRegistry.registerAlias("FIFO", FifoCache.class);typeAliasRegistry.registerAlias("LRU", LruCache.class);typeAliasRegistry.registerAlias("SOFT", SoftCache.class);typeAliasRegistry.registerAlias("WEAK", WeakCache.class);typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);languageRegistry.register(RawLanguageDriver.class);}}

上面代码:可以看出Configuration 就是 mybatis-config.xml 的对象实体。

mybatis配置文件是由XMLConfigBuilder来解析的,以下是主要功能代码:

//调用此方法对mybatis配置文件进行解析,返回Configuration对象
public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;//xNode包含整个mybatis配置文件的所有内容,通过xNode我们可以得到配置文件中所有属性值。XNode xNode = parser.evalNode("/configuration");//将xNode的所有一级标签数据读取出来。parseConfiguration(xNode);return configuration;
}
/*** MyBatis配置文件的核心标签是properties和environments标签。* 下面所有的标签都是一级标签* 从上面可以看出可以配置10个子节点, 分别为:* properties、typeAliases、plugins、objectFactory、objectWrapperFactory、* settings、environments、databaseIdProvider、typeHandlers、mappers。* @param root*/
private void parseConfiguration(XNode root) {try {// issue #117 read properties first//拿到mybatis配置文件中的properties标签XNode properties = root.evalNode("properties");propertiesElement(properties);Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);loadCustomLogImpl(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));//mappers标签配置的是mybatis.xml文件的路径mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}

可以看到解析mapper文件的加载解析是从 mapperElement(root.evalNode("mappers"));  开始处理的。

我们继续看mapperElement(XNode parent)方法的代码:

//处理mybatis.xml配置文件中的mapper标签
private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {//解析package扫描指定package下的所有mapper接口String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {//解析mapper节点String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());//将Mapper标签里面的resource标签取出来解析,resource标签配的是Mapper.xml配置文件mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url);InputStream inputStream = Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) {Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}
}

可以看到,mybatisConfig.xml配置下的mappers节点有2种子节点:package节点和mapper节点,我这里先讨论xml的模式,先看mapper节点。mapper节点配置有3个属性:resource,url,class。他们处理的优先级依次是resource,url,class,3个属性只处理一种。resource,url属性映射的是xml的路径,class是mapper接口的类路径。

从源码中我们看到,通过读取resource或url属性得到xml的访问路径后,交给XMLMapperBuilder对象来解析。

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
//将Mapper标签里面的resource标签取出来解析,resource标签配的是Mapper.xml配置文件
mapperParser.parse();

我们查看XMLMapperBuilder的parse方法:

public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));    //从mapper节点开始解析configuration.addLoadedResource(resource);  //标记已经加载了次xml资源bindMapperForNamespace();  //绑定到命名空间}parsePendingResultMaps();  //将resultMap映射信息转换成ResultMap对象parsePendingChacheRefs();  //将cache映射信息转换成Cache对象parsePendingStatements();  //将sql映射转换成MappedStatement
}//解析xml
private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);}
}
//绑定到命名空间
private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {//ignore, bound type is not required}if (boundType != null) {if (!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#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}
}

其中configurationElement(XNode context)负责解析所有的xml元素,bindMapperForNamespace() 绑定到命名空间,而parsePendingResultMaps(), parsePendingChacheRefs(), parsePendingStatements()则分别将对应的xml信息转换成ResultMap对象,Cache对象和MappedStatement对象。

ResultMap的处理

private void parsePendingResultMaps() {Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();synchronized (incompleteResultMaps) {Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();while (iter.hasNext()) {try {iter.next().resolve();  //实际处理的是ResultMapResolver.resolve()方法iter.remove();} catch (IncompleteElementException e) {// ResultMap is still missing a resource...}}}}

看代码得知,实际处理的是ResultMapResolver.resolve()方法

package org.apache.ibatis.builder;import java.util.List;import org.apache.ibatis.mapping.Discriminator;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;/*** @author Eduardo Macarron*/
public class ResultMapResolver {private final MapperBuilderAssistant assistant;private String id;private Class<?> type;private String extend;private Discriminator discriminator;private List<ResultMapping> resultMappings;private Boolean autoMapping;public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {this.assistant = assistant;this.id = id;this.type = type;this.extend = extend;this.discriminator = discriminator;this.resultMappings = resultMappings;this.autoMapping = autoMapping;}public ResultMap resolve() {return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);}}

接着发现最终调用的是MapperBuilderAssistant.addResultMap 方法

public ResultMap addResultMap(String id,Class<?> type,String extend,Discriminator discriminator,List<ResultMapping> resultMappings,Boolean autoMapping) {id = applyCurrentNamespace(id, false);extend = applyCurrentNamespace(extend, true);ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);if (extend != null) {if (!configuration.hasResultMap(extend)) {throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");}ResultMap resultMap = configuration.getResultMap(extend);List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());extendedResultMappings.removeAll(resultMappings);// Remove parent constructor if this resultMap declares a constructor.boolean declaresConstructor = false;for (ResultMapping resultMapping : resultMappings) {if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {declaresConstructor = true;break;}}if (declaresConstructor) {Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();while (extendedResultMappingsIter.hasNext()) {if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {extendedResultMappingsIter.remove();}}}resultMappings.addAll(extendedResultMappings);}resultMapBuilder.discriminator(discriminator);ResultMap resultMap = resultMapBuilder.build();configuration.addResultMap(resultMap);  //添加到Configurationreturn resultMap;}

Cache的处理

private void parsePendingChacheRefs() {Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();synchronized (incompleteCacheRefs) {Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();while (iter.hasNext()) {try {iter.next().resolveCacheRef();iter.remove();} catch (IncompleteElementException e) {// Cache ref is still missing a resource...}}}}

调用的是org.apache.ibatis.builder.CacheRefResolver.resolveCacheRef()方法:

public Cache resolveCacheRef() {return assistant.useCacheRef(cacheRefNamespace);
}

最终调用org.apache.ibatis.builder.useCacheRef(String namespace)方法创建Cache对象并添加到Configuration:

public Cache useCacheRef(String namespace) {if (namespace == null) {throw new BuilderException("cache-ref element requires a namespace attribute.");}try {unresolvedCacheRef = true;Cache cache = configuration.getCache(namespace);if (cache == null) {throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");}currentCache = cache;unresolvedCacheRef = false;return cache;} catch (IllegalArgumentException e) {throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);}}

MappedStatement的处理从parsePendingStatements()方法开始跟踪:

private void parsePendingStatements() {Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();synchronized (incompleteStatements) {Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();while (iter.hasNext()) {try {iter.next().parseStatementNode(); //调用XMLStatementBuilder的parseStatementNode方法iter.remove();} catch (IncompleteElementException e) {// Statement is still missing a resource...}}}}

然后调用org.apache.ibatis.builder.xml.XMLStatementBuilder的parseStatementNode方法

public void parseStatementNode() {String id = context.getStringAttribute("id");String databaseId = context.getStringAttribute("databaseId");if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;Integer fetchSize = context.getIntAttribute("fetchSize");Integer timeout = context.getIntAttribute("timeout");String parameterMap = context.getStringAttribute("parameterMap");String parameterType = context.getStringAttribute("parameterType");Class<?> parameterTypeClass = resolveClass(parameterType);String resultMap = context.getStringAttribute("resultMap");String resultType = context.getStringAttribute("resultType");String lang = context.getStringAttribute("lang");LanguageDriver langDriver = getLanguageDriver(lang);Class<?> resultTypeClass = resolveClass(resultType);String resultSetType = context.getStringAttribute("resultSetType");StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);String nodeName = context.getNode().getNodeName();SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);boolean useCache = context.getBooleanAttribute("useCache", isSelect);boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);// Include Fragments before parsingXMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());// Parse selectKey after includes and remove them.processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);String resultSets = context.getStringAttribute("resultSets");String keyProperty = context.getStringAttribute("keyProperty");String keyColumn = context.getStringAttribute("keyColumn");KeyGenerator keyGenerator;String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);if (configuration.hasKeyGenerator(keyStatementId)) {keyGenerator = configuration.getKeyGenerator(keyStatementId);} else {keyGenerator = context.getBooleanAttribute("useGeneratedKeys",configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))? new Jdbc3KeyGenerator() : new NoKeyGenerator();}builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}

最后MappedStatement由org.apache.ibatis.builder.MapperBuilderAssistant的addMappedStatement方法创建,并加入到Configuration

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");id = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);statementBuilder.resource(resource);statementBuilder.fetchSize(fetchSize);statementBuilder.statementType(statementType);statementBuilder.keyGenerator(keyGenerator);statementBuilder.keyProperty(keyProperty);statementBuilder.keyColumn(keyColumn);statementBuilder.databaseId(databaseId);statementBuilder.lang(lang);statementBuilder.resultOrdered(resultOrdered);statementBuilder.resulSets(resultSets);setStatementTimeout(timeout, statementBuilder);setStatementParameterMap(parameterMap, parameterType, statementBuilder);setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);MappedStatement statement = statementBuilder.build();configuration.addMappedStatement(statement);return statement;}

 

 

 

本文转载:https://www.cnblogs.com/zsg88/p/7538909.html

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 【运维开发】window环境下使用 vagrant+VirtualBox安装centos7+docker环境

    window环境下使用 vagrant+VirtualBox安装centos7+docker环境 互联网开发中,我们为了在本地模拟线上环境,但是又局限于我们机器的数量,所有我们在我们的window机子上按照docker容易来装我们日常使用的服务,由于我们生成环境大多都是用centos,所有我们需要在window环境下搭…...

    2024/4/17 11:36:21
  2. java多线程的工具

    转载自品略图书馆 http://www.pinlue.com/article/2020/07/0222/0510943719042.html1、CountDownLatch 计数器CountDownLatch允许一个或多个线程等待其他线程完成操作。应用场景:我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数…...

    2024/4/16 2:18:44
  3. aop获取参数上的注解

    Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); Object[] args = joinPoint.getArgs();//参数注解,1维是参数,2维是注解Annotation[][] annotations = method.getParameterAnnotations();for (int i = 0; i < annotations.length; i…...

    2024/5/5 5:20:13
  4. 值得反思:强人都是如何“炼”成的?

    在知乎话题榜上,看到个发人深省的问题“要怎样努力,才能成为很厉害的人?”说到厉害,你脑海里面浮现的是什么?年幼青葱时,觉得父母很厉害,盼望成为他们那样的人,可随着年龄增长,会发现父母也是平凡人,并没有想象中那么厉害。青春年少时,总想远离父母的唠叨和嘱咐,为…...

    2024/4/16 2:19:40
  5. Arthas - 定位处理监控工具

    目录前言1、Arthas 介绍2、Arthas 使用场景3、Arthas 怎么用3.1 安装3.2 运行3.3 web console3.4 常用命令3.5 退出4、Arthas 常用操作4.1 全局监控4.2 CPU 为什么起飞了4.3 线程池线程状态4.4 线程死锁4.5 反编译4.6 查看字段信息4.7 查看方法信息4.8 对变量的值很是好奇4.9 程…...

    2024/4/16 2:19:50
  6. 大数据开发必读书目(持续更新)

    1.HadoopHadoop大数据开发案例教程与项目实战.pdf Hadoop技术内幕 深入解析YARN架构设计与实现原理.pdf Hadoop技术内幕 深入理解MapReduce架构设计与实现原理.pdf Hadoop技术内幕:深入解析Hadoop Common和HDFS架构设计与实现原理.pdf Hadoop权威指南.大数据的存储与分析.第4版…...

    2024/5/5 4:11:56
  7. 详解Spring全家桶

    https://blog.csdn.net/CSDN_000000000000001/article/details/90255707 详解Spring全家桶...

    2024/4/13 14:42:08
  8. win10 IntelliJ IDEA 配置JDK

    win10 IntelliJ IDEA 配置JDK https://blog.csdn.net/dudu3332/article/details/104492561...

    2024/4/16 2:19:45
  9. 数据库原理复习之数据库系统的结构与组成

    数据库原理复习--1.3数据库系统的结构与1.4数据库系统的组成数据库系统模式的概念数据库系统的三级模式结构模式外模式内模式数据库的二级映像功能与数据独立性外模式/模式映像模式/内模式映像数据库系统的组成硬件平台及数据库软件人员 数据库系统模式的概念1、型是指对某一类…...

    2024/4/16 2:20:36
  10. linux系统设置命令集

    linux系统设置命令集 alias 功能说明:设置指令的别名。 语  法:alias语  法:setconsole [video][serial][ttya][ttyb] 补充说明:setconsole可用来指定系统终端。 参  数:serial  使用PROM终端。ttya,cua0或ttyS0  使用第1个串口设备作为终端。ttyb,cua1或ttyS1  …...

    2024/4/24 13:27:06
  11. Spring Aop 源码笔记和源码阅读个人技巧分享

    概述 用过spring 框架的小伙伴都知道,aop对spring 的重要性,学习源码必不可少,文本记录一些源码跟踪源码技巧学习心得,需要纠错和改正的请在下方留言 aop 原理大致分析 这个网上一搜一大堆,重复阐述没有意义,说下我个人理解,关键两个字 代理 什么叫代理,和银行一样,你…...

    2024/4/13 10:23:04
  12. Apache Tomcat 源码分析环境搭建

    官网下载 https://tomcat.apache.org/download-80.cgi这里下载apache-tomcat-8.5.50-src可以选择其它版本 添加 pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="…...

    2024/4/21 9:44:16
  13. handler机制及使用场景

    1.handler是什么?是android提供用于更新ui的一套机制,也是处理消息的机制作用:a.新启动的线程中发送消息b.主线程中获取,处理消息2.为什么要使用handler?答:android在设计的时候,封装了一套消息创建,传递,处理机制,如果不遵循这套机制就不能更新ui,会抛出异常信息(不…...

    2024/5/2 20:18:26
  14. 七、如何配置STM32通过SWD(JTAG下载程序)

    ①原因分析使用STM32CubeMX生成的工程,使用SWD下载,需要配置。②解决办法:更改STM32CubeMX配置SYS>Debug>SerialWire如何通过代码控制:③固件下载操作:按住复位键 > 点击 Download > 提示框更新 如下内容 > 松开复位键 > 正常下载④为了安全防止黑客通过…...

    2024/5/2 7:10:55
  15. 互联网常用架构学习

    随着业务逻辑越来越复杂,很多公司都开始用起了微服务,架构也和上图大同小异,本文主要记录下对此架构学习的笔记 CDN CDN,全称Content Delivery Network,即内容分发网络。可以加快用户的访问速度,原理就是使用缓存。 CDN一般会缓存js脚本,css样式表,图片,图标,Flash等…...

    2024/5/3 15:34:34
  16. 51单片机 Proteus仿真 数码管风扇PWM 定时

    三个按键对应三个风扇档位,档位越高PWM占空比越高,风扇转速越快。 电源按键可以打开或者关闭风扇。 定时加可以一次加一分钟,定时减可以一次减一分钟,倒计时完毕后风扇关闭。...

    2024/4/19 16:14:49
  17. 本地运行web项目

    1、安装npm install -g live-server或者cnpm install live-server -gf2、live-server 将服务启动...

    2024/4/28 21:13:13
  18. 1082 射击比赛 (20分)

    本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军;谁差得最远,谁就是菜鸟。本题给出一系列弹洞的平面坐标(x,y),请你编写程序找出冠军和菜鸟。我们假设靶心在原点(0,0)。 输入格式: 输入在第一行中给出一个正整数 N(≤ 10 000)。随后 N 行,每行…...

    2024/5/2 2:22:57
  19. 线程通信之wait() 和notify() 方法

    线程通信之wait() 和notify() 方法 通信方法 wait():调用wait方法的线程,当前持有锁的该线程等待,直至该对象的另一个持锁线程调用notify/notifyAll操作。 wait(long timeOut)、wait(long timeOut,int nanos) notify():通知持有该对象锁的所有线程中的的随意一个线程被唤醒 n…...

    2024/4/19 15:24:24
  20. LeetCode-----第七十九题-----单词搜索

    单词搜索难度:中等给定一个二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。示例:board = [[A,B,C,E],[S,F,C,S],[A,D,E…...

    2024/4/16 2:20:56

最新文章

  1. 【Github】将github仓库作为图床使用

    创建github仓库 首先创建一个github仓库专门用于存储图片&#xff0c;具体步骤如下&#xff1a; 1.点击新的仓库按钮 2.初始配置&#xff1a;随便填写一个仓库名&#xff1b;这里的仓库状态一定要是public公开的&#xff0c;不然后面访问不了图片 下载PicGo PicGo官网 在A…...

    2024/5/5 7:11:26
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 【Locust分布式压力测试】

    Locust分布式压力测试 https://docs.locust.io/en/stable/running-distributed.html Distributed load generation A single process running Locust can simulate a reasonably high throughput. For a simple test plan and small payloads it can make more than a thousan…...

    2024/5/4 16:36:43
  4. javaWeb网上零食销售系统

    1 绪 论 目前&#xff0c;我国的网民数量已经达到7.31亿人&#xff0c;随着互联网购物和互联网支付的普及&#xff0c;使得人类的经济活动进入了一个崭新的时代。淘宝&#xff0c;京东等网络消费平台功能的日益完善&#xff0c;使得人们足不出户就可以得到自己想要的东西。如今…...

    2024/5/3 10:08:04
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/4 23:55:17
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/4 2:59:34
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57