每一个程序员,在刚入行时学的第一个框架应该就是ssm了,其他的各种学习框架都是后期在工作中,随着业务的不断复杂,在工作和bug中不断成长起来的,但是最经典的应该还是最一开始的时候ssm框架吧

当时刚学习这个时候,就觉得,好牛啊,这样就可以实现一个网站,这群大神是怎么做到的啊,嘿嘿嘿,不知道大家当时有没有这样的问题产生,所以今天就带大家来搭建一个简单的mvc框架,从原理讲起,也能帮助大家更好的理解底层源码

好了,话不多说,我们来看一下

Springmvc基本原理流程

SpringMvc本质上就是对Servlet的封装。

因为创建一个Maven项目,然后在pom文件中增加一个依赖:

<dependency><groupId>javax.servletgroupId><artifactId>servlet-apiartifactId><version>2.5version>

<scope>providedscope>dependency>

2,创建DispatcherServlet,并注册到web.xml中

package com.dxh.edu.mvcframework.servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class DxhDispatcherServlet extends HttpServlet {/**     *  接收处理请求     */@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        doPost(req, resp);    }@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }}

web.xml:

span style="border-width: 0px;border-style: initial;border-color: initial;">web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Applicationdisplay-name><servlet><servlet-name>dxhmvcservlet-name><servlet-class>com.dxh.edu.mvcframework.servlet.DxhDispatcherServletservlet-class><init-param><param-name>contextConfigLocationparam-name><param-value>springmvc.propertiesparam-value>init-param>servlet><servlet-mapping><servlet-name>dxhmvcservlet-name><url-pattern>/*url-pattern>servlet-mapping>web-app>

2,注解开发

因为要使用到注解,所以首先要自定义几个注解:

这里就不赘述如何自定义注解了,详情请看:https://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

Controller注解:

@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface DxhController {    String value() default "";}

Service注解:

@Documented@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface DxhService {    String value() default "";}

RequestMapping注解:

@Documented@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface DxhRequestMapping {    String value() default "";}

Autowired注解:

@Documented@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface DxhAutowired {    String value() default "";}

编写测试代码:

测试代码我们放在同项目中的com.dxh.demo包中:

package com.dxh.demo.controller;import com.dxh.demo.service.IDemoService;import com.dxh.edu.mvcframework.annotations.DxhAutowired;import com.dxh.edu.mvcframework.annotations.DxhController;import com.dxh.edu.mvcframework.annotations.DxhRequestMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@DxhController@DxhRequestMapping("/demo")public class DemoController {@DxhAutowired    private IDemoService demoService;/**     * URL:/demo/query     */@DxhRequestMapping("/query")    public String query(HttpServletRequest request, HttpServletResponse response, String name){return demoService.get(name);    }}
package com.dxh.demo.service;public interface IDemoService {String get(String name);}
package com.dxh.demo.service.impl;import com.dxh.demo.service.IDemoService;import com.dxh.edu.mvcframework.annotations.DxhService;@DxhService("demoService")public class IDemoServiceImpl implements IDemoService {@Override    public String get(String name) {System.out.println("Service实现类中的Name:"+ name);return name;    }}

目录结构:


3,编写自定义DispatcherServlet中的初始化流程:

在创建好的DxhDispatcherServlet中重写init()方法,并在init方法中做初始化配置:

  1. 加载配置文件 springmvc.properties

  2. 扫描相关的类——扫描注解

  3. 初始化Bean对象(实现IOC容器,基于注解)

  4. 实现依赖注入

  5. 构造一个handleMapping处理器映射器,将配置好的url和method建立映射关系

@Overridepublic void init(ServletConfig config) throws ServletException {//1. 加载配置文件  springmvc.properties    String contextConfigLocation = config.getInitParameter("contextConfigLocation");    doLoadConfig(contextConfigLocation);//2. 扫描相关的类——扫描注解    doScan("");//3. 初始化Bean对象(实现IOC容器,基于注解)    doInstance();//4. 实现依赖注入    doAutoWired();//5. 构造一个handleMapping处理器映射器,将配置好的url和method建立映射关系    initHandleMapping();    System.out.println("MVC 初始化完成");//6. 等待请求进入处理请求}

以及5个空方法,这篇文章自定义MVC框架其实就是需要对这5个步骤的编写。

//TODO 5,构造一个映射器private void initHandleMapping() {}//TODO 4,实现依赖注入private void doAutoWired() {}//TODO 3,IOC容器private void doInstance() {}//TODO 2,扫描类private void doScan(String scanPackage) {}//TODO 1,加载配置文件private void doLoadConfig(String contextConfigLocation) {}

3.1 加载配置文件

  1. 首先在resource目录中创建一个配置文件——springmvc.properties
    表示要扫描com.dxh.demo下的所有注解。

  2. 然后在web.xml中进行配置:

这样,就可以通过config.getInitParameter("contextConfigLocation")获得这个路径。

  1. 在DxhDispatcherServlet中定义一个属性,我们把加载后的配置文件中的信息,存储在Properties 中

private Properties properties = new Properties();;
//1,加载配置文件private void doLoadConfig(String contextConfigLocation) {//根据指定路径加载成流:    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);try {        properties.load(resourceAsStream);    } catch (IOException e) {        e.printStackTrace();    }}

3.2 扫描相关的类,扫描注解

  1. 上一步骤,我们已经把需要扫描的包存在Properties中,所以这里需要取出:

//2. 扫描相关的类——扫描注解doScan(properties.getProperty("scanPackage"));
  1. 把扫描到的类型的全类名存在一个List中缓存起来,等待使用,在DxhDispatcherServlet中定义一个list:

//缓存扫描到的类的全类名private List classNames = new ArrayList<>();
  1. 从配置文件中我们得到了一个需要扫描的包名(com.dxh.demo),我们需要根据classPath+包名,来得到它实际上在磁盘上存的路径,然后递归,直到把所有的该包下(包括子包...)所有的类文件(.class结尾)。然后存在在List classNames中。

//2,扫描类//scanPackage :com.dxh.demo    package--->磁盘的文件夹(File)private void doScan(String scanPackage) {//1.获得classPath路径String clasPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();//2.拼接,得到scanPackage在磁盘上的路径String  scanPackagePath= clasPath + scanPackage.replaceAll("\\.","/");    File pack = new File(scanPackagePath);    File[] files = pack.listFiles();for (File file : files) {if (file.isDirectory()){ //子 package//递归            doScan(scanPackage+"."+file.getName());  //com.dxh.demo.controller        }else if(file.getName().endsWith(".class")){String className = scanPackage + "." + file.getName().replace(".class", "");            classNames.add(className);        }    }}

3.3 初始化Bean对象(实现IOC容器,基于注解)

上一步骤我们把扫描到的类的全类名放到了,list中,那么本次步骤需要遍历整个list:

  1. 遍历List,依次得到所有的全类名

  2. 通过反射得到类对象

  3. 根据类对象判断有没有注解,并区分controller和servicecontroller,它的id此处不做过多处理,不取value了,用类的首字母小写作为id,保存到IOC容器中。service,service层往往是有接口的,再以接口名为id再存入一份bean到ioc,便于后期根据接口类型注入

  4. 完成

代码实现:

//IOC容器private Map<String,Object> ioc = new HashMap<>();
//3,IOC容器//基于classNames缓存的类的全限定类名,以及反射技术,完成对象创建和管理private void doInstance()  {if (classNames.size()==0) return;try{for (int i = 0; i < classNames.size(); i++) {            String className = classNames.get(i);  //com.dxh.demo.controller.DemoController//反射            Class> aClass = Class.forName(className);//区分controller ,区分serviceif (aClass.isAnnotationPresent(DxhController.class)){//controller的id此处不做过多处理,不取value了,用类的首字母小写作为id,保存到IOC容器中                String simpleName = aClass.getSimpleName();//DemoController                String lowerFirstSimpleName = lowerFirst(simpleName); //demoController                Object bean = aClass.newInstance();                ioc.put(lowerFirstSimpleName,bean);            }else if (aClass.isAnnotationPresent(DxhService.class)){                DxhService annotation = aClass.getAnnotation(DxhService.class);//获取注解的值                String beanName = annotation.value();//指定了id就以指定的id为准if (!"".equals(beanName.trim())){                    ioc.put(beanName,aClass.newInstance());                }else{//没有指定id ,首字母小写                    String lowerFirstSimpleName = lowerFirst(aClass.getSimpleName());                    ioc.put(lowerFirstSimpleName,aClass.newInstance());                }//service层往往是有接口的,再以接口名为id再存入一分bean到ioc,便于后期根据接口类型注入                Class>[] interfaces = aClass.getInterfaces();for (Class> anInterface : interfaces) {//以接口的类名作为id放入。                    ioc.put(anInterface.getName(),aClass.newInstance());                }            }else {continue;            }        }    }catch (Exception e){        e.printStackTrace();    }}

3.4 实现依赖注入:

上一步骤把所有需要加载的bean,存入了ioc Map中,此时,我们就需要遍历这个map然后依次得到每个bean对象,然后判断对象中有没有被@****DxhAutowired修饰的属性。

  1. 遍历ioc这个map,得到每个对象

  2. 获取对象的字段(属性)信息

  3. 判断字段是否被@DxhAutowired修饰

  4. 判断@DxhAutowired有没有设置value值有,直接从ioc容器中取出,然后设置属性。无,需要根据当前字段的类型注入(接口注入)

代码实现:

//4,实现依赖注入private void doAutoWired() {if (ioc.isEmpty()){return;}//1,判断容器中有没有被@DxhAutowried注解的属性,如果有需要维护依赖注入关系for (Map.Entry entry: ioc.entrySet()){//获取bean对象中的字段信息        Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();for (Field declaredField : declaredFields) {if (!declaredField.isAnnotationPresent(DxhAutowired.class)){continue;            }//有该注解:            DxhAutowired annotation = declaredField.getAnnotation(DxhAutowired.class);            String beanName = annotation.value(); //需要注入的bean的Idif ("".equals(beanName.trim())){//没有配置具体的beanId,需要根据当前字段的类型注入(接口注入)  IDemoService                beanName = declaredField.getType().getName();            }//开启赋值            declaredField.setAccessible(true);try {//字段调用,两个参数:(哪个对象的字段,传入什么)                declaredField.set(entry.getValue(),ioc.get(beanName));            } catch (IllegalAccessException e) {                e.printStackTrace();            }        }    }}

3.5 构造一个handleMapping处理器映射器

构造一个handleMapping处理器映射器,将配置好的url和method建立映射关系****。

手写MVC框架最关键的环节

假设有一个:

那么如何通过/demo/query定位到 DemoController类中的query这个方法 ?

之前我们所有被@DxhController(自定义Controller注解)的类,都存在了ioc 这个map中。
我们可以遍历这个map,得到每个bean对象
然后判断是否被@DxhController所修饰(排除@DxhService所修饰的bean)
然后判断是否被@DxhRequestMapping所修饰,有的话,就取其value值,作为baseUrl
然后遍历该bean对象中的所有方法,得到被@DxhRequestMapping修饰的方法。得到其value值,作为methodUrl。
baseUrl + methodUrl = url
我们把url和当前method绑定起来,存在map中,也就是建立了url和method建立映射关系。代码实现:

//handleMapping ,存储url和method直接的映射关系private Map<String,Object> handleMapping = new HashMap<>();
//5,构造一个映射器,将url和method进行关联private void initHandleMapping() {if (ioc.isEmpty()){return;}for (Map.Entry entry: ioc.entrySet()){//获取ioc中当前遍历对象的class类型        Class> aClass = entry.getValue().getClass();//排除非controller层的类if (!aClass.isAnnotationPresent(DxhController.class)){continue;        }        String baseUrl = "";if (aClass.isAnnotationPresent(DxhRequestMapping.class)){//Controller层 类上 注解@DxhRequestMapping中的value值            baseUrl = aClass.getAnnotation(DxhRequestMapping.class).value();        }//获取方法        Method[] methods = aClass.getMethods();for (Method method : methods) {//排除没有@DxhRequestMapping注解的方法if (!method.isAnnotationPresent(DxhRequestMapping.class)){continue;}//Controller层 类中方法上 注解@DxhRequestMapping中的value值            String methodUrl = method.getAnnotation(DxhRequestMapping.class).value();            String url = baseUrl+methodUrl;//建立url和method之间的映射关系,用map缓存起来            handleMapping.put(url,method);        }    }}

4,测试一下:

到目前位置,还没有完全写完,但是不妨碍我们测试一下看看刚才写的那部分内容有没有什么问题:

完整的pom文件:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0modelVersion><groupId>com.dxh.edugroupId><artifactId>mvcartifactId><version>1.0-SNAPSHOTversion><packaging>warpackaging><name>mvc Maven Webappname><url>http://www.example.comurl><properties><project.build.sourceEncoding>UTF-8project.build.sourceEncoding><maven.compiler.source>11maven.compiler.source><maven.compiler.target>11maven.compiler.target>properties><dependencies><dependency><groupId>junitgroupId><artifactId>junitartifactId><version>4.12version><scope>testscope>dependency><dependency><groupId>javax.servletgroupId><artifactId>servlet-apiartifactId><version>2.5version>

<scope>providedscope>dependency>dependencies><build><plugins><plugins>

<plugin><groupId>org.apache.maven.pluginsgroupId><artifactId>maven-compiler-pluginartifactId><version>3.2version><configuration><source>11source><target>11target><encoding>utf-8encoding>

<compilerArgs><arg>-parametersarg>compilerArgs>configuration>plugin>    <plugin><groupId>org.apache.tomcat.mavengroupId><artifactId>tomcat7-maven-pluginartifactId><version>2.2version><configuration><port>8080port><path>/path>configuration>plugin>plugins>build>project>

pom文件中加入了一个tomcat插件,并设置端口为8080,因此我们通过tomcat启动项目:

启动完成后,打开浏览器url中输入:

http://localhost:8080/demo/query

浏览器中什么都没返回(我们的代码还没真正的完成,尚未编写处理请求步骤),同时控制台中打印了MVC初始化完成,可以认为,目前的代码没有明显的缺陷。我们继续~~~~~


5,改造initHandleMapping()

5.1 为什么改造?

DxhDispatcherServlet这个类继承了HttpServlet,并重写了doGet和doPost方法,在doGet中调用了doPost方法,当我们使用反射调用方法时(method.invoke(......))发现少了一部分参数:

因此我们要改造initHandleMapping(),修改url和method的映射关系(不简简单单的存入map中)。

5.2 新建Handler类

package com.dxh.edu.mvcframework.pojo;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import java.util.regex.Pattern;public class Handler {//method.invoke(obj,)需要private Object controller;private Method method;//spring中url支持正则private Pattern pattern;//参数的顺序,为了进行参数绑定  ,Key 参数名, Value 代表第几个参数private Map paramIndexMapping;public Handler(Object controller, Method method, Pattern pattern) {this.controller = controller;this.method = method;this.pattern = pattern;this.paramIndexMapping = new HashMap<>();    }//getset方法这里省略,实际代码中需要...}

在Handler类中编写了4个属性:

  • private Object controller:method.invoke(obj,)需要

  • private Method method:与url绑定的方法

  • private Pattern pattern:可以通过正则匹配,也可以直接写String url。

  • private Map paramIndexMapping:参数的顺序,为了进行参数绑定 ,Key 参数名, Value 代表第几个参数

5.3 修改initHandleMapping()

首先,就不能直接通过Map的得方式进行关系映射了,使用一个list,泛型是刚才创建的Handler。

    //handleMapping ,存储url和method直接的映射关系//    private Map handleMapping = new HashMap<>();private List handlerMapping = new ArrayList<>();

改动前,改动后代码对比:

改动后的initHandleMapping():

//5,构造一个映射器,将url和method进行关联private void initHandleMapping() {if (ioc.isEmpty()){return;}for (Map.Entry entry: ioc.entrySet()){//获取ioc中当前遍历对象的class类型        Class> aClass = entry.getValue().getClass();//排除非controller层的类if (!aClass.isAnnotationPresent(DxhController.class)){continue;        }        String baseUrl = "";if (aClass.isAnnotationPresent(DxhRequestMapping.class)){//Controller层 类上 注解@DxhRequestMapping中的value值            baseUrl = aClass.getAnnotation(DxhRequestMapping.class).value();        }//获取方法        Method[] methods = aClass.getMethods();for (Method method : methods) {//排除没有@DxhRequestMapping注解的方法if (!method.isAnnotationPresent(DxhRequestMapping.class)){continue;}//Controller层 类中方法上 注解@DxhRequestMapping中的value值            String methodUrl = method.getAnnotation(DxhRequestMapping.class).value();            String url = baseUrl+methodUrl;//把method所有信息以及url封装为Handler            Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url));//处理计算方法的参数位置信息            Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {                Parameter parameter = parameters[i];//不做太多的参数类型判断,只做:HttpServletRequest request, HttpServletResponse response和基本类型参数if (parameter.getType()==HttpServletRequest.class||parameter.getType()==HttpServletResponse.class){//如果时request和response对象,那么参数名称存 HttpServletRequest 和 HttpServletResponse                    handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),i);                }else{                    handler.getParamIndexMapping().put(parameter.getName(),i);                }            }            handlerMapping.add(handler);        }    }}

6, 请求处理开发 doPost():

上一步骤,我们配置了 uri和method的映射关系,并封装到了Handler中存入list,那么接下来,就要通过HttpServletRequest,取出uri,然后找到具体的Handler:

  1. 通过HttpServletRequest取出uri找到具体的Handler

  2. 得到将调用方法的参数的数组

  3. 根据上述数组长度创建一个新的数组(参数数组,传入反射调用的)

  4. 通过req.getParameterMap()得到前台传来的参数parameterMap

  5. 遍历parameterMap

  6. 通过StringUtils.join方法把name=1&name=2格式的参数变为name[1,2] (需要commons-lang依赖)

  7. 参数匹配并设置

private Handler getHandler(HttpServletRequest req) {if (handlerMapping.isEmpty()){return null;}    String url = req.getRequestURI();//遍历 handlerMappingfor (Handler handler : handlerMapping) {        Matcher matcher = handler.getPattern().matcher(url);if (!matcher.matches()){continue;}return handler;    }return null;}
 @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws  IOException {//根据uri获取到能够处理当前请求的Handler(从handlerMapping中(list))        Handler handler = getHandler(req);if (handler==null){            resp.getWriter().write("404 not found");return;        }//参数绑定//该方法所有参数得类型数组        Class>[] parameterTypes = handler.getMethod().getParameterTypes();//根据上述数组长度创建一个新的数组(参数数组,传入反射调用的)Object[] paramValues = new Object[parameterTypes.length];//以下就是为了向参数数组中设值,而且还得保证参数得顺序和方法中形参顺序一致。Map<String,String[]> parameterMap = req.getParameterMap();//遍历request中所有的参数 ,(填充除了request、response之外的参数)for (Map.Entry<String,String[]> entry: parameterMap.entrySet()){//name=1&name=2 name[1,2]String value = StringUtils.join(entry.getValue(), ",");// 如同 1,2//如果参数和方法中的参数匹配上了,填充数据if (!handler.getParamIndexMapping().containsKey(entry.getKey())){continue;}//方法形参确实有该参数,找到它得索引位置,对应得把参数值放入paramValues            Integer index = handler.getParamIndexMapping().get(entry.getKey());//把前台传递过来的参数值,填充到对应得位置去            paramValues[index] = value;        }        Integer requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());        paramValues[requestIndex] = req;        Integer responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());        paramValues[responseIndex] = resp;//最终调用handler得method属性try {Object invoke = handler.getMethod().invoke(handler.getController(), paramValues);//简单操作,把方法返回的数据以字符串的形式写出resp.getWriter().write(invoke.toString());        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        }    }

7,测试:

打开浏览器,url中输入:http://localhost:8080/demo/query?name=lisi

返回:

控制台中打印出:

OK完成~

而这一些都是基于对于spring底层原理有一定程度的理解上进行的,当你完整实现这个项目之后,你就能够对于spring有一个全新的理解

点个在看 你最好看

往期推荐

终于有人把Java8和Spring5完美合体了,业界堪称神迹

腾讯强推Redis成长手册!原理+应用+集群+拓展+源码五飞

阿里要求其程序员必须精通的并发编程笔记:原理+模式+应用

饿了么架构师发布“绝版”Java并发实现原理:JDK源码剖析

不吹不擂,10年架构师公开分享SQL工作笔记,5字真言总结到位

由浅入深吃透容器云+微服务+K8S+MQ+阿里云内部实施手册

ssm框架requestmapping找不到_从MVC原理开始手敲一个MVC框架,带你体会当大神的乐趣...相关推荐

  1. ssm框架requestmapping找不到_框架学习,就是介么简单

    框架学习 程序员凯小白 最近实训ssm框架,SSM框架,是Spring + Spring MVC + MyBatis的缩写,这个是继SSH之后,目前比较主流的Java EE企业级框架,适用于搭建各种大 ...

  2. 【RPC框架、RPC框架必会的基本知识、手写一个RPC框架案例、优秀的RPC框架Dubbo、Dubbo和SpringCloud框架比较】

    一.RPC框架必会的基本知识 1.1 什么是RPC? RPC(Remote Procedure Call --远程过程调用),它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络的技术. ...

  3. 手写一个RPC框架,理解更透彻(附源码)

    作者:烟味i www.cnblogs.com/2YSP/p/13545217.html 一.前言 前段时间看到一篇不错的文章<看了这篇你就会手写RPC框架了>,于是便来了兴趣对着实现了一遍 ...

  4. 面试官让我手写一个RPC框架

    如今,分布式系统大行其道,RPC 有着举足轻重的地位.Dubbo.Thrift.gRpc 等框架各领风骚,学习RPC是新手也是老鸟的必修课.本文带你手撸一个rpc-spring-starter,深入学 ...

  5. 一个connection对象可以创建一个或一个以上的statement对象_从 0 开始手写一个 Mybatis 框架,三步搞定...

    来自:开源中国,作者:我叫刘半仙 链接:https://my.oschina.net/liughDevelop/blog/1631006 MyBatis框架的核心功能其实不难,无非就是动态代理和jdb ...

  6. python pygame模块怎么写游戏_使用 Python 和 Pygame 模块构建一个游戏框架

    这系列的第一篇通过创建一个简单的骰子游戏来探究 Python.现在是来从零制作你自己的游戏的时间. 在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的.基于文本的骰子游戏 ...

  7. 从0开始手写一个 SpringMVC 框架,向高手进阶!

    Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的,但了解之后才知道有很多巧妙的设计在里面.如果不看Spring的源码,你将会失去一次和大师学习的机会:它的代码规范,设 ...

  8. 手撸一个RPC框架——傻瓜式教程(一)

    RPC框架-傻瓜式教程(一) 前言,太久没写博客了,有点手生,总结一下自己对RPC框架的学习过程 首先我们知道RPC的全名是,全程服务调用,我们用它来做什么,简单地说就是客户端通过接口调用服务端的函数 ...

  9. 自己手写一个Mybatis框架(简化)

    继上一篇手写SpringMVC之后,我最近趁热打铁,研究了一下Mybatis.MyBatis框架的核心功能其实不难,无非就是动态代理和jdbc的操作,难的是写出来可扩展,高内聚,低耦合的规范的代码.本 ...

最新文章

  1. 使用 PowerShell 创建 Linux 虚拟机
  2. python爬虫新手项目-33个Python爬虫项目实战(推荐)
  3. em算法详细例子及推导_EM算法详解(例子+推导)
  4. careercup-数组和字符串1.7
  5. python初学篇笔记_Python学习笔记(基础篇)
  6. 4-数组、指针与字符串1.4-动态内存分配
  7. python 内建函数basestring笔记
  8. 支离破碎的 Android
  9. 山西煤炭职业技术学院计算机信息管理,山西煤炭职业技术学院计算机信息系
  10. 2006毕业设计Delphi篇(二)
  11. php UUID 分布式生成用不重复的随机数方法
  12. 杰控组态软件的“远程连接服务”使用要点
  13. pillow生成企业营业执照
  14. 成品app直播源码,fragment切换 常用写法
  15. 太突然!北大方正破产了!负债3029亿元!
  16. 修改计算机管理员administrator的密码
  17. 干货丨时序数据库DolphinDB脚本语言的混合范式编程
  18. java 文件上传乱码_java上传txt文件,出现中文乱码
  19. 解决Android Studio不提示控件的XML属性
  20. Python编程:通过百度地图接口抓取机构的地址和电话信息

热门文章

  1. matlab 合并相同元素,matlab中删除相邻相同元素
  2. NIO与零拷贝和AIO
  3. P1510 精卫填海
  4. javascript进行遍历
  5. YUV422(UYVY)转RGB565源代码及其讲解.md
  6. pointers on c (day 1,chapter3)
  7. hdu4410(Boomerang)(计算几何)
  8. silverlight动态添加xaml物件
  9. VStudio 2003 remote debug
  10. 案例:按照JSP Model2思想实现用户注册功能