JavaWeb——Servlet(笔记总结超详细)



Servlet

Servlet之前,我们首先得学Tomcat服务器。Tomcat是一个Web服务器(同时也是Servlet容器),通过它我们可以很方便地接收和返回到请求。可以结合下面两篇文章一起看

Tomcat基本认识:https://blog.csdn.net/qq_47256069/article/details/117199512

IDEA配置Maven+Tomcat构建Web项目:https://blog.csdn.net/qq_47256069/article/details/116140082

什么是Servlet?

Servlet是JavaEE规范之一,规范就是接口ServletJavaWeb三大组件之一,三大组件分别是:Servlet程序Filter过滤器Listener监听器。它是一个能够实现动态web页面,在 JavaWeb 项目中,它可以接受客户端发过来得请求,并响应数据给客户端Servlet其实就是一个遵循Servlet开发的java类,Serlvet是由服务器调用的,运行在服务器端(例如Tomcat服务器上)

在开始之前先了解一下/(斜杠)路径的问题(所有的相对路径在工作时候都会参照当前浏览器地址栏中的地址来进行跳转)

第一种

/ 如果被浏览器解析,得到的地址是http://ip:port/(例如表单、还有a标签、请求重定向等)

<a href="/">a标签</a>
...

第二种

/ 如果被服务器解析,得到的地址是http://ip:port/工程路径/(且映射到IDEA就是Web工程,这里的Web工程就是webapp目录

//例如Servlet程序的
req.getRequestDispatcher("/logout.html").forward(req,resp);//‘/’后面的内容表示的是webapps的子目录即某一项目名称
...

或者是…(下面例子)等

<url-pattern>/show</url-pattern>

实现Servlet接口

Servlet的核心技术是Servlet,我们编写Servlet程序必须直接或者间接实现的一个接口。在学Servlet的时候,我们先了解一下Servlet生命周期继承体

Servlet生命周期

Servlet生命周期大致可以分为4个步骤

  • 执行Servlet构造器方法,加载Servlet的时候。如果Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例(第一次访问Servlet程序的时候,构造器、init()service()都被调用了
  • 执行init()初始化方法,init英译过来是初始化的意思,当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象。
  • 执行service()方法,所有的请求都是service()方法处理的,当浏览器访问Servlet程序的时候,Servlet
    会调用service()方法处理请求(第二次访问Servlet程序的时候,service()被调用了,每次访问都会调用
  • 执行destroy()销毁方法,当web工程停止的时候会自动调用destroy()方法(注意:一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
package javax.servlet;
//Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。
public interface Servlet {
    
    //第一次访问Servlet程序的时候,init()和service()都被调用了
    void init(javax.servlet.ServletConfig servletConfig) throws javax.servlet.ServletException;

    //GetServletConfig():返回一个ServletConfig对象,该对象中包含了web.xml文件中<servlet>元素初始化参数信息(例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用servletConfig.getServletName()方法来获取!)
    javax.servlet.ServletConfig getServletConfig();

    //service方法是专门用来处理请求和响应的,ServletRequest中封装了当前的Http请求。ServletResponse表示当前用户的Http响应,我们只需直接操作ServletResponse对象就能把响应给用户。
    void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws javax.servlet.ServletException, java.io.IOException;

    //getServletInfo():返回一个字符串,在该字符串中包含 servlet 的创建者,版本和版权等信息
    java.lang.String getServletInfo();

    //当Servlet调用完destroy()方法后,等待垃圾回收。下次需要使用这个Servlet程序的时候,会重新调用init()方法进行初始化操作。
    void destroy();
}

第一个Servlet程序

使用IDEA+Maven配置Tomcat服务器然后编写一个Servlet程序(具体看下面这篇文章)

https://blog.csdn.net/qq_47256069/article/details/116140082

上面这篇文章没有说到基于父模块创建子模块(这里简单说一下),但创建过程都一样,先构建一个普通的Maven项目(没有骨架的),然后删除里面的src目录,然后在里面建立子Moudel(主工程导依赖,子Moudel用骨架生成,然后补全目录)只不过pom.xml文件有点不同,在主模块创建子模块,创建好后每个模块中的pom.xml文件都会生成下面的依赖(没有的话请自行添加,要不然会报错)为了方便理解,下面有个例子

工程构建完成后,主工程的pom.xml有

上图父Moudel里面有两个Moudel,对应的pom.xml文件也不一样

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cjjy</groupId>
    <artifactId>Web-Maven-0</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Servlet_0</module>
        <module>Servlet_1</module>
    </modules>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>

子模块的pom.xml有

<!--两个Moudel的pom文件都必须要有这个依赖,要不然父Moudel主导的依赖,子Moudel不能使用-->
<parent>
    <groupId>com.cjjy</groupId>
    <artifactId>Web-Maven-0</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

然后在主工程导入程序所需要的依赖

<!-- Servlet依赖 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>

补充

更换IDEA的xml文件

为了让实验环境达到最佳,Maven环境优化,所以把IDEAweb.xml替换成外部Tomcat目录下的web.xml文件(保持一致,都是4.0)还有一些不必要的东西都可以删了,下面是web.xml文件的模板

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

还有pom.xml文件,有些东西可以删了,不会影响运行环境

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
    
  <groupId>com.cjjy</groupId>
  <artifactId>Servlet_0</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

</project>

还有就是添加项目所需要的依赖

<!--加入所需的依赖(pom.xml)-->
<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.2.1</version>
        </dependency>

    </dependencies>

实现Servlet接口

在一个应用程序中,每种Servlet类型只能有一个实例。Servlet容器是将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。下面是一个简单的实例

package com.cjjy.test;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class ServletDemo implements Servlet {
    /*
     * 1、编写一个类实现Servlet接口
     * 2、实现service方法,处理请求,并响应数据
     * 3、到web.xml中配置servlet程序的访问地址(映射)
     */

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
		System.out.println("初始化");
    }

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

    //service方法是专门用来处理请求和响应的,只要访问ServletDemo这个程序,它就会执行这个方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException  {
        //因为我们页面get请求和post请求都会走这个service()方法,但由于servletrequest没有getmethod()这个方法,所以得向下转型(也就是去找子接口HttpServletRequest)才有,所以我们必须向下转型得到HttpServletResponse对象,然后就可以获取请求的方式(public interface HttpServletResponse extends ServletResponse)    
        System.out.println("service:我被访问了");
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        //获取请求方式
        String method = httpServletRequest.getMethod();
        if ("GET".equals(method)){
            System.out.println("get请求");
        }else if ("POST".equals(method)){
            System.out.println("post请求");
        }
        
    }

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

    @Override
    public void destroy() {
		System.out.println("销毁");
    }
}
/*
输出结果:初始化
service:我被访问了
post请求
*/

HTML提交页面

注意:网页不要写到这个目录(WEB-INF)去(WEB-INF目录是一个受服务器保护的目录,浏览器无法直接访问到此目录的内容)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/cjjy/show" method="post">
    <input type="submit" value="登录">
</form>
</body>
</html>

到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_4_0.xsd"
         version="4.0"
         metadata-complete="true">
    
    
    
    <!--servlet标签给tomcat配置servlet程序-->
  <servlet>
      <!--servlet-name标签是给Servlet程序起个别名(一般是类名)-->
    <servlet-name>ServletDemo</servlet-name>
      <!--servlet-class是Servlet程序的全类名-->
    <servlet-class>com.cjjy.test.ServletDemo</servlet-class>
  </servlet>
    
    
    <!--servlet-mapping标签给servlet程序配置访问地址-->
  <servlet-mapping>
      <!--servlet-name和上面servlet-name保持一致,告诉服务器,我当前配置的地址给哪个Servlet程序使用-->
    <servlet-name>ServletDemo</servlet-name>
      <!--url-pattern标签配置访问地址
		/:斜杠表示在服务器解析的时候,表示地址为:http://ip:port/工程路径(也就是idea配置tomcat时的Application context)
		/demo:Servlet程序的访问地址,http://ip:port/工程路径/demo
		-->
    <url-pattern>/show</url-pattern>
  </servlet-mapping>
    
</web-app>

Servlet类的继承体系

javax.servlet.http大致继承关系

一般在实际项目开发中,都是使用继承HttpServlet类的方式去实现Servlet程序,在上面我们实现Servlet接口,要实现5个方法。HttpServlet类已经实现了Servlet接口的所有方法,编写Servlet程序的时候,只需要继承HttpServlet,重写doGet()doPost()方法即可,并且它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能还要强大

GenericServlet抽象类

继承GenericServlet抽象类,只需要重写一个方法,底层其他的方法都做了空实现(仅声明了方法,方法体内没有内容)只有service()方法做了抽象(abstract)需要其他方法时重写就行,一般项目开发也不用这个,上面继承图我们可以看到HttpServlet继承了GenericServlet抽象类,为了屏蔽get请求和post请求方式处理逻辑(继承HttpServlet,复写方法doGet()dopost()方法)。因为将来都是调用service()方法,service()做一个方法分发,是哪个方式就调用哪个方法(简单来说就是HttpServlt已经帮我们分发好了get请求和post请求)

这里使用了尚硅谷老师的图(方便理解)

HttpServlt已经帮我们分发好了get请求和post请求

上面我们说了所有的请求都是service()方法处理,一般来说这个方法是不需要重写的,因为在HttpServlet中已经有了很好的实现,它会根据请求的方式,调用doGet()doPost()以及其他的doXXX()方法,也就是说service是用来转向的,所以我们一般写一个Servlet程序继承HttpServlet抽象类,只需要重写doGet()doPost()方法即可!

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo extends HttpServlet {
    //doGet()在get请求的时候调用
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    //doPost()在post请求的时候调用
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

ServletConfig接口

ServletConfigServlet规范中的一个规则,它是一个接口.ServletConfig有两个实现类:一个是GenericServlet,另一个是HttpServletServlet程序和ServletConfig对象都是由Tomcat负责创建的,我们负责使用;Servlet程序默认是第一次访问的时候创建,ServletConfig是每个Servlet程序创建的时候,每个Servlet对象对应的也都有一个封装Servlet配置的ServletConfig对象

ServletConfig作用

  • ServletConfig对象对应web.xml文件中的<servlet>元素(例如<servlet-name>的值)那么可以使用servletConfig.getServletName()方法获取
  • 可以获取初始化参数init-param
  • 可以获取ServletContext对象
package com.cjjy.test;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo implements Servlet {
    // Servlet容器初始化Servlet时,Servlet容器会给Servlet的init()方法的参数就是ServletConfig类型的。也就是说Tomcat在调用init()方法时,会传递ServletConfig对象
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        //可以获取Servlet程序的别名servlet-name的值
        System.out.println(servletConfig.getServletName());//输出结果:ServletDemo
        //获取初始化参数init-param
        System.out.println(servletConfig.getInitParameter("url"));//输出结果:jdbc:mysql://localhost:3306/bank
        //获取ServletContext对象
        System.out.println(servletConfig.getServletContext());//输出结果:org.apache.catalina.core.ApplicationContextFacade@75b9e42f
    }

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

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    }

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

    }
}

web.xml映射

因为我们编写一个类实现Servlet接口,下面我们直接继承HttpServlet,然后进行类操作;为什么需要映射呢?因为我们写的Java程序,但是通过浏览器访问,而浏览器需要连接web服务器,所以我们需要再Web服务器中注册我们写的Servlet,还需给他一个浏览器能够访问的路径

  <servlet>
    <servlet-name>ServletDemo</servlet-name>
    <servlet-class>com.cjjy.test.ServletDemo</servlet-class>

    <!--init-param是初始化参数-->
    <init-param>
      <!--参数名-->
      <param-name>username</param-name>
      <!--参数值-->
      <param-value>root</param-value>
    </init-param>

    <init-param>
      <!--参数名-->
      <param-name>url</param-name>
      <!--参数值-->
      <param-value>jdbc:mysql://localhost:3306/bank</param-value>
    </init-param>

    <servlet-mapping>
      <servlet-name>ServletDemo</servlet-name>
      <url-pattern>/show</url-pattern>
  	</servlet-mapping>
      
  </servlet>

补充

package com.cjjy.test;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServletDemo extends HttpServlet {

	//这里有个注意点就是如果在这里重写void init(ServletConfig config)这个方法的话,因为底层GenericServlet类持有一个的ServletConfig接口的引用【private transient ServletConfig config】,且底层GenericServlet类提供的getServletConfig()方法返回的是ServletConfig config这个对象,底层GenericServlet类的init()方法是保存ServletConfig config这个对象的【底层GenericServlet类也有init这个方法{this.config=config;this.init();}】,而这个对象我们上面说了封装了初始化参数,如果重写的话,没有添加这个super.init(config);语句的话,会报空指针异常【因为当子类和父类都有init()这个方法,当调用init的时候,调用的是子类的,父类的保存操作就会消失】(简单来说就是这个config是父类中init方法中赋值的)
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("重写init方法");

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        ServletConfig servletConfig = getServletConfig();

        System.out.println(servletConfig.getServletName());
        System.out.println(servletConfig.getInitParameter("username"));
        System.out.println(servletConfig.getInitParameter("url"));

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

ServletContext接口

servletContext接口是Servlet中最大的一个接口,呈现了Web应用的Servlet视图。ServletContext实例是通过 getServletContext()方法获得的,由于HttpServlet继承GenericServlet的关系,GenericServlet类和HttpServlet类同时具有该方法。ServletContext它表示Servlet上下文对象,这个对象中封装了上下文(应用程序)的环境详情。一个Web工程,只有一个ServletContext对象实例(简单来说对于每一个应用程序,Servlet容器会创建一个ServletContext对象,每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象ServletContext是在web工程部署启动的时候创建,在web工程停止的时候销毁ServletContext对象是一个域对象。ServletContextServletConfig都是由项目所拥有的,但是区别就是ServletContext内的数据类似于整个域共享,但是ServletConfig内拥有整个项目中对每个类的个性化配置

ServletContext作用

  • 获取web.xml中配置的上下文参数context-param
  • 获取当前的工程路径,格式:/工程路径
  • 获取工程部署后在服务器硬盘上的绝对路径
  • Map一样存储数据
    • 可以像Map一样存取数据的对象,叫域对象。这里的域指的是存取数据的操作范围,指的是整个Web工程
package com.cjjy.test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletContext extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        //获取web.xml中配置的上下文参数context-param
        javax.servlet.ServletContext servletContext = getServletConfig().getServletContext();
        
        
        String username = servletContext.getInitParameter("username");
        System.out.println(username);//输出结果:root

        //获取当前的工程路径,格式:/工程路径
        System.out.println(servletContext.getContextPath());//getContextPath()获得的是当前的项目名 /cjjy,拿到的是你的web工程的根路径,就是webRoot。

        //获取工程部署后在服务器硬盘上的绝对路径(/ 斜杠表示被服务器解析地址为:http://ip:port/工程名/ 映射到IDEA代码的Web目录)
        System.out.println(servletContext.getRealPath("/"));//输出结果:D:\IDEA\Maven-Web-0\Servlet_0\target\Servlet_0-1.0-SNAPSHOT\(整个工程的路径)
        
        //像Map一样存储数据
        servletContext.setAttribute("namme","Mike");
        System.out.println(servletContext.getAttribute("name"));
          
    }

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

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

  <!--context-param是上下文参数(它属于整个Web工程)可以配置多组-->
  <context-param>
    <param-name>username</param-name>
    <param-value>root</param-value>
  </context-param>

  <context-param>
    <param-name>password</param-name>
    <param-value>111</param-value>
  </context-param>

</web-app>

使用ServletContext实现共享文件

package com.cjjy;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
* 实现共享数据
*
* */
public class WebTest_0 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String username="张三";
        servletContext.setAttribute("username",username);

    }
}

这里就省略了web.xml映射(自行去映射),然后启动Tomcat

package com.cjjy;

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

public class WebTest_1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       resp.setContentType("text/html;charset=utf-8");
        ServletContext servletContext = this.getServletContext();
        //这里注意的是ServletContext是在web工程部署启动的时候创建,在web工程停止的时候销毁,也就是说如果重新部署项目后,直接访问WebTest_1是取不到值的
        String username = (String)servletContext.getAttribute("username");
        System.out.println(username);
    }

    //所有的请求都是service方法处理的,service方法会转发给dopost,doget,dodelete(请求相关都在httpservletrequest对象里)所有的请求方式都是等价的(方法复用)
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

HttpServletRequest

用户通过HTTP协议访问服务器,服务器会把HTTP请求中的所有的信息会被封装到Request对象中去。然后传递到service()方法(doGet和doPost)中给我们使用,可以获取客户端的所有的信息。我们可以通过HttpServletRequest对象,获取到所有请求的信息。

HttpServletRequest类的常用方法

HttpServletRequest常用方法说明
getRequestURI()获取请求的资源路径
getRequestURL()获取请求的统一资源定位符(绝对路径)
getServletPath()可以获取与url-pattern中匹配的路径(注意是完全匹配的部分)*的部分不包括
getRemoteHost()获取客户端的ip地址
getHeader()获取请求头
getParameter()获取请求的参数
getParameterValues()获取请求的参数(多个值的时候使用)
getMethod()获取请求的方式
setAttribute(key,value)设置域数据
getAttribute(key)获取域数据
getRequestDispatcher()获取请求转发对象

常用方法

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HttpServletDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(req.getRequestURI());//	/cjjy/show
        
        
        System.out.println(req.getRequestURL());//	http://localhost:8081/cjjy/show
        /*
            注意:使用localhost访问的时候,得到的客户端ip地址是127.0.0.1
            使用127.0.0.1访问的时候,得到的客户端ip地址是127.0.0.1
            使用真实的ip地址访问的时候,得到的客户端ip地址是真实的客户端ip地址
        */
        System.out.println(req.getRemoteHost());//	0:0:0:0:0:0:0:1
        
        System.out.println(req.getHeader("User-Agent"));//	Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36

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

获取参数

获取前端表单传递进来的参数

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

public class RequestDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //设置请求体的字符集为utf-8(要在获取请求参数之前调用)
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbys));

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

表单

<html>
<body>
<!--${pageContext.request.contextPath}/这个代表当前项目;且这里提交得路径,需要寻找到项目的路径-->

    <div class="gobal">
        <form action="${pageContext.request.contextPath}/requesttest" method="post">
            <div class="login-0">
                <div class="account">用户名</div>
                <input type="text" name="username">
            </div>
            <div class="logi-1">
                <div class="pwd">密码</div>
                <input type="password" name="password">
            </div>

            <div class="checkbox">
                <input type="checkbox" name="hobbys" value="唱歌"><span>唱歌</span>
                <input type="checkbox" name="hobbys" value="跳舞"><span>跳舞</span>
                <input type="checkbox" name="hobbys" value="学习"><span>学习</span>
                <input type="checkbox" name="hobbys" value="代码"><span>代码</span>
            </div>
            <div class="commit">
                <input type="submit" value="Login">
            </div>
        </form>
    </div>
</body>
</html>

web.xml映射

  <servlet>
    <servlet-name>requesttest</servlet-name>
    <servlet-class>com.cjjy.RequestDemo</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>requesttest</servlet-name>
    <url-pattern>/requesttest</url-pattern>
  </servlet-mapping>

请求的转发

什么是请求转发?

请求转发是指服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发

请求转发的特点?

  • 浏览器的地址栏不会改变

  • 它们是一次请求,且共享Request域中的数据

  • 可以转发到WEB-INF目录下

    • req.getRequestDispatcher("/WEB-INF/logout.html").forward(req,resp);
      
package com.cjjy.test;

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

public class RequestForward extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //请求转发,必要要以‘/’开头,这里的‘/’代表http://ip:port/工程名/(也就是当前的web工程)
        req.getRequestDispatcher("/logout.html").forward(req,resp);

        //request.getRequestDispatcher(url).forward(request,response)请求转发,在跳转页面的时候是带着原来页面的request和response跳转,request对象始终存在,不会重新创建。转发数据不会丢失,forward 就是转寄的意思,就是把客户端第一次请求在服务器内部完成,带着之前的请求request 和服务器解析成response 一并转发过去。
    }

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

表单页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/cjjy/show" method="post">

    <div class="user">
        <label>用户:</label>
        <input type="text">
    </div>
    <div class="pwd">
        <label>密码:</label>
        <input type="password">
    </div>

    <div class="commit">
        <input type="submit" value="登录">
    </div>
</form>
</body>
</html>

web映射

<servlet>
    <servlet-name>HttpServletDemo</servlet-name>
    <servlet-class>com.cjjy.test.RequestForward</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>HttpServletDemo</servlet-name>
    <url-pattern>/show</url-pattern>
  </servlet-mapping>

HttpServletResponse

Web服务器接收客户端的http请求,针对这个请求,会分别创建一个代表请求HttpServletRequest对象和一个代表响应的HttpServletResponse对象。HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息。如果需要设置返回给客户端的信息,可以通过HttpServletResponse对象来进行设置

HttpServletResponse常用方法

HttpServletResponse常用方法说明
getCharacterEncoding()响应对象采用的编码格式(Java的默认编码是ISO-8859-1)
setHeader(name, value)设置响应头信息
setCharacterEncoding(java.lang.String s)设置服务器字符集编码
setContentLength(int len)设置响应中的内容长度。在HTTP servlet 中,这个方法就是设置HTTP Content-Length header
setContentType(java.lang.String s)设置正被发往客户端的响应的内容类型。 内容类型可以包括所用的字符编码类型,例如, text/html; charset=ISO-8859-4

响应流

响应流分为字节流字符流,在使用的过程中只能使用一个。如果使用了字节流,则不能使用字符流

javax.servlet.ServletOutputStream getOutputStream() throws java.io.IOException;//常用于下载(传递二进制数据)
java.io.PrintWriter getWriter() throws java.io.IOException;//常用于回传字符串(比较常用) 

回传字符串数据

package com.cjjy.test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class PracticeDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //设置服务器字符集为utf-8
        resp.setCharacterEncoding("utf-8");

        //通过响应头,设置浏览器也使用utf-8
        resp.setHeader("Content-Type","text/html;charset=utf-8");
        
        //往客户端回传数据
        PrintWriter writer = resp.getWriter();
        writer.write("come om!!!!");
    }

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

下载文件

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

public class DownloadDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //下载文件
        String realPath = "D:\\IDEA\\JavaWeb_Skeleton\\Web-Maven-0\\Servlet_0\\target\\classes\\cxy.jpg";

        //要获取下载文件的路径
        String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//获取要下载的文件名

           //让浏览器支持下载我们需要的东西;设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码(//想办法让浏览器支持下载我们需要的东西)
        //attachment表示附件,表示下载使用;filename表示指定下载的文件名
           resp.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(fileName, "UTF-8"));

            //获取下载文件的输入流
           InputStream in = new FileInputStream(realPath);

            //创建缓冲区
           int len = 0;
           byte[] buffer = new byte[1024];
           //获取OutputStream对象
           OutputStream out = resp.getOutputStream();

           while ((len = in.read(buffer)) > 0) {
               //将OutputStream流写入到buffer缓冲区
                   out.write(buffer,0,len);
               }

        out.close();
        in.close();
       }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

重定向

什么是重定向?

重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)(A去访问B,B告知A去C找)

重定向特点?

  • 浏览器地址栏会发生改变
  • 一共是两次请求
  • 不共享Request域中数据
  • 不能访问到WEB-INF目录下的资源
  • 可以访问工程外的资源

HttpServletResponse实现重定向

这里就不写配置信息或者页面了,无非就是启动服务器后,访问某个网页,然后请求登录或者跳转,通过web.xml映射找到类的所在,实现重定向

package com.cjjy;

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

public class RedirectDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重定向时候一定要注意,路径问题,不然的话会404
        resp.sendRedirect ("/cjjy/demotest");
    }

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

重定向和转发区别

相同点:都是转发

不同点:请求转发的时候,url不会产生变化(响应状态码:307);重定向的时候,url地址栏会发生(响应状态码:302)并且通知新的地址(Location响应头:新地址)请求转发可以访问WEB-INF目录下的资源,且不能访问工程外的资源,相反重定向可以

Cookie

什么是Cookie?

Cookie(监视器)翻译过来是饼干的意思。Cookie是服务器通知客户端保存键值对的一种技术。客户端有了Cookie后,每次请求都发送给服务器。每个Cookie大小限制4kb

下面是百度百科介绍

Cookie,有时也用其复数形式 Cookies。类型为“小型文本文件”,是某些网站为了辨别用户身份,进行Session跟踪而储存在用户本地终端上的数据(通常经过加密)由用户客户端计算机暂时或永久保存的信息。

Cookie的创建

    package com.cjjy.cookie;
    import javax.servlet.ServletException;
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class CookieDemo extends HttpServlet {

        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doPost(req, resp);
        }
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //创建cookie对象
            Cookie cookie1 = new Cookie("key1","values1");

            //通知客户但保存cooike(通过响应头Set-Cookie: key1=values1,服务器给客户端响应(发送)一个cookie)
            resp.addCookie(cookie1);//且cookie是保存在客户端
            resp.getWriter().println("cookie创建成功");

            
            
            //Cookie值的修改有两种,一种是创建同名的cookie对象。然后进行覆盖操作进行赋值;另一种则是查找要修改的cookie对象,去调用setValue()方法去赋值
            Cookie cookie2 = new Cookie("key1","values2");
            //通知客户端去保存修改
            resp.addCookie(cookie2);
            //第二种
            Cookie cookie1 = new Cookie("key1","values1");
            cookie1.setValue("values2");
            //通知客户端去保存修改
            resp.addCookie(cookie1);

            //cookie服务器端从客户端获取;返回数组,说明cookie可能存在多个。遍历cookie获取值
            Cookie[] cookies = req.getCookies();
            for (Cookie cookie1 : cookies) {
                System.out.print(cookie1.getName()+"=");//获取cookie的key
                System.out.println(cookie1.getValue());//获取cookie的value
            }

        }
    }
/*
key1=values1
JSESSIONID=BEA296E3424D851A79EE535900F38256
Idea-b1c127b7=3607ec9c-a179-44b3-8c60-36f7ccf2151c
*/

Cookie生命控制

Cookie生命控制指的是Cookie什么时候被销毁(删除)如果不设置过期时间,则表示这个Cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,Cookie就会自动失效。所以我们可以设置Cookie对象的有效时间, setMaxAge()方法便可以设置Cookie对象的有效时间。正数表示指定秒数后过期;负数则表示浏览器关闭时,Cookie就会被删除(默认值是-1);0表示马上删除Cookie

package com.cjjy.cookie;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CookieDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //创建cookie对象
        Cookie cookie1 = new Cookie("key1","values1");
        //设置cookie的有效期
        cookie1.setMaxAge(24*60*60);

        //创建一个cookie,显示最后一次登录的时间
        Cookie cookie2 = new Cookie("LastLoginTime", System.currentTimeMillis()+"");
        //服务器给客户端响应(发送)一个cookie
        resp.addCookie(cookie2);
    }
}

Cookie有效路径的设置

Cookie的路径可通过setPath("/xxx")方法来进行设置,这个路径可以过滤决定服务器的请求是否会从浏览器中加载某些Cookie

package com.cjjy.cookie;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CookieDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //创建cookie对象
        Cookie cookie1 = new Cookie("key1","values1");
        //req.getContextPath()得到工程路径,也就是这个路径下的文件的cookie才会保存到浏览器
        cookie1.setPath(req.getContextPath()+"/static");
        resp.addCookie(cookie1);
    }
}

总结

Cookie特点

  • Cookie:一般会保存在本地的,用户目录下appdata
  • 一个Cookie只能保存一个信息;
  • 一个web站点可以给浏览器发送多个Cookie,每个站点最多可以存放20个Cookie;
  • Cookie大小限制4kb;
  • 300个Cookie是浏览器的上限(这里好像不同浏览器是有点不同的)

Cookie的删除有两种方式

一种是不设置有效期,关闭浏览器,自动失效 ;另一种则是设置有效期为0

Session会话

Session:表示会话的意思,它是一个接口(HttpSession),简单理解就是用户打开一个浏览器,点击了许多的超链接,访问了多个网站资源,关闭浏览器,这个过程可以称为会话(它是用来维护一个客户端和服务器之间关联的一种技术)每个客户端都有自己的一个Session会话;Session会话常用来保存用户登录之后的信息。Session使用场景可以是登录用户的信息、购物车信息等。在整个网站中会使用的数据,我们将它保存在Session

百度百科

Session(会话):在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web页时,如果该用户还没有会话,则Web服务器将自动创建一个 Session对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在Session对象中。有关使用Session 对象的详细信息,请参阅“ASP应用程序”部分的“管理会话”。注意会话状态仅在支持Cookie的浏览器中保留。

Session创建或者获取id的方法举例

package com.cjjy.session;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;

public class SessionDemo extends HttpServlet {

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

        //创建Session会话对象(第一次调用创建Session会话,之后都是获取前面已经创建好的Session会话对象,注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session)Session会话是存储在服务器端,一般为了防止在服务器的内存中(为了高速存取)
        HttpSession session = req.getSession();
        
        //设置属性
        session.setAttribute("name","张三");


        //isNew():判断是不是新建的出来的,返回的是boolean类型
        boolean aNew = session.isNew();
        //判断session是不是新创建的
        if (aNew){
            resp.getWriter().write("session创建成功,id为:"+id);
        }else {
            resp.getWriter().write("已经创建,id为:"+id);
        }

        //获取Session的唯一标识的id,每个会话都有一个类似身份证的id
        String id = session.getId();
		
      
    }
}

Session生命周期

服务器会把长时间没有活动的Session从服务器内存中清除,如果超过指定的时长,Session就会被销毁。值为正数的时候,设定超时的时长;值为负数的时候表示永不超时,这里没有0,只有invalidate()这个方法可以马上让Session会话无效。getMaxInactiveInterval()可以获取Tomcat中Session的默认超时时长为30分钟

public void setMaxInactiveInterval(int interval);//可以设置默认Session的超时时间,值为正数的时候,设定超时的时长;值为负数的时候表示永不超时,这里没有0,只有invalidate()这个方法可以马上让Session会话无效
<!--因为在Tomcat服务器的配置文件web.xml有默认以下配置,可以设置session默认的失效时间-->
  <session-config>
    <!--15分钟后session自动失效,以分钟为单位-->
    <session-timeout>15</session-timeout>
  </session-config>
<!--这里有个注意点就是session的超时指的是,客户端两次请求的最大间隔时长,比如设置了4秒的超时时长,必须等到4秒,中间不能有其他操作-->

Session删除的方式

  • web.xml配置文件设置Session配置文件

  • 关闭浏览器

  • session.invalidate();//调用invalidate(),可以手动注销session
    

Session存储

客户端第一次访问服务器(此时没有Cookie信息),客户端发送请求,然后服务器会创建一个Session(通过getSession()获取Session对象)和一个Cookie对象,(这个cookie对象上面说了,用来存储键值对,key是用来存储JSESSIONID,而这个ID会在随后的请求中被用来重新获得已经创建的Session(除非关闭浏览器或者通过invalidate() 方法超时,使得不能被重新使用;服务器端会把这个session对象存储在本地内存中) 之后客户端想要访问服务器都会带着这个Cookie对象(请求头中),这也就是服务器端可以根据Session可以来区分不同用户的原因。

总结

Session特点

  • 服务器会给每一个用户(浏览器)创建一个Session对象
  • 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在
  • 用户登录之后,整个网站都可以访问(保存用户信息啥的)

Cookie和Session区别?

  • Cookie是保存在客户端,而Session是保存在服务器端;存储数据量大小不同,Cookie有限,不超过4k;Session无限
  • Session把用户的数据写到用户独占Session中,服务端保存(保存重要的信息,减少服务器资源的浪费)
  • Session对象由服务创建

Filter过滤器

Filter过滤器是JavaWeb三大组件之一,三大组件分别是:Servlet程序Filter过滤器Listener监听器。且Filter过滤器它也是JavaEE的规范。也是接口。Filter用来过滤网站的数据(通过web.xml 注册了一个 Filter 来对某个 Servlet 程序进行拦截处理时,它可以决定是否将请求继续传递给 Servlet 程序,以及对请求和响应消息是否进行修改) 例如可以处理乱码、权限检查、日记操作、事务管理等。Filter 程序是一个实现了特殊接口的 Java 类,与 Servlet 类似,也是由 Servlet 容器进行调用和执行的。

注意在多个Filter过滤器执行的时候,它们执行的优先顺序是由它们在web.xml中从上到下配置顺序决定的,且request域对象共享,而且执行在用一个线程中

Filter实现权限拦截

import javax.servlet.*;
import java.io.IOException;
//导包认准:javax.servlet
/*
 * Filter生命周期:构造器》init()(前面两步在Web工程启动的时候执行;Filter已经创建)》doFilter()(每次拦截到请求就会执行)》destroy()(停止Web工 
 * 程就会执行销毁过滤器)
 */
public class FilterDemoTest implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //初始化:web服务器启动,就被初始化了,随时等待过滤对象出现
        System.out.println("初始化");
    }
    
    //当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service()方法,而是调用 Filter 的 doFilter()方法,再由 doFilter()方法决定是否去激活 service()方法。
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        /* 
        * filterChain:链
        * doFilter()方法,专门用于拦截请求
        * 1、过滤中的所有代码,在过滤特定请求的时候都会执行
        * 2、必须要让过滤器继续同行(filterChain.doFilter(servletRequest,servletResponse);)
        */
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html,charset=utf-8");
        System.out.println("执行前");
        
        //让程序继续往下访问用户的目标资源(执行下一个过滤器(如果有过滤器的话)或者调用service()方法),否则程序到这里就被拦截停止了。如果没有过滤器的话,Filter.doFilter 方法中不能直接调用 Servlet 的 service()方法,而是调用 FilterChain.doFilter()方法来激活目标 Servlet 的 service()方法,FilterChain 对象时通过 Filter.doFilter()方法的参数传递进来的。
        filterChain.doFilter(servletRequest,servletResponse);
        
        System.out.println("执行后");
    }
    @Override
    public void destroy() {
        //web服务器关闭的时候,过滤会销毁
        System.out.println("销毁");
    }
}

映射

<?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">
	<!--filter标签用于配置一个Filter过滤器-->
    <filter>
        <filter-name>filtering</filter-name>
        <filter-class>com.cjjy.FilterDemoTest</filter-class>
    </filter>

    <!--filter-mapping配置Filter的拦截路径,其映射路径跟Servlet差不多-->
    <filter-mapping>
        <filter-name>filtering</filter-name>
        <!--可以配置多个拦截路径-->
        <url-pattern>/show/*</url-pattern>
        <url-pattern>/test/*</url-pattern>
    </filter-mapping>
</web-app>

利用过滤器避免页面重复提交

原本我们登录成功后,拿着登录成功后的地址,然后注销登录,本来注销后是不可以直接登录的,我们可以利用过滤器避免二次登录,也就是登录成功(注销后)后拿着登录成功后的地址进行二次登录

下面是简单的一个实例(以下有Servlet程序自行在web.xml配置)

简单的登录页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
    <form action="/servlet/login" method="post">
        <input type="text" name="username">
        <input type="submit">
    </form>
</body>
</html>

登录成功页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功</title>
</head>
<body>
    <h1>主页</h1>
    <h2>恭喜你登录成功</h2>
    <p>
        <a href="/servlet/logout">注销</a>
    </p>
</body>
</html>

登录失败页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>错误</title>
</head>
<body>
    <h1>错误404</h1>
    <h2>权限不够,不是管理员</h2>
    <p>
        <a href="/Login.jsp">注销</a>
    </p>
</body>
</html>

定义一个常量类

package com.cjjy.servlet;

public class Constant {
    public final static String USER_NAME="USER_NAME";
}

LoginServlet程序

登录如果不是admin就返回失败页面,成功就重定向到成功页面

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

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //获取前端请求的参数
        String username = req.getParameter("username");

        if (username.equals("admin")){
            //登录成功的话,把某个信息放在某个地方(一般把信息放在session里)
            req.getSession().setAttribute(Constant.USER_NAME,req.getSession().getId());

            resp.sendRedirect("/sys/success.jsp");

        }else {
            resp.sendRedirect("/error.jsp");
        }

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

        doGet(req, resp);
    }
}

LogoutServlet

注销(但注销后还能登录成功,得实现过滤器)

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object user_name = req.getSession().getAttribute(Constant.USER_NAME);
        if (user_name!=null){
            req.getSession().removeAttribute(Constant.USER_NAME);
            //请求转发因为共享request域中数据,所以会出现表单重复提交的bug;重定向不共享request域中数据(两次请求)所以再次提交请求时,不会出现重复提交表单请
            resp.sendRedirect("/Login.jsp");
        }

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

LoginDemo过滤器

用来登录成功后注销,拿着登录成功的地址搜索后返回错误页面

import com.cjjy.servlet.Constant;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginDemo implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {

        //ServletRequest HttpServletRequest
        HttpServletRequest request=(HttpServletRequest)req;
        HttpServletResponse response=(HttpServletResponse)resp;

        if (request.getSession().getAttribute(Constant.USER_NAME)==null){
            response.sendRedirect("/error.jsp");
        }
        filterChain.doFilter(request,response);
    }
    @Override
    public void destroy() {

    }
}

FilterConfig接口

Tomcat每次创建Filter的时候,Servlet 规范将代表 ServletContext 对象和 Filter 的配置参数信息都封装到一个称为 FilterConfig 的对象中。FilterConfig 接口则用于定义 FilterConfig 对象应该对外提供的方法,以便在 Filter 程序中可以调用这些方法来获取 ServletContext 对象,以及获取在 web.xml 文件中为 Filter 设置的友好名称和初始化参数。

FilterConfig接口定义的各个方法

FilterConfig接口定义方法说明
getFilterName()返回 <filter-name> 元素的设置值。
getServletContext()返回 FilterConfig 对象中所包装的 ServletContext 对象的引用。
getInitParameter()返回在 web.xml 文件中为 Filter 所设置的某个名称的初始化的参数值。
getInitParameterNames()返回一个 Enumeration 集合对象。

Listener监听器

什么是监听器?

Listener监听器是JavaWeb的三大组件之一。上面我们说了三大组件分别是:Servlet程序Filter过滤器Listener监听器Listener它是JavaEE的规范,也是接口。Listener监听器,可以监听某种事物的变化。然后通过回调函数,反馈给程序或者客户,去做一些相应的处理

ServletContextListener监听器

ServletContextListener它可以监听ServletContext对象的创建和销毁。ServletContext对象在Web工程启动的时候创建,在Web工程停止的时候销毁

public interface ServletContextListener extends EventListener {

    public default void contextInitialized(ServletContextEvent sce) {
        //在ServletContext对象创建之后马上调用,做初始化
    }
    public default void contextDestroyed(ServletContextEvent sce) {
        //在ServletContext对象销毁之后调用
    }
}
package com.cjjy.test;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ServletContextListenerDemo implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext对象启动");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext对象销毁");
    }
}

web.xml配置

<listener>
    <listener-class>com.cjjy.test.ServletContextListenerDemo</listener-class>
</listener>

HttpSessionListener监听器

HttpSessionListener是个监听器,看方法可以知道 sessionCreated()sessionDestroyed()。就是当session被创建和被销毁的时候执行相应的方法。

实现一个HttpSessionListener监听接口统计人数

package com.cjjy;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

//在线人数监听,统计session
public class OnlineCountListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        //创建session监听:查看你的一举一动
        //一旦创建session就会触发一次这个事件
        ServletContext sc = se.getSession().getServletContext();
        Integer onlineCount = (Integer) sc.getAttribute("OnlineCount");

        if (onlineCount==null){
            onlineCount=new Integer(1);
        }else {
            int count =onlineCount.intValue();
            onlineCount=new Integer(count+1);
        }

        sc.setAttribute("OnlineCount",onlineCount);
    }


    @Override
    public void sessionDestroyed(HttpSessionEvent se) {

        //销毁session监听
        //一旦销毁session就会触发一次这个事件!
        ServletContext sc = se.getSession().getServletContext();
        Integer onlineCount = (Integer) sc.getAttribute("OnlineCount");

        if (onlineCount==null){
            onlineCount=new Integer(0);
        }else {
            int count =onlineCount.intValue();
            onlineCount=new Integer(count-1);
        }

        sc.setAttribute("OnlineCount",onlineCount);
    }
}

映射监听器

<listener>
	<listener-class>com.cjjy.OnlineCountListener</listener-class>
</listener>

补充

XML文件

xml是什么?

可扩展标记语言(标准通用标记语言的子集)是一种简单的数据存储语言。使用一系列简单的标记描述数据,而这些标记可以用方便的方式建立,虽然可扩展标记语言占用的空间比二进制数据要占用更多的空间,但可扩展标记语言极其简单易于掌握和使用。

xml的主要作用

  • 用来保存数据,而且这些数据具有自我描述性
  • 它还可以作为项目或者模块的配置文件
  • 还可以作为网络传输数据的格式(不过现在传输数据以JSON为主)

xml命名规则

  • 名称可以含字母、数字以及其他的字符
  • 名称不能以数字或者标点符号开始
  • 名称不能包含空格

注意

和转义字符

有时候我们写xml文件,如果想在xml中使用特殊符号(如”<””>””&”),必须将其转义为实体,这样才能保存进xml文档。

<name>张<三</name> <!--注意这样写会报错,在xml中,<、>、&等字符是不能直接存入的-->

<!--你要么可以这样写-->
<name>&lt;</name>
<!--所以为了方便我们还可以这样写,首先被<![CDATA[]]>这个标记所包含的内容将表示为纯文本,比如<![CDATA[<]]>表示文本内容<-->

Servlet程序映射路径问题

<!--一个Servlet可以指定一个映射路径-->
<servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/test</url-pattern>
</servlet-mapping>
<!--一个Servlet可以指定多个映射路径-->
<!--Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/test1</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/test2</url-pattern>
    </servlet-mapping>

<!--一个Servlet可以指定通用映射路径-->
	<servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/test/*</url-pattern>
    </servlet-mapping>

<!--默认请求-->
	<servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
	<servlet-mapping>
        <servlet-name>test</servlet-name>
        <!--表示请求地址必须以.html结尾才会映射到,*不能以/开头-->
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

这里有个细节,可以自定义后缀实现请求映射,*前面不能加项目映射的路径。还有就是比如有2个类,添加了2个映射,一个通配符 / * 一个是另一个类的映射名字。也就是说指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求。还有一个注意点映射的顺序是按从上到下,前提是拦截方式都是url的方式,如果含有拦截名称的方式,则拦截名称方式的过滤器在url拦截方式之后执行

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
ServletJavaEE 规范中的一部分,是处理 Web 请求的组件。Servlet 运行在服务器端,能够接收客户端发来的请求,并给客户端响应结果。下面我们来看看 Servlet 的基本概念和使用方法。 ## 一、Servlet 的基本概念 ### 1.1 Servlet 的生命周期 Servlet 的生命周期包含以下三个阶段: - 初始化阶段(init):当 Servlet 实例化后,Web 容器会调用其 init() 方法进行初始化操作。在此阶段,Servlet 可以执行一些初始化操作,例如读取配置信息、建立数据库连接等。 - 请求处理阶段(service):当客户端发来请求时,Web 容器会创建一个线程调用 Servlet 的 service() 方法处理请求。在此阶段,Servlet 可以获取请求参数、处理请求并生成响应数据。 - 销毁阶段(destroy):当 Web 应用停止或 Servlet 被卸载时,Web 容器会调用 Servlet 的 destroy() 方法进行清理工作。在此阶段,Servlet 可以释放资源、关闭数据库连接等。 ### 1.2 Servlet 的配置 在使用 Servlet 时,需要在 web.xml 文件中进行配置。以下是一个 Servlet 的基本配置: ```xml <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.example.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <url-pattern>/myservlet</url-pattern> </servlet-mapping> ``` 其中,servlet-name 表示 Servlet 的名称,servlet-class 表示 Servlet 的类名,url-pattern 表示请求的 URL 匹配规则。 ## 二、Servlet 的使用方法 ### 2.1 编写 Servlet 编写 Servlet 有两种方法:一种是继承 HttpServlet 类,另一种是实现 Servlet 接口。这里以继承 HttpServlet 类为例: ```java public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理 GET 请求 PrintWriter out = resp.getWriter(); out.println("Hello, world!"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理 POST 请求 PrintWriter out = resp.getWriter(); out.println("Hello, world!"); } } ``` 在 Servlet 中,doGet() 方法用于处理 GET 请求,doPost() 方法用于处理 POST 请求。通过调用 HttpServletResponse 对象的 getWriter() 方法可以向客户端返回响应数据。 ### 2.2 部署 Servlet 将编写好的 Servlet 部署到 Web 容器中,有两种方法:一种是将 Servlet 类打成 war 包放到 Web 容器的 webapps 目录下,另一种是通过 Eclipse 等开发工具将 Servlet 部署到 Web 容器中。部署完成后,可以通过访问 Servlet 的 URL 来测试 Servlet 是否正常工作。 ## 三、总结 本文介绍了 Servlet 的基本概念和使用方法。ServletJava Web 开发中非常重要的组件,掌握 Servlet 的使用方法对于 Java Web 开发人员来说是必不可少的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值