mybatis源码阅读(二):mybatis初始化上

转载自  mybatis源码阅读(二):mybatis初始化上

1.初始化入口

//Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与数据库进行交互
private static SqlSessionFactory getSessionFactory() {SqlSessionFactory sessionFactory = null;String resource = "configuration.xml";try {sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));} catch (IOException e) {e.printStackTrace();}return sessionFactory;
}

那么,我们就先从SqlSessionFactoryBuilder入手, 咱们先看看源码是怎么实现的

SqlSessionFactoryBuilder

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// 读取配置文件XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);//解析配置得到Configuration对象,创建DefaultSqlSessionFactory对象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.}}
}

XMLConfigBuilder

XMLConfigBuilder是BaseBuilder的众多子类之一,核心字段如下

//表示是否已经解析过了
private boolean parsed;
//用于解析配置文件的对象
private final XPathParser parser;
//配置文件中表示<environment>的名称  默认读取default属性
private String environment;
// 负责和创建Reflector对象
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();

XMLConfigBuilder.parse()方法是解析mybatis-config.xml配置文件的如。它调用parseConfiguration()方法实现整个解析过程。具体实现如下:

/*** 解析配置文件的入口* @return*/
public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;
}/*** 对配置文件每个节点具体的解析过程* configuration节点为根节点。* 在configuration节点之下,我们可以配置11  个子节点,* 分别为:properties、settings、typeAliases、plugins、objectFactory、objectWrapperFactory、reflectorFactory、* environments、databaseIdProvider、typeHandlers、mappers。* @param root 根节点*/
private void parseConfiguration(XNode root) {try {// 解析properties节点propertiesElement(root.evalNode("properties"));//解析settings节点Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);//设置vfsImpl字段//解析typeAliases节点typeAliasesElement(root.evalNode("typeAliases"));//解析plugins节点pluginElement(root.evalNode("plugins"));//解析objectFactory节点objectFactoryElement(root.evalNode("objectFactory"));//解析objectWrapperFactory节点objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));//解析reflectorFactory节点reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);//解析environments节点environmentsElement(root.evalNode("environments"));//解析databaseIdProvider节点databaseIdProviderElement(root.evalNode("databaseIdProvider"));//解析typeHandlers节点typeHandlerElement(root.evalNode("typeHandlers"));//解析mappers节点mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}
}

propertiesElement()方法会解析配置文件中的properties节点并形成Java.util.Properties对象,之后将改对象设置到XpathParse和Configguration的variables字段中,占位符就是用Properties中的信息替换的,具体实现如下:

/*** 解析properties的具体方法* @param context* @throws Exception*/
private void propertiesElement(XNode context) throws Exception {if (context != null) {// 将子节点的 name 以及value属性set进properties对象// 这儿可以注意一下顺序,xml配置优先, 外部指定properties配置其次Properties defaults = context.getChildrenAsProperties();// 获取properties节点上 resource属性的值String resource = context.getStringAttribute("resource");// 获取properties节点上 url属性的值, resource和url不能同时配置String url = context.getStringAttribute("url");if (resource != null && url != null) {//url 和resource不能同时配置throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");}// 把解析出的properties文件set进Properties对象if (resource != null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {defaults.putAll(Resources.getUrlAsProperties(url));}// 将configuration对象中已配置的Properties属性与刚刚解析的融合// configuration这个对象会装载所解析mybatis配置文件的所有节点元素,以后也会频频提到这个对象Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}// 把装有解析配置propertis对象set进解析器, 因为后面可能会用到parser.setVariables(defaults);// set进configuration对象configuration.setVariables(defaults);}
}

settings节点下的配饰是mybatis的全局性配置,修改的是configuration对象的属性,具体说明参考官方文档

/*** settings标签就是设置configuration对象的各种属性,* 具体属性说明可以参考mybatis官方文档* @param props* @throws Exception*/
private void settingsElement(Properties props) throws Exception {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.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")));@SuppressWarnings("unchecked")Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));configuration.setDefaultEnumTypeHandler(typeHandler);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"));@SuppressWarnings("unchecked")Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));configuration.setLogImpl(logImpl);configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}

environments元素节点主要配置数据库事物,数据源。可以配置多个environment子节点,假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment, 两个id分别对应开发环境(development)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 例如,我将environments的deault属性的值配置为development, 那么就会选择dev的environment。具体实现如下

/*** 解析enviroments元素节点的方法* @param context* @throws Exception*/
private void environmentsElement(XNode context) throws Exception {if (context != null) {if (environment == null) {//获取 <environments default="development"> 中的default值environment = context.getStringAttribute("default");}// 循环environments的子节点for (XNode child : context.getChildren()) {// 获取 <environment id="development"> z中的idString id = child.getStringAttribute("id");if (isSpecifiedEnvironment(id)) {//根据由environments的default属性去选择对应的enviroment// 事物 mybatis有两种:JDBC 和 MANAGED, 配置为JDBC则直接使用JDBC的事务,配置为MANAGED则是将事务托管给容器// <transactionManager type="JDBC"/>TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//enviroment节点下面就是dataSource节点了,解析dataSource节点DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));DataSource dataSource = dsFactory.getDataSource();Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory).dataSource(dataSource);// 将dataSource设置进configuration对象configuration.setEnvironment(environmentBuilder.build());}}}
}

typeAliases节点主要用来设置别名,其实这是挺好用的一个功能, 通过配置别名,我们不用再指定完整的包名

/*** 解析typeAliases 节点* <typeAliases>*     <!--<package name="com.lpf.entity"></package>-->*     <typeAlias alias="UserEntity" type="com.lpf.entity.User"/>* </typeAliases>* @param parent*/
private void typeAliasesElement(XNode parent) {if (parent != null) {for (XNode child : parent.getChildren()) {//如果子节点是package, 那么就获取package节点的name属性, mybatis会扫描指定的packageif ("package".equals(child.getName())) {String typeAliasPackage = child.getStringAttribute("name");//TypeAliasRegistry 负责管理别名, 这儿就是通过TypeAliasRegistry 进行别名注册configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);} else {//如果子节点是typeAlias节点,那么就获取alias属性和type的属性值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);}}}}
}

具体的别名注册类

public class TypeAliasRegistry {// 别名通过一个HashMap来实现, key为别名, value就是别名对应的类型(class对象)private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();/*** mybatis默认为我们注册的别名*/public TypeAliasRegistry() {registerAlias("string", String.class);registerAlias("byte", Byte.class);registerAlias("long", Long.class);registerAlias("short", Short.class);registerAlias("int", Integer.class);registerAlias("integer", Integer.class);registerAlias("double", Double.class);registerAlias("float", Float.class);registerAlias("boolean", Boolean.class);registerAlias("byte[]", Byte[].class);registerAlias("long[]", Long[].class);registerAlias("short[]", Short[].class);registerAlias("int[]", Integer[].class);registerAlias("integer[]", Integer[].class);registerAlias("double[]", Double[].class);registerAlias("float[]", Float[].class);registerAlias("boolean[]", Boolean[].class);registerAlias("_byte", byte.class);registerAlias("_long", long.class);registerAlias("_short", short.class);registerAlias("_int", int.class);registerAlias("_integer", int.class);registerAlias("_double", double.class);registerAlias("_float", float.class);registerAlias("_boolean", boolean.class);registerAlias("_byte[]", byte[].class);registerAlias("_long[]", long[].class);registerAlias("_short[]", short[].class);registerAlias("_int[]", int[].class);registerAlias("_integer[]", int[].class);registerAlias("_double[]", double[].class);registerAlias("_float[]", float[].class);registerAlias("_boolean[]", boolean[].class);registerAlias("date", Date.class);registerAlias("decimal", BigDecimal.class);registerAlias("bigdecimal", BigDecimal.class);registerAlias("biginteger", BigInteger.class);registerAlias("object", Object.class);registerAlias("date[]", Date[].class);registerAlias("decimal[]", BigDecimal[].class);registerAlias("bigdecimal[]", BigDecimal[].class);registerAlias("biginteger[]", BigInteger[].class);registerAlias("object[]", Object[].class);registerAlias("map", Map.class);registerAlias("hashmap", HashMap.class);registerAlias("list", List.class);registerAlias("arraylist", ArrayList.class);registerAlias("collection", Collection.class);registerAlias("iterator", Iterator.class);registerAlias("ResultSet", ResultSet.class);}/*** 处理别名, 直接从保存有别名的hashMap中取出即可*/@SuppressWarnings("unchecked")// throws class cast exception as well if types cannot be assignedpublic <T> Class<T> resolveAlias(String string) {try {if (string == null) {return null;}// issue #748String key = string.toLowerCase(Locale.ENGLISH);Class<T> value;if (TYPE_ALIASES.containsKey(key)) {value = (Class<T>) TYPE_ALIASES.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);}}/*** 配置文件中配置为package的时候,扫描包下的Javabean ,然后自动注册别名* 默认会使用 Bean 的首字母小写的非限定类名来作为它的别名* 也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user)*/public void registerAliases(String packageName){registerAliases(packageName, Object.class);}public void registerAliases(String packageName, Class<?> superType){ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();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);}}}public void registerAlias(Class<?> type) {String alias = type.getSimpleName();Alias aliasAnnotation = type.getAnnotation(Alias.class);if (aliasAnnotation != null) {alias = aliasAnnotation.value();} registerAlias(alias, type);}//向hashMap中注册别名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 (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");}TYPE_ALIASES.put(key, value);}public void registerAlias(String alias, String value) {try {registerAlias(alias, Resources.classForName(value));} catch (ClassNotFoundException e) {throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);}}/*** 获取保存别名的HashMap, Configuration对象持有对TypeAliasRegistry的引用,* 因此,如果需要,我们可以通过Configuration对象获取*/public Map<String, Class<?>> getTypeAliases() {return Collections.unmodifiableMap(TYPE_ALIASES);}}

typeHandlers节点的解析和typeAlianses节点的解析类似

/*** 解析typeHandlers节点* 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,* 还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。* Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,* Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。* @param parent* @throws Exception*/
private void typeHandlerElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {//子节点为package时,获取其name属性的值,然后自动扫描package下的自定义typeHandlerif ("package".equals(child.getName())) {String typeHandlerPackage = child.getStringAttribute("name");typeHandlerRegistry.register(typeHandlerPackage);} else {//子节点为typeHandler时, 可以指定javaType属性, 也可以指定jdbcType, 也可两者都指定//javaType 是指定java类型//jdbcType 是指定jdbc类型(数据库类型: 如varchar)String javaTypeName = child.getStringAttribute("javaType");String jdbcTypeName = child.getStringAttribute("jdbcType");//handler就是我们配置的typeHandlerString handlerTypeName = child.getStringAttribute("handler");Class<?> javaTypeClass = resolveClass(javaTypeName);//JdbcType是一个枚举类型,resolveJdbcType方法是在获取枚举类型的值JdbcType jdbcType = resolveJdbcType(jdbcTypeName);Class<?> typeHandlerClass = resolveClass(handlerTypeName);//注册typeHandler, typeHandler通过TypeHandlerRegistry这个类管理if (javaTypeClass != null) {if (jdbcType == null) {typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);} else {typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);}} else {typeHandlerRegistry.register(typeHandlerClass);}}}}
}

插件是mybatis提供的扩展机制,用户可以通过添加自定义插件在SQL语句执行的过程中某一环节进行拦截,mybatis中的自定义插件只需实现Interceptor接口,并通过注解指定拦截的方法签名,这个后面具体介绍。

/*** 解析plugins标签* mybatis中的plugin其实就是个interceptor,* 它可以拦截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 的部分方法,处理我们自己的逻辑。* @param parent* @throws Exception*/
private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();// 我们在定义一个interceptor的时候,需要去实现InterceptorInterceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();interceptorInstance.setProperties(properties);// 向configuration对象中注册拦截器configuration.addInterceptor(interceptorInstance);}}
}

mybatis初始化时,出了加载mybatis-config.xml的全局配置文件,还会加载全部的映射配置文件,即mappers节点配置的mapper.

/*** 解析mapper文件,mapper可以理解为dao的实现* @param parent* @throws Exception*/
private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {//如果mappers节点的子节点是package, 那么就扫描package下的文件, 注入进configurationif ("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, url, class 三选一if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);//mapper映射文件都是通过XMLMapperBuilder解析XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());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.");}}}}
}

mybatis初始化过程中对mybatis-config.xml配置文件的解析过程到这吧,下一个就叫啥mapper配置文件的解析过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322755.shtml

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

相关文章

P1983-车站分级【图论,记忆化dfs,构图】

正题 题目链接:https://www.luogu.org/problemnew/show/P1983 题目大意 一个辆车会一个一个值xxx&#xff0c;如果等级大于等于xxx的车站都会停靠(包括起点和终点)。给每辆车的停靠点&#xff0c;求至少要将车站分多少级。 解题思路 对于一辆车&#xff0c;若一个点他经过了…

slot的使用

普通插槽 在嵌入的组件标签中加入内容&#xff0c;如果子组件中有slot就显示&#xff0c;否则就是不显示 父组件 <h1>Hello 父组件</h1> <Child><p>这是一些初始内容</p><p>这是更多的初始内容</p> </Child>子组件 <h1&…

mybatis源码阅读(三):mybatis初始化(下)mapper解析

转载自 mybatis源码阅读(三)&#xff1a;mybatis初始化&#xff08;下&#xff09;mapper解析 MyBatis 的真正强大在于它的映射语句&#xff0c;也是它的魔力所在。由于它的异常强大&#xff0c;映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比…

改造独立部署(SCD)模式下.NET Core应用程序 dotnet的exe文件启动过程

设置一个小目标 改造前 改造后 独立部署SCD模式&#xff0c;是指在使用dotnet publish 命令时带上-r 参数运行时标识符&#xff08;RID&#xff09;。 目标提出原因&#xff1a;SCD模式下文件太乱了&#xff0c;很多文件在开发时大多又涉及不到&#xff0c;发布后如果能把文件…

P1268-树的重量【图论】

正题 题目大意 一棵树有nnn个叶子节点&#xff0c;给出每两个叶子节点之间的距离。求这棵树的边权之和。 解题思路 我们考虑每次加入一个节点。两个节点时不用说。 加入第三个节点时&#xff0c;肯定是加入在节点1和节点2之间。 之后我们开始推导&#xff1a;当我们加入节点…

mybatis源码阅读(四):mapper(dao)实例化

转载自 mybatis源码阅读(四)&#xff1a;mapper(dao)实例化 在开始分析之前&#xff0c;先来了解一下这个模块中的核心组件之间的关系&#xff0c;如图&#xff1a; 1.MapperRegistry&MapperProxyFactory MapperRegistry是Mapper接口及其对应的代理对象工程的注册中心&…

ajax的封装使用

面试的时候有人问到我ajax的使用&#xff0c;当时回答的不算好&#xff0c;这里想重新总结下&#xff1a; 1、如何将配置等信息传到ajax函数里面去 这个采用的是在参数里加一个对象&#xff0c;对象里面放入字段&#xff0c;然后在ajax里设置一个option&#xff0c;通过option…

P3385-[模板]负环【SPFA】

正题 题目大意 求点1可不可以走到负环。 解题思路 用cnticnt_icnti​表示到iii的最短路经过了多少个点&#xff0c;然后求若cnti≥ncnt_i\geq ncnti​≥n且这条路是负数那么就有负环。 codecodecode #include<cstdio> #include<queue> #include<cstring> …

自定义路由匹配和生成

前言 前两篇文章主要总结了CMS系统两个技术点在ASP.NET Core中的应用&#xff1a; 《ASP.NET Core 中的SEO优化&#xff08;1&#xff09;&#xff1a;中间件实现服务端静态化缓存》 《ASP.NET Core 中的SEO优化&#xff08;2&#xff09;&#xff1a;中间件中渲染Razor视图》…

mybatis多个参数(不使用@param注解情况下),sql参数占位符正确写法

转载自 mybatis多个参数(不使用param注解情况下)&#xff0c;sql参数占位符正确写法 useActualParamName配置 useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性&#xff0c;你的工程必须采用Java 8编译&#xff0c;并且加上-parameters选项。&…

如何封装并发布一个属于自己的ui组件库

以前就一直有个想法自己能不能封装一个类似于elementui一样的组件库&#xff0c;然后发布到npm上去&#xff0c;毕竟前端说白了&#xff0c;将组件v上去&#xff0c;然后进行数据交互。借助这次端午&#xff0c;终于有机会&#xff0c;尝试自己去封装发布组件库了 我这里了只做…

P1768-天路【负环,SPFA,01分数规划,二分答案】

正题 题目链接:https://www.luogu.org/problemnew/show/P1768 题目大意 求一条回路使得路上∑vi∑pi\frac{\sum v_i}{\sum p_i}∑pi​∑vi​​最大。 解题思路 考虑01分数规划 ∑vi∑pians\frac{\sum v_i}{\sum p_i}ans∑pi​∑vi​​ans ∑vians∗∑pi\sum v_ians*\sum p_i…

听云支持.NET Core的应用性能监控

随着微软于2017年8月正式发布.NET Core 2.0&#xff0c; .NET Core 社区开始活跃&#xff0c;众多.NET开发者开始向跨平台转变。 听云于2017年11月推出了.NET Core应用监控工具&#xff0c;和听云其他语言的监控工具一样&#xff0c;.NET Core应用监控工具具有以下特征&#xf…

mybatis源码阅读(五) ---执行器Executor

转载自 mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_RESULT_HANDLER null;// 执行update&#xff0c;delete&#xff0c;insert三种类型的sql语句int update(MappedStatement ms, Object parameter…

.sync的一个用法

面试时&#xff0c;有人问了我修饰符是什么&#xff0c;就是一个点后面加一个单词&#xff0c;我当时还以为是什么文件夹后缀呢。很是尴尬 这里主要学习下.sync的一个用法 假设下场景&#xff1a; 这里有一个父组件&#xff0c;父组件中有个money&#xff0c;需要传到子组件中…

nssl1296-猫咪的进化【dp】

正题 题目大意 nnn次&#xff0c;每次有3种选择&#xff1a; 休息获得viv_ivi​点价值获得vi2v_i^2vi2​点价值且下一回合要休息 解题思路 定义fi,0/1/2f_{i,0/1/2}fi,0/1/2​表示第iii次为休息/叫一声/叫两声时的最大价值。 fi,0f_{i,0}fi,0​可以由前面任何状态转移过来。 …

[52ABP实战系列] .NET CORE实战入门第三章更新了

早安 各位道友好&#xff0c;.NET CORE入门视频的第三章也算录制完毕了。欢迎大家上传课网进行学习。 更新速度 大家也知道最近的社会新闻比较多。频繁发生404、关键字打不出来&#xff0c;我个人也在关注这些事件。导致精力分散&#xff0c;没有做到稳定更新&#xff0c;现在呢…

如何安装nuxt

因为vue是单页面应用&#xff0c;所以不被Seo&#xff0c;如百度和Google抓取到&#xff0c;在Vue中如果想要爬虫爬到就必须使用nuxt 那么如何安装使用呢&#xff1f; yarn create nuxt-app <project-name> cd <project-name> yarn build yarn start必须先build&a…

mybatis源码阅读(六) ---StatementHandler了解一下

转载自 mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler&#xff1a;一个抽象类&#xff0c;只是实现了一些不涉及具体操作的方法 RoutingStatementHandler&#xff1a;类似路由器&#xff0c;根据配置文件来路由…

nssl1298-网站计划【线段树】

正题 题目大意 若干个区间操作l,rl,rl,r 让答案增加(lr)∗max{ai}(i∈[l..r])(lr)*max\{a_i\}(i\in[l..r])(lr)∗max{ai​}(i∈[l..r]) 并把最大的数变为0 解题思路 线段树&#xff0c;上传最大值时多上传一个位置&#xff0c;然后单点修改。 codecodecode #include<cstd…