zoukankan      html  css  js  c++  java
  • 初学 Java Web 开发,请远离各种框架,从 Servlet 开始(转)

    本文是OSChina开源中国老大“红薯”写的,觉得对新手非常有用,转来慢慢看。

    OSCHINA 软件库有一个分类——Web框架,该分类中包含多种编程语言的将近500个项目。

    Web框架是开发者在使用某种语言编写Web应用服务端时关于架构的最佳实践。很多Web框架是从实际的Web项目抽取出来的,仅和Web的请求和响应处理有关,形成一个基础,在开发别的应用项目的时候则可以从这个剥离出来的基础做起,让开发者更关注更具体的业务问题,而不是Web的请求和响应的控制。

    框架很多,但套路基本类似,帮你隐藏很多关于 HTTP 协议细节内容,专注功能开发。

    但对一个初学者来说,过早的接触框架往往是事倍功半!同样一个问题,换一种框架你可能需要从头开始研究。

    下面是针对初学 Java 开发 Web 过程一些个人见解和思路,高手可略过。

    1. 基本要求:Java 编程基础

    有良好的 Java 语言编程基础,这是必须的,在讨论 Web 开发技术时提了一个 Java 编程基础的问题会被鄙视的。

    2. 环境准备 (Eclipse + Tomcat)

    选择一个你喜爱的Servlet容器,或者说大一点就是应用服务器,推荐 Tomcat 、Resin 或者 Jetty 这些轻量级的产品。这三个产品下载 zip 包解压后就可以用了。如果你不熟悉 Tomcat 的话请不要使用 exe 版本的 Tomcat,那会徒增很多烦恼。也不建议在 Eclipse 等一些开发环境中集成 Tomcat 的做法,也会徒增烦恼。

    把应用服务器启动起来并能访问到其默认的页面为准。

    关于开发工具

    不推荐使用 MyEclipse 和 Eclipse 的 JEE 版本,徒增烦恼、运行缓慢而且还让你无法了解 Web 项目的结构。普通的 Eclipse 或者你喜欢的开发工具就足够了,能支持普通 Java 项目开发即可。

    为了方便,我做了一个最基本的Java 项目 —— ServletDemo.zip ,你可将它导入到 Eclipse 里就是一个完整的、最简单的 Web 项目。

    然后将下面 XML 内容替换 Tomcat 下的 conf/server.xml 文件:

    <?xml version='1.0' encoding='utf-8'?>
    <Server port="8005" shutdown="SHUTDOWN">
      <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>
        <Engine name="Catalina" defaultHost="localhost">
          <Host name="localhost">
        <Context path="" docBase="D:WORKDIRServletDemowebapp" reloadable="true"/>
          </Host>
        </Engine>
      </Service>
    </Server>

    其中 D:WORKDIRServletDemo 替换为你导入的项目路径,再次启动 Tomcat 后在浏览器打开 http://localhost:8080/hello 便可看到 Hello World 的输出信息。

    3. 了解 Servlet 和 Filter

    好了,我已经把环境搭起来了,接下来该干嘛呢?

    前面的步骤为的是搭建一个测试的环境,然后让你了解一个最基本的 Java Web 项目的结构。

    一个最基本的 Java Web 项目所需的 jar 包只需要一个 servlet-api.jar ,这个 jar 包中的类大部分都是接口,还有一些工具类,共有 2 个包,分别是 javax.servlet 和 javax.servlet.http。我把这个jar包放到了 webapp 目录外的一个独立 packages 文件夹里,这是因为所有的 Servlet 容器都带有这个包,你无需再放到Web项目里,我们放到这里只不过是编译的需要,运行是不需要的。如果你硬是把 servlet-api.jar 放到 webapp/WEB-INF/lib 目录下,那么 Tomcat 启动时还会报一个警告信息。

    Java Web 项目还需要一个非常重要的配置文件 web.xml ,在这个项目中已经被我最小化了,只保留有用的信息:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
        "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
     
        <servlet>
            <servlet-name>hello_world</servlet-name>
            <servlet-class>demo.HelloServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
     
        <servlet-mapping>
            <servlet-name>hello_world</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
     
    </web-app>

    每个 servlet 都必须在 web.xml 中定义并进行 URL 映射配置,早期 Java 开发 Web 在没有框架满天飞的时候,这个文件会定义了大量的 servlet,或者有人为了省事干脆来一个 /servlet/* 来通过类名直接调用 Servlet。

    Servlet 规范里还有另外一个非常重要而且非常有用的接口那就是 Filter 过滤器。

    下面是一个最简单的 Filter 类以及相应的定义方法:

    package demo;
     
    import java.io.IOException;
     
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
     
    public class HelloFilter implements Filter {
     
        @Override
        public void init(FilterConfig arg0) throws ServletException {
            System.out.println("Filter 初始化");
        }
     
        @Override
        public void doFilter(ServletRequest req, ServletResponse res,
                FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest)req;
            System.out.println("拦截 URI="+request.getRequestURI());
            chain.doFilter(req, res);
        }
     
        @Override
        public void destroy() {
            System.out.println("Filter 结束");
        }
    }

    在 web.xml 中的配置必须放在 Servlet 的前面:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
                             "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
     
        <filter>
            <filter-name>helloFilter</filter-name>
            <filter-class>demo.HelloFilter</filter-class>
        </filter>
     
        <filter-mapping>
            <filter-name>helloFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
     
        <servlet>
            <servlet-name>hello_world</servlet-name>
            <servlet-class>demo.HelloServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
     
        <servlet-mapping>
            <servlet-name>hello_world</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
     
    </web-app>

    访问 http://localhost:8080/hello 时看看 Tomcat 控制台有何输出信息。

    4. Servlet 和 HTTP 的对应关系

    Servlet 是 J2EE 最重要的一部分,有了 Servlet 你就是 J2EE 了,J2EE 的其他方面的内容择需采用。而 Servlet 规范你需要掌握的就是 servlet 和 filter 这两项技术。绝大多数框架不是基于 servlet 就是基于 filter,如果它要在 Servlet 容器上运行,就永远也脱离不开这个模型。

    为什么 Servlet 规范会有两个包,javax.servlet 和 javax.servlet.http ,早先设计该规范的人认为 Servlet 是一种服务模型,不一定是依赖某种网络协议之上,因此就抽象出了一个 javax.servlet ,同时在提供一个基于 HTTP 协议上的接口扩展。但是从实际运行这么多年来看,似乎没有发现有在其他协议上实现的 Servlet 技术。

    javax.servlet 和 javax.servlet.http 这两个包总共加起来也不过是三十四个接口和类。你需要通过 J2EE 的 JavaDoc 文档 熟知每个类和接口的具体意思。特别是下面几个接口必须熟知每个方法的意思和用途:

    HttpServlet
    ServetConfig
    ServletContext
    Filter
    FilterConfig
    FilterChain
    RequestDispatcher
    HttpServletRequest
    HttpServletResponse
    HttpSession
    一些 Listenser 类
    再次强调 HttpServletRequest 和 HttpServletResponse 这两个接口更应该是烂熟于心。

    如果你从字面上无法理解某个方法的意思,你可以在前面那个项目的基础上做实验看看其输出,再不行你可以到讨论区提问,这样的提问非常明确,很多人都可以帮到你。

    为什么我这么强调 HttpServletRequest 和 HttpServletResponse 这两个接口,因为 Web 开发是离不开 HTTP 协议的,而 Servlet 规范其实就是对 HTTP 协议做面向对象的封装,HTTP协议中的请求和响应就是对应了 HttpServletRequest 和 HttpServletResponse 这两个接口。

    你可以通过 HttpServletRequest 来获取所有请求相关的信息,包括 URI、Cookie、Header、请求参数等等,别无它路。因此当你使用某个框架时,你想获取HTTP请求的相关信息,只要拿到 HttpServletRequest 实例即可。

    而 HttpServletResponse接口是用来生产 HTTP 回应,包含 Cookie、Header 以及回应的内容等等。

    5. 再谈谈 Session

    HTTP 协议里是没有关于 Session 会话的定义,Session 是各种编程语言根据 HTTP 协议的无状态这种特点而产生的。其实现无非就是服务器端的一个哈希表,哈希表的Key就是传递给浏览器的名为 jsessionid 的 Cookie 值。

    当需要将某个值保存到 session 时,容器会执行如下几步:

    a. 获取 jsessionid 值,没有的话就生成一个,也就是 request.getSession() 这个方法
    b. 拿到的 HttpSession 对象实例就相当于一个哈希表,你可以往哈希表里存放数据(setAttribute)
    c. 你也可以通过 getAttribute 来获取某个值

    而这个名为 jsessionid 的 Cookie 在浏览器关闭时会自动删除。把 Cookie 的 MaxAge 值设为 -1 就能达到浏览器关闭自动删除的效果。

    6. 关于 JSP

    首先我已经不用 JSP 很多年了,现在一直是使用 Velocity 模板引擎。

    任何一个 JSP 页面在执行的时候都会编译成一个 Servlet 类文件,如果是 Tomcat 的话,这些生成的 java 文件会放置在 {TOMCAT}/work 目录下对应项目的子目录中,例如 Tomcat 生成的类文件如下:

    package org.apache.jsp;
     
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.jsp.*;
    import java.util.*;
     
    public final class test_jsp extends org.apache.jasper.runtime.HttpJspBase
        implements org.apache.jasper.runtime.JspSourceDependent {
     
      private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();
     
      private static java.util.List<String> _jspx_dependants;
     
      private javax.el.ExpressionFactory _el_expressionfactory;
      private org.apache.tomcat.InstanceManager _jsp_instancemanager;
     
      public java.util.List<String> getDependants() {
        return _jspx_dependants;
      }
     
      public void _jspInit() {
        _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
      }
     
      public void _jspDestroy() {
      }
     
      public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
            throws java.io.IOException, ServletException {
     
        final PageContext pageContext;
        HttpSession session = null;
        final ServletContext application;
        final ServletConfig config;
        JspWriter out = null;
        final Object page = this;
        JspWriter _jspx_out = null;
        PageContext _jspx_page_context = null;
     
     
        try {
          response.setContentType("text/html;charset=utf-8");
          pageContext = _jspxFactory.getPageContext(this, request, response,
                      null, true, 8192, true);
          _jspx_page_context = pageContext;
          application = pageContext.getServletContext();
          config = pageContext.getServletConfig();
          session = pageContext.getSession();
          out = pageContext.getOut();
          _jspx_out = out;
     
          out.write("
    ");
          out.write("<html>
    ");
          out.write("    <title>Test</title>
    ");
          out.write("    <style>
    ");
          out.write("    </style> 
    ");
          out.write("  <body>
    ");
          out.write("<h1>Test Demo (oschina)</h1>
    ");
          out.write("<table cellspacing="1" cellpadding="5">
    ");
     
    Enumeration Names=request.getHeaderNames();
    while(Names.hasMoreElements())
    {String name=(String)Names.nextElement();
    String value=request.getHeader(name);
     
          out.write("
    ");
          out.write(" <tr>
    ");
          out.write(" <td>");
          out.print(name);
          out.write("</td>
    ");
          out.write("  <td>");
          out.print(value);
          out.write("</td>
    ");
          out.write(" 
    ");
          out.write(" </tr>
    ");
          out.write(" ");
     
     }
     
          out.write("
    ");
          out.write("</table>
    ");
          out.write("  </body>
    ");
          out.write("</html>");
        } catch (Throwable t) {
          if (!(t instanceof SkipPageException)){
            out = _jspx_out;
            if (out != null && out.getBufferSize() != 0)
              try { out.clearBuffer(); } catch (java.io.IOException e) {}
            if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
          }
        } finally {
          _jspxFactory.releasePageContext(_jspx_page_context);
        }
      }
    }

    在 servlet 中有一个包 javax.servlet.jsp 是跟 JSP 相关的一些接口规范定义。JSP 比 Servlet 方便的地方在于可直接修改立即生效,不像 Servlet 修改后必须重启容器才能生效。

    因此 JSP 适合用来做视图,而 Servlet 则适合做控制层。

    7. 总结

    罗哩罗嗦一大堆,归纳一下就是下面几点:

    熟知 Servlet 规范之前,请不要学习任何框架
    使用最简单的工具,不要任何向导和可视化
    熟知 HTTP 协议
    等你真的掌握了 Servlet 规范再去看框架,便会觉得一些都小菜。总之一点:不要被框架牵着鼻子走,框架是你的工具,它应该听你的!

  • 相关阅读:
    leetcode108 Convert Sorted Array to Binary Search Tree
    leetcode98 Validate Binary Search Tree
    leetcode103 Binary Tree Zigzag Level Order Traversal
    leetcode116 Populating Next Right Pointers in Each Node
    Python全栈之路Day15
    Python全栈之路Day11
    集群监控
    Python全栈之路Day10
    自动部署反向代理、web、nfs
    5.Scss的插值
  • 原文地址:https://www.cnblogs.com/liuxgnu/p/3535454.html
Copyright © 2011-2022 走看看