JavaWeb之Servlet技术

Posted 生命是有光的

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaWeb之Servlet技术相关的知识,希望对你有一定的参考价值。

✍目录总览

✍javaEE进阶之旅

🔥javaEE进阶之旅地址
🔥JavaWeb之HTTP协议(一)https://blog.csdn.net/Augenstern_QXL/article/details/119580669
🔥JavaWeb之Servlet技术(二)https://blog.csdn.net/Augenstern_QXL/article/details/119581366
🔥JavaWeb之JSP技术(三)https://blog.csdn.net/Augenstern_QXL/article/details/119581658
🔥JavaWeb之Cookie和Session技术(四)https://blog.csdn.net/Augenstern_QXL/article/details/119713398
🔥JavaWeb之Filter和Listener技术(五)https://blog.csdn.net/Augenstern_QXL/article/details/119725322

1.Servlet

1.1、Servlet简介

  • Servlet就是sun公司开发动态web的一门技术

  • Sun在API中提供了一个接口:Servlet ,如果逆向开发一个Servlet程序,只需要完成两个小步骤

    • 编写一个类,实现Servlet接口
    • 把开发好的Java类部署到web服务器中
  • 把实现了Servlet接口的Java程序叫做Servlet

1.2、HelloServlet

  1. 构建一个普通的Maven项目

  1. 删掉里面的Src目录,这个空的工程就是Maven主工程

  2. pom.xml 里面添加 servlet,jsp 依赖

    • Maven Repository仓库里面搜索 javax-servlet-api 和 javax.servlet.jsp-api
    <dependencies>
            
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    
    
    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
    </dependency>
    
        
    </dependencies> 
    
  3. Load Maven changes

  1. 新建Module

  1. 父项目的pom.xml文件有
<modules>
        <module>servlet-01</module>
</modules>
  1. 子项目的pom.xml文件有
<parent>
        <artifactId>javaweb-02-maven</artifactId>
        <groupId>com.kuang</groupId>
        <version>1.0-SNAPSHOT</version>
</parent>

父项目中的java子项目可以直接使用,意思其实就是子项目可以不用再次导入jar包,直接可以使用父项目导入的jar包

7.在 main 目录下新建Directory javaresources

​ 并且右键-Mark Directory as ,分别转换为java目录和资源目录

  1. Maven环境优化

    • 修改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_4_0.xsd"
              version="4.0"
              metadata-complete="true">
      </web-app>
      

上面这一大串我们是从哪里来的呢?其实我们是在tomcat\\webapps\\ROOT\\WEB-INF\\web.xml 中与tomcat相对应的版本

  1. 新建包,新建类

  1. 写一个Servlet程序,Serlvet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet
  • 编写一个普通类
  • 实现Servlet接口,这里我们直接继承HttpServlet
  • 重写doGet、doPost方法
  • 编写Servlet的映射

为什么需要映射?

  • 我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要再web服务中注册我们写的Servlet,还需给他一个浏览器能够访问的路径

2.继承HttpServlet

public class HelloServlet extends HttpServlet {
    // get或者post只是请求实现的不同方式,可以互相调用,业务逻辑都一样
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入doGet方法");
        // 在这边重写完方法之后就去web.xml里面编写Servlet的映射
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

3.编写Servlet的映射

<!--    1.注册servlet-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<!--    servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 配置并启动tomcat

  1. 请求我们自己映射的/hello

1.3、Mapping问题

  1. 一个Servlet可以指定一个映射路径
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello-->
<servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定多个映射路径
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello2-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello3-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello3</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello4-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello4</url-pattern>
</servlet-mapping>
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello5-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello5</url-pattern>
</servlet-mapping>
  1. 一个Servlet可以指定通用映射路径
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello/****-->
<!--	****代表任何字母数字均可	-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello/*</url-pattern>
</servlet-mapping>
  1. 默认请求路径
<!--默认请求路径-->
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/****-->
<!--	****代表任何字母数字均可	-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
  1. 指定一些后缀或者前缀等等….
<!--可以自定义后缀实现请求映射-->
<!--注意点,*前面不能加项目映射的路径-->
<!--当我们这样配置映射路径的时候,我们访问的名称为:localhost:8080/s1/hello/****.qinjiang-->
<!--  例如:localhost:8080/s1/hello/sajdlkajda.qinjiang	-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>*.qinjiang</url-pattern>
</servlet-mapping>
  1. 优先级问题
<servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
</servlet-mapping>

<!--404-->
<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.kuang.servlet.Error</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

例如当我们配置上方映射时候,我们访问:localhost:8080/s1/hello ,思考此时是访问/*还是访问/hello呢?

  • 答案是访问/hello
  • 指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
  • 带有通配符的映射方式,有斜杠/的比没斜杠/的优先级高
    • 例3比例5的优先级高

1.4、Servlet生命周期

对象的生命周期,就是对象从生到死的过程,即:出生——活着——死亡。用更偏向 于开发的官方说法就是对象创建到销毁的过程。

出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。

活着:服务器提供服务的整个过程中,该对象一直存在,每次只是执行service方法。

死亡:当服务停止时,或者服务器宕机时,对象消亡。

通过分析Servlet的生命周期我们发现,它的实例化和初始化只会在请求第一次到达Servlet时执行,而销毁只会在Tomcat服务器停止时执行,由此我们得出一个结论,Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

1.4.1、启动时创建Servlet

我们前面讲解了Servlet的生命周期,Servlet的创建默认情况下是请求第一次到达Servlet时创建的。但是我们都知道,Servlet是单例的,也就是说在应用中只有唯一的一个实例,所以在Tomcat启动加载应用的时候就创建也是一个很好的选择。那么两者有什么区别呢?

  • 第一种:应用加载时创建Servlet,它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。它的弊端也同样明显,因为在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。
  • 第二种:请求第一次访问是创建Servlet,它的优势就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。而它的弊端就是,如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。

通过上面的描述,相信我们都能分析得出何时采用第一种方式,何时采用第二种方式。就是当需要在应用加载就要完成一些工作时,就需要选择第一种方式。当有很多Servlet的使用时机并不确定是,就选择第二种方式。

在web.xml中是支持对Servlet的创建时机进行配置的,配置的方式如下:我们就以ServletDemo3为例。

  • 配置标签:<load-on-startup> 1 </load-on-startup>

  • 配置项的取值只能是正整数(包括0),数值越小,表明创建的优先级越高

<!--配置ServletDemo3-->
<servlet>
    <servlet-name>servletDemo3</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo3</servlet-class>
    <!--配置Servlet的创建顺序,当配置此标签时,Servlet就会改为应用加载时创建
        配置项的取值只能是正整数(包括0),数值越小,表明创建的优先级越高
    -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>servletDemo3</servlet-name>
    <url-pattern>/servletDemo3</url-pattern>
</servlet-mapping>

1.4.2、默认Servlet

默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中

<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>

它的映射路径是<url-pattern>/<url-pattern>,我们在发送请求时,首先会在我们应用中的web.xml中查找映射配置,找到就执行,这块没有问题。但是当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。所以,一切都是Servlet。

1.5、Servlet线程安全

  • 由于Servlet运用了单例模式,即整个应用中只有一个实例对象,所以我们需要分析这个唯一的实例中的类成员是否线程安全。

  • 在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,所以说明了多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为Servlet它不是线程安全的。

  • 分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。

  • 解决这个问题也非常简单,就是在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。

1.6、Servlet原理

Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:

2.ServletContext

2.1、基本介绍

  • ServletContext对象,它是应用上下文对象
  • web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用
  • 它可以实现让应用中所有Servlet间的数据共享。

2.2、生命周期

出生——活着——死亡

出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)

活着:只要应用一直提供服务,该对象就一直存在。

死亡:应用被卸载(或者服务器挂了),该对象消亡。

2.3、域对象

在Servlet规范中,一共有4个域对象。ServletContext就是其中一个。它也是我们接触的第一个域对象。它是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间的数据共享功能。

2.4、共享数据

  • 我们在这个Servlet中保存的数据,可以在另外一个servlet中拿到
  • 但是都是通过同一个ServletContext对象拿到的
    • context.setAttribute(String name,Object value)
    • context.getAttribute(String name)

HelloServlet.java

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        //数据
        String username = "秦疆"; 
         //将一个数据保存在了ServletContext中,名字为:username 。值 username
        context.setAttribute("username",username);

    }
}
  • 编写mapper映射
<!--    存放username数据	-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

GetServlet.java

public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) 			context.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
         // 将获取到的数据显示在网页中
        resp.getWriter().print("名字"+username);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 编写mapper映射
<!--    获取username数据-->
<servlet>
    <servlet-name>getc</servlet-name>
    <servlet-class>com.kuang.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getc</servlet-name>
    <url-pattern>/getc</url-pattern>
</servlet-mapping>

测试访问结果;

  • 先输入 : http://localhost:8080/s2/hello 访问/hello会存放数据
  • 再输入:http://localhost:8080/s2/getc 访问/getc会取出数据

2.5、获取初始化参数

  • 获取初始化参数:context.getInitParameter("参数名")

ServletContext既然被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。

我们可以在web.xml中配置一些web应用初始化参数,在ServletContext对象中可以拿到它

配置的方式,需要在<web-app>标签中使用<context-param>来配置初始化参数。具体代码如下:

GetInitParameter.java

/**
 * 获取初始化参数
 */
public class GetInitParameter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        // 将获取到的url打印到网页上
        resp.getWriter().print(url);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

在web.xml中配置初始化参数和注册servlet映射为 /gp

<!--配置初始化参数 参数为url,值为 jdbc:mysql://localhost:3306/mybatis  -->
<context-param>
    <!--用于获取初始化参数的key-->
    <param-name>url</param-name>
    <!--初始化参数的值-->以上是关于JavaWeb之Servlet技术的主要内容,如果未能解决你的问题,请参考以下文章

JavaWeb核心之Servlet

javaWeb核心之servlet

JavaWeb核心之Servlet

JavaWeb核心之Servlet

JavaWeb之Servlet技术

JavaWeb核心之Servlet