Mybatis源码学习(二)配置文件解析到SqlSessionFactory构建

    • 前言
    • 1.SqlSessionFactory源码详解
      • 1.1 DefaultSqlSessionFactory源码详解
      • 1.2 SqlSessionManager源码详解
    • 2. SqlSessionFactoryBuilder源码详解
      • 1. 以Reader为基础
      • 2. 以InputStream为基础
      • 3. 以Configuration为基础
      • 自己YY
    • 3. XMLConfigBuilder源码详解
      • 1.私有的构造方法解析
      • 2.parse()方法解析
      • 3.parseConfiguration(XNode root)方法解析
      • 4. 解析Properties
      • 5. 解析Setting
      • 6. 根据Setting设置虚拟文件系统VFS
      • 7.根据Setting设置LogImpl
      • 8. 解析别名
        • 8.1 通过包名注册别名
        • 8.2 单个别名的注册
      • 9. 解析自定义拦截器
      • 10. 解析对象工厂
      • 11.解析对象包装工厂
      • 12. 解析反射工厂
      • 13. 设置Configuration的各个属性
      • 14.设置Environment
      • 15. 解析数据库厂商
      • 16. 解析TypeHandler
      • 17.解析所有的Mapper配置
        • 17.1 package 配置 和 class配置
        • 17.2 resource 配置和url 配置
    • 总结
    • 文章链接

前言

上一篇文章中简单的介绍了Mybatis的执行流程.
执行的第一步就是构建一个SqlSessionFactory, 那到底我们是如何获取到SqlSessionFactory的呢?
接下来让我们一起学习一下吧!

1.SqlSessionFactory源码详解

SqlSessionFactory是一个接口,引用代码的文档解释: 它的作用就是从Connection或者DataSource中创建一个SqlSession
它有两个实现DefaultSqlSessionFactorySqlSessionManager

public interface SqlSessionFactory {/**获取一个默认配置的SqlSession* @return              默认配置的SqlSession*/SqlSession openSession();/**获取一个指定事务提交方式的SqlSession* @param autoCommit    事务提交方式, true,false 自动或手动* @return              指定事务提交方式的SqlSession*/SqlSession openSession(boolean autoCommit);/**获取一个给定数据库连接的SqlSession* @param connection    数据库连接* @return              指定数据库连接的SqlSession*/SqlSession openSession(Connection connection);/**获取一个给定事务隔离级别的SqlSession* @param level         事务隔离级别* @return              给定事务隔离级别的SqlSession*/SqlSession openSession(TransactionIsolationLevel level);/**获取一个给定执行器类型的SqlSession* @param execType      DefaultSqlSession中维护的一个执行器的类型* @return              给定执行器类型的SqlSession*/SqlSession openSession(ExecutorType execType);/**获取一个给定执行器类型和事务提交方式的SqlSession* @param execType      DefaultSqlSession中维护的一个执行器的类型* @param autoCommit    事务提交方式, true,false 自动或手动* @return              给定执行器类型和事务提交方式的SqlSession*/SqlSession openSession(ExecutorType execType, boolean autoCommit);/**获取一个给定执行器类型和事务隔离级别的SqlSession* @param execType      DefaultSqlSession中维护的一个执行器的类型* @param level         事务隔离级别* @return              给定执行器类型和事务隔离级别的SqlSession*/SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);/**获取一个给定执行器类型和数据库连接的SqlSession* @param execType      DefaultSqlSession中维护的一个执行器的类型* @param connection    数据库连接* @return              给定执行器类型和数据库连接的SqlSession*/SqlSession openSession(ExecutorType execType, Connection connection);/**获取核心配置文件对象* @return              核心配置文件对象*/Configuration getConfiguration();}

1.1 DefaultSqlSessionFactory源码详解

DefaultSqlSessionFactorySqlSessionFactory的默认实现,接口的openSession(xxx …)的实现都是通过内部的两个函数实现
openSessionFromDataSource()openSessionFromConnection()
这也和接口的文档上描述的一致
在这里插入图片描述
openSessionFromDataSource()和openSessionFromConnection() 的实现对比
他们的主体实现基本一致,区别是在于事务的构造上
openSessionFromDataSource 是通过数据源和参数指定的事务隔离级别,参数指定的是否自动提交来构建事务
openSessionFromConnection 是通过连接中的属性来构建事务
它们最终返回的都是
DefaultSqlSession

/**从数据源中获取SqlSession* @param execType      执行器的类型* @param level         事务隔离级别* @param autoCommit    事务提交方式, true,false 自动或手动* @return              DefaultSqlSession*/private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {//读取环境参数final Environment environment = configuration.getEnvironment();//获取事务工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//使用事务工厂创建事务tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//创建一个sql执行器final Executor executor = configuration.newExecutor(tx, execType);//构建一个DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {//如果失败,关闭事务closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}/**从连接中获取SqlSession* @param execType      执行器的类型* @param connection    数据库连接* @return              DefaultSqlSession*/private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {try {boolean autoCommit;try {//从连接中获取事务提交类型autoCommit = connection.getAutoCommit();} catch (SQLException e) {// Failover to true, as most poor drivers// or databases won't support transactions//默认为true, 有的数据库或驱动不支持事务autoCommit = true;}//读取环境参数final Environment environment = configuration.getEnvironment();//获取事务工厂final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//使用事务工厂创建事务final Transaction tx = transactionFactory.newTransaction(connection);//创建一个sql执行器final Executor executor = configuration.newExecutor(tx, execType);//构建一个DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

1.2 SqlSessionManager源码详解

SqlSessionManager实现了SqlSessionFactory和SqlSession两个接口,本章我们主要学习下SqlSessionFactory相关的接口.
后续再进行SqlSession的学习

在这里插入图片描述
SqlSessionManager内部维护了一个SqlSessionFactory和一个SqlSession对象还有一个ThreadLocal变量,他提供的newInstance()方法都是创建一个DefaultSqlSessionFactory,然后调用私有的构造方法进行构造.所以他的openSession()相关方法的实现都是通过DefaultSqlSessionFactory去实现的
在这里插入图片描述
从上述的学习我们可以发现DefaultSqlSessionFactory目前都是通过SqlSessionFactoryBuilder的build()方法构建的,接下来我们就来学习SqlSessionFactoryBuilder

2. SqlSessionFactoryBuilder源码详解

打开SqlSessionFactory源码, 我们可以看见提供了build()方法的9种重载方式
在这里插入图片描述

具体可以分为以下三类

1. 以Reader为基础

 前四个方法都是以Reader为基础的, 真正逻辑的实现在第四个方法,前三个方法通过传递默认参数null进行内部调用
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.}}
}

2. 以InputStream为基础

 第五到第八个方法都是以InputStream为基础的, 真正逻辑的实现在第八个方法,前三个方法通过传递默认参数null进行内部调用
 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);return build(parser.parse());} 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.}}}

3. 以Configuration为基础

public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);
}

其实第一种和第二种的实现方式都是类似,通过构建XMLConfigBuilder,然后调用parse()方法获得一个Configuration对象.
最终调用第三种方式获取SqlSessionFactory

自己YY

第二种方式也可以改造成如下方式, 通过InputStreamReader来包装InputStream的方式获得一个Reader,然后调用以Reader为基础的方法进行操作

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {return build(new InputStreamReader(inputStream), environment, properties);}

3. XMLConfigBuilder源码详解

我们对XMLConfigBuilder进行简单的分解

  1. 六个公共的构造方法提供调用
  2. 一个私有的构造方法,是仅供内部调用, 六个公共的构造方法最终都是通过私有方法去实现初始化
  3. 一个公共的parse()方法,调用了内部的N个私有方法
  4. 一堆私有的方法, 在parse()中被调用
    在这里插入图片描述

1.私有的构造方法解析

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;
}

在这个构造方法中主要做了两件事

  1. 调用父类的构造方法,并创建一个Configuration对象传入
  2. 将参数设置到属性中

2.parse()方法解析

public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;
}

在parse()方法中完成的逻辑

  1. 判断是否解析过,没解析则进行解析,并返回Configuration对象
  2. 如果已经被解析过,则抛出异常,一个XMLConfigBuilder只能解析一次

3.parseConfiguration(XNode root)方法解析

private void parseConfiguration(XNode root) {try {// issue #117 read properties first//解析properties然后放入Configuration中propertiesElement(root.evalNode("properties"));//解析settingsProperties settings = settingsAsProperties(root.evalNode("settings"));//VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源loadCustomVfs(settings);//自定义log实现loadCustomLogImpl(settings);//处理别名typeAliasesElement(root.evalNode("typeAliases"));//处理插件pluginElement(root.evalNode("plugins"));//处理自定义对象工厂objectFactoryElement(root.evalNode("objectFactory"));//处理自定义对象包装工厂objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//处理反射工厂reflectorFactoryElement(root.evalNode("reflectorFactory"));//解析自定义setting,并设置到ConfigurationsettingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631//解析环境配置environmentsElement(root.evalNode("environments"));//解析数据库提供厂商databaseIdProviderElement(root.evalNode("databaseIdProvider"));//解析类型处理器typeHandlerElement(root.evalNode("typeHandlers"));//解析自定义mappermapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}

4. 解析Properties

private void propertiesElement(XNode context) throws Exception {if (context != null) {//读取所有子节点构建一个Properties, 遍历读取name和value属性,然后进行赋值Properties defaults = context.getChildrenAsProperties();//读取resourceString resource = context.getStringAttribute("resource");//读取urlString url = context.getStringAttribute("url");//url和resource不能共存if (resource != null && url != null) {throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");}if (resource != null) {//从resource中解析Properties,并放入默认的Propertiesdefaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {//从url中解析Properties,并放入默认的Propertiesdefaults.putAll(Resources.getUrlAsProperties(url));}Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}//合并configuration中已存在的配置和默认配置,并设置到configuration中parser.setVariables(defaults);configuration.setVariables(defaults);}}

5. 解析Setting

private Properties settingsAsProperties(XNode context) {//为空,返回一个默认全空的Propertiesif (context == null) {return new Properties();}//读取所有子节点作为PropertiesProperties props = context.getChildrenAsProperties();//检查所有的配置能被Configuration识别// Check that all settings are known to the configuration classMetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);for (Object key : props.keySet()) {if (!metaConfig.hasSetter(String.valueOf(key))) {throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");}}return props;
}

6. 根据Setting设置虚拟文件系统VFS

private void loadCustomVfs(Properties props) throws ClassNotFoundException {//获取虚拟文件的自定义实现类String value = props.getProperty("vfsImpl");if (value != null) {String[] clazzes = value.split(",");for (String clazz : clazzes) {if (!clazz.isEmpty()) {@SuppressWarnings("unchecked")//反射创建虚拟文件的自定义实现类,并设置到configurationClass<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);configuration.setVfsImpl(vfsImpl);}}}
}

7.根据Setting设置LogImpl

private void loadCustomLogImpl(Properties props) {//解析LogImplClass<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));//设置LogImplconfiguration.setLogImpl(logImpl);
}

其中resolveClass(props.getProperty(“logImpl”))的底层是调用了TypeAliasRegistry的resolveAlias(String string) 实现

public <T> Class<T> resolveAlias(String string) {try {if (string == null) {return null;}// issue #748String key = string.toLowerCase(Locale.ENGLISH);Class<T> value;//typeAliases是一个维护了常用的java类的一个Map, 是在TypeAliasRegistry的构造函数中进行的初始化赋值//使用常用类是不用每次通过反射创建Class对象if (typeAliases.containsKey(key)) {value = (Class<T>) typeAliases.get(key);} else {value = (Class<T>) Resources.classForName(string);}return value;} catch (ClassNotFoundException e) {throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);}
}

8. 解析别名

private void typeAliasesElement(XNode parent) {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String typeAliasPackage = child.getStringAttribute("name");//通过包名解析类configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);} else {//注册单个别名String alias = child.getStringAttribute("alias");String type = child.getStringAttribute("type");try {Class<?> clazz = Resources.classForName(type);if (alias == null) {typeAliasRegistry.registerAlias(clazz);} else {typeAliasRegistry.registerAlias(alias, clazz);}} catch (ClassNotFoundException e) {throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);}}}}
}

8.1 通过包名注册别名

public void registerAliases(String packageName, Class<?> superType) {ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();resolverUtil.find(new ResolverUtil.IsA(superType), packageName);Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();for (Class<?> type : typeSet) {// Ignore inner classes and interfaces (including package-info.java)// Skip also inner classes. See issue #6if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {registerAlias(type);}}
}

ResolverUtil的find()方法

public ResolverUtil<T> find(Test test, String packageName) {//包名解析为路径名String path = getPackagePath(packageName);try {//通过文件系统获取所有的路径List<String> children = VFS.getInstance().list(path);for (String child : children) {if (child.endsWith(".class")) {//找到所有.class文件,然后进行匹配,匹配成功加入到内部维护的Set<Class<? extends T>> matchesaddIfMatching(test, child);}}} catch (IOException ioe) {log.error("Could not read package: " + packageName, ioe);}return this;
}

8.2 单个别名的注册

if (alias == null) {typeAliasRegistry.registerAlias(clazz);
} else {typeAliasRegistry.registerAlias(alias, clazz);
}

如果别名未给定
获取有没有Alias.class注解,如果有注解,则用注解的值作为别名,否则取类型名字作为别名

public void registerAlias(Class<?> type) {String alias = type.getSimpleName();Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) {alias = aliasAnnotation.value();}registerAlias(alias, type);
}

给定别名的情况
如果别名已经被注册并且重新注册的值和之前不同,抛出异常

public void registerAlias(String alias, Class<?> value) {if (alias == null) {throw new TypeException("The parameter alias cannot be null");}// issue #748String key = alias.toLowerCase(Locale.ENGLISH);if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");}typeAliases.put(key, value);
}

9. 解析自定义拦截器

private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {//解析所有的interceptorString interceptor = child.getStringAttribute("interceptor");//即系interceptor下的属性值Properties properties = child.getChildrenAsProperties();//反射获取Interceptor的实例Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();//设置属性interceptorInstance.setProperties(properties);//添加interceptorconfiguration.addInterceptor(interceptorInstance);}}
}

10. 解析对象工厂

private void objectFactoryElement(XNode context) throws Exception {if (context != null) {//解析typeString type = context.getStringAttribute("type");//解析对象工厂的属性Properties properties = context.getChildrenAsProperties();//根据type解析对象工厂类ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();//设置属性factory.setProperties(properties);//设置对象工厂configuration.setObjectFactory(factory);}
}

11.解析对象包装工厂

private void objectWrapperFactoryElement(XNode context) throws Exception {if (context != null) {//解析typeString type = context.getStringAttribute("type");//根据type解析对象包装工厂类ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();//设置对象包装工厂configuration.setObjectWrapperFactory(factory);}
}

12. 解析反射工厂

private void reflectorFactoryElement(XNode context) throws Exception {if (context != null) {//解析typeString type = context.getStringAttribute("type");//根据type解析反射工厂类ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();//设置对象反射工厂类configuration.setReflectorFactory(factory);}}

13. 设置Configuration的各个属性

private void settingsElement(Properties props) {configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));configuration.setLogPrefix(props.getProperty("logPrefix"));configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));}

14.设置Environment

private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {environment = context.getStringAttribute("default");}for (XNode child : context.getChildren()) {String id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)) {//事务工厂TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//数据源工厂DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//构建数据源DataSource dataSource = dsFactory.getDataSource();//构建EnvironmentEnvironment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);//设置Environmentconfiguration.setEnvironment(environmentBuilder.build());}}}}

15. 解析数据库厂商

private void databaseIdProviderElement(XNode context) throws Exception {DatabaseIdProvider databaseIdProvider = null;if (context != null) {String type = context.getStringAttribute("type");// awful patch to keep backward compatibilityif ("VENDOR".equals(type)) {type = "DB_VENDOR";}Properties properties = context.getChildrenAsProperties();databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();databaseIdProvider.setProperties(properties);}Environment environment = configuration.getEnvironment();if (environment != null && databaseIdProvider != null) {String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());configuration.setDatabaseId(databaseId);}
}

16. 解析TypeHandler

private void typeHandlerElement(XNode parent) {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {//指定包名方式配置String typeHandlerPackage = child.getStringAttribute("name");//指定包下进行扫描,找到所有为TypeHandler.class的class,然后进行注册//会排除接口,匿名内部类,还有抽象类typeHandlerRegistry.register(typeHandlerPackage);} else {//指定单个的处理器String javaTypeName = child.getStringAttribute("javaType");String jdbcTypeName = child.getStringAttribute("jdbcType");String handlerTypeName = child.getStringAttribute("handler");Class<?> javaTypeClass = resolveClass(javaTypeName);JdbcType jdbcType = resolveJdbcType(jdbcTypeName);Class<?> typeHandlerClass = resolveClass(handlerTypeName);if (javaTypeClass != null) {if (jdbcType == null) {typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);} else {typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);}} else {typeHandlerRegistry.register(typeHandlerClass);}}}}
}

17.解析所有的Mapper配置

private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {//通过包名配置String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");//优先从resource进行解析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());mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {//其次从url解析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解析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.");}}}}
}

17.1 package 配置 和 class配置

package配置通过扫描包名,找到所有的.class文件,然后调用MapperRegistryaddMapper方法
class配置通过反射获取Mapper类,然后调用MapperRegistryaddMapper方法

public <T> void addMapper(Class<T> type) {//只对指定包下的接口进行处理if (type.isInterface()) {//如果重复解析,会抛出异常if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {//为每个Interface生成一个代理工厂MapperProxyFactory,后续接口的方法执行时获通过代理工厂生成的代理进行执行//后续章节我们再详细的对MapperProxyFactory进行解析knownMappers.put(type, new MapperProxyFactory<>(type));// It's 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 won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);//解析类中的方法和注解等parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {//如果加载未完成,map中移除该对象knownMappers.remove(type);}}}
}

17.2 resource 配置和url 配置

都是通过构造XMLMapperBuilder,调用parse()方法进行解析

public void parse() {if (!configuration.isResourceLoaded(resource)) {//解析mapper文件的各个节点configurationElement(parser.evalNode("/mapper"));//加载资源configuration.addLoadedResource(resource);//绑定命名空间和Mapper,最终调用MapperRegistry的addMapper方法bindMapperForNamespace();}//解析resultMapparsePendingResultMaps();//解析缓存parsePendingCacheRefs();//解析语句parsePendingStatements();
}

总结

在这里插入图片描述

通过上述的代码学习,可以发现,大量的工作都是在一开始的xml解析.通过xml解析之后,获取Configuration对象,然后构造SqlSessionFactory.文中并没有对XMLMapperBuilder和MapperAnnotationBuilder中的sql语句解析等进行详细的学习感兴趣的读者可以留言,也可以专门写一遍解析的文章,大家一起学习!

喜欢的小伙伴请动动小手关注和点赞吧,也可留言一起探讨怎样更好的学习源码!!!

文章链接

Mybatis源码学习(一)初探执行流程
Mybatis源码学习(二)配置文件解析到SqlSessionFactory构建
Mybatis源码学习(三)SqlSession详解
Mybatis源码学习(四)自定义Mapper方法执行流程
Mybatis源码学习(五)Executor和StatementHandler详解
Mybatis源码学习(六)结果集自动封装机制
Mybatis源码学习(七)mybatis缓存详解
Mybatis源码学习(八)Mybatis设计模式总结

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

相关文章

  1. 工业相机及镜头的相关概念与相机及镜头的选型

    工业相机及镜头的相关概念与相机及镜头的选型 一、相机的主要参数 1.像素&#xff08;pixel&#xff09; ​ 像素是图像上的最小组成单元。图像由小方格即像素组成的&#xff0c;这些小方块都有一个明确的位置和被分配的色彩数值&#xff0c;小方格颜色和位置就决定该图像所…...

    2024/4/22 13:57:17
  2. JavaScript隐式转换的理解

    不同类型的变量比较要先转类型&#xff0c;叫做类型转换&#xff0c;类型转换也叫隐式转换。 隐式转换通常发生在运算符加减乘除&#xff0c;等于&#xff0c;还有小于&#xff0c;大于等。。 基本类型的转换 1.字符串加数字,数字就会转成字符串。 2.数字减字符串&#xff…...

    2024/4/25 9:39:22
  3. Could not find a version that satisfies the requirement torch==1.4.0

    装pytorch 1.4.0的时候总是装不上。看到一个博客的方法可行&#xff01; 原博链接 解决方法&#xff1a; pip install torch1.3.1 -f https://download.pytorch.org/whl/torch_stable.html pip install torchvision0.4.1...

    2024/5/2 12:00:35
  4. Jenkins-docker-从gitlab上拉取项目代码(二十七)

    Jenkins添加项目 Jenkins配置 JenkinFile配置 参数化构建 // git凭证ID def git_auth "44e38a42-3679-442e-a628-400f17fba105" // git的URL地址 def git_url "http://192.168.17.129:82/eureka_group/eureka-security-server.git"node {stage(拉取代码…...

    2024/5/2 15:56:08
  5. Mybatis源码学习(三)SqlSession详解

    Mybatis源码学习&#xff08;三&#xff09;SqlSession详解前言1. SqlSession源码详解2. DefaultSqlSession源码详解2.1DefaultSqlSession源码2.2 configuration.getMappedStatement(statement)2.3 Executor3. SqlSessionManager源码详解3.1 私有的构造方法3.2 SqlSession的代理…...

    2024/4/30 2:42:20
  6. 第三方更换通讯证书导致SSLHandshakeException

    异常突现 在这普通的一天&#xff0c;我写普通的代码&#xff0c;却突然收到不普通的报警 javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation 查看日志访问的请求全部报错&#xff0c;紧急联系对方&#xff0c;得知对方更换…...

    2024/4/29 23:02:48
  7. jupyter notebook中使用anaconda的虚拟环境

    windows下 一&#xff0c;需要安装ipykernel 使用conda或者pip安装都可以 conda安装命令如下 conda install ipykernelpip安装命令如下 pip install ipykernel二&#xff0c;将环境添加进jupyter中 python -m ipykernel install --user --name 环境名称 --display-name &q…...

    2024/4/22 16:39:54
  8. 7、springMVC Ajax技术

    文章目录1、简介1、新建一个module导入web支持&#xff01;2、配置web.xml文件3、配置spring-mvc.xml文件4、编写一个controller进行测试&#xff0c;查看配置是否正确2、编写一个 ajax-frame.html 使用 iframe 测试&#xff0c;感受下效果3、jQuery.ajax(重点关注)1、编写一个…...

    2024/4/29 16:43:45
  9. 分布式RPC系统框架Dubbo-08多协议支持

    除了Dubbo服务暴露Dubbo协议外&#xff0c;Dubbo框架还支持另外8种服务暴露协议&#xff1a;RMI协议、Hessian协议、HTTP协议、WebService协议、Thrift协议、Memcached协议、Redis协议、Rest协议,但在实际生产中&#xff0c;使用最多的就是Dubbo服务暴露协议。 1 各个协议的特…...

    2024/5/2 11:20:47
  10. MongoDB_如何搭建集群?

    搭建步骤1.准备三台虚拟机服务器2.添加replSet配置修改mongodb.conf文件然后分别启动三台服务器&#xff1a;3.初始化复制集4.查看集群状态5.测试6.关闭数据库7. 主复制集添加仲裁者(arbiter)1.准备三台虚拟机服务器 各自安装好mongoDB —>技巧&#xff1a;可先准备配置好一…...

    2024/5/2 16:10:09
  11. 【SSM框架入门】mybatis入门案例

    在Test中创建MybatisTest类 package com.ssm.test;import com.ssm.dao.IUserDao; import com.ssm.domain.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apach…...

    2024/4/30 0:11:40
  12. LLVM简明安装教程

    官网下载地址&#xff1a; https://releases.llvm.org/ 截止2020.9.13&#xff0c;最新版本为LLVM10.0.1 下载源码进行手动编译 一般情况下只需要下载LLVM source code、Clang source code、compiler-rt source code这三个文件。将其进行解压&#xff08;windows下可能需要以…...

    2024/4/26 1:12:04
  13. SuperGlue:基于图神经网络和注意力机制的特征点匹配网络

    SuperGlue Background SuperGlue是Magicleap团队在SuperPoint后的又一力作&#xff0c;其思路与方法之巧妙不输SuperPoint&#xff0c;完成的任务也是传统视觉问题中特征点提取和描述&#xff08;SuperPoint&#xff09;的后一步–特征点匹配。 说起匹配&#xff0c;最传统的就…...

    2024/5/2 9:40:21
  14. OpenStack理论学习(三)

    【Neutron模块】 Neutron介绍&#xff1a; 虚拟化网络的实现方式&#xff1a; OSI七层模型&#xff1a; 网络命名空间&#xff08;netns&#xff09;&#xff1a; 网络分类&#xff1a; 网络分类&#xff08;二&#xff09;&#xff1a; 虚拟网络类型定义&#xff08;…...

    2024/4/30 15:18:43
  15. 力扣(重点题)

    文章目录力1. 两数之和力 2. 两数相加力 3. 无重复字符的最长子串力 5. 最长回文子串力7. 整数反转力9. 回文数力14. 最长公共前缀力15. 三数之和力16. 最接近的三数之和力 18四数之和力1. 两数之和 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出…...

    2024/4/29 0:54:39
  16. Opencv+python(五)过滤器

    图像平滑处理均值滤波方框滤波高斯滤波中值滤波想要滤波的程度大一些&#xff0c;就需要把核的大小弄大一些&#xff1b;中值滤波和高斯滤波好像处理时稍好 均值滤波 任意一点的像素值&#xff0c;都是周围N*N给像素值的均值 5*5 的模糊程度< 10*10的模糊程度 ocv2.imrea…...

    2024/4/1 17:06:39
  17. elementUI实现数组(需要验证的数据是以数组的形式呈现)表单验证

    elementUI实现数组表单验证 <el-form :model"couponForm" ref"couponForm" label-width"200px"><el-form-item v-for"(item,idx) in couponForm.actIdList" :label"活动期间第 (idx 1) 次下单得" :prop"a…...

    2024/4/29 5:00:15
  18. 碎片化学习Java(六)-- Java预测子女身高

    本文参考 嗨客网 Java 实战 关键词&#xff1a; Java变量运算 Java预测子女身高案例 Java预测子女身高 题目 答案 原文 若要查看详细 解题思路、解题步骤、运行结果 原文链接&#xff1a;https://haicoder.net/case/java/java-variable-operator2.html 后续 大纲&#x…...

    2024/4/30 4:34:21
  19. Netty拾遗(四)——Netty的helloworld

    前言 其实前几篇博客下来&#xff0c;简单介绍了NIO的一些东西&#xff0c;可以发现NIO使用起来确实很繁琐&#xff0c;这也是为啥这么多人愿意选择使用Netty的重要原因之一&#xff0c;这一篇博客打算正式开始Netty的总结 依旧以《Netty权威指南》中的查询服务器当前时间的操…...

    2024/4/29 11:24:22
  20. 京淘项目的完成步骤

    3.5 关于JSON串说明 3.5.1 JSON介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 3.5.2 Object对象格式 在这里插入图片描述 例子: {“id”:“1”,“name”:“tomcat猫”} 3.5.3 数组格式 在这里插入图片描…...

    2024/5/2 4:21:26

最新文章

  1. TypeScript学习笔记:迈向更安全的JavaScript编程

    &#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

    2024/5/2 18:21:35
  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/2 10:47:24
  4. Vue3学习笔记+报错记录

    文章目录 1.创建Vue3.0工程1.1使用vue-cli创建1.2 使用vite创建工程1.3.分析Vue3工程结构 2.常用Composition2.1 拉开序幕的setup2.2 ref函数_处理基本类型2.3 ref函数_处理对象类型2.4 ref函数使用总结 1.创建Vue3.0工程 1.1使用vue-cli创建 查看vue/cli版本&#xff0c;确保…...

    2024/4/30 17:15:04
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/5/2 11:19:01
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/5/2 16:04:58
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/5/1 21:18:12
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/5/2 9:47:31
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/5/2 9:47:31
  10. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/5/2 6:03:07
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/5/2 9:47:30
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/5/1 11:24:00
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/5/2 5:31:39
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/5/1 20:22:59
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/5/2 9:47:28
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/5/2 9:47:27
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/5/2 0:07:22
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/5/2 8:37:00
  19. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/5/2 9:47:26
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/5/2 9:47:25
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/5/1 14:33:22
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/5/1 11:51:23
  23. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/5/2 7:30:11
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/5/1 20:56:20
  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