【Java Web编程 七】Servlet基本概念和生命周期

简介: 【Java Web编程 七】Servlet基本概念和生命周期

好了,通过四篇blog我们已经充分了解了JSP的基本语法、内置对象、JavaBean以及EL&JSTL了。从今天开始学习下Servlet技术,也就是我们MVC架构中的C,controller层。

Servlet基本概念

Servlet是什么?官方定义:Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。这个中间层就是对请求进行分发和控制。

Servlet解决什么问题

JSP是JavaWeb应用中处理和显示动态网页的技术,而JSP页面中出现了大量逻辑处理并且和HTML元素混在了一起,这使得JSP页面显得混乱、不易维护,职责不清晰。

  • JSP是建立在静态Html基础之上,使得数据显示时具有动态特点,所以JSP的职责就是显示数据,而不是逻辑处理
  • Servlet的出现很好的规避了这一点,将复杂的逻辑处理、请求响应交给Servlet处理,JSP则只负责渲染显示页面

我们之前反复提到JSP优化和发展历史的一切努力就是简化脚本代码,让JSP专注于页面显示,控制逻辑则交给Servlet去做,而事实上,JSP编译以后也就成为了Servlet。

JSP和Servlet区别和用途

分别了解下JSP和Servlet区别和用途,明确它们的使用范围。

JSP和Servlet区别

了解了JSP和Servlet的区别后才能更好的理解它们各自的用途:

  1. JSP经编译后就变成了Servlet,JSP的本质就是Servlet,JVM只能识别Java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的Java类
  2. JSP更擅长表现于页面显示,Servlet更擅长于逻辑控制
  • Servlet能够很好地组织业务逻辑代码,但是在Java源文件中通过字符串拼接的方式生成动态HTML内容会导致代码维护困难、可读性差
  • JSP虽然规避了Servlet在生成HTML内容方面的劣势,但是在HTML中混入大量、复杂的业务逻辑同样也是不可取的
  1. Servlet中没有内置对象,JSP中的内置对象有些是通过Servlet相关对象得到的,例如:HttpServletRequest对象,HttpServletResponse对象

JSP是Servlet的一种简化,使用JSP只需要完成程序员需要输出到客户端的内容,JSP中的Java脚本如何镶嵌到一个类中,由JSP容器完成。而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应

JSP和Servlet用途

依据它们的不同之处来进行分析,Servlet的短板在展示,如果在Servlet里描述html元素:

@Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        // Hello
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }

那么会产生大量的html的输出代码,影响业务逻辑梳理。而JSP的短板在逻辑,如果在JSP里加大量逻辑:

<body>
<%
    String print = "我是脚本语句内容";
    List<String> strList = new ArrayList<>();
    strList.add("脚本语句1");
    strList.add("脚本语句2");
    strList.add("脚本语句3");
    //  strList.add("脚本语句4");  //这是一个Java注释,用来注释Java代码,查看源码时不可见
%>
<a href="hello-servlet">我的第一个JavaWeb项目</a> <!-- 这是一个html注释,可见<%=new Date().getTime() %> -->
<br/>
<%--这是一个JSP注释,查看源码时不可见--%>
声明语句示例:这是第 <%=count()%> 次访问该页面
<br/>
表达式语句示例: <%="我是一个表达式语句"%>
<br/>
脚本语句示例: <%=print%>, 打印列表 <%=strList%>
<jsp:forward page="jsp/result.jsp"></jsp:forward>
跳转语句之后,代码不会被执行:<%="我是一个跳转后的表达式语句"%>
</body>

也会使页面看起来比较杂乱。所以双剑合璧,在加上我们之前提到的JavaBean就构成了简易版的MVC模式:

在这种模式【模型(Model)、视图(View)和控制器(Controller)】下

  1. Web浏览器发送HTTP请求到服务端,被Controller(Servlet)获取并进行处理(例如参数解析、请求转发)
  2. Controller(Servlet)调用核心业务逻辑——Model部分,获得结果
  3. Controller(Servlet)将逻辑处理结果交给View(JSP),动态输出HTML内容

MVC模式在Web开发中的好处是非常明显,它规避了JSP与Servlet各自的短板,Servlet只负责业务逻辑而不会通过out.append()动态生成HTML代码;JSP中也不会充斥着大量的业务代码,如果JSP使用标签去显示甚至不需要业务代码。

创建第一个Servlet

创建和请求第一个Servlet的流程如下,通过IDEA非常简单:

创建Servlet类

通过idea创建Servlet非常简单,只需要创建选项时选择Servlet即可:

重命名Servlet

创建好后重命名为我们包含逻辑的Servlet,例如重命名为FirstServlet,IDEA默认为我们生成了代码:

package com.example.myfirstweb.controller; /**
 * * @Name ${NAME}
 * * @Description
 * * @author tianmaolin
 * * @Data 2021/7/19
 */
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "Servlet", value = "/Servlet")
public class FirstServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
}

需要注意的是在servlet3.0以后,我们可以不用再web.xml里面配置servlet,只需要加上@WebServlet注解就可以修改该servlet的属性了:

这个也算是注解的一个应用,解放配置化的东西,让参数可配置,关于注解可以参照我这篇Blog注解的基本概念和使用

修改注解并访问

注解的默认属性值如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package javax.servlet.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    String name() default "";
    String[] value() default {};
    String[] urlPatterns() default {};
    int loadOnStartup() default -1;
    WebInitParam[] initParams() default {};
    boolean asyncSupported() default false;
    String smallIcon() default "";
    String largeIcon() default "";
    String description() default "";
    String displayName() default "";
}

调整注解的value值为我们想要的url地址:

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + "这是第一个Servlet页面" + "</h1>");
        out.println("</body></html>");
    }

启动项目后输入该地址查看:

Servlet请求流程

当我们请求一个Servlet的时候发生了什么呢,完整的请求流程如下

  1. 用户在浏览器地址栏中输入请求URL地址:http://localhost:8080/myfirstweb/myFirstServlet
  2. Tomcat 服务器根据请求获取 URL 中最后需要访问的资源,即/myFirstServlet
  3. Tomcat 服务器根据获取的/myFirstServlet的url地址找到注解中url属性为该路径的Servlet
  4. 找到之后服务器实例化Servlet
  5. 执行Servlet对应的请求处理方法,例如doGet,完成给用户响应

以上就是一个完整的查找并响应的Servlet流程。如果没有匹配到对应路径,则界面会出现404异常

Servlet生命周期

Servlet 生命周期是由 Servlet 的容器来控制的,Servlet 生命周期分为 4 个阶段:

  1. 第一阶段:加载并实例化,Servlet 容器负责加载和实例化,在服务器启动或第一次请求时执行,如果需要服务器启动时加载,则需在注解中加入loadOnStartup配置(默认为-1总是启动时加载),数值越小优先级越高,加载后该Servlet实例将常驻内存
  2. 第二阶段:初始化,在 Servlet 实例化之后,容器将调用 init() 方法,并传递实现 ServletConfig 接口的对象,在 Servlet 的整个生命周期内,init()方法只被调用一次,一般根据需要是否添加init方法,如果Servlet需要初始化一些数据,可以在该方法中完成代码
  3. 第三阶段:请求处理,当容器收到对这一 Servlet 的请求,就调用 Servlet 的service() 方法,并把请求和响应对象作为参数传递,service() 方法检查 HTTP 请求类型(GET、POST等),并在适当的时候调用 doGet()、doPost() 等方法,每次请求都会调用对应的doGet或doPost方法
  4. 第四阶段:销毁,一旦 Web服务器停止服务,Servlet会自动执行其 destroy() 方法,以释放资源

我们可以看一下上述提到的一些方法的源码,从类继承关系的角度去看:

我们可以看到其实是从最顶层的Servlet接口来定义生命周期的,它定义了生命周期的几个默认方法:

同时我们可以看到,init方法在GenericServlet有两个重载方法,我认为tomcat使用了带有配合的init方法。

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

同时我们可以看到service方法是在HttpServlet类中具体实现的,就是来匹配请求方法的:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
            this.service(request, response);
        } else {
            throw new ServletException("non-HTTP request or response");
        }
    }
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    }

总结一下

JSP其实就是特殊的Servlet,创建一个Servlet也非常简单,需要注意的是要重新doGet或doPost方法,还有就是Servlet的继承结构和生命周期也需要详细理解下,总之Servlet是tomcat操作的对象,tomcat创建Servlet实例并接管它的全生命周期,而我们需要做的就是重写业务逻辑达成我们的目标

相关文章
|
15天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
32 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
1月前
|
Java Maven Spring
Java Web 应用中,资源文件的位置和加载方式
在Java Web应用中,资源文件如配置文件、静态文件等通常放置在特定目录下,如WEB-INF或classes。通过类加载器或Servlet上下文路径可实现资源的加载与访问。正确管理资源位置与加载方式对应用的稳定性和可维护性至关重要。
48 6
|
1月前
|
存储 安全 搜索推荐
理解Session和Cookie:Java Web开发中的用户状态管理
理解Session和Cookie:Java Web开发中的用户状态管理
60 4
|
1月前
|
Java 持续交付 项目管理
使用Maven进行项目管理:提高Java Web开发的效率
Maven 是一款强大的项目管理和构建自动化工具,广泛应用于Java社区。它通过依赖管理、构建生命周期管理、插件机制和多模块项目支持等功能,简化了项目的构建过程,提高了开发效率。本文将介绍Maven的核心功能及其在Java Web开发中的应用。
62 0
WK
|
1月前
|
安全 Java 编译器
C++和Java哪个更适合开发web网站
在Web开发领域,C++和Java各具优势。C++以其高性能、低级控制和跨平台性著称,适用于需要高吞吐量和低延迟的场景,如实时交易系统和在线游戏服务器。Java则凭借其跨平台性、丰富的生态系统和强大的安全性,广泛应用于企业级Web开发,如企业管理系统和电子商务平台。选择时需根据项目需求和技术储备综合考虑。
WK
69 0
|
4月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
40 3
|
4月前
|
存储 缓存 前端开发
Servlet与JSP在Java Web应用中的性能调优策略
Servlet与JSP在Java Web应用中的性能调优策略
37 1
|
4月前
|
存储 Java 关系型数据库
基于Servlet和JSP的Java Web应用开发指南
基于Servlet和JSP的Java Web应用开发指南
77 0
|
4月前
|
前端开发 安全 Java
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
在Java服务器端开发的浩瀚宇宙中,Servlet与JSP犹如两颗璀璨的明星,它们联袂登场,共同编织出动态网站的绚丽篇章。
31 0
|
6月前
|
自然语言处理 前端开发 Java
Servlet与JSP:Java Web开发的基石技术详解
【6月更文挑战第23天】Java Web的Servlet与JSP是动态网页的核心。Servlet是服务器端的Java应用,处理HTTP请求并响应;JSP则是结合HTML与Java代码的页面,用于动态内容生成。Servlet通过生命周期方法如`init()`、`service()`和`destroy()`工作,而JSP在执行时编译成Servlet。两者在MVC架构中分工,Servlet处理逻辑,JSP展示数据。尽管有Spring MVC等框架,Servlet和JSP仍是理解Web开发基础的关键。
112 12