文档章节

手写简单mvc框架之xuchen-mvc-v1.0

 徐志
发布于 06/25 15:39
字数 1317
阅读 7
收藏 0
点赞 0
评论 0

package org.mvc.framework.servlet;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.mvc.framework.annotation.XCcomponent;
import org.mvc.framework.annotation.XCcontroller;
import org.mvc.framework.annotation.XCrequestMapping;
import org.mvc.framework.annotation.XCrequestMethod;
import org.mvc.framework.annotation.XCrequestParam;
import org.mvc.framework.annotation.XCservice;
import org.mvc.framework.constant.CommonConstant;
import org.mvc.framework.entity.InnerHandler;
import org.mvc.framework.entity.Model;
import org.mvc.framework.entity.ModelMap;
import org.mvc.framework.handler.ClassReflectHandler;
import org.mvc.framework.handler.XmlBeanHandler;
import org.mvc.framework.utils.ContextPathUtil;
import org.mvc.framework.utils.StringCaseUtil;
import org.mvc.framework.utils.XmlUtils;
import org.mvc.framework.view.XCView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class XCDispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = -5009321460671490398L;
    private static final Logger logger = LoggerFactory.getLogger(XCDispatcherServlet.class);
    private static final String CONTEXTCOMPONENTSCAN = "//" + CommonConstant.XML_CONTEXT_SCAN_PACAKAGE;
    private Map<String, Object> iocBeanMap = new HashMap<String, Object>();
    private Map<String, InnerHandler> handlerMapper = new HashMap<String, InnerHandler>();
    private Properties contextProperties = new Properties();

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        System.out.println("[INFO ] @Override doPost 收到用户请求。。。。");
    }
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("[INFO ] @Override service 收到用户请求。。。。");
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        String reqPath = request.getRequestURL().toString();
        String basePath = request.getScheme() + "://" + request.getServerName()
                + ":" + request.getServerPort() + request.getContextPath();
        String url = reqPath.replace(basePath, "").replaceAll("/+", "/");
        System.out.println("[INFO ] " + url);
        InnerHandler inner = handlerMapper.get(url);
        if (null == inner) {
            System.out.println("[INFO ] service 404 not Fund reuest path " + url);
            response.getWriter().write("404 not Fund reuest");
        } else {
            doService(request, response, inner);
        }
    }

    /**
     * 处理请求
     * @throws ServletException 
     */
    private void doService(HttpServletRequest request, HttpServletResponse response, 
            InnerHandler inner) throws IOException, UnsupportedEncodingException, ServletException {
        request.setCharacterEncoding("UTF-8");//客户端网页我们控制为UTF-8
        String requestMethodType = request.getMethod();
        
        Method method0 = inner.getMethod();
        if(!validateRequstMethodType(requestMethodType, method0)) {
            response.getWriter().write(requestMethodType.toUpperCase() + " method not soupport......");
            return;
        }
        
        List<Object> invokeParamVal = new ArrayList<Object>();
        List<String> methodParamNames = inner.getMethodParameterNames();
        Model model = null;
        
        if (null!=methodParamNames && !methodParamNames.isEmpty()) {
            StringBuilder body = new StringBuilder();
            Map<String, Object> postQueryParam = null;
            if (CommonConstant.REQUEST_METHOD_TYPE_POST.equals(requestMethodType)) {
                doPostRequest(request, body);
                postQueryParam = StringCaseUtil.queryParam2Map(body.toString());
            }
            for (int i = 0; i < methodParamNames.size(); i++) {
                String methodParamName = methodParamNames.get(i);
                Class<?> methodParamType = inner.getMethodParameterTypes()[i];
                if (methodParamType.isAssignableFrom(HttpServletRequest.class)) {
                    invokeParamVal.add(request);
                } else if (methodParamType.isAssignableFrom(HttpServletResponse.class)) {
                    invokeParamVal.add(response);
                } else if (methodParamType.isAssignableFrom(Model.class)) {
                    model = new ModelMap();
                    invokeParamVal.add(model);
                } else {
                    String methodParamVal = "";
                    boolean notnull = false;
                    Annotation[][] annotationParam = inner.getParameterAnnotations();
                    if (null != annotationParam[i]&& annotationParam[i].length > 0) {
                        Annotation annotation = annotationParam[i][0];
                        if (annotation instanceof XCrequestParam) {
                            notnull = true;
                            methodParamName = ((XCrequestParam) annotation).value();
                        }
                    }
                    if (CommonConstant.REQUEST_METHOD_TYPE_GET.equals(requestMethodType)) {
                        logger.info("[INFO ] GET request......");
                        methodParamVal = request.getParameter(methodParamName);
                        if (notnull && StringUtils.isEmpty(methodParamVal)) {
                            response.getWriter().write(methodParamName + " cannot be null or empty");
                            return;
                        }
                    } else {
                        System.out.println("[INFO ] POST request deal with ......");
                        if (null != postQueryParam ) { 
                            if(postQueryParam.containsKey(methodParamName)) {
                                methodParamVal = String.valueOf(postQueryParam.get(methodParamName));
                                invokeParamVal.add(ClassReflectHandler.invokeMethodParamValue(methodParamType, methodParamVal));
                            } else {
                                // 判断请求参数不是简单的数据类型,判断是某个的类
                                try {
                                    Field[] fileds = methodParamType.getDeclaredFields();
                                    Object instance = null;
                                    for (Field field : fileds) {
                                        field.setAccessible(true);
                                        String fieldName = field.getName();
                                        if(postQueryParam.containsKey(fieldName)) {
                                            if (null == instance) {
                                                instance = methodParamType.newInstance();
                                            }
                                            // (反射)如果请求的字段是该类的一个属性,那么,就设置创建一个实例并且设置值
                                            String setMethodName = "set" + StringCaseUtil.upperFirstCase(fieldName);
                                            Method method = methodParamType.getMethod(setMethodName, new Class<?>[]{field.getType()});
                                            
                                            String fieldValue = String.valueOf(postQueryParam.get(fieldName));
                                            method.invoke(instance, ClassReflectHandler.invokeMethodParamValue(field.getType(), fieldValue));
                                        }
                                    }
                                    invokeParamVal.add(instance);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                } 
            }
        }
        try {
            Object methodClazz = inner.getMethodClass().newInstance();
            Object result = inner.getMethod().invoke(methodClazz, invokeParamVal.toArray());
            // 反射执行方法后 先去判断model 中是否有值,有就放到request
            if (null != model && model instanceof ModelMap) {
                @SuppressWarnings("unchecked")
                Map<String, Object> modelMap = (Map<String, Object>) model;
                copyModel2Request(request, modelMap);
            }
            
            if (result instanceof XCView) {
                XCView view = XCView.valueOf(result);
                String path = view.getPath();
                if (StringUtils.isNotEmpty(path)) {
                    if (path.startsWith("/")) {
                        response.sendRedirect(request.getContextPath() + path);
                    } else {
                        Map<String, Object> modelMap = view.getModel();
                        copyModel2Request(request, modelMap);
                        request.getRequestDispatcher(CommonConstant.JSP_BASE_PATH + path).forward(request, response);
                    }
                }
            } else if (result instanceof JSONObject) {
                response.setContentType("application/json; charset=utf-8");  
                response.getWriter().write(JSON.toJSONString(result));
            } else if (result instanceof String) {
                String path = "";
                if (String.valueOf(result).startsWith("/")) {
                    path = request.getContextPath() + String.valueOf(result);
                    response.sendRedirect(path.replaceAll("/+", "/"));
                } else {
                    path = CommonConstant.JSP_BASE_PATH + String.valueOf(result);
                    request.getRequestDispatcher(path.replaceAll("/+", "/")).forward(request, response);
                }
            }
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
    }

    private boolean validateRequstMethodType(String requestMethodType, Method method0) {
        if (null == method0) {
            return false;
        }
        if (method0.isAnnotationPresent(XCrequestMapping.class)) {
            XCrequestMapping requestMapping = method0.getAnnotation(XCrequestMapping.class);
            List<XCrequestMethod> requestMethodlist = Arrays.asList(requestMapping.method());
            if (requestMethodType.toUpperCase().equals("GET")) {
                if (requestMethodlist.contains(XCrequestMethod.GET)) {
                    return true;
                }
            } else {
                if (requestMethodlist.contains(XCrequestMethod.POST)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void copyModel2Request(HttpServletRequest request, Map<String, Object> modelMap) {
        if (null != modelMap) {
            for (Map.Entry<String, Object> entry : modelMap.entrySet()) {
                request.setAttribute(entry.getKey(), entry.getValue());
            }
        }
    }

    private void doPostRequest(HttpServletRequest request, StringBuilder body)
            throws IOException {
        Enumeration<String> contentType = request.getHeaders("Content-Type");
        System.out.println("[INFO ] POST request...contentType " + JSON.toJSONString(contentType));
        ServletInputStream servletInputStream = request.getInputStream();
        ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
        try {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = servletInputStream.read(buffer))!= -1) {
                byteOutStream.write(buffer, 0, len);
            }
            byteOutStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        try {
            inputStream = new ByteArrayInputStream(byteOutStream.toByteArray());
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                body.append(StringCaseUtil.urlChinseEncode(line));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                inputStream.close();
            }
            if (null != bufferedReader) {
                bufferedReader.close();
            }
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("[INFO ] initailize XCDispatcherServlet start ...");

        // 初始化 context 容器
        doInitContext(config);
        // 扫描bean 初始化 IOC
        String scanPack = contextProperties.getProperty(CommonConstant.XML_CONTEXT_SCAN_PACAKAGE);
        doInitIocScanPack(scanPack);
        // DI 依赖注入
        doInitDIBean();
        // 初始化 hanldeMapping
        doInitHandlerMapping();
        handlerMapper.forEach((key, value)->{
            System.out.println("[INFO ] " + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS").format(new Date())) + " XCDispatcherServlet " + key);
        });
        // 注册servlet
        ServletContext servletContext = config.getServletContext();
        // 注册jsp servlet
        ServletRegistration jspRegistration = servletContext.getServletRegistration("jsp");
        jspRegistration.addMapping(CommonConstant.JSP_BASE_PATH + "*");
        
        ServletRegistration defaultRegistration = servletContext.getServletRegistration("default");
        defaultRegistration.addMapping(CommonConstant.STATIC_FILE_BASE_PATH + "*");
    }

    private void doInitHandlerMapping() {
        System.out.println("[INFO ] ------------ bean init handler mapping start -------------");
        if (null == iocBeanMap || iocBeanMap.isEmpty()) {
            return;
        }
        for (Entry<String, Object> entry : iocBeanMap.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (clazz.isAnnotationPresent(XCcontroller.class)) {
                ClassReflectHandler.doBeanHandlerMapping(clazz, handlerMapper);
            }
        }
    }

    private void doInitDIBean() {
        System.out.println("[INFO ] ------------ bean di start -------------");
        try {
            if (null == iocBeanMap || iocBeanMap.isEmpty()) {
                return;
            }
            for (Entry<String, Object> entry : iocBeanMap.entrySet()) {
                Class<?> claxx = entry.getValue().getClass();
                if (claxx.isAnnotationPresent(XCcontroller.class) || claxx.isAnnotationPresent(XCservice.class)
                        || claxx.isAnnotationPresent(XCcomponent.class)) {
                    ClassReflectHandler.doBeanDiInstation(entry, claxx,iocBeanMap);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 空格=%20 ; !=%21 ; "=%22 ; #=%23 ; $=%24 ; %=%25 ; & = %26 ; ' = %27 ( =%28
     * ; )=%29 ; *=%2A ; +=%2B ; ,=%2C ; -=%2D ; .=%2E ; /=%2F
     * 
     * @param scanPackPath
     */
    private void doInitIocScanPack(String scanPackPath) {
        if (StringUtils.isEmpty(scanPackPath)) {
            return;
        }
        try {
            String scanRootPath = scanPackPath.replace(".", "/");
            Enumeration<URL> rootUrl = this.getClass().getClassLoader().getResources(scanRootPath);
            while (rootUrl.hasMoreElements()) {
                URL url = rootUrl.nextElement();
                String urlProtocol = url.getProtocol();
                if (CommonConstant.PACKAGE_PROTOCOL_FILE.equals(urlProtocol)) {
                    scanCurrentFile(scanPackPath, url);
                } else {
                    // 扫描 jar 包
                    if (CommonConstant.PACKAGE_PROTOCOL_JAR.equals(urlProtocol)) {
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        if (null != jarURLConnection) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (null != jarFile) {
                                Enumeration<JarEntry> jarEntries = jarFile.entries();
                                while (jarEntries.hasMoreElements()) {
                                    JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
                                    String jarEntryName = jarEntry.getName();
                                    if (jarEntryName.endsWith(".class")) {
                                        String className = jarEntryName.substring(0,jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
                                        iocBeanMap.put(className,ClassReflectHandler.loadClass(className, false));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void scanCurrentFile(String scanPackPath, URL url)
            throws ClassNotFoundException, InstantiationException,
            IllegalAccessException {
        String clazzPath = url.getPath().replaceAll("%20", "");
        File[] files = new File(clazzPath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File filePath) {
                return (filePath.isFile()&& filePath.getName().endsWith(".class") 
                        || filePath.isDirectory());
            }
        });
        if (null == files || files.length <= 0) {
            return;
        }
        for (File file : files) {
            String fileName = file.getName();
            if (file.isDirectory()) {
                String subScanPackPath = scanPackPath + "." + fileName;
                doInitIocScanPack(subScanPackPath);
            } else {
                String clazzName = scanPackPath + "." + fileName.substring(0, fileName.lastIndexOf("."));
                Class<?> claxx = Class.forName(clazzName);
                String instanceName = "";
                if (claxx.isAnnotationPresent(XCcontroller.class)
                        || claxx.isAnnotationPresent(XCcomponent.class)) {
                    instanceName = StringCaseUtil.lowerFirstCase(claxx
                            .getSimpleName());
                } else if (claxx.isAnnotationPresent(XCservice.class)) {
                    XCservice xCservice = claxx.getAnnotation(XCservice.class);
                    instanceName = xCservice.value();
                    if (StringUtils.isEmpty(instanceName)) {
                        instanceName = claxx.getInterfaces()[0].getSimpleName();
                    }
                }
                if (StringUtils.isNotEmpty(instanceName)) {
                    iocBeanMap.put(instanceName, claxx.newInstance());
                }
            }
        }
    }

    private void doInitContext(ServletConfig config) {
        String contextPath = config.getInitParameter("applicationLocalContext");
        List<String> contextlist = ContextPathUtil.splitContextPath(contextPath);
        if (null == contextlist || contextlist.isEmpty()) {
            throw new RuntimeException("dispacherservlet init context exception ...");
        }
        contextlist.stream().forEach(destPath -> {
            if (destPath.toUpperCase().endsWith("xml".toUpperCase())) {
                XmlBeanHandler.initXmlBean(XmlUtils.dealWithXml(this.getClass().getClassLoader(), 
                        destPath, CONTEXTCOMPONENTSCAN),iocBeanMap, contextProperties);
            } else {
                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(destPath);
                try {
                    contextProperties.load(new InputStreamReader(inputStream));
                } catch (IOException e) {
                    logger.error("init servletconfig exception {}",e);
                    e.printStackTrace();
                }
            }
        });
    }
}
 

© 著作权归作者所有

共有 人打赏支持
粉丝 1
博文 19
码字总数 20780
作品 0
崇明
轻量级PHP框架--DoitPHP

DoitPHP是一个简单易用,易于扩展的轻量级PHP框架。运行高效,操作灵活。基于BSD开源协议发布,适用于MVC项目开发。 自2010年5月4日正式发布TommyFramework V1.0(DoitPHP前身)以来,如今Doi...

dotiphp
2011/07/06
5.9K
1
Java程序员,最常用的30%技术有哪些?

两个月,这让我想到了我当年第一份工作,也是两个月从零学JAVA。 当时就买了3本书,强啃了6周。记住一点,时间有限,别听网上很多大牛的,他们说的太杂。你要抓重点,你只要让人家知道,你足...

2017/12/24
0
0
Java程序员,最常用的20%技术有哪些?

两个月,这让我想到了我当年第一份工作,也是两个月从零学JAVA。当时就买了3本书,强啃了6周。记住一点,时间有限,别听网上很多大牛的,他们说的太杂。你要抓重点,你只要让人家知道,你足够...

嘿你好夏天
2017/12/24
0
2
DoitPHP v2.5 正式发布

DoitPHP 是一个简单易用,易于扩展的轻量级 PHP 框架。运行高效,操作灵活。基于 BSD 开源协议发布,适用于 MVC 项目开发。 自 2010 年 5 月 4 日正式发布 TommyFrameworkV1.0(DoitPHP 前身...

dotiphp
2016/12/27
1K
10
三流程序员与一流程序员之间的区别,看看你是属于哪一类?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

茶轴的青春
04/17
0
0
YMP v1.0 发布,轻量级 Web 开发框架

轻量级的Web应用开发框架YMP正式发布v1.0版本! 在经历了近一年时间实际公司项目环境下测试和迭代,优化和修复了若干问题,提高YMP框架代码的成熟度和稳定性。变更详情点击:http://git.oschi...

有理想的鱼
2014/10/25
4.2K
15
Bamboo 1.0 发布,lua MVC web开发框架

历经接近1年的开发,Bamboo v1.0终于发布了。它是一个MVC,融合了非常多的创新理念在其中。 Bamboo v1.0新特性如下: Bamboo is a MVC framework; cooperates with mongrel2, zeromq and re...

红薯
2011/11/11
1K
4
.Net轻量级开源ORM框架--Dos.ORM

Dos.ORM(原Hxj.Data)于2009年发布、2015年正式开源,该组件已在数百个成熟项目中应用,是目前国内.Net用户量最多、最活跃、最完善的国产ORM。初期开发过程中参考了NBear与MySoft,吸取了他...

ITdos
2015/05/12
239.3K
34
Java 学习框架--Mutouren

简介: Mutouren是一个java学习框架,内含一些通用的系统模块, 这些模块由流行的hibernate、mybatis、struts、spring、spring mvc、memcache、redis等技术分别实现, 可以观察、对比这些技术...

sjz木头人
2015/04/14
1K
0
Java开发者不会这些永远都只能是三流程序员,细数一下你是不是?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

美的让人心动
04/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

spring boot中swagger2使用

1.pom.xml中添加 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version>......

说回答
2分钟前
0
0
tomcat虚拟路径的几种配置方法

tomcat虚拟路径的几种配置方法 一般我们都是直接引用webapps下面的web项目,如果我们要部署一个在其它地方的WEB项目,这就要在TOMCAT中设置虚拟路径了,Tomcat的加载web顺序是先加载 $Tomcat_ho...

Helios51
14分钟前
1
0
Mac 安装jupyter notebook的过程

MAC台式机 python:mac下自带Python 2.7.10 1.先升级了pip安装工具:sudo python -m pip install --upgrade --force pip 2.安装setuptools 工具:sudo pip install setuptools==33.1.1 3.安装......

火力全開
20分钟前
0
0
导航守卫解释与例子

“导航”表示路由正在发生改变。 正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。 记住...

tianyawhl
20分钟前
0
0
Java日志框架-logback配置文件多环境日志配置(开发、测试、生产)(原始解决方法)

说明:这种方式应该算是最通用的,原理是通过判断标签实现。 <!-- if-then form --> <if condition="some conditional expression"> <then> ... </then> </if> ......

浮躁的码农
34分钟前
1
0
FTP传输时的两种登录方式和区别

登录方式 匿名登录 用户名为: anonymous。 密码为:任何合法 email 地址。 授权登录 用户名为:用户在远程系统中的用户帐号。 密码为:用户在远程系统中的用户密码。 区别 匿名登录 只能访问...

寰宇01
35分钟前
0
0
plsql developer 配置监听(不安装oracle客户端)

plsql developer 配置监听(不安装oracle客户端)

微小宝
42分钟前
1
0
数据库(分库分表)中间件对比

本人的宗旨就是,能copy的,绝对不手写。 分区:对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm。 根据一定的规则把数据文件(MYD)和索...

奔跑吧代码
46分钟前
2
0
Netty与Reactor模式详解

在学习Reactor模式之前,我们需要对“I/O的四种模型”以及“什么是I/O多路复用”进行简单的介绍,因为Reactor是一个使用了同步非阻塞的I/O多路复用机制的模式。 I/O的四种模型 I/0 操作 主要...

hutaishi
52分钟前
1
0
【2018.07.16学习笔记】【linux高级知识 20.16-20.19】

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析

lgsxp
58分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部