深入理解Mybatis(第一讲)——手写ORM框架(简易版Mybatis)
我们来自定义一个持久层框架,也就是Mybatis的简易版。
使用端的搭建
idea中新建maven工程IPersistence_test:
在resources目录下新建sqlMapConfig.xml文件,
<Configuration><dataSource><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/lagou?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false"></property><property name="user" value="root"></property><property name="password" value="000"></property></dataSource><mapper resource="UserMapper.xml"></mapper></Configuration>
UserMapper.xml:
<mapper namespace="user"><select id="selectOne" paramterType="com.lagou.pojo.User"resultType="com.lagou.pojo.User">select * from user where id = #{id} and username =#{username}</select><select id="selectList" resultType="com.lagou.pojo.User">select * from user</select></mapper>
User实体类:
package com.lagou.test;/*** @author liuyj* @Title: User* @create 2020-05-29 15:06* @ProjectName lagou_project* @Description: TODO*/public class User {private Integer id;private String username;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +'}';}}
使用端暂时就搭建完成。
持久层框架端的搭建
下面我们来搭建持久层框架:
新建一个module,maven项目:IPersistence
pom.xml中引入一下依赖
<dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.44</version></dependency><!-- 连接池--><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version></dependency><!-- 解析xml文件--><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency></dependencies>
创建一个Configuration类,主要是存放从sqlMapConfig.xml和Usermapper.xml配置文件中解析出来的一些元素和内容,用来一层层向下传递:
package com.lagou.pojo;import javax.sql.DataSource;import java.util.HashMap;import java.util.Map;/*** @author liuyj* @Title: Configuration* @create 2020-05-27 15:20* @ProjectName IPersistence* @Description: 存放sqlMapConfig.xml解析出来的内容*/public class Configuration {//存放数据库配置信息,从sqlMapConfig.xml中解析出来private DataSource dataSource;//存放Mapper.xml中解析出来的内容,key是statementIdprivate Map<String,MappedStatement> mappedStatementMap=new HashMap<String, MappedStatement>();public DataSource getDataSource() {return dataSource;}public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public Map<String, MappedStatement> getMappedStatementMap() {return mappedStatementMap;}public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {this.mappedStatementMap = mappedStatementMap;}}
其中DataSource 封装的是数据库信息,Configuration中封装了一个对象MappedStatement:
package com.lagou.pojo;/*** @author liuyj* @Title: MappedStatement* @create 2020-05-27 15:13* @ProjectName IPersistence* @Description: 存放UserMapper.xml解析出来的内容*/public class MappedStatement {//id标识private String id;//返回值类型private String resultType;//传入参数类型private String paramenterType;//sqlprivate String sql;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}public String getParamenterType() {return paramenterType;}public void setParamenterType(String paramenterType) {this.paramenterType = paramenterType;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}}
主要是用来存储从映射配置文件中解析出来的sql查询标签的id及传入参数、返回结果类型、查询的sql等,其中Mapper.xml中每一个标签,
比如:
<select id="selectList" resultType="com.lagou.pojo.User">select * from user</select>
都会封装成一个MappedStatement对象,然后所有的MappedStatement对象都被存储到Configuration类中的Map集合mappedStatementMap当中去,Map集合中的key是statementId(statementId由两部分组成,一是Mapper.xml中的namespace,二是每一个标签中的id,比如UserMapper.xml中的selectOne,在Configuration中map集合中的key值就是user.selectOne)。
Resource文件:
主要用来读取xml文件,作为一个字节流存储在内存中:
package com.lagou.io;import java.io.InputStream;/*** @author liuyj* @Title:* @create 2020-05-27 14:48* @ProjectName IPersistence* @Description: TODO*/public class Resources {//根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中public static InputStream getResourceAsStream(String path){InputStream resourceStream= Resources.class.getClassLoader().getResourceAsStream(path);return resourceStream;}}
SqlSessionFactoryBuilder:
package com.lagou.sqlSession;import com.lagou.config.XMLConfigBuilder;import com.lagou.pojo.Configuration;import org.dom4j.DocumentException;import java.beans.PropertyVetoException;import java.io.InputStream;/*** @author liuyj* @Title: SqlSessionFactoryBuilder* @create 2020-05-27 15:37* @ProjectName IPersistence* @Description: TODO*/public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException {//第一,使用dom4j解析配置文件,将解析出来的内容封装到configuration中XMLConfigBuilder xmlConfigBuilder=new XMLConfigBuilder();Configuration configuration = xmlConfigBuilder.parseConfig(in);//第二:创建sqlSessionFactory对象:工厂类:生产sqlSession:会话对象DefaultSqlSessionFactory defaultSqlSessionFactory=new DefaultSqlSessionFactory(configuration);return defaultSqlSessionFactory;}}
XMLConfigBuilder :
使用dom4j解析sqlMapConfig.xml文件,并调用XMLMapperBuilder 中的方法解析Mapper.xml映射文件,将结果封装在Configuration对象中:
package com.lagou.config;import com.lagou.io.Resources;import com.lagou.pojo.Configuration;import com.mchange.v2.c3p0.ComboPooledDataSource;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.beans.PropertyVetoException;import java.io.InputStream;import java.util.List;import java.util.Properties;/*** @author liuyj* @Title: XMLConfigBuilder* @create 2020-05-27 15:39* @ProjectName IPersistence* @Description: TODO*/public class XMLConfigBuilder {private Configuration configuration;public XMLConfigBuilder(){this.configuration=new Configuration();}/****该方法就是使用dom4j将配置文件解析,封装为Configuration*/public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {Document document = new SAXReader().read(inputStream);//<Configuration>标签Element rootElement = document.getRootElement();List<Element> list = rootElement.selectNodes("//property");Properties properties=new Properties();for (Element element : list) {String name = element.attributeValue("name");String value = element.attributeValue("value");properties.setProperty(name,value);}ComboPooledDataSource comboPooledDataSource=new ComboPooledDataSource();comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));comboPooledDataSource.setUser(properties.getProperty("user"));comboPooledDataSource.setPassword(properties.getProperty("password"));configuration.setDataSource(comboPooledDataSource);//解析sqlMapConfig.xml里面的mapper标签,// 解析mapper.xml:拿到路径--字节输入流--dom4j解析List<Element> mapperList = rootElement.selectNodes("//mapper");for (Element element : mapperList) {String mapperPath = element.attributeValue("resource");InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath);XMLMapperBuilder xmlMaperBuilder=new XMLMapperBuilder(configuration);xmlMaperBuilder.parse(resourceAsStream);}return configuration;}}
XMLMapperBuilder :
用来解析映射配置文件Mapper.xml中的内容:
package com.lagou.config;import com.lagou.pojo.Configuration;import com.lagou.pojo.MappedStatement;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.InputStream;import java.util.List;/*** @author liuyj* @Title: XMLMapperBuilder* @create 2020-05-28 10:59* @ProjectName lagou_project* @Description: 解析mapper.xml文件*/public class XMLMapperBuilder {private Configuration configuration;public XMLMapperBuilder(Configuration configuration) {this.configuration=configuration;}public void parse(InputStream inputStream) throws DocumentException {Document document = new SAXReader().read(inputStream);Element rootElement = document.getRootElement();String namespace = rootElement.attributeValue("namespace");List<Element> list = rootElement.selectNodes("//select");for (Element element : list) {String id = element.attributeValue("id");String parameterType = element.attributeValue("parameterType");String resultType = element.attributeValue("resultType");String sqlText = element.getTextTrim();String key=namespace+"."+id;MappedStatement mappedStatement=new MappedStatement();mappedStatement.setId(id);mappedStatement.setParamenterType(parameterType);mappedStatement.setResultType(resultType);mappedStatement.setSql(sqlText);configuration.getMappedStatementMap().put(key,mappedStatement);}}}
package com.lagou.sqlSession;/*** @author liuyj* @Title: SqlSessionFactory* @create 2020-05-27 15:38* @ProjectName IPersistence* @Description: TODO*/public interface SqlSessionFactory {SqlSession openSession();}
package com.lagou.sqlSession;import com.lagou.pojo.Configuration;/*** @author liuyj* @Title: DefaultSqlSessionFactory* @create 2020-05-28 11:38* @ProjectName lagou_project* @Description: TODO*/public class DefaultSqlSessionFactory implements SqlSessionFactory{private Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}public SqlSession openSession() {return new DefaultSqlSession(configuration);}}
SqlSession及实现类DefaultSqlSession:
package com.lagou.sqlSession;import java.util.List;/*** @author liuyj* @Title: SqlSession* @create 2020-05-28 11:56* @ProjectName lagou_project* @Description: TODO*/public interface SqlSession {//查询所有public <E> List<E> selectList(String statementid, Object... params) throws Exception;//根据条件查询单个public <T> T selectOne(String statementid,Object... params) throws Exception;}
package com.lagou.sqlSession;import com.lagou.pojo.Configuration;import com.lagou.pojo.MappedStatement;import java.lang.reflect.*;import java.util.List;/*** @author liuyj* @Title: DefaultSqlSession* @create 2020-05-28 11:57* @ProjectName lagou_project* @Description: TODO*/public class DefaultSqlSession implements SqlSession {private Configuration configuration;public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;}public <E> List<E> selectList(String statementid, Object... params) throws Exception {//将要去完成对simpleExecutor里的query方法的调用SimpleExecutor simpleExecutor = new SimpleExecutor();MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);return (List<E>) list;}public <T> T selectOne(String statementid, Object... params) throws Exception {List<Object> objects = selectList(statementid, params);if(objects.size()==1){return (T) objects.get(0);}else {throw new RuntimeException("查询结果为空或者返回结果过多");}}}
SqlSession实现类中调用的Executor及其实现类SimpleExecutor :
package com.lagou.sqlSession;import com.lagou.pojo.Configuration;import com.lagou.pojo.MappedStatement;import java.util.List;/*** @author lyj* @Title: Executor* @ProjectName lagou_project* @Description: TODO* @date 2020/5/28 22:00*/public interface Executor {public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;}
package com.lagou.sqlSession;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
- @author lyj
 - @Title: SimpleExecutor
 - @ProjectName lagou_project
 - @Description: TODO
 - @date 2020/5/28 21:59
*/ 
public class SimpleExecutor implements Executor {
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {//注册驱动,获取数据库连接Connection connection = configuration.getDataSource().getConnection();//获取sql语句:select * from user where id=#{id} and username=#{username}//转换sql:select * from user where id=? and username=?,同时需要对#{}里面的值进行解析存储String sql = mappedStatement.getSql();BoundSql boundSql=getBoundSql(sql);//获取预处理对象preparedStatementPreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());//设置参数//获取到了参数的全路径String paramenterType = mappedStatement.getParamenterType();Class<?> parametertypeClass=getClassType(paramenterType);List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();for (int i = 0; i < parameterMappingList.size(); i++) {ParameterMapping parameterMapping = parameterMappingList.get(i);String content = parameterMapping.getContent();//反射Field declaredField = parametertypeClass.getDeclaredField(content);//暴力访问declaredField.setAccessible(true);Object o = declaredField.get(params[0]);preparedStatement.setObject(i+1,o);}//执行sqlResultSet resultSet = preparedStatement.executeQuery();String resultType = mappedStatement.getResultType();Class<?> resultTypeClass = getClassType(resultType);ArrayList<Object> objects = new ArrayList<Object>();//封装返回结果集while (resultSet.next()) {Object o = resultTypeClass.newInstance();//元数据ResultSetMetaData metaData = resultSet.getMetaData();for (int i = 1; i <= metaData.getColumnCount(); i++) {//字段名String columnName = metaData.getColumnName(i);//字段值Object value = resultSet.getObject(columnName);//使用反射或者内省,根据数据库表和实体的对应关系,完成封装PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);Method writeMethod = propertyDescriptor.getWriteMethod();writeMethod.invoke(o,value);}objects.add(o);}return (List<E>) objects;}/*** 完成对#{}的解析工作:1.将#{}使用?代替 ,2.解析出#{}里面的值进行存储* @param sql* @return*/private BoundSql getBoundSql(String sql) {//标记处理类:配置标记解析器GenericTokenParser来完成对配置文件的解析工作,其中TokenHandler主要完成处理ParameterMappingTokenHandler parameterMappingTokenHandler=new ParameterMappingTokenHandler();
//GenericTokenParser:通用的标记解析器,完成了对代码中占位符的解析,然后再根据给定的标记处理器(TokenHandler)来进行表达式的处理
//三个参数:分别为openToken(开始标记)、closeToken(结束标记)、handler(标记处理器)
GenericTokenParser genericTokenParser=new GenericTokenParser("#{","}",parameterMappingTokenHandler);//解析出来的sqlString parseSql = genericTokenParser.parse(sql);//从#{}里面解析出来的参数名称List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();BoundSql boundSql=new BoundSql(parseSql,parameterMappings);return boundSql;}}```
BoundSql
```java
package com.lagou.sqlSession;
import com.lagou.utils.ParameterMapping;
import java.util.ArrayList;
import java.util.List;
/**
- @author lyj
 - @Title: BoundSql
 - @ProjectName lagou_project
 - @Description: TODO
 - @date 2020/5/28 22:03
*/ 
public class BoundSql {
private String sqlText;
private ListparameterMappingList=new ArrayList();
public BoundSql(String sqlText, List<ParameterMapping> parameterMappingList) {this.sqlText = sqlText;this.parameterMappingList = parameterMappingList;}public String getSqlText() {return sqlText;}public void setSqlText(String sqlText) {this.sqlText = sqlText;}public List<ParameterMapping> getParameterMappingList() {return parameterMappingList;}public void setParameterMappingList(List<ParameterMapping> parameterMappingList) {this.parameterMappingList = parameterMappingList;}
}
SimpleExecutor 中使用的几个标记解析器,也附录一下吧,是从mybatis源码中直接拿来用的:GenericTokenParser```java/*** Copyright 2009-2017 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.lagou.utils;/*** @author Clinton Begin*/public class GenericTokenParser {private final String openToken; //开始标记private final String closeToken; //结束标记private final TokenHandler handler; //标记处理器public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {this.openToken = openToken;this.closeToken = closeToken;this.handler = handler;}/*** 解析${}和#{}* @param text* @return* 该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据。* 其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现*/public String parse(String text) {// 验证参数问题,如果是null,就返回空字符串。if (text == null || text.length()==0) {return "";}// 下面继续验证是否包含开始标签,如果不包含,默认不是占位符,直接原样返回即可,否则继续执行。int start = text.indexOf(openToken, 0);if (start == -1) {return text;}// 把text转成字符数组src,并且定义默认偏移量offset=0、存储最终需要返回字符串的变量builder,// text变量中占位符对应的变量名expression。判断start是否大于-1(即text中是否存在openToken),如果存在就执行下面代码char[] src = text.toCharArray();int offset = 0;final StringBuilder builder = new StringBuilder();StringBuilder expression = null;while (start > -1) {// 判断如果开始标记前如果有转义字符,就不作为openToken进行处理,否则继续处理if (start > 0 && src[start - 1] == '\\') {builder.append(src, offset, start - offset - 1).append(openToken);offset = start + openToken.length();} else {//重置expression变量,避免空指针或者老数据干扰。if (expression == null) {expression = new StringBuilder();} else {expression.setLength(0);}builder.append(src, offset, start - offset);offset = start + openToken.length();int end = text.indexOf(closeToken, offset);while (end > -1) {////存在结束标记时if (end > offset && src[end - 1] == '\\') {//如果结束标记前面有转义字符时// this close token is escaped. remove the backslash and continue.expression.append(src, offset, end - offset - 1).append(closeToken);offset = end + closeToken.length();end = text.indexOf(closeToken, offset);} else {//不存在转义字符,即需要作为参数进行处理expression.append(src, offset, end - offset);offset = end + closeToken.length();break;}}if (end == -1) {// close token was not found.builder.append(src, start, src.length - start);offset = src.length;} else {//首先根据参数的key(即expression)进行参数处理,返回?作为占位符builder.append(handler.handleToken(expression.toString()));offset = end + closeToken.length();}}start = text.indexOf(openToken, offset);}if (offset < src.length) {builder.append(src, offset, src.length - offset);}return builder.toString();}}
ParameterMapping:
package com.lagou.utils;public class ParameterMapping {private String content;public ParameterMapping(String content) {this.content = content;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}}
ParameterMappingTokenHandler:
package com.lagou.utils;import java.util.ArrayList;import java.util.List;public class ParameterMappingTokenHandler implements TokenHandler {private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();// context是参数名称 #{id} #{username}public String handleToken(String content) {parameterMappings.add(buildParameterMapping(content));return "?";}private ParameterMapping buildParameterMapping(String content) {ParameterMapping parameterMapping = new ParameterMapping(content);return parameterMapping;}public List<ParameterMapping> getParameterMappings() {return parameterMappings;}public void setParameterMappings(List<ParameterMapping> parameterMappings) {this.parameterMappings = parameterMappings;}}
package com.lagou.utils;/*** @author Clinton Begin*/public interface TokenHandler {String handleToken(String content);}
测试使用:
先生成jar包,执行mvn install命令。
mvn install

在使用端IPersistence_test中引入jar包:
<dependency><groupId>com.lagou</groupId><artifactId>IPersistence</artifactId><version>1.0-SNAPSHOT</version></dependency>
数据库建表:


测试:
package com.lagou.test;import com.lagou.dao.IUserDao;import com.lagou.io.Resources;import com.lagou.sqlSession.SqlSession;import com.lagou.sqlSession.SqlSessionFactory;import com.lagou.sqlSession.SqlSessionFactoryBuilder;import org.dom4j.DocumentException;import org.junit.Before;import org.junit.Test;import java.beans.PropertyVetoException;import java.io.InputStream;/*** @author liuyj* @Title: IPersistenctTest* @create 2020-05-27 15:08* @ProjectName IPersistence* @Description: TODO*/public class IPersistenctTest {private SqlSession sqlSession;@Beforepublic void before() throws PropertyVetoException, DocumentException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);sqlSession = sqlSessionFactory.openSession();}@Testpublic void test() throws Exception {//调用User user=new User();user.setId(1);user.setUsername("张三");User user2 = sqlSession.selectOne("user.selectOne", user);System.out.println(user2);}}
运行结果:

大功告成。
下节我们来讲对于我们这个框架的一个优化。
深入理解Mybatis(第一讲)——手写ORM框架(简易版Mybatis)的更多相关文章
- 重学 Java 设计模式:实战中介者模式「按照Mybaits原理手写ORM框架,给JDBC方式操作数据库增加中介者场景」
		
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同龄人的差距是从什么时候拉开的 同样的幼儿园.同样的小学.一样 ...
 - 基于springJDBC手写ORM框架
		
一.添加MySQLjar包依赖 二.结构 三.文件内容 (一).bean包 1.ColumnInfo.java 2.javaFiledInfo.java 3.TableInfo.java 4.Conf ...
 - 手写Spring框架,加深对Spring工作机制的理解!
		
在我们的日常工作中,经常会用到Spring.Spring Boot.Spring Cloud.Struts.Mybatis.Hibernate等开源框架,有了这些框架的诞生,平时的开发工作量也是变得越 ...
 - 要想精通Mybatis?从手写Mybatis框架开始吧!
		
1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...
 - 第一个手写Win32窗口程序
		
第一个手写Win32窗口程序 一 Windows编程基础 1 Win32应用程序的基本类型 1.1 控制台程序 不需要完善的Windows窗口,可以使用DOS窗口 的方式显示. 1.2 Win32窗口 ...
 - 手写DAO框架(一)-从“1”开始
		
背景: 很久(4年)之前写了一个DAO框架-zxdata(https://github.com/shuimutong/zxdata),这是我写的第一个框架.因为没有使用文档,我现在如果要用的话,得从头 ...
 - Spring 08: AOP面向切面编程 + 手写AOP框架
		
核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...
 - 手写一套迷你版HTTP服务器
		
本文主要介绍如何通过netty来手写一套简单版的HTTP服务器,同时将关于netty的许多细小知识点进行了串联,用于巩固和提升对于netty框架的掌握程度. 服务器运行效果 服务器支持对静态文件css ...
 - 手写DAO框架(二)-开发前的最后准备
		
-------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...
 
随机推荐
- 威联通(NAS)应用篇:自建OwnCloud网盘(百度网盘,拜拜~~~)
			
基础环境: 威联通一台 已安装好 ContainerStation 公网 IP(非必须) 自有公网域名 下载镜像文件 提醒:建议先把威联通的自带镜像源改为国内的阿里云镜像源,教程:https://ww ...
 - 使用kubeadm部署k8s集群[v1.18.0]
			
使用kubeadm部署k8s集群 环境 IP地址 主机名 节点 10.0.0.63 k8s-master1 master1 10.0.0.63 k8s-master2 master2 10.0.0.6 ...
 - P1750 出栈序列
			
这好像是普及难度的吧~ 感觉再次被小学生吊打了........ \(\color{Red}{----------------------=|(●'◡'●)|=我是手动的分割线------------- ...
 - Tomcat服务器的下载与安装,修改端口号
			
安装及简单配置Tomcat服务器: 1.登录www.apache.org 网站,之后点击Projects , 点击Project List,找到Tomcat. 2.点击Tomcat之后,之后进入Tom ...
 - 聊聊 TypeScript 中的类型保护
			
聊聊 TypeScript 中的类型保护 在 TypeScript 中使用联合类型时,往往会碰到这种尴尬的情况: interface Bird { // 独有方法 fly(); // 共有方法 lay ...
 - Web_php_include
			
0x01 函数分析 <?php show_source(__FILE__); echo $_GET['hello']; $page=$_GET['page']; while (strstr($p ...
 - P3366【模板】最小生成树
			
P3366[模板]最小生成树 Kruskal #include <bits/stdc++.h> using namespace std; typedef long long ll; ; ; ...
 - mysql小白系列_06 备份与恢复
			
1.使用mydumper工具全库备份. 1)源码编译安装 2)全库备份 2.误操作truncate table gyj_t1;利用mysqldump的备份和binlog日志对表gyj_t1做完全恢复. ...
 - UVALive8518 Sum of xor sum
			
题目链接:https://vjudge.net/problem/UVALive-8518 题目大意: 给定一个长度为 $N$ 的数字序列 $A$,进行 $Q$ 次询问,每次询问 $[L,R]$,需要回 ...
 - ShoneSharp语言(S#)软件更新13.7版
			
ShoneSharp语言(S#)编辑解析运行器 软件更新13.7版 作者:Shone 近期在写博客过程中对S#进行增强,把语法规则更新到2.0版,并同步更新软件到ShoneSharp.13.7.exe ...