JavaWeb系列六: 动态WEB开发核心(Servlet) 上

发布于:2024-06-29 ⋅ 阅读:(12) ⋅ 点赞:(0)

在这里插入图片描述

官网文档

官网文档: https://tomcat.apache.org/tomcat-8.0-doc/servletapi/index.html

离线文档
JAVA_EE_api_中英文对照版

●Servlet和Tomcat的关系: 一句话, Tomcat支持Servlet
在这里插入图片描述

为什么会出现Servlet

1.提出需求: 请用你现有的html css javascript, 开发网站, 比如可以让用户留言/购物/支付, 你能搞定吗?

2.引入我们动态网页(能和用户交互)技术 ⇒ Servlet

3.对Java Web技术体系的流程图改造说明(细化).[整体的概念]
在这里插入图片描述

什么是Servlet

Servlet在开发动态WEB工程中, 得到广泛的应用. 掌握好Servlet非常重要, Servlet(基石)是SpringMVC的基础

Servlet(java 服务器小程序), 它的特点:
1.它是由服务器端调用和执行的(一句话: 是Tomcat解析和执行)
2.它使用java语言编写的, 本质就是Java
3.它是按照Servlet规范开发的(除了tomcat->Servlet weblogic->Servlet)
4.功能强大, 可以完成几乎所有的网站功能(在以前, 老程序员, 使用Servlet开发网站) 技术栈要求高, 内卷(硬起来)

Servlet在JavaWeb项目位置

在这里插入图片描述

Servlet基本使用

Servlet开发方式说明

1.servlet3.0前使用web.xml, servlet3.0版本以后(包括3.0)支持注解, 同时支持web.xml配置

2.如何查看servlet版本[如图]
在这里插入图片描述

3.讲解SpringBoot时, 我们用注解方式, 从ssm, springboot后面全部使用注解

4.这里专门讲servlet, 为让大家更清晰地知道servlet使用原理, 我们用配置方式(说明, 原生的Servlet在项目中使用很少)

5.不管使用哪种方式, 本质都一样

快速入门- 手动开发 servlet

需求说明
1.开发一个HelloServlet
2.当浏览器 访问 http://localhost:8080/web应用名/helloServlet时, 后台输出 hi HelloServlet

●具体步骤
1.编写类HelloServlet去实现Servlet接口
2.实现service方法, 处理请求, 并响应数据
3.在web.xml中去配置servlet程序的访问地址

1.创建servlet Java Web工程, 并配置好Tomcat. IDEA开发部署JavaWeb工程

2.添加servlet-api.jar(在tomcat/lib下) 到工程, 因为servlet.jar 不是jdk自带的, 要引入

3.在src目录 com.zzw.servlet包下新建HelloServlet.java, 并实现Servlet接口

package com.zzw.servlet;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author 赵志伟
 * @version 1.0
 * 1.开发一个Servlet需要实现Servlet接口
 * 2.实现Servlet接口的五个方法
 */
@SuppressWarnings({"all"})
public class HelloServlet implements Servlet {

    private int count;

    /**
     * 1.初始化 servlet
     * 2.当创建HelloServlet实例时, 会调用init方法
     * 3.该方法只会被调用一次
     *
     * @param servletConfig
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init() 被调用");
    }

    /**
     * 返回ServletConfig 也就是返回Servlet的配置
     *
     * @return
     */
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /**
     * 1.service方法处理浏览器的请求(包括get和post)
     * 2.当浏览器每次请求Servlet时,就会调用一次service方法
     * 3.当tomcat调用该方法时,会把http请求的数据封装成实现了ServletRequest接口的request对象
     * 4.通过servletRequest对象,可以得到用户提交的数据
     * 5.servletResponse对象可以用于返回数据给tomcat->浏览器
     *
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest,
                        ServletResponse servletResponse) throws ServletException, IOException {
        //如果count的值在不停地累积, 说明HelloServlet是单例的
        System.out.println("hi HelloServlet~, count=" + count++);
    }

    /**
     * 返回servlet的信息,使用较少
     * @return
     */
    @Override
    public String getServletInfo() {
        return null;
    }

    /**
     * 1.该方法是在servlet被销毁时,调用
     * 2.只会调用一次
     */
    @Override
    public void destroy() {

    }
}

web.xml注释时发现前面有空格
在这里插入图片描述
解决方案 Settings->Editor->Code Style->XML, 取消勾选
在这里插入图片描述
IDEA配置快捷键: settings->Keymap
重启(Alt+R)选择重新部署,速度快, 不要选Restart server, 这样会把JDK都重启, 速度很慢
在这里插入图片描述

  1. 在web.xml文件中配置由Tomat访问HelloServlet的路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--小技巧:web.xml主要用来配置该web应用使用到的Servlet-->
    <!--
        1.servlet-name: 给Servlet取名(由程序员决定), 这个名字是不能重复取的
        2.servlet-class:该Servlet的类的全路径, Tomcat在反射生成Servlet时需要使用
        3.url-pattern: 这个就是访问该Servlet的url地址
          这时我们可以这样访问:http://localhost:8080/servlet/helloServlet
        4.url-pattern 访问地址是由程序员决定的
        5.load-on-startup 表示在tomcat 启动时, 会自动地加载servlet实例
        小技巧: alt+R -> Redeploy选择重新部署, 速度更快
    -->
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.zzw.servlet.HelloServlet</servlet-class>
        <load-on-startup>1</load-on-startup> 
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/helloServlet</url-pattern>
    </servlet-mapping>
</web-app>

浏览器请求Servlet UML分析

在这里插入图片描述

Servlet生命周期

●主要有三个方法
1.init方法
2.service方法
3.destroy方法

在这里插入图片描述

●初始化阶段

Servlet容器(比如:Tomcat) 加载Servlet, 加载完成后, Servlet容器会创建一个Servlet实例并调用init方法(创建的实例放入HashMap), init方法只会调用一次, Servlet容器在下面的几种情况下会装载Servlet:

在这里插入图片描述
1.Servlet容器(比如: Tomcat)启动时自动装载某些servlet, 实现这个需要在web.xml文件中添加
<load-on-startup>1</load-on-startup> 1表示装载的顺序
2.在Servlet容器启动后, 浏览器首次向Servlet发送请求
3.Servlet重新装载时(比如tomcat 进行redeployredeploy会销毁所有的Servlet实例】), 浏览器再向Servlet发送请求的第1

●处理浏览器请求阶段(service方法)
1.每收到一个http请求, 服务器就会产生一个新的线程处理[线程]
2. 创建一个用于封装HTTP请求消息的ServletRequest对象和一个代表HTTP响应消息的ServletResponse对象
3. 然后调用Servletservice()方法并将请求和响应对象, 作为参数传递进去


●终止阶段(destroy方法)(体现Servlet完整的生命周期)
web应用被终止, 或者Servlet容器终止运行, 或者Servlet类重新装载时, 会调用destroy()方法, 比如重启tomcat, 或者 redeploy web应用

GET和POST请求分发处理

●开发Servlet, 通常编写doGet, doPost方法, 来对表单的getpost请求进行分发处理

●代码演示
1.web路径下新建register.html

<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet3/helloServlet" method="post">
    u: <input type="text" name="username"/><br/>
    <input type="submit" value="注册用户"/>
</form>
</body>

2.修改HelloServletservice方法完成分发请求

/**
 * 1.service方法处理浏览器的请求(包括get和post)
 * 2.当浏览器每次请求Servlet时, 就会调用一次service方法
 * 3.当tomcat调用该方法时, 会把http请求的数据封装成实现了ServletRequest接口的request对象
 * 4.通过servletRequest对象, 可以得到用户提交的数据
 * 5.servletResponse对象可以用于返回数据给tomcat->浏览器
 *
 * @param servletRequest
 * @param servletResponse
 * @throws ServletException
 * @throws IOException
 */
@Override
public void service(ServletRequest servletRequest,
                    ServletResponse servletResponse) throws ServletException, IOException {
    //如果count的值在不停地累积, 说明HelloServlet是单例的
    System.out.println("hi HelloServlet ~~count="+ ++count);
    //Tomcat每处理一次http请求, 就生成一个新的线程
    System.out.println("当前线程id=" + Thread.currentThread().getId());

    //思考->从servletRequest对象来获取请求方式
    //1.ServletRequest 没有得到提交方式的方法
    //2.ServletRequest 看看ServletRequest子接口有没有相关方法
    //3.老师小技巧: ctrl+alt+b 可以看到接口的子接口和实现子类
    //4.把servletRequest转成 HttpServletRequest引用
    //5.仍然是Java基础的OOP
    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    String method = httpServletRequest.getMethod();
    System.out.println("method=" + method);//method=GET 大写的
    if ("GET".equals(method)) {
        doGet();//用doGet() 处理GET请求
    } else if ("POST".equals(method)) {
        doPost();//用doPost() 处理POST请求
    }
}

/**
 * 用于响应get请求
 */
public void doGet() {
    System.out.println("HelloServlet doGet()...");
}

/**
 * 用于响应post请求
 */
public void doPost() {
    System.out.println("HelloServlet doPost()...");
}

3.测试…

通过继承HttpServlet开发Servlet

HttpServlet介绍
在实际项目中, 都是使用继承 HttpServlet 类来开发 Servlet 程序, 更加方便

HttpServlet类图如下
在这里插入图片描述

Http介绍
1.通过继承HttpServlet开发一个HiServlet
2.当浏览器 访问 http://localhost:8080/web应用名/hiServlet时,后台输出hi HiServlet

●具体的开发步骤
1.编写一个类去继承 HttpServlet
2.根据业务需要重写doGetdoPost 方法
3.到 web.xml 中配置 Servlet程序

1.com.zzw.servlet包下新建HiServlet

public class HiServlet extends HttpServlet {

    //重写HttpServlet的doGet 和 doPost
    //alt + insert
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HiServlet doGet()...");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HiServlet doPost()...");
    }
}

2.配置web.xml

<!--配置HiServlet-->
    <servlet>
        <servlet-name>HiServlet</servlet-name>
        <servlet-class>com.zzw.servlet.HiServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HiServlet</servlet-name>
        <url-pattern>/hiServlet</url-pattern>
    </servlet-mapping>

IDEA配置Servlet

在WEB-INF目录下的lib文件夹下导入servlet-api.jar包并且Add As Library后右键才有新建Servlet的选项

在这里插入图片描述
在这里插入图片描述
会自动生成Servlet类和web.xml配置

public class OkServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //可以写自己的业务处理代码
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //可以写自己的业务处理代码
    }

web.xml自动生成以下半截代码, 这里需要手动配置url-pattern
在这里插入图片描述

Servlet注意事项和细节

  1. Servlet是一个供其它Java程序(Servlet)引擎调用的Java类, 不能独立运行
  2. 针对浏览器的多次Servlet请求, 通常情况下, 服务器只会创建一个Servlet实例对象, 也就是说Servlet实例对象一旦创建, 它就会驻留在内存中,存在于Tomcat维护的HashMap中, 为后续的其他请求而服务, 直至web容器退出/或者redeploy该web应用, servlet实例对象才会销毁, 从HashMap中移除
  3. 在Servlet的整个生命周期中,init方法只被调用一次, 而对每次请求都导致Servlet引擎调用一次servlet的service方法
  4. 对于每次访问请求, Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象, 然后将这两个对象作为参数传递给它调用的Servlet的service()方法, service方法再根据请求方式分别调用doGet()方法和doPost()方法
  5. 如果在<servlet>元素中配置了一个<load-on-startup>元素, 那么web应用程序在启动时, 就会装载并创建Servlet的实例对象, 以及调用Servlet实例对象的init()方法

Servlet注解方式

快速入门

省去了在web.xml配置的麻烦

方法外不能使用.var

/**
 * 显示方法之间的分割线 Editor->General->Appearance->Show method separators
 * 1.@WebServlet是一个注解
 * 2.@WebServlet的源码
 * 3.urlPatterns 对应web.xml中的 <url-Pattern></url-Pattern>
 *   {"/ok1", "/ok2"} 可以给OkServlet配置多个 urlPattern
 *   相当于这个@WebServlet(urlPatterns={"/ok1", "/ok2"}) 代替了web.xml的配置
 * 4.浏览器可以这样访问OkServlet: http://localhost:8080/servlet/ok1 或者 http://localhost:8080/servlet/ok2
 */
@WebServlet(urlPatterns = {"/ok1", "/ok2"})
public class OkServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("annotation doGet method");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("annotation doPost method");
    }
}

@WebServlet注解源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented => 在javadoc工具生成记录
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 "";
}

注解详解

在这里插入图片描述

说明: 注解是对包进行扫描, 如果发现某个类是用@WebServlet, 就说明该类是Servlet, 然后读取urlPatterns, 看看请求的资源和urlPatterns的参数是否一致

●模拟代码

/**
 * @author 赵志伟
 * @version 1.0
 * 模拟Tomcat是如何通过注解@WebServlet(urlPatterns={"/ok1", "/ok2"})来装载一个Servlet的
 */
@SuppressWarnings({"all"})
public class TestAnnotationServlet {

    private static final HashMap<String, HttpServlet> hm = new HashMap<>();
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //1.首先要得到扫描的包 路径 IO, 进而得到类的全路径
        String path = "com.zzw.servlet.annotation.OkServlet";
        //2.得到OkServlet的Class对象
        Class<?> aClass = Class.forName(path);
        //3.根据class对象得到对应的注解
        WebServlet annotation = aClass.getAnnotation(WebServlet.class);
        String[] urlPatterns = annotation.urlPatterns();
        for (String urlPattern : urlPatterns) {
            System.out.println("urlPattern=" + urlPattern);
        }
        //如果匹配url, 如果是第一次, tomcat就会创建一个OkServlet实例, 放入到HashMap中
        Object instance = aClass.newInstance();
        System.out.println("instance=" + instance);//OkServlet实例

        hm.put("OkServlet", (HttpServlet) instance);
        System.out.println(hm);
    }
}

initParams

@WebServlet(urlPatterns = {"/ok1", "/ok2"}, initParams = {@WebInitParam(name = "zzw", value = "123"), @WebInitParam(name = "xx", value = "yy")})
public class OkServlet extends HttpServlet {}

等同于

<servlet>
        <servlet-name>OkServlet</servlet-name>
        <servlet-class>com.zzw.servlet.OkServlet</servlet-class>

        <init-param>
            <param-name>zzw</param-name>
            <param-value>123</param-value>
        </init-param>
        <init-param>
            <param-name>xx</param-name>
            <param-value>yy</param-value>
        </init-param>
    </servlet>

Servlet urlPattern 配置

精准匹配

@WebServlet(urlPatterns = {"/ok1/aa/bb", "/ok2"}

目录匹配

@WebServlet(urlPatterns = {"/ok1/*", "/ok2"})
http://localhost:8888/servlet/ok1
http://localhost:8888/servlet/ok1/aa
http://localhost:8888/servlet/ok1/aa/bb/cc

扩展名匹配

@WebServlet(urlPatterns = {"*.action", "/ok2"}
http://localhost:8888/servlet/zzw.action
http://localhost:8888/servlet/zzw.521.action

3.2不能带斜杠

@WebServlet(urlPatterns = {"/*.action", "/ok2"},这样是错误的

任意匹配

@WebServlet(urlPatterns = {"/", "/ok2"}
或者
@WebServlet(urlPatterns = {"/*", "/ok2"}
http://localhost:8888/servlet/aa/bb/cc
http://localhost:8888/servlet/zzw.521.action

注解注意事项

shortcuts: ctrl+alt+⬅ 回到上次访问的窗口

1.当Servlet配置了"/", 会覆盖掉tomcat的DefaultServlet, 当其它的的url-pattern都匹配不上时, 就会走这个默认的Servlet, 这样可以拦截到其它请求中的静态资源

这个默认的Servlet是处理静态资源的, 一旦拦截, 静态资源则不能处理

 <!-- The default servlet for all web applications, that serves static     -->
  <!-- resources.  It processes all requests that are not mapped to other   -->
  <!-- servlets with servlet mappings (defined either here or in your own   -->
  <!-- web.xml file). -->
	<servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

2.当Servlet配置了"/*", 表示可以匹配任意访问路径

3.建议不要使用/ 和 /*, 尽量使用精准匹配

4.优先级遵守: 精确路径 > 目录路径 > 扩展名路径 > /* > / > DefaultServlet(访问静态资源)

访问:http://localhost:8888/servlet/ok1/aa
@WebServlet(urlPatterns = {"/ok1/aa"}//优先
@WebServlet(urlPatterns = {"/ok1/*"}