Servlet入门

今日目标

  1. 1. 创建servlet
  2. xml
  3. anno(注解)
  4. 2. servlet执行原理
  5. 3. servlet生命周期
  6. 4. servlet体系结构

一 Servlet概述

  • servlet= server+applet 运行在服务器端的java小程序。
  • Servlet是一个接口(规范),一个类要想通过浏览器被访问到,那么这个类就必须直接或间接的实现Servlet接口

作用

接收请求,处理业务,响应结果

1590801764571.png

二 Servlet快速入门

目标:编写一个普通的java类,通过浏览器可以访问

#编写步骤 1. 定义一个类,实现Servlet接口,重写所有的抽象方法(特别是service方法) 2. 配置web.xml文件

2.1 代码编写

① 创建web项目

1586827330585.png

② 编写普通java类,实现servlet接口

重写抽象方法(service方法)

package com.itheima01.servlet;

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

/*
*  ctrl+i(implements) : 提示重写所有抽象方法
*   ctrl+ enter: 万能提示
* */
public class MyServlet implements Servlet {

    /*
    * servletRequest : 请求
    * servletResponse : 响应
    * */
    @Override
    public void service(ServletRequest servletRequest,
                        ServletResponse servletResponse) throws ServletException, IOException {

        System.out.println("接收到请求"); //控制台打印
        //网页上显示出来,不要写中文,会乱码
        servletResponse.getWriter().print("hello servlet");//响应给浏览器
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

③ 配置web.xml

配置servlet网络访问路径

<?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_3_1.xsd"
         version="3.1">

    <!--
        1. servlet和servlet-mapping标签里都有servlet-name子标签
            内部文本可以随意命名,但是必须一致
        2.  servlet-class标签: 写Servlet实现类的全限定名(包名+类名)
        3. url-pattern : 指定的是该Servlet的虚拟路径
    -->
    <servlet>
        <servlet-name>MyServlet01</servlet-name>
        <servlet-class>com.itheima01.servlet.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet01</servlet-name>
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>
</web-app>

④ 部署web项目

1586827863361.png

⑤ 启动测试

1586827999815.png

2.2 执行原理

1607759884731.png

三 Servlet相关API

3.1 生命周期方法

3.1.1 思想介绍

  • 生命周期:指的是一个对象从生(创建)到死(销毁)的一个过程
// 1. servlet对象创建时,调用此方法
public void init(ServletConfig servletConfig);
// 2. 用户访问servlet时,调用此方法 (每次访问都会调用一次)
public void service(ServletRequest servletRequest, ServletResponse servletResponse);
// 3. servlet对象销毁时,调用此方法
public void destroy();

1586831283207.png

* 创建
    1)默认情况下
        用户第一次访问时,创建servlet,执行init方法
    2)修改创建时机
        <load-on-startup></load-onstartup>
            正数:5-N  【服务器启动时,创建】
                补充:Tomcat的web.xml里有1,4 所以推荐5-n 
            负数(默认值):-1 【用户第一次访问时,创建】

* 运行(提供服务)
        用户每次访问时,都执行service方法

* 销毁
        服务器正常关闭时,销毁servlet,执行destroy方法

3.1.2 代码演示

① LifeServlet

package com.itheima02.life;

import javax.servlet.*;
import java.io.IOException;
/*
    # 观察
    1. 第一次访问LifeServlet
        init
        service

    2. 再次访问LifeServlet
        service

    3. 关闭tomcat之前
        destroy

   # Servlet中的API
   1. 生命周期方法(lifecycle)
        对象从创建到销毁的整个过程

      1). init : 初始化
            a. 默认情况下,浏览器第一次访问时调用
            b. 适合执行初始化相关的操作: 加载初始配置
      2). service : 服务
            a. 浏览器每次访问,都会执行一次
            b. 处理业务
      3). destroy
            a. tomcat在关闭之前,先执行此方法
            b. 适合释放资源,保存数据

   2. 启动加载
       1). 问题:   init方法默认浏览器第一次访问才调用,
                        如果这时候执行加载数据,为时太晚, 用户体验不好

       2). 解决: tomcat一启动就加载这个Servlet,并且调用init方法
       3). 方式:
                在web.xml的servlet下配置
 */
public class LifeServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("LifeServlet init");
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service");
    }

    @Override
    public void destroy() {
        System.out.println("destroy");
    }

    @Override
    public ServletConfig getServletConfig() {
        System.out.println("getServletConfig");
        return null;
    }



    @Override
    public String getServletInfo() {
        System.out.println("getServletInfo");
        return null;
    }


}

② 配置web.xml

<servlet>
        <servlet-name>LifeServlet</servlet-name>
        <servlet-class>com.itheima02.life.LifeServlet</servlet-class>
        <!--
            load-on-startup (写在servlet标签内)
            1. 取值是整数n
            2. 默认 n=-1,表示不启动加载(第一次访问才会被加载)
            3. 当n>=0的时候, 启动加载(数字越小,优先级越高)
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>LifeServlet</servlet-name>
        <url-pattern>/life</url-pattern>
    </servlet-mapping>

3.1.3 启动加载

# 启动加载
*       1)问题: 发现 init 默认第一次被访问的时候才调用,适合用来初始化项目数据
*           如果项目数据很多, 加载就需要一定的时间,这样就会给第一个用户的体验不好,因为要等比较久
*
*       2)解决: 服务器一启动,就执行init方法
*       
*       3) 实现: 要在web.xml配置
<?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_3_1.xsd"
         version="3.1">
    <servlet>
        <servlet-name>MyServlet_sb</servlet-name>
        <servlet-class>com.itheima01.servlet.MyServlet</servlet-class>
        <load-on-startup>6</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet_sb</servlet-name>
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>

    <!--
       # 启动加载
       1)问题: 发现 init 默认第一次被访问的时候才调用,适合用来初始化项目数据
           如果项目数据很多, 加载就需要一定的时间,这样给第一个用户打来的体验不好,因为要等比较久

       2)解决: 服务器一启动,就执行init方法

       3) 实现: 要在web.xml配置
            1). servlet标签内补充一个子标签 load-on-startup (启动时加载)
            2). 在load-on-startup里写一个非负整数
                n >= 0  , 数字越小,优先级越高 (我们自己写n=5开始写, 0~4被tomcat使用掉了)
                默认n=-1, 表示启动时不加载
    -->
    <servlet>
        <servlet-name>LifecycleServlet</servlet-name>
        <servlet-class>com.itheima02.life.LifecycleServlet</servlet-class>
        <load-on-startup>5</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>LifecycleServlet</servlet-name>
        <url-pattern>/LifecycleServlet</url-pattern>
    </servlet-mapping>
</web-app>

四 Servlet体系结构

快捷键:
    1. ctrl + alt + u : 查看一个类的继承结构图
    2. ctrl + h : 这个类的简化版继承结构(类)

1586832839002.png

4.1 GenericServlet

1. 问题: 
    Servlet中使用频率最高,最重要的方法是service方法(大部分场景)
    但是我们每次编写Servlet实现类,都是直接实现Servlet接口,重写5个抽象方法(太冗余了)

2. 需求: 如果我们以后编写Servlet实现类,只要重写service方法就好了
3. 解决:
        1). 建立一个抽象类GenericServlet, 继承Servlet接口,重写除了service之外的四个抽象方法,空实现    
        2). 以后编写其他Servlet,只要继承 GenericServlet, 这时候就只要重写service方法

4. 发现 : 很巧, 开发包已经有了这个类GenericServlet

① 编写普通java类,继承GenericServlet抽象类

public class GoodServlet extends GenericServlet {

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("GoodServlet被访问");
    }

}

② 配置web.xml

    <servlet>
        <servlet-name>GoodServlet</servlet-name>
        <servlet-class>com.itheima03.generic.GoodServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>GoodServlet</servlet-name>
        <url-pattern>/goodServlet</url-pattern>
    </servlet-mapping>

4.2 HttpServlet

1. 问题: 
      我们在前端的form表单中,method属性, 学习过有两种常用的请求方式(get/post)
      我们现在的service方法是这样的: 用户发送请求,无论是什么请求方式,都会统一的执行service方法, 我们无法很好的区别是哪一种请求方式

2. 需求: 我们如果想确切的知道是哪一种请求方式,必须要先了解HttpServletRequest

3. 解决: 自己封装了一个MyHttpServlet  -> 开发包 HttpServlet

① 编写前端html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

        <a href="http://localhost:8080/day04-servlet/HahaServlet">HahaServlet</a>
        <form action="http://localhost:8080/day04-servlet/HahaServlet" method="post">
            <input type="submit">
        </form>
</body>
</html>

② 编写普通java类,继承HttpServlet抽象类

package com.itheima04.http;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
    HttpServlet是GenericServlet的子类

    1. HttpServlet重写了service方法
    2. 在service方法中,根据请求方式(http协议)的不同,将service拆分成不同的方法
         1). 有两种常用的请求方式
                get
                post
         2). 用户发送请求时(用户在浏览器中访问servlet的地址)
                service方法就会执行 (接着就会判断)
                    get请求方式 -> doGet
                    post请求方式 -> doPost

 */
public class HahaServlet extends HttpServlet {

    //这是post请求方式的service方法
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("post请求方式");
    }
    //基本上请求方式默认都是get
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get请求方式");
    }
}

③ 配置web.xml

 <servlet>
        <servlet-name>HahaServlet</servlet-name>
        <servlet-class>com.itheima04.http.HahaServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HahaServlet</servlet-name>
        <url-pattern>/HahaServlet</url-pattern>
    </servlet-mapping>

4.3 经验值分享

① 响应状态码405

请求方法没有重写…..

    /*
        注意: 如果我们不重写doGet/doPost方法, 那么父类的doGet/doPost方法会执行(继承)
            给浏览器响应一个错误: 状态码405 (http1.1)
     */

1586835525081.png

② 响应状态码500

java代码写错了…

1586835683969.png

五 Servlet路径

5.1 url-pattern

作用:将一个请求网络地址和servlet类建立一个映射关系
1586835899369.png

5.1.1 Servlet映射多个url

1586836124261.png

5.1.2 url映射模式【了解】

配置 url地址取值可以是:

1. 精确匹配(掌握) 
         /servletDemo3  
2. 目录匹配 
        /aa/*
3. 后缀匹配 
        *.xxx    例如:*.do
<servlet-mapping>
        <servlet-name>PathServlet</servlet-name>
        <!--
            Servlet的路径(虚拟路径)
            1. 一个Servlet可以配置多个虚拟路径  -> 了解 (在Filter中有用)
            2. 一个Servlet的路径有三种写法
                2.1  精确匹配(掌握)
                        /servletDemo3
                2.2  目录匹配
                        /aa/*
                2.3 后缀匹配
                        *.xxx    例如:*.do

        -->
        <url-pattern>/bbb/ccc</url-pattern>
        <url-pattern>/aa/*</url-pattern>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
<servlet-mapping>
    <servlet-name>PathServlet</servlet-name>
    <url-pattern>/pathServlet03</url-pattern>
</servlet-mapping>

5.2 相对/绝对路径

  • 现阶段我们访问资源的方式越来越多,请求路径在编写时难免出现混淆
  1. 浏览器的地址栏 (输入服务器某个资源地址,敲回车会发送请求)
  2. a标签的href属性 (超链接被点击的时候会发送请求)
  3. form表单的action属性 (form中的submit按钮被点击的时候,发送请求)
  4. js的loation.href属性 (只要设置, 触发相应的)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="http://www.baidu.com">超链接</a> <br>

    <form action="http://www.baidu.com">
        <input type="text" name="username"> <br>
        <input type="submit">
    </form>

    <input type="button" value="按钮" id="myid">
    <script>
        //js
        document.getElementById("myid").onclick = function () {
            //bom对象: window,location地址栏
            location.href = "http://www.baidu.com"
        }
    </script>
</body>
</html>

这里我们复习下路径规则:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        <!--
            html页面中,难免书写url

            1. 绝对路径
                a. http://localhost:8080/day01-servlet/PathServlet
                b. /day01-servlet/PathServlet (省略了三要素,前提: 当前页面和所访问的资源必须在同一服务器上)
                        内部资源, 推荐!!!

            2. 相对路径
                1) ../ 是上一级
                2) ./ 是平级,可以省略
        -->
        <a href="http://www.baidu.com">百度</a> <br>
        <a href="http://localhost:8080/day01-servlet/PathServlet">我的PathServlet</a> <br>
        <a href="/day01-servlet/PathServlet">我的PathServlet2</a> <br>

        <a href="http://localhost:8080/day01-servlet/doc/aaa.html">aaa.html绝对路径写法</a>
        <a href="doc/aaa.html">aaa.html相对写法</a>
</body>
</html>

六 Servlet3.0

  • 通过注解配置Servlet,简化web.xml配置Servlet复杂性,提高开发效率,几乎所有的框架都在使用注解

① 创建web工程【注解】

1586845454312.png

② 编写普通java类,继承HttpServlet

public class QuickServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("QuickServlet....3.0");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("QuickServlet....3.0");
    }
}

③ 配置@WebServlet

1586845665415.png

// @WebServlet(name = "QuickServlet",urlPatterns = "/quickServlet")
// @WebServlet(urlPatterns = "/quickServlet")
// @WebServlet(value = "/quickServlet")
@WebServlet("/quickServlet") // 注解中有且仅有一个属性,名为value时,属性名可以省略...
public class QuickServlet extends HttpServlet {

}

idea创建web模块的规律

1590802580581.png

tomcat可以同时运行多个web模块,但是现在一般只运行一个模块

注意: project structure中要设置这两项

1590803666349.png