理解 Servlet 和 JSP

理解 Servlet 和 JSP

最初接触 Java 的时候用它来做一些桌面程序,所以对 Java Web 方向没有深入研究过。后来接触了 SSM 和 Spring Boot 等框架之后渐渐转向了 Web 方向。不过,对于 Servlet 和 JSP,虽然经常在招聘信息和别人推荐的 Java 学习路线上面看到,对它们的理解一直处于模糊的状态。最近,专门找了相关的资料进行学习,花了两三个小时就大致看完了它们在 Java Web 中的地位和原理,特写此文记录一些理解。

1、Servlet 和 JSP 的重要性

首先,我们日常 Web 开发中很少接触 Servlet 和 JSP 是因为:

  1. 对于 Servlet,我们一直在使用。之所以没有印象是因为它们被封装进了框架中,比如 Spring MVC. 尽管我们使用 Spring MVC,还是要在 web.xml 中配置 Servlet 的信息。这就是 Servlet 配置,而 Servlet 的实现则交给了 Spring MVC 处理。
  2. 对于 JSP,如果我们做的是一些简单的页面,那么我们可以直接使用 Spring MVC 和 JSP 进行编写。这在一些项目中仍然在使用。但对于一些比较大型的网站,因为前后端分离,所以 Java Web 则更多地负责给前端提供 RESTFul 类型的接口。因此,我们接触和使用 JSP 的机会变少了而已。

尤其是 Spring Boot 出来之后,甚至 Tomcat 都被作为 Spring Boot 整体的一部分。我们接触 Servlet 和 JSP 的基础越来越少,那么我们还有必要学习它们吗?

答案是 YES. 这是因为 Spring MVC 也好,Spring Boot 也罢,本质上只是对 Servlet 进行了封装,简化了开发流程,使我们能够把更多的精力放在业务逻辑实现上。但如果想要深入研究和理解这些框架的原理,就无法绕开 Servlet 和 JSP 了。

2、Servlet、JSP 以及 Tomcat 之间的关系

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,用来接收请求并对请求进行处理,并返回响应结果。Servlet 可以处理任何类型的请求,不过多数情况下,我们用它来处理来自浏览器的 Http 请求。我们可以在 Servlet 根据请求信息,将一个 HTML 作为文本响应给客户端,并最终呈现一个页面给用户。

而 JSP(全称 Java Server Pages)是一种动态网页开发技术,以 Java 作为脚本语言,文件后缀名为 *.jsp。从形式上面看 JSP 包含了 Java 和 HTML 代码。当请求达到服务器的时候,当服务器识别出该请求是 JSP 请求之后,会将该请求传递给 JSP 引擎。JSP 引擎从磁盘中载入 JSP 文件,然后将它们转化为 Servlet(Java 代码)。然后就得到了 JSP 对应的 Servlet,之后就将其作为 Servlet 使用,接收请求,处理,并返回结果。

所以,我们可以理解成 JSP 是 Servlet 的一种简化使用方式。因为在 Servlet 中,你不得不以文字的形式拼接出一个 HTML 字符串并返回。而使用 JSP,把责任反了过来,在 HTML 中嵌入 Java 代码,然后再把整个文件翻译成 Servlet 代码。

那么 Tomcat 呢?Tomcat 是 Servlet 容器,除了 Tomcat,还有 Jetty 可以作为 Servlet 容器。本质上,这类的容器做了一些工作,来提升我们 Servlet 程序的效率,比如对多线程进行管理等等。所以,从这个角度上将,我们的 Servlet 更像是一个接口,我们定义了要实现的逻辑,然后它们被放进 Tomcat 中执行。(Tomcat 本身也就是启了一个 main 函数和一个无限循环来不断对请求进行处理,所以,我们的 Servlet 对 Tomcat 而言只是一段代码。)

3、Servlet 的使用和规范

3.1 Servlet 的基础用例

Servlet 可以使用 javax.servlet 和 javax.servlet.http 包创建。Servlet 有三个生命周期方法它们会在各自的生命周期中被调用:

  1. init() 方法进行初始化。
  2. service() 方法来处理客户端的请求。
  3. destroy() 方法终止。

service() 方法中会对请求进行处理。以 HttpServlet 为例,它会在 service() 方法中,根据请求的 Method 信息调用 doGet()doPost()doPut() 等方法。以下面的代码为例,

public class HelloWorld extends HttpServlet {
 
    private String message;

    @Override
    public void init() throws ServletException {
        // 执行必需的初始化
        message = "Hello World";
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置响应内容类型
        response.setContentType("text/html");
        // 实际的逻辑是在这里
        PrintWriter out = response.getWriter();
        out.println("<h1>" + message + "</h1>");
    }

    @Override
    public void destroy() {
        // 什么也不做
    }
}

这里用来处理 GET 类型的请求,并通过 HttpServletResponse 中获取的输出流将最终的 HTML 返回给客户端。这里的处理比较简单,如果需要获取其他的信息,比如 Cookie、Session 等信息都可以从 HttpServletRequest 中进行获取。如果想要将 Cookie、Session 等返回给客户端,也是通过 HttpServletResponse 进行输出。如果是 POST 类型提交的表单,我们也是使用 HttpServletRequest 和 HttpServletResponse 进行读写的。它们提供了哪些方法,以及如何进行读写我们不再进行详细说明,参考相关的 API 或者示例代码即可。(可以参考:Servlet-教程)。

按照上面这样写完了 Servlet 之后,我们还需要在 Tomcat 中配置才行。默认情况下,Servlet 应用程序位于路径 Tomcat 安装目录/webapps/ROOT 下,且类文件放在 Tomcat 安装目录/webapps/ROOT/WEB-INF/classes 中。如果您有一个完全合格的类名称 com.myorg.MyServlet,那么这个 Servlet 类必须位于 WEB-INF/classes/com/myorg/MyServlet.class 中。

现在,让我们把 HelloWorld.class 复制到 Tomcat 安装目录/webapps/ROOT/WEB-INF/classes 中,并在位于 Tomcat 安装目录/webapps/ROOT/WEB-INF/ 的 web.xml 文件中创建以下条目:

<web-app>      
    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>HelloWorld</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/HelloWorld</url-pattern>
    </servlet-mapping>
</web-app>  

这里的 <servlet> 标签配置了 Servlet 的类,<servlet-mapping> 标签中配置了给 Servlet 用来处理的 URL 的类型。

3.2 Servlet 过滤器

配置在 Web.xml 中的内容,除了 Servlet 还有 Servlet 过滤器。过滤器用来动态地拦截请求和响应,处理,并交给 Servlet 执行。可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet。可以被用来进行字符串编码、身份验证过滤器、数据压缩、加密、触发资源访问事件、图像转换过滤器等。

过滤器是需要用到 javax.servlet.Filter 接口,它也提供了三个生命周期相关的方法:

  1. init() 初始化过滤器的时候调用
  2. doFilter() 该方法完成实际的过滤操作
  3. destroy() 在销毁过滤器实例前调用该方法

在使用的时候,我们只需要覆写这三个方法,然后将其注册到 web.xml 中即可。比如,

// 实现 Filter 类
public class LogFilter implements Filter {
    public void  init(FilterConfig config) throws ServletException {
        // 获取初始化参数
        String site = config.getInitParameter("Site"); 
        // 输出初始化参数
        System.out.println("网站名称: " + site); 
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 输出站点名称
        System.out.println("站点网址:http://www.runoob.com");
        // 把请求传回过滤链
        chain.doFilter(request,response);
    }

    public void destroy() {
        /* 在 Filter 实例被 Web 容器从服务移除之前调用 */
    }
}

然后,将它注册到 web.xml 中,

<?xml version="1.0" encoding="UTF-8"?>  
<web-app>  
    <filter>
        <filter-name>LogFilter</filter-name>
        <filter-class>com.runoob.test.LogFilter</filter-class>
        <init-param>
            <param-name>Site</param-name>
            <param-value>菜鸟教程</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>LogFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>  
        <!-- 类名 -->  
        <servlet-name>DisplayHeader</servlet-name>  
        <!-- 所在的包 -->  
        <servlet-class>com.runoob.test.DisplayHeader</servlet-class>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>DisplayHeader</servlet-name>  
        <!-- 访问的网址 -->  
        <url-pattern>/TomcatTest/DisplayHeader</url-pattern>  
    </servlet-mapping>  
</web-app>  

从上面的配置文件中可以看出,配置过滤器的方式和配置 Servlet 一样,都是先通过标签定义本身,然后再定义一个 mapping 并指定要匹配的 url. 注意:web.xml 中的 filter-mapping 元素的顺序决定了 Web 容器应用过滤器到 Servlet 的顺序。

4、JSP

4.1 JSP 的生命周期

JSP 生命周期中所走过的几个阶段:

  1. 编译阶段:Servlet 容器编译 Servlet 源文件,生成 Servlet 类;
  2. 初始化阶段:加载与 JSP 对应的 Servlet 类,创建其实例,并调用它的初始化方法,可以通过覆写 jspInit() 实现该阶段的逻辑;
  3. 执行阶段:调用与 JSP 对应的 Servlet 实例的服务方法;
  4. 销毁阶段:调用与 JSP 对应的 Servlet 实例的销毁方法,然后销毁 Servlet 实例,可以通过覆写 jspDestroy() 实现该阶段的逻辑。

以下是 JSP 的一个示例,它的作用的原理就是,将 HTML 部分变成字符串,然后 <%! %> 包裹的 Java 代码转换成 Servlet 的代码。最终执行 Java 代码之后将完整的字符串返回给客户端,

<!-- 解决中文编码问题 -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
    <head>
        <title>life.jsp</title>
    </head>
    <body>
        <%! 
        private int initVar=0;
        private int serviceVar=0;
        private int destroyVar=0;
        %>
        
        <%!
        public void jspInit(){
            initVar++;
            System.out.println("jspInit(): JSP被初始化了"+initVar+"次");
        }
        public void jspDestroy(){
            destroyVar++;
            System.out.println("jspDestroy(): JSP被销毁了"+destroyVar+"次");
        }
        %>

        <%
        serviceVar++;
        System.out.println("_jspService(): JSP共响应了"+serviceVar+"次请求");

        String content1="初始化次数 : "+initVar;
        String content2="响应客户请求次数 : "+serviceVar;
        String content3="销毁次数 : "+destroyVar;
        %>
        <h1>菜鸟教程 JSP 测试实例</h1>
        <p><%=content1 %></p>
        <p><%=content2 %></p>
        <p><%=content3 %></p>
    </body>
</html>

4.2 JSP 的语法

JSP 的脚本程序:可以包含任意量的Java语句、变量、方法或表达式,语法格式是:

<% 代码片段 %>

JSP 声明:可以声明一个或多个变量、方法,供后面的 Java 代码使用。语法格式:

<%! declaration; [ declaration; ]+ ... %>

JSP 表达式:包含的脚本语言表达式,先被转化成 String,然后插入到表达式出现的地方。语法格式:

<%= 表达式 %>

JSP 注释:常用的主要有两种:

  1. <%-- 注释 --%> JSP注释,注释内容不会被发送至浏览器甚至不会被编译
  2. <!-- 注释 --> HTML注释,通过浏览器查看网页源代码时可以看见注释内容

JSP 指令:用来设置与整个JSP页面相关的属性。语法格式:

<%@ directive attribute="value" %>

这里有三种指令标签:

指令描述
<%@ page ... %>定义页面的依赖属性,比如脚本语言、error 页面、缓存需求等等
<%@ include ... %>包含其他文件
<%@ taglib ... %>引入标签库的定义,可以是自定义标签

JSP 行为:使用 XML 语法结构来控制 Servlet 引擎。语法格式:

<jsp:action_name attribute="value" />

一些可用的 JSP 行为标签:

一些可用的 JSP 行为标签

JSP 隐含对象:JSP 支持九个自动定义的变量,江湖人称隐含对象。这九个隐含对象的简介见下表:

JSP 隐含对象

其他:JSP 的控制语句、运算符号和变量类型与 Java 一致。

参考:

  1. http://www.runoob.com/jsp/jsp-tutorial.html
  2. http://www.runoob.com/servlet/servlet-tutorial.html
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值