zoukankan      html  css  js  c++  java
  • Servlet、ServletConfig、ServletContext深入学习

    1.Servlet学习

    1.Servlet生命周期 

    Servlet 加载—>实例化—>服务—>销毁。

    • init(servletConfig):(经过自己的测试发现会先调用这个而不是init(),而且不会调用空参的init(),是在执行init(servletConfig)的时候调用init())
      在Servlet的生命周期中,仅执行一次init(servletConfig)方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象(也就是容器中只有一个servlet对象,所以线程非安全)。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init(servletConfig)。查看父类这个方法的源代码发现它做了两件事:初始化servletConfig和调用init(),所以我们在重写了init()方法也会生效:
    public void init(ServletConfig config) throws ServletException {  
        this.config = config;  
        this.init();  
    }  
        public void init() throws ServletException {
    
        }   
    • service():

      它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。并根据请求的方式调用对应的doGet和doPost方法,所以我们需要在程序中做的就是重写doGet或者doPost方法。查看下面一段源码:

    • destroy():
      仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。

      应该知道,在Servlet初始化的时候,会自动调用init(ServletConfig config),Container会自动收集一些该Servlet的配置信息,生成一个ServletConfig的实例,通过调用该实例的四个getXXX方法(即ServletConfig接口中的四个方法),我们可以得到该Servlet的这些配置信息。

      而该实例我们如何获取呢?是通过Servlet#getServletConfig()得到的,该方法同样在GenericServlet中实现,具体如下:(在init(servletConfig方法中会调用init()方法))

    public void init(ServletConfig config) throws ServletException {  
        this.config = config;  
        this.init();  
    }  
    public ServletConfig getServletConfig() {  
        return config;  
    } 

      我认为init()的出现,是为了解决一个问题,那就是有些人最开始时候,重写init(ServletConfig config)方法,但却总是忘记去调用“super.init(config);”。试想,如果真的出现这种情况,那么,就会造成一种结果,容器收集的Servlet的配置信息,不能初始化给GenericServlet的config属性,以至于当调用getServletConfig()的时候,会得不到有用的config实例,而只能得到一个null。这样也就无法获取该Servlet的配置信息了。(我刚开始重写这个方法没有调用super.init(config)所以就获取不到servletConfig对象了)

        而无参数的init()方法在GenericServlet中的出现,解决了这种或许人为造成的获取不到ServletConfig对象的尴尬。可以看到含参init方法调用了无参init,这就让我们可以在处理Servlet初始化参数的时候,只需要重写无参的init()方法就行了。初始化config对象的操作仍然在Container调用init(ServletConfig config)时候完成,然后调用你重写了的init()方法,完成其他初始化操作。

    测试代码如下:

    package cn.qlq.servlet;
    
    import java.io.IOException;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class ServletConfigTest extends HttpServlet {
    
        private static Logger log = LoggerFactory.getLogger(ServletConfigTest.class);
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            super.init(config);// 必须调用这个,否则会导致servlet的ServletConfig为null,且不会调用init()方法
    
            // 调用init的方法进行初始化工作
            System.out.println("init(ServletConfig config)方法--------");
        }
    
        @Override
        public void init() throws ServletException {
            System.out.println("无参的构造函数init()");
        }
    
        @Override
        public void destroy() {
            System.out.println("ServletConfigTest  被销毁");
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("doGet方法进行服务------------");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req, resp);
        }
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("service()方法执行----------");
            super.service(req, resp);
        }
        
    }

    配置:

        <servlet>
            <servlet-name>servletConfigTest</servlet-name>
            <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
            <init-param>
                <param-name>name</param-name>
                <param-value>qlq</param-value>
            </init-param>
            <init-param>
                <param-name>password</param-name>
                <param-value>111</param-value>
            </init-param>
        </servlet>
        
        <servlet-mapping>
            <servlet-name>servletConfigTest</servlet-name>
            <url-pattern>/servletConfigTest</url-pattern>
        </servlet-mapping>

    结果:当我们浏览器访问的时候才会创建该servlet对象,而且多次访问只会创建一个对象,所以servlet是线程非安全的。

    信息: Server startup in 1987 ms
    无参的构造函数init()
    init(ServletConfig config)方法--------
    service()方法执行----------
    doGet方法进行服务------------
    service()方法执行----------
    doGet方法进行服务------------
    service()方法执行----------
    doGet方法进行服务------------
    ................
    ServletConfigTest  被销毁

    将上面的调用父类对象的init(servletConfig)的方法注释掉,同时在服务方法中调用servletConfig读取配置的参数会报错:

    package cn.qlq.servlet;
    
    import java.io.IOException;
    import java.util.Enumeration;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class ServletConfigTest extends HttpServlet {
    
        private static Logger log = LoggerFactory.getLogger(ServletConfigTest.class);
    
        @Override
        public void init(ServletConfig config) throws ServletException {
    //        super.init(config);// 必须调用这个,否则会导致servlet的ServletConfig为null,且不会调用init()方法
    
            // 调用init的方法进行初始化工作
            System.out.println("init(ServletConfig config)方法--------");
        }
    
        @Override
        public void init() throws ServletException {
            System.out.println("无参的构造函数init()");
        }
    
        @Override
        public void destroy() {
            System.out.println("ServletConfigTest  被销毁");
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.getServletConfig().getInitParameter("name");
            System.out.println("doGet方法进行服务------------");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req, resp);
        }
    
    }

    结果: 

    init(ServletConfig config)方法--------
    八月 04, 2018 3:26:06 下午 org.apache.catalina.core.StandardWrapperValve invoke
    严重: Servlet.service() for servlet [servletConfigTest] in context with path [/jwxt] threw exception
    java.lang.NullPointerException
        at cn.qlq.servlet.ServletConfigTest.doGet(ServletConfigTest.java:40)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
        at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1139)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

    解释:

      覆盖掉init(servletConfig)没有调用super.init(servletConfig)所以不会调用init()方法,也不会初始化servletConfig对象,所以执行getServletConfig也会报错。

    2.Servlet对象的创建时机:

    在servlet的配置当中,<load-on-startup>5</load-on-startup>的含义是:

      标记容器是否在启动的时候就加载这个servlet。

      当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;

      当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。

      正数的值越小,启动该servlet的优先级越高。

    例如:修改servlet的配置如下:

        <servlet>
            <servlet-name>servletConfigTest</servlet-name>
            <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
            <init-param>
                <param-name>name</param-name>
                <param-value>qlq</param-value>
            </init-param>
            <init-param>
                <param-name>password</param-name>
                <param-value>111</param-value>
            </init-param>
            <!-- 参数为0或这大于0表示容器启动加载servlet,为负数或者没设置表示第一次访问的时候创建对象 -->
            <load-on-startup>0</load-on-startup>
        </servlet>
        
        <servlet-mapping>
            <servlet-name>servletConfigTest</servlet-name>
            <url-pattern>/servletConfigTest</url-pattern>
        </servlet-mapping>

    结果:

    3.Servlet的路径映射问题: 

    如下面一段配置:

        <servlet>
            <servlet-name>servletConfigTest</servlet-name>
            <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
            <init-param>
                <param-name>name</param-name>
                <param-value>qlq</param-value>
            </init-param>
            <init-param>
                <param-name>password</param-name>
                <param-value>111</param-value>
            </init-param>
            <!-- 参数为0或这大于0表示容器启动加载servlet,为负数或者没设置表示访问的时候创建对象 -->
            <load-on-startup>0</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>servletConfigTest</servlet-name>
            <url-pattern>/servletConfigTest</url-pattern>
        </servlet-mapping>

    需要注意的是:

    1、一个<servlet>可以对应多个<serlvet-mapping>,从而一个Servlet可以有多个路径来访问

    2、url-partten中的路径也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

    由于*的引入,有可能一个路径被多个urlpartten匹配,这是优先级判断条件如下:

    • 哪个越精确找哪个
    • *.后缀的格式永远匹配级最低

    【举例】

    对于如下的一些映射关系:

    • Servlet1 映射到 /abc/*
    • Servlet2 映射到 /*    (表示任何路径都能匹配)
    • Servlet3 映射到 /abc
    • Servlet4 映射到 *.do

    问题:

    • 当请求URL为“/abc/a.html”时,“/abc/*”和“/*”都匹配,哪个servlet响应?Servlet引擎将调用Servlet1。
    • 当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应?Servlet引擎将调用Servlet3。
    • 当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应?Servlet引擎将调用Servlet1。
    • 当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应?Servlet引擎将调用Servlet2。
    • 当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应?Servlet引擎将调用Servlet2。

    缺省Servlet:

      如果有一个Servlet的url-partten被配置为了一根正斜杠"/",这个Servlet就变成了缺省Serlvet.其他Servlet都不处理的请求,由缺省Servlet来处理.

      其实对于静态资源的访问就是由缺省Servlet来执;设置404页面、500页面等提示页面也是由缺省Servlet来执行。通常我们不会自己去配置缺省Servlet

    总结:

      servlet在创建的时候调用init(ServletConfig)进行实例化,在init(ServletConfig)方法中调用init()方法。在整个容器中只有一个servlet的实例,因此Servlet是线程非安全的。servlet的创建时机可以由load-on-start参数指定是在容器启动的时候创建还是在第一次访问该servlet的时候创建。

      当有servlet请求的时候servlet调用service()方法进行服务,service方法根据请求方式是get、Post或者其他调用对应的doXXX方法提供服务。

      容器关闭的时候servlet销毁,会调用destroy()方法。

     2.ServletConfig深入学习 (代表当前Servlet在web.xml中的配置信息,每个servlet都有一个此实例(用的不多))

    • String getServletName()  -- 获取当前Servlet在web.xml中配置的名字

    • String getInitParameter(String name) -- 获取当前Servlet指定名称的初始化参数的值

    • Enumeration getInitParameterNames()  -- 获取当前Servlet所有初始化参数的名字组成的枚举

    • ServletContext getServletContext()  -- 获取代表当前web应用的ServletContext对象

      在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。

      当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init(servletConfig)方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。(重写了Servlet的init(ServletConfig)方法必须调用父类的init(ServletConfig)方法,否则在该servlet中获取不到servletConfig对象)

      这样做的好处是:如果将数据库信息、编码方式等配置信息放在web.xml中,如果以后数据库的用户名、密码改变了,则直接很方便地修改web.xml就行了,避免了直接修改源代码的麻烦

    例如:

    package cn.qlq.servlet;
    
    import java.io.IOException;
    import java.util.Enumeration;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class ServletConfigTest extends HttpServlet {
    
        private static Logger log = LoggerFactory.getLogger(ServletConfigTest.class);
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            super.init(config);//调用父类的该方法初始化servletConfig对象,否则在该实例中获取不到servletConfig对象
            System.out.println("-----------init servlet-----------------");
        }
        
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取Servlet对象
            ServletConfig config = getServletConfig();
            
            //获取servletName
            String servletName = config.getServletName();//获取servlet的名称
            System.out.println("------------servletName :"+servletName);
            
            //获取servletContext对象
            ServletContext servletContext = config.getServletContext();//获取ServletContext对象
            
            //获取配置文件的单个参数
            String name = config.getInitParameter("name");
            System.out.println("-------------the value of name param:"+name);
            
            //获取枚举类型的参数
            Enumeration<String> params = config.getInitParameterNames();
            while (params.hasMoreElements()) {
                String param = params.nextElement();
                System.out.println("参数:"+param+"	值:"+config.getInitParameter(param));
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req, resp);
        }
    
    }

    web.xml配置:

        <servlet>
            <servlet-name>servletConfigTest</servlet-name>
            <servlet-class>cn.qlq.servlet.ServletConfigTest</servlet-class>
            <init-param>
                <param-name>name</param-name>
                <param-value>qlq</param-value>
            </init-param>
            <init-param>
                <param-name>password</param-name>
                <param-value>111</param-value>
            </init-param>
            <!-- 参数为0或这大于0表示容器启动加载servlet,为负数或者没设置表示访问的时候创建对象 -->
            <load-on-startup>0</load-on-startup>
        </servlet>
        
        <servlet-mapping>
            <servlet-name>servletConfigTest</servlet-name>
            <url-pattern>/servletConfigTest</url-pattern>
        </servlet-mapping>

    结果:

    总结:

      getServlerConfig()获取的是init(ServletConfig)的实例,而且servlet也有getInitPatameter(name)方法,实际是获取servletConfig之后调用该servletConfig的getInitParameter(name)方法。查看源码如下:

        public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
        }
        public ServletConfig getServletConfig() {
        return config;
        }

    servlet的  getInitParameter(name)方法原理:

        public String getInitParameter(String name) {
            ServletConfig sc = getServletConfig();
            if (sc == null) {
                throw new IllegalStateException(
                    lStrings.getString("err.servlet_config_not_initialized"));
            }
    
            return sc.getInitParameter(name);
        }
        

    3.ServletContext深入学习(jsp的application域)

    WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用

    ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。

    由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象

    0.ServletContext的三种获取方式:

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //3种获取ServletContext的方法
            ServletContext servletContext1 = this.getServletContext();        
            ServletContext servletContext2 = this.getServletConfig().getServletContext();
            ServletContext servletContext3 = req.getSession().getServletContext();
        }

    下面分别阐述ServletContext对象的作用:

    1.读取web.xml中的全局配置

      类似于ServletConfig,只不过ServletConfig读取的是单个Servlet的配置参数,而ServletContext读取的全局的配置参数。

    例如:

    web.xml中配置:

        <context-param>
            <param-name>user.name</param-name>
            <param-value>张三</param-value>
        </context-param>
        <context-param>
            <param-name>user.password</param-name>
            <param-value>123456</param-value>
        </context-param>
        

    代码:

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext servletContext = this.getServletContext();        
            
            //1.读取全局配置文件
            String username = servletContext.getInitParameter("user.name");
            System.out.println("user.name:"+username);
            //获取枚举类型的参数
            Enumeration<String> params = servletContext.getInitParameterNames();
            while (params.hasMoreElements()) {
                String param = params.nextElement();
                System.out.println("参数:"+param+"	值:"+servletContext.getInitParameter(param));
            }
        }

    结果:

    user.name:张三
    参数:user.password    值:123456
    参数:user.name    值:张三

    2.做为域对象可以在整个web应用范围内共享数据。(JSP的application域)

    这里涉及到一些概念:

    • 域对象:在一个可以被看见的范围内共享数据用到对象
    • 作用范围:整个web应用范围内共享数据
    • 生命周期:当服务器启动web应用加载后创建出ServletContext对象后,域产生。当web应用被移除出容器或服务器关闭,随着web应用的销毁域销毁

    例如:存、取、删数据

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext servletContext = this.getServletContext();
            // 存数据
            servletContext.setAttribute("name", "nonono");
            servletContext.setAttribute("password", "123456");
    
            // 取数据
            servletContext.getAttribute("name");
            
            //删除域数据
            servletContext.removeAttribute("password");
        }

    3.实现Servlet的请求转发

    • 请求重定向:302+Location(两次请求两次响应)
    • 请求转发:服务器内不进行资源流转 (一次请求一次响应,来实现资源流转)

    注:上方括号中的内容是二者的区别。打个比方,假如你找我借钱,如果是请求重定向的话,我告诉你张三有,你再去找张三借;如果是请求转发的话,那我去找张三借,然后再借给你。

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             ServletContext servletContext = this.getServletContext();
             servletContext.getRequestDispatcher("/index.jsp").forward(req, resp);//注意路径是从项目的根路径开始
        }

     补充:request对象也可以实现请求转发,response可以实现重定向:

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             req.getRequestDispatcher("/index.jsp").forward(req, resp);//注意路径是从项目的根路径开始
             resp.sendRedirect("http://qiaoliqiang.cn/");//地址可以从http协议开始
        }

    4.加载资源

      加载从webapp目录下的文件,可以解决路径问题:

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             ServletContext servletContext = this.getServletContext();
             String realPath2 = servletContext.getRealPath("/index.jsp");//我们获取的是webapp目录开始的文件
             String readFileToString = FileUtils.readFileToString(new File(realPath2));//读取文件
             System.out.println(realPath2);
             System.out.println(readFileToString);
        }

    结果:

     如果是非web项目用类加载器加载资源参考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/7272821.html

     5.可以获取服务器信息、Servlet版本以及项目的映射名称等信息 

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             ServletContext servletContext = this.getServletContext();
             String serverInfo = servletContext.getServerInfo();//获取tomcat服务器信息
             int majorVersion = servletContext.getMajorVersion();//最大的ServletAPI版本
             int minorVersion = servletContext.getMinorVersion();//最小的ServletAPI版本
             String contextPath = servletContext.getContextPath();//获取项目名称
             System.out.println("服务器信息"+serverInfo);
             System.out.println("支持最大的ServletAPI版本:"+majorVersion);
             System.out.println("支持最小的ServletAPI版本:"+minorVersion);
             System.out.println("项目名称::"+contextPath);
        }

    结果:

    服务器信息Apache Tomcat/7.0.88
    支持最大的ServletAPI版本:3
    支持最小的ServletAPI版本:0
    项目名称::/jwxt

    6.servletContext还有一个重要作用就是统计一个网站的访问量:

      可以向servletContext中存一个全局变量,当然也可以通过数据库实现

    参考:http://www.cnblogs.com/smyhvae/p/4140529.html

  • 相关阅读:
    虚拟机磁盘和OSNetworkManagement
    虚拟化的设计考量
    魔獸世界台服身份證ID生成器
    完美的Windows Server 2008 R2 SP1 模板
    Windows 脚本主机概述
    创建动态链接库的方法简介
    iPad3越狱方法
    关于用netsh.exe配置系统防火及网络
    金蝶客户端多用户远程桌面解决方案
    poj 1459 最大流(EK实现)
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/9418336.html
Copyright © 2011-2022 走看看