JavaWeb基础

文章目录

Tomcat服务器

Tomcat-ZIP-Download

概述

  Tomcat是一款实现了Servlet标准和JSP标准的JavaWeb容器。Tomcat本身使用Java语言开发,因此使用Tomcat必须安装JVM.

Tomcat安装

  使用压缩包方式,解压完成即Tomcat安装完成。双击安装目录下的bin/startup.bat即可启动Tomcat,使用浏览器访问http://localhost:8080,出现Tomcat页面即Tomcat安装成功。双击安装目录下的bin/shutdown.bat即可关闭Tomcat
  也可使用命令行启动Tomcat服务器:

1.cd C:\DevKits\Tomcat - 使用cd命令定位到Tomcat的安装(解压)目录
2.catalina run - 使用"catalina run"命名启动Tomcat服务器

Tomcat基础配置

1.修改端口号:记事本打开 安装目录/conf/server.xml,将<Connector.../>中的端口号改为8080
2.修改用户名和密码:记事本打开 安装目录/conf/tomcat-users.xml,
  在<tomcat-users>...</tomcat-users>中添加以下内容:
  <role rolename="manager-gui"/>
  <!-- 仅修改用户名和密码 -->
  <user username="your name" password="your password" roles="manager-gui"/>
3.重启Tomcat服务器

Tomcat项目部署

Tomcat目录介绍

bin : 存放可执行文件
conf : 存放配置文件
lib : 存放Tomcat服务器的jar包
logs : 存放Tomcat服务器运行时输出的日志
temp : 存放临时数据
webapps : 存放部署的web工程
work : 用来存放Tomcat运行时jsp翻译为Servlet的源码以及Session钝化(序列化)的目录

项目部署方式

1.将工程拷贝至 Tomcat安装(解压)目录/webapps 中
2.在 Tomcat安装(解压)目录/conf/Catalina/localhost 中添加xml文件并输入以下内容:
    <!-- path指定实际工程路径的映射路径,浏览器访问时使用此映射路径;docBase指定实际工程路径
         注意xml文件必须使用utf-8编码格式 -->
    <Context path="映射路径" docBase="实际工程路径"/>
3.在浏览器中使用 http://ip:port/路径 访问页面

IDEA整合Tomcat

1.依次点击IDEA的File>Build,Execution,Deployment>Application Servers
2.点击"+"选择"Tomcat Server",在弹出的窗口中为"Tomcat Home"输入框选择Tomcat安装(解压)路径
3.新建模块,选择"Java Enterprise项目类型",勾选"Web Application",勾选"Create web.xml"
4.模块新建成功后,在 模块/web/WEB-INF 中添加lib文件夹,用于存放jar包




HTTP协议

  HTTP协议数据又称为HTTP报文,HTTP报文又分为请求报文和响应报文,客户端向服务器发送的数据为请求报文,服务器向客户端回传的数据为响应报文。

HTTP请求

GET请求

  常见的GET请求:

1.form标签中的method=GET的form表单
2.a标签
3.link标签引入CSS
4.Script标签引入JavaScript
5.img标签引入图片
6.iframe标签引入html页面
7.在浏览器地址栏输入地址的搜索行为

  GET请求的报文组成为:

1.请求行
    (1).请求方式:GET
    (2).请求资源路径+参数:资源路径?name1=value&name2=value...
    (3).请求的协议版本号:HTTP/1.1
2.请求头
    key1:value
    key2:value
    ...

  例:

GET /ServletDemo/hello HTTP/1.1
Accept:image/jpeg,*/*
Accept-Language:zh-CN
User-Agent:Mozilla/4.0
Accept-Encoding:gzip,deflate
Host:localhost:8080
Connection:Keep-Alive
...

POST请求

  POST请求的报文组成为:

1.请求行
    (1).请求方式:POST
    (2).请求资源路径+参数:资源路径?name1=value&name2=value...
    (3).请求的协议版本号:HTTP/1.1
2.请求头
    (1).多个请求头键值对
    (2).空行(请求头末尾与请求体之间有一个额外的空行)	
3.请求体
    发送给服务器的数据

  例:

POST /ServletDemo/login HTTP/1.1
Accept:image/jpeg,*/*
Referer:http://localhost:8080/ServletDemo/login.html
Accept-Language:zh-CN
User-Agent:Mozilla/4.0
Content-Type:application/x-www-form-urlencoded
Accept-Encoding:gzip,deflate
Host:localhost:8080
Content-Length:100
Connection:Keep-Alive
Cache-Control:no-cache

action=login&username=root&password=123456

常用请求头

GET与POST都使用的请求头
Accept:声明服务器客户端可以接收的数据类型,"*/*"表示可以接收任意类型
Accept-Language:声明服务器客户端可以接收的语言类型,"zh_CN"表示中文中国,"en_US"表示英文美国
User-Agent:用户代理,即浏览器信息
Accept-Encoding:声明服务器客户端可以接收的数据压缩或编码方式
Host:表示服务器的IP和端口号
Connection:声明服务器如何处理当前连接,"Keep-Alive"表示回传数据后不要马上关闭,
    继续保持一定时间的连接,"Closed"表示回传数据后立即关闭连接

仅POST使用的请求头
Referer:请求发起时浏览器地址栏的输入的地址(可以记录用户的访问链接,防止盗链)
Content-Type:表示客户端发送的数据类型,有两种类型:
    application/x-www-form-urlencoded(以name=value&name=value...
    的形式提交数据至服务器,然后进行URL编码)
    multipart/form-data(以流的形势提交数据至服务器,用于文件上传)
Content-Length:表示发送的数据的字符长度    
Cache-Control:指定缓存的控制方式

HTTP响应

  响应报文的格式:

1.响应行
    (1).响应的协议及版本号
    (2).响应状态码
    (3).响应状态描述符
2.响应头
    (1).多个响应头键值对
    (2).空行(响应头末尾与响应体之间有一个额外的空行)	
3.响应体
    服务器回传给客户端的数据

  例:

HTTP/1.1 200 OK
Server:Apache-Coyote/1.1
Accept-Ranges:bytes
ETag:W:/"375-..."
Last-Modified:Mon,13 Jan 2020 08:22:53 GMT
Content-Type:text/html
Content-Length:500
Data:Mon,13 Jan 2020 08:34:42 GMT

<!DOCTYPE html>
<html lang="zh_CN">
    <head>
        <meta charset="UTF-8">
        <title>Hello world</title>
    </head>
    <body>
        Hello world!
    </body>
</html>

  常见的响应码:

200:表示请求成功
302:表示请求重定向
404:表示服务器已经收到请求,但请求数据不存在
500:表示服务器已经收到请求,但服务器内部出现错误

MIME类型

  MIMEHTTP中的数据类型,格式为"大类型/小类型"

文件类型MIME类型
超文本标记语言文本text/html
普通文本text/plain
RTF文本application/rtf
GIF图形image/gif
JPEG图形image/jpeg
au声音文件audio/basic
MIDI音乐文件audio/midi,audio/x-midi
RealAudio音乐文件audio/x-pn-realaudio
MPEG文件video/mpeg
AVIvideo/x-msvideo
GZIP文件application/x-gzip
TAR文件application/x-tar




Servlet

概述

  使用Servlet必须有servlet-api.jar的支持,可在Tomcat安装(解压)路径/lib中引用。
  ServletJavaEE的三大核心组件(Servlet,Filter,Listener)之一,是运行在服务器上的小程序,用于处理及响应浏览器或客户端对服务器的HTTP请求。Servlet是标准Web容器必须支持的规范。

Servlet接口介绍

  Servlet接口在jakarta.servlet包下(新版Tomcat),实现Servlet接口用于处理及响应浏览器或客户端对具体Web资源的请求。Servlet中包含了处理各种资源请求行为的方法。

package jakarta.servlet;
import java.io.IOException;

public interface Servlet {
    /* Servlet接口实现类初始化时由Web容器调用,定义初始化时刻的行为 */
    void init(ServletConfig var1) throws ServletException;
    /* Web容器获取Servlet配置信息 */
    ServletConfig getServletConfig();
    /* 用户发出请求时由Web容器调用,定义具体的业务逻辑 */
    void service(ServletRequest var1,ServletResponse var2) 
        throws ServletException,IOException;
    /* Web容器获取Servlet对象信息 */
    String getServletInfo();
    /* Web容器关闭/Webapp重新部署/该Servlet对象长时间未被访问时由Web容器调用 */
    void destroy();
}

Servlet接口的简单使用

  第1步,创建Java Enterprise工程。

  第2步,指定webapp工程名,依次点击IDEA的按钮Run>Edit Configurations,在弹出的窗口中选择正在使用的Tomcat服务器,点击Deployment选项卡,在Application context输入框中输入以"/"开头的字符串作为webapp工程名,例如"/ServletDemo"

  第3步,实现Servlet接口并实现方法。

HelloServlet.java

public class HelloServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {}
    @Override
    public ServletConfig getServletConfig() {return null;}
    @Override
    public void service(ServletRequest servletRequest,ServletResponse servletResponse) 
        throws ServletException,IOException {
        System.out.println("Servlet is accessed.");
    }
    @Override
    public String getServletInfo() {return null;}
    @Override
    public void destroy() {}
}

  第4步,编辑web/WEB-INF/web.xml.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    
    <!-- 上下文参数 -->
    <context-param>
        <param-name>name1</param-name>
        <param-value>value1</param-value>
    </context-param>
    <context-param>
        <param-name>name2</param-name>
        <param-value>value2</param-value>
    </context-param>
    ...

    <servlet>
        <!-- servlet-name可任意取字符串,但必须与servlet-mapping中的servlet-name保持一致;
             此servlet-name是Servlet接口实现类的标识,用于与同名的资源访问路径对应 -->
        <servlet-name>HelloServlet</servlet-name>
        <!-- 对应的servlet实现类路径 -->
        <servlet-class>com.lishaoyin.servletdemo.HelloServlet</servlet-class>
        
        <!-- 可添加若干初始化参数 -->
        <init-param>
            <param-name>name1</param-name>
            <param-value>value1</param-value>
        </init-param>
        <init-param>
            <param-name>name1</param-name>
            <param-value>value1</param-value>
        </init-param>
        ...
    </servlet>

    <servlet-mapping>
        <!-- servlet-name可任意取字符串,但必须与servlet-mapping中的servlet-name保持一致;
             此servlet-name是资源访问路径的标识,用于与同名的Servlet接口实现类对应 -->
        <servlet-name>HelloServlet</servlet-name>
        <!-- 资源访问路径名,可取任意字符串,但必须以"/"开头;
             "/"指代"http://ip:port/project/",
             因此"/hello"即为"http://ip:port/project/hello" -->
        <url-pattern>/hello</url-pattern>
        <!-- 可以编写多个url-pattern,从而实现访问多个资源调用同一个Servlet接口实现类 -->
        <url-pattern>...</url-pattern>
        <url-pattern>...</url-pattern>
        ...
    </servlet-mapping>
    
</web-app>

  servlet标签和servlet-mapping标签将浏览器或客户端对资源的访问映射到指定的Servlet接口实现类,从而浏览器或客户端访问该资源时,服务器将调用对应的Servlet接口实现类中的方法。

  第5步,访问资源,在浏览器中输入:

http://localhost:8080/ServletDemo/hello

使用注解进行Servlet映射

  以上对Servlet的简单实用中,通过添加servlet标签和servlet-mapping标签实现Servlet接口实现类与资源访问的映射,也可以使用@WebServlet注解指定Servlet接口实现类与资源访问的映射。@WebServlet的属性:

public @interface WebServlet {
    /* 指定Servlet的名字,等价于web.xml中的<servlet-name> */
    String name() default "";
    /* 与urlPatterns属性等价,但两者不可同时使用,等价于web.xml中的<url-pattern> */
    String[] value() default {};
    /* 与value属性等价,但两者不可同时使用,等价于web.xml中的<url-pattern> */
    String[] urlPatterns() default {};
    /* 指定服务器开启时Servlet加载的顺序,默认为-1指定服务器开启时默认不加载Servlet */
    int loadOnStartup() default -1;
    /* 指定一组Servlet初始化参数,等价于web.xml中的<init-param>参数 */
    WebInitParam[] initParams() default {};
    /* 指定是否支持异步操作模式,等价于web.xml中的<async-supported>标签 */
    boolean asyncSupported() default false;
    /* Servlet的描述信息,等价于web.xml中的<description> */
    String description() default "";
    /* 该Servlet的显示名,等价于web.xml中的<display-name> */
    String displayName() default "";
    /* 指定Servlet显示的小图标的图标地址 */
    String smallIcon() default "";
    /* 指定Servlet显示的大图标的图标地址 */
    String largeIcon() default "";
}

  示例:

/* 访问http://ip:port/project/hello时将调用此Servlet实现类 */
@WebServlet(value="/hello")
public class HelloServlet implements Servlet {...}

  映射多个资源访问时,可以使用数组:

@WebServlet(name="Servlet1",value={"/hello1","/hello2","/hello3"})
public class HelloServlet implements Servlet {...}

  @WebServlet注解与web.xml并存使用使,如果web.xml中的根标签(<web-app>)中的metadata-complete属性为true,表示所有配置信息完全由web.xml完成,@WebServlet注解将失效,需将metadata-complete属性设置为false

<?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="false">
    ...
</web-app>

Servlet对象生命周期

  Servlet对象的生命周期由Web容器(例如Tomcat)管理,Servlet对象中的方法也由Web容器调用。

1.用户发出请求
2.Web容器截取请求路径
3.Web容器根据容器上下文寻找Servlet对象
4.如果未找到Servlet对象:
    4.1.Web容器读取web.xml文件,读取Servlet实现类路径名,使用反射机制创建Servlet对象
    4.2.Web容器调用Servlet对象的init()方法完成初始化操作
    4.3.Web容器调用Servlet对象的service()方法提供服务
5.如果找到Servlet对象,直接调用Servlet对象的service()方法提供服务
6.当Web容器关闭/Webapp重新部署/该Servlet对象长时间未被访问时,Web容器会将该Servlet对象销毁,
  销毁前调用Servlet对象的destroy()方法      

  总结:

1.Servlet接口实现类的构造器只执行一次
2.Servlet对象的init()方法只执行一次
3.Servlet对象的service()方法,只要用户请求一次,就被执行一次
4.Servlet对象的destroy()方法只执行一次
5.默认情况下,服务器启动阶段不会实例化Servlet接口实现类,可以在web.xml文件中的
  servlet标签添加以下内容开启服务器启动阶段实例化:
  <servlet>
      ...
      <!-- load-on-startup中填写自然数,值越小,服务器启动阶段实例化优先级越高 -->
      <load-on-startup>0</load-on-startup>
  </servlet>

Servlet相关接口

ServletConfig接口

  在Servlet接口实现类中,ServletConfig接口的实例由Web容器通过init()方法传给Servlet接口实现类,ServletConfig对象封装了Servlet接口实现类的配置信息,等同于web.xml@WebServlet注解中的信息。

常用的方法
/* 获取Servlet名,与web.xml中的<servlet-name>对应 */
String getServletName();
/* 获取Servlet上下文对象 */
ServletContext getServletContext();
/* 获取指定初始化参数的值,与web.xml中的<init-param>对应 */
String getInitParameter(String name);
/* 获取所有初始化参数的名字,与web.xml中的<init-param>对应 */
Enumeration<String> getInitParameterNames();
getServletConfig()

  Servlet接口中的getServletConfig()方法用于间接获取Web容器传给Servlet对象的ServletConfig对象。

public class HelloServlet implements Servlet {
    private ServletConfig servletConfig;
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
    }
    /* 此方法供子类使用获取ServletConfig对象 */
    @Override
    public ServletConfig getServletConfig() {
        return servletConfig;
    }
    ...
}

ServletContext接口

  ServletContext用于封装Servlet上下文信息,Web容器将ServletConfig对象通过init()方法传入Servlet对象,通过此ServletConfig对象可获取ServletContext对象,所有Servlet对象共享同一个ServletContext对象。ServletContext对象代表web.xml文件。
  需要所有用户共享的数据,可放在ServletContext对象中,即ServletContext对象可实现跨用户传递数据。ServletContext对象是多线程共享的,因此不建议修改其中的数据。

常用方法
/* 获取指定的属性值,对应web.xml中的<context-param>中的值 */
Object getAttribute(String name);
/* 设置指定的属性值 */
void setAttribute(String name,Object value);
/* 移除指定的属性值 */
void removeAttribute(String name);

/* 获取指定初始化参数的值,与web.xml中的<init-param>对应 */
String getInitParameter(String name);
/* 获取所有初始化参数的名字,与web.xml中的<init-param>对应 */
Enumeration<String> getInitParameterNames();

/* 获取指定资源的绝对路径 */
String getRealPath(String path);

Servlet的实现方式

实现Servlet接口

public class HelloServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {}
    @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() {}
}

继承HttpServlet抽象类

  HttpServlet的继承体系为:

public abstract class HttpServlet extends GenericServlet {...}

  继承HttpServlet抽象类以实现Servlet接口是最常用的方式。

public class HelloServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req,HttpServletResponse resp) 
        throws ServletException,IOException {...} 
}

继承GenericServlet抽象类

  GenericServlet的继承体系为:

public abstract class GenericServlet implements Servlet,ServletConfig,Serializable {...}

  与HttpServlet相比,GenericServlet的方法更少。

public class HelloServlet extends GenericServlet {
    @Override
    public void service(ServletRequest req,ServletResponse resp) 
        throws ServletException,IOException {...} 
}

Servlet请求与响应

HttpServletRequest接口

  HttpServletRequest接口是ServletRequest接口的子接口,用于获取各种请求信息。service()方法的第一个形参即为ServletRequest类型,且ServletRequest实例由Web容器创建后传入service()方法,所有关于请求的处理都围绕此实例进行。

常用方法
/* 获取完整请求URL(从"http"开始,到"?"之前结束):http://ip:port/project/... */
StringBuffer getRequestURL();
/* 获取请求URL中的资源名称部分(从项目名开始,到"?"之前结束):project/... */
String getRequestURI();
/* 获取请求行中的参数部分(从"?"开始到整个URL结尾):name1=value&name2=value... */
String getQueryString();
/* 获取请求方法("GET"和"POST") */
String getMethod();
/* 获取HTTP版本号 */
String getProtocol();
/* 获取webapp名称(项目对外访问路径,即servlet-mapping标签中的url-pattern指定的字符串) */
String getContextPath();

/* 获取指定参数的值,name对应HTML表单中元素的name属性,返回值对应value属性 */
String getParamater(String name);
/* 获取指定参数的所有值,某些表单元素(例如复选框)具有多个值 */
String[] getParamaterValues(String name);

/* 设置请求编码,可解决乱码问题,例如:
   req.setCharacterEncoding("UTF-8"); */
void setCharacterEncoding(String env);

/* 获取请求转发器,传入转发目的地的路径,可以是Servlet实现类,也可以是jsp或html页面 */
RequestDispatcher getRequestDispatcher(String var1);

/* 设置指定属性的值 */
void setAttribute(String name,Object value);
/* 读取指定属性的值 */
Object getAttribute(String name);
/* 移除指定属性 */
void removeAttribute(String name);
解决请求乱码

  直接使用setCharacterEncoding()方法:

req.setCharacterEncoding("UTF-8");

  setCharacterEncoding()方法仅对POST方法有效,如需对所有方法有效,可以采用如下方法:

/* Tomcat的请求接收编码默认使用ISO-8859-1标准,此标准不支持中文,
   可以先获取所有字节,再使用UTF-8编码 */
String value = new String(req.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");
请求转发

  请求转发指将一个Servlet截取的请求转发至另一个Servlet,或转发至前台(jsp页面或html页面)。请求转发使用HttpServletRequest(ServletRequest)接口中的方法实现:

/* 获取请求转发器,传入转发目的地的路径,可以是Servlet实现类,也可以是jsp或html页面 */
RequestDispatcher getRequestDispatcher(String var1);

/* 使用RequestDispatcher的forward()方法可将请求和响应信息转发至目的地 */
void forward(ServletRequest var1, ServletResponse var2) 
    throws ServletException, IOException;

  示例:

@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    /* 将请求转发至另一个Servlet */
    req.getRequestDispatcher("/hello1").forward(req,resp);
} 

@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    /* 将请求转发至html页面 */
    req.getRequestDispatcher("index.html").forward(req,resp);
} 

@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    /* 将请求转发至jsp页面 */
    req.getRequestDispatcher("login.jsp").forward(req,resp);
} 

  请求转发是服务器端的行为,不会改变浏览器地址栏中的url字符串。请求转发可能调用了多个Servlet,但请求转发过程中,只有一个请求,即请求可以被多个Servlet共享。

域对象

  请求被从一个Servlet转发到另一个Servlet时,这两个Servlet拥有一个公共的作用域,Servlet为这个公共的作用域创建了一个公有对象,称为域对象。转发发起方在转发之前可以设置域对象中指定属性的值,转发接收方可以读取域对象中指定的值。通过HttpServletRequest(ServletRequest)接口中的以下方法实现:

/* 设置指定属性的值 */
void setAttribute(String name,Object value);
/* 读取指定属性的值 */
Object getAttribute(String name);
/* 移除指定属性 */
void removeAttribute(String name);

  域对象中的数据只在一次请求转发中有效,一次转发中只在转发发起方和转发接收方之间有效。例:

/* 转发发起方 */
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    req.setAttribute("name","Tony");
    req.setAttribute("password","123456")
    /* 将请求转发至另一个Servlet */
    req.getRequestDispatcher("/hello1").forward(req,resp);
} 

/* 转发接收方 */
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    System.out.println("user is " + req.getAttribute("name") 
                       + ",password is " + req.getAttribute("password"));
} 

HttpServletResponse接口

  HttpServletResponse接口是ServletResponse接口的子接口,用于请求响应。service()方法的第二个形参即为ServletResponse类型,且ServletResponse实例由Web容器创建后传入service()方法,所有关于请求响应的处理都围绕此实例进行。

常用方法
/* 获取字节输出流,可向前台输出任何类型的数据 */
ServletOutputStream getOutputStream() throws IOException;
/* 获取字符输出流,可向前台输出字符类型的数据 */
PrintWriter getWriter() throws IOException;

/* 获取服务器端输出流编码格式 */
String getCharacterEncoding();
/* 设置服务器端输出流编码格式 */
String setCharacterEncoding(String charset);

/* 获取响应体数据类型 */
String getContentType();
/* 设置响应体数据类型 */
void setContentType(String type);
/* 设置响应体数据字符长度 */
void setContentLength(int len);

/* 获取指定响应头的值 */
String getHeader(String name);
/* 获取指定响应头的多个值 */
Collection<String> getHeaders(String name);
/* 设置指定响应头的值 */
void setHeader(String name,String value);
/* 添加响应头并设置值 */
void addHeader(String name,String value);
/* 判断是否设置了指定响应头 */
boolean containsHeader(String name);

/* 设置响应状态码 */
void setStatus(int sc);

/* 发送重定向地址 */
void sendRedirect(String location) throws IOException;

/* 添加指定Cookie */
void addCookie(Cookie cookie);
解决响应乱码

  服务器端编码方式与客户端编码方式不一致时将出现乱码现象。方式一:先设置服务器端编码方式,在设置相关响应头,指定客户端应当使用的编码方式:

/* 设置服务器端编码方式 */
resp.setCharacterEncoding("UTF-8");
/* 设置Content-Type响应头,指定客户端应当使用的编码方式 */
resp.setHeader("Content-Type","charset=UTF-8");
/* 如果回传内容是html页面,需加上对应的MIME类型,否则所有字符将原样展示 */
resp.setHeader("Content-Type","text/html;charset=UTF-8");

  方式二:直接设置响应内容的编码方式:

/* 设置回传内容的编码方式 */
resp.setContentType("charset=UTF-8");
/* 如果回传内容是html页面,需加上对应的MIME类型,否则所有字符将原样展示 */
resp.setContentType("text/html;charset=UTF-8");
响应重定向

  响应重定向是由服务器引导,客户端的行为。客户端发送第一个请求,服务器处理后向客户端发送一个新地址,客户端接收到新地址后会立刻自动跳转至该地址,浏览器地址栏发生改变,服务器再次接受请求并作出响应,重定向完成。一次响应重定向中,有两次请求和两次响应。响应重定向使用的方法:

/* 发送重定向地址 */
void sendRedirect(String location) throws IOException;

  响应重定向有非常广泛的用途,例如客户端未登录却试图访问个人信息时,服务器可以根据页面的登录状态引导客户端重定向到登录地址。

Cookie

  Cookie是浏览器提供的技术,用于将少量的需要存储在客户端的数据存储在计算机本地,不需要经过网络传输,可以提高网页的运行效率。例如浏览器中的记住密码即使用Cookie技术实现。Cookie中的常用方法:

/* 创建Cookie对象 */
public Cookie(String name,String value) {...}

/* 获取Cookie名 */
public String getName() {...}
/* 获取Cookie值 */
public String getValue() {...}
/* 为Cookie设置新名称 */
public void setValue(String newValue) {...}

/* 获取Cookie的有效时间(秒) */
public int getMaxAge() {...}
/* 设置Cookie的有效时间(秒) */
public void setMaxAge(int enpiry) {...}

/* 获取Cookie的允许访问路径 */
public String getPath() {...}
/* 设置Cookie的允许访问路径 */
public void setPath(String uri) {...}

  Cookie的创建:

/* Cookie构造器,传入key和value */
public Cookie(String name,String value);
Cookie cookie = new Cookie("password","123456789");

  Cookie的发送是一种服务器端响应行为,需使用service()方法中的HttpServletResponse参数进行发送:

@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    /* 发送Cookie */
    resp.addCookie(cookie);
} 

  Cookie的接收是一种客户端请求行为,需使用service()方法中的HttpServletRequest参数进行接收:

@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) 
    throws ServletException,IOException {
    Cookie[] cookies = req.getCookies();
    for(Cookie cookie : cookies) {
        System.out.println(cookie.getName() + " : " + cookie.getValue());
    }
} 

  Cookie的有效时间默认为浏览器关闭后即失效,可以通过方法设置Cookie的有效时间:

/* 设置Cookie的有效时间,传入的值为以下时:
   1.负数,Cookie不存活,仅在浏览器内存中存活,maxAge的默认值为-1,关闭浏览器后Cookie失效
   2.正整数,Cookie存活指定的时间(单位是秒)
   3.0,Cookie的有效时间设置为0时,Cookie被删除!!!
     通过使用cookie.setMaxAge(0)删除用户内存及硬盘上的指定Cookie!!! */
public void setMaxAge(int expiry);
public int getMaxAge();

  通过设置Cookie路径可指定服务器下的哪些项目或资源可以访问该Cookie访问路径中包含Cookie路径时,可访问该Cookie,不包含则不能访问。

Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/"表示当前服务器下的任何项目都可访问此Cookie */
cookie.setPath("/");
resp.addCookie(cookie);

Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/项目名"表示仅当前项目可访问此Cookie,这是默认情况,无需显式设置 */
cookie.setPath("/Project1");
resp.addCookie(cookie);

Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/当前项目名"表示仅当前项目可访问此Cookie,这是默认情况,无需显式设置 */
cookie.setPath("/Project1");
resp.addCookie(cookie);

Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"/其它项目名"表示仅指定的其它项目可访问此Cookie */
cookie.setPath("/Project2");
resp.addCookie(cookie);

Cookie cookie = new Cookie("password","123456789");
/* 设置Cookie路径为"资源路径"表示仅指定的资源可访问此Cookie */
cookie.setPath("/Project1/src1");
resp.addCookie(cookie);

  Cookie的使用注意点:

1.Cookie存储在当前浏览器中,如果更换浏览器则无法访问Cookie数据
2.Cookie不支持存储中文数据,如果有相关需求,发送时使用URLEncoder.encode()进行编码,
  获取时使用URLDecoder.decode()进行解码
3.同一域名下Cookie的name不重复,如果服务器发送重复名称的Cookie,将覆盖之前的Cookie
4.浏览器可存储的Cookie数量是有限的

Session

  Session表示客户端与服务器的会话,是一种维护客户端与服务器关联的技术,SessionHTTP规范之一。每个客户端拥有不同的SessionSession常用于保存用户登录之后的信息,例如用户关闭浏览器后再打开登陆页面可自动登录并展示信息。Session底层使用Cookie技术实现,常用的Session-APIHttpSession接口。

获取Session

  使用HttpServlet对象中service()方法的HttpServletRequest参数获取HttpSession对象:

/* 获取HttpSession对象时,如果存在HttpSession对象则直接获取,不存在则创建HttpSession对象 */
req.getSession();

  对于JSP页面,必须设置page指令的session属性为true(默认为true)才会创建Session对象。

常用方法

/* 获取Session创建时间 */
long getCreateTime();
/* 获取会话标识,即Session对象的唯一标识 */
String getId();
/* 获取最后一次访问时间 */
long getLastAccessedTime();
/* 获取Servlet上下文 */
ServletContext getServletContext();
/* 判断Session是否为本次新创建 */
boolean isNew();

/* 获取指定属性 */
Object getAttribute(String name);
/* 获取所有属性名 */
Enumeration<String> getAttributeNames();
/* 设置属性值,如果不存在该属性,创建新属性 */
void setAttribute(String name,Object value);
/* 移除指定属性 */
void removeAttribute(String name);

/* 设置Session的超时时长(秒),会话超过指定时长,Session将被销毁;
   负数指定Session永不超时 */
void setMaxInactiveInterval(int interval);
/* 获取Session的超时时长(秒) */
int getMaxInactiveInterval();

/* 立即销毁Session */
void invalidate();

Session域对象

  Session对象表示服务器与客户端的会话,会话中可包含多次访问,Session域对象用于存储共享数据,会话中的所有访问均可使用该共享数据。

/* 获取会话 */
HttpSession session = req.getSesson();
/* 设置属性值 */
session.setAttribute("user","Lishaoyin");
session.setAttribute("password","123456789");
/* 获取属性值 */
String user = (String)session.getAttribute()
/* 移除属性 */
session.removeAttribute("password");

Session超时时长

  会话存活时间超过超时时长后,Session将销毁。Tomcat的默认Session最大不活动时间为30min,可以在Tomcat安装(解压)目录/conf/web.xml中全局修改。

<session-config>
    <!-- 修改全局HttpSession的超时时长,单位为min -->
    <session-timeout>30</session-timeout>
</session-config>

  通过web.xml修改的是所有Session的超时时长,通过方法可以设置指定Session的超时时长。

/* 设置HttpSession的超时时长(秒) */
session.setMaxInactiveInterval(1000);
/* 获取HttpSession的超时时长(秒) */
int time = session.getMaxInactiveInterval();
/* 立即销毁HttpSession对象 */
session.invalidate();

  客户端每次访问服务器的资源时,资源对应的Session都会更新访问时间,Session的超时时间是以客户端的最新一次访问为起点(并非以Session创建时间为起点),下一次访问之前超过超时时长,Session失效。

Session生命周期

1.Session的创建
    (1).Servlet或JSP第一次被访问时,调用req.getSession()将创建新的Session
    (2).Servlet或JSP上一次Session超时后,调用req.getSession()将创建新的Session
    (3).Servlet或JSP未超时,但调用invalidate()方法手动销毁Session后,
        调用req.getSession()将创建新的Session
2.Session的访问
    (1).客户端访问Servlet或JSP时,Session将自动更新访问时间,Session的失效时间为
        最新访问时间+Session超时时长
    (2).关闭浏览器或客户端行为不会销毁Session对象
3.Session的销毁
    (1).Session超时
    (2).调用invalidate()方法手动销毁Session
    (3).服务器端webapp关闭

Servlet示例

文件上传

前台页面

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Upload</title>
</head>
<body>
    <!-- 1.文件上传的表单方法必须使用"POST"
         2.enctype必须设置为"multipart/form-data"才能支持文件上传
         3.action指定资源访问路径,与@WebServlet设置的路径对应 -->
    <form method="POST" 
          enctype="multipart/form-data"
          action="http://localhost:8080/ServletDemo/UploadServlet">
        用户<input type="text" name="user"/><br/>
        密码<input type="text" name="password"/><br/>
        <input type="file" name="file"/><br/>
        <button type="submit">上传</button>
    </form>
</body>
</html>
后台代码

UploadServlet.java

/* 必须添加@MultipartConfig注解,Servlet才能支持文件上传 */
@MultipartConfig
@WebServlet(value="/UploadServlet")
public class UploadServlet extends HttpServlet {
    private ServletConfig config;
    @Override
    public void init(ServletConfig config) {
        this.config = config;
    }
    
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
    
    @Override
    protected void service(HttpServletRequest request,HttpServletResponse response) 
        throws ServletException,IOException {
        /* 设置请求编码 */
        request.setCharacterEncoding("UTF-8");
        
        /* 获取普通数据,传入表单输入框的"name"属性 */
        String user = request.getParameter("user");
        String password = request.getParameter("password");
        System.out.println("user : " + user + " , " + "password : " + password);
        
        /* 获取上传的文件必须首先获取Part(接口)对象;
           获取文件数据,传入表单输入框的"name"属性 */
        Part part = request.getPart("file");
        
        /* 获取文件名 */
        String fileName = part.getSubmittedFileName();
        /* 获取服务器上指定URI的实际计算机路径 */
        String serverPath = config.getServletContext().getRealPath("/");
        String filePath = serverPath + File.separator + fileName;
        
        System.out.println(filePath);
        
        /* 将文件上传至指定路径 */
        part.write(filePath);
        
        /* 设置回传内容类型及编码 */
        response.setContentType("text/html;charset=UTF-8");
        /* 获取响应输出流 */
        OutputStream os = response.getOutputStream();
        /* 响应 */
        os.write("上传完成".getBytes("UTF-8"));
        /* 关闭流 */
        os.close();
    }
}

文件下载

超链接下载

download-a.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Upload</title>
</head>
<body>
    <!-- 对于服务器可以识别的文件格式,点击超链接时将原样展示 -->
    <a href="/download/file1.txt">文件1</a><br/>
    <a href="/download/file2.png">文件2</a><br/>
    <!-- 对于服务器不能识别的文件格式,点击超链接时将自动下载 -->
    <a href="/download/file3.rar">文件3</a><br/>
    <hr/>
    <!-- 使用"download"属性可是服务器下载指定文件,download属性值指定下载后的文件名 -->
    <a href="/download/file1.txt" download="test.txt">文件1</a><br/>
    <a href="/download/file2.png" download="test.png">文件2</a>
</body>
</html>
Servlet下载

  前台页面:

download.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Download</title>
</head>
<body>
    <div>
        <form method="GET" 
              action="http://localhost:8080/ServletDemo/file-download">
            <!-- 告知服务器需要下载哪个文件 -->
            <button type="submit" name="file" value="file1.txt">文件1下载</button>
        </form>
    </div>
    <div>
        <form method="GET" 
              action="http://localhost:8080/ServletDemo/file-download">
            <button type="submit" name="file" value="file2.png">文件2下载</button>
        </form>
    </div>
    <div>
        <form method="GET" 
              action="http://localhost:8080/ServletDemo/file-download">
            <button type="submit" name="file" value="file3.rar">文件3下载</button>
        </form>
    </div>
</body>
</html>

  后台代码:

DownloadServlet.java

@WebServlet(value="/file-download")
public class DownloadServlet extends HttpServlet {
    private ServletConfig config;
    @Override
    public void init(ServletConfig config) {
        this.config = config;
    }
    
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
    
    @Override
    protected void service(HttpServletRequest request,HttpServletResponse response) 
        throws ServletException,IOException {
        
        /* 设置请求编码 */
        request.setCharacterEncoding("UTF-8");
        
        /* 获取客户端请求的文件名 */
        String fileName = request.getParameter("file");
        /* 获取文件在服务器上的完整路径 */
        String filePath = config.getServletContext().getRealPath("/download/") 
            + File.separator + fileName;
        
        File file = new File(filePath);
        /* 判断指定文件是否存在以及是否是标准的文件格式 */
        if(file.exists() && file.isFile()) {
            
            /* 设置响应内容类型为浏览器不可识别的应用类型,否则页面将原样打印输出流内容 */
            response.setContentType("application/x-msdownload");
            /* 设置响应头,此部分内容在浏览器的下载图标上展示 */
            response.setHeader("Content-Disposition","attachment;filename=" + fileName);
            
            /* 获取文件输入流 */
            InputStream is = new FileInputStream(file);
            /* 获取响应输出流 */
            OutputStream os = response.getOutputStream();
            
            byte[] bytes = new byte[1024];
            int len;
            /* 响应输出 */
            while((len = is.read(bytes)) != -1) {
                os.write(bytes,0,len);
            }
            
            /* 关闭流 */
            is.close();
            os.close();
        } 
        else {
            response.setContentType("text/html;charset=UTF-8");
            OutputStream os = response.getOutputStream();
            os.write("文件不存在!".getBytes("UTF-8"));
            os.close();
        }
    }
}




JSP

概述

  使用JSP必须有jsp-api.jarservlet-api.jar的支持,可在Tomcat安装(解压)路径/lib中引用。
  JSP(Java Server Page)SUN公司提供的动态网页编程技术,是服务器端的动态资源。JSP用于代替Servlet程序回传数据,因为Servlet回传HTML页面是一项非常繁琐的工作。
  JSP是可以内嵌Java代码的HTML页面,服务器会将JSP翻译为Servlet源码再编译运行,JSP实质就是Servlet程序。根据目前已有的技术,不建议将Java代码嵌入JSP页面,这种做法将增加代码的耦合度。

JSP示例

  JSP的编写方式与HTML基本一致。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>$Title$</title>
    </head>
    <body>
        $END$
    </body>
</html>

JSP的作用

  JSP之所以称为动态网页,是因为JSP同时具有排版网页和响应请求的功能。JSP将服务器对请求的响应输出转换为网页标签的形式,通过在JSP中编写的逻辑,可以实现在网页中指定位置响应输出指定内容。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Hello world</title>
    </head>
    <body>
        <h1>Hello world,Hello coder!</h1>
        <table>
            <tr>
                <td>姓名</td>
                <td>学号</td>
                <td>班级</td>
            </tr>
            <tr>
                <!-- 在网页的指定位置响应输出指定内容 -->
                <td><%=request.getParameter("name") %></td>
                <td><%=request.getParameter("gender") %></td>
                <td><%=request.getParameter("class") %></td>
            </tr>
        </table>
    </body>
</html>

JSP基本使用

page指令

  JSP文档开头的<%@ page ... %>称为page指令,通过修改其中的属性,可以配置很多重要信息。

<%@ page contentType="text/html;charset=UTF-8" language="java"%>

  常用属性:

language:表示JSP翻译后的语言类型
contentType:表示JSP返回的数据类型,与JSP翻译后Servlet对象中response.setContentType(...)等价
pageEncoding:表示JSP页面本身的字符集
import:用于导入Java的包或类
errorPage:设置当JSP页面运行出错时,自动跳转的页面路径,路径字符串一般由"/"开头
isErrorPage:设置当前JSP页面是否是错误信息页面,默认为false,如果设置为true,可以打印错误异常信息
session:设置访问当前JSP页面,是否会创建HttpSession对象,默认是true
extends:设置JSP翻译为Java代码后的Servlet对象类默认继承的Servlet实现类
autoFlush:设置out输出流缓冲区满后,是否自动刷新缓冲区,默认值是true
buffer:设置out缓冲区大小,默认是8kb

注释

1.<!-- 显式注释,继承自HTML,浏览器控制台可见 -->
2.<%-- 隐式注释,JSP自身的注释,浏览器控制台不可见 --%>
3.// 隐式注释,单行注释,继承自Java,浏览器控制台不可见
4./* 隐式注释,多行注释,继承自Java,浏览器控制台不可见 */

脚本小程序

  JSP页面中嵌入的Java代码称为脚本小程序,JSP中共有三种脚本小程序编写方法:

1.Java代码段,用于声明局部变量,编写语句
    <%
        // Java代码段
    %>
2.声明代码段,用于声明全局变量、方法、类等,以及静态代码块
    <%!
        // 声明代码段
    %>
3.输出表达式,用于输出变量或字面量到浏览器页面
    <%=数值 %>

  示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <!-- 声明代码段 -->
        <%!
            static {
                System.out.println("Hello world!");
            }
            private Map<String,Object> map;
        %>
        <!-- Java代码段 -->
        <%
            map.put("key1",20);
            map.put("key2",30);
            System.out.println(map.get("key1"));
        %>
        <!-- 输出表达式 -->
        <%=new String("Hello world!") %>
    </body>
</html>

  每种脚本小程序都可以编写多个,且多个脚本小程序编译时会拼接为一段完整的Java程序。

<%
    for(int i=0;i<100;i++) {
        System.out.println("Hello world!");
    }
%>

<%-- 等价于 --%>
<%
    for(int i=0;i<100;i++) {
%>
<%
        System.out.println("Hello world!");
    }
%>

  可将Java代码段与输出表达式结合使用,实现非常灵活的功能:

<!-- 以下代码可以循环生成表格单元 -->
<table border="1" cellspacing="0">
    <%
        for(int i=0;i<10;i++) {
    %>
    <tr>
        <td>第<%=i+1%>行第1列</td>
        <td>第<%=i+1%>行第2列</td>
        <td>第<%=i+1%>行第3列</td>
    </tr>
    <%
        }
    %>
</table>

  每执行一次<%=...%>相当于使用response打印一次数据至页面,因此可以使用Java代码段与输出表达式结合使用简化重复繁琐的工作。

JSP运行原理

  Web容器第一次运行时将JSP翻译为类似于Servlet接口实现类的Java源码,再将翻译后的Java源码编译为class文件放在特定位置交给JVM运行。JSP中的三种脚本小程序对应翻译为Servlet接口实现类的不同部分:

<!-- <%! ... %> -->
<%! ... %>中的静态代码块 -> Servlet实现类的静态代码块
<%! ... %>中的变量 -> Servlet实现类的成员变量
<%! ... %>中的方法 -> Servlet实现类的成员方法

<!-- <% ... %> -->
<% ... %>中的变量 -> service()方法(实际是jspService()方法)中的局部变量
<% ... %>中的语句 -> service()方法(实际是jspService()方法)方法中的语句

<!-- <%= ... %> -->
<%= ... %>中的内容使用out.write()方法写入到网页的指定位置

  可重写JSP的内置方法实现Servlet的其它方法:

<!-- 必须在<%! ... %>中重写,jspService()方法不可自定义 -->
<%!
    /* Servlet对象初始化调用,对应init()方法,
       ServletConfig参数已被JSP全局化为config内置对象 */
    public void jspInit() {
        super.jspInit();
        System.out.println("Hello.");
    }
    /* Servlet对象销毁调用,对应destroy()方法 */  
    public void jspDestroy() {
        super.jspDestroy();
        System.out.println("Bye."); 
    }
%>

  JSP翻译后的Servlet实现类的service()方法(实际是jspService()方法)中包含了很多out.write()调用,这些调用从HTML的文档声明开始,结合翻译后<%= ... %>中的内容逐字符的输出网页内容到缓冲区中,网页输出完毕后,调用out.flush()方法将缓冲区的内容响应到客户端的网页上。

/* 逐字符输出HTML页面内容至缓冲区 */
out.write("<!DOCTYPE html ...>\r\n");
out.write("<html>\r\n");
out.write(" <head>\r\n");
out.write("  <title>...</title>\r\n");
...
/* 将缓冲区内容响应至客户端页面 */
out.flush();

  服务器只在第一次运行时翻译JSP代码并编译产物,以后运行不需要再重复,因此JSP只在第一次运行时比传统Servlet效率低。

JSP语法

JSP内置对象

  编写JSP页面嵌入Java代码时,可以直接使用Servlet相关的某些对象,这些对象称为JSP内置对象。

request(HttpServletRequest类):请求对象
response(HttpServletResponse类):响应对象
pageContext(PageContextImpl类):JSP的上下文对象
session(HttpSession类):会话对象   
application(ServletContext类):Servlet上下文
config(ServletConfig)类:Servlet配置对象
out:JSP输出流    
page:指向当前JSP页面的对象
exception:异常对象,需开启isErrorPage=true    

JSP域对象

  JSP内置对象中包含了四个JSP域对象,这些域对象都能像Map一样存取或者移除数据,功能一致,只是数据的作用范围不同。

pageContext:数据在当前JSP页面内有效(最小范围域)
request:数据在一次请求内有效
session:数据在一个会话内有效(打开浏览器访问服务器,直到关闭浏览器)
application:数据在整个Web工程内有效(最大范围域)

  例:

<%
    <%-- 添加数据 --%>
    pageContext.setAttribute("key","pageContext");
    request.setAttribute("key","request");
    session.setAttribute("key","session");
    application.setAttribute("key","application");
%>
<!-- 获取数据 -->
<%=pageContext.getAttribute("key") %><br/>
<%=request.getAttribute("key") %><br/>
<%=session.getAttribute("key") %><br/>
<%=application.getAttribute("key") %><br/>

JSP常用标签

静态包含

  对于某些被重复使用的页面内容,可以使用静态包含将外部JSP内容引用到当前JSP页面中。静态包含的底层原理是将被引用JSP文件使用out.write()原样添加到引用位置。

<!-- file属性指定被引用的JSP文件地址 -->
<%@ include file="/..." %>

  例:

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        I am index.jsp<br/>
        <!-- "/"表示"http://ip:port/project" -->
        <%@ include file="/test.jsp" %>
    </body>
</html>

test.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        Hello world!
    </body>
</html>
动态包含

  动态包含同样可以引用外部JSP文件,不同的是被引入的JSP页面的内置对象会被当前JSP的内置对象替代,因此当前JSP页面可以向被引入的JSP页面传递数据。

<!-- page指定引入页面的路径 -->
<jsp:include page="/...">
    <%-- jsp:param标签用于向被引入页面传递数据 --%>
    <jsp:param name="..." value="..."/>
    <jsp:param name="..." value="..."/>
    <jsp:param name="..." value="..."/>
    ...
</jsp:include>

  例:

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        I am index.jsp<br/>
        <!-- 引用外部JSP文件 -->
        <jsp:include page="/test.jsp">
            <%-- 向外部JSP文件传递数据 --%>
            <jsp:param name="name" value="Lishaoyin"/>
            <jsp:param name="number" value="1703181407"/>
        </jsp:include>
    </body>
</html>

test.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        Hello world!<br/>
        <!-- 接收数据 -->
        <%=request.getParameter("name") %><br/>
        <%=request.getParameter("number") %>
    </body>
</html>
请求转发

  可以使用专门的请求转发标签代替request.getRequestDispatcher(...).forward(...)实现请求转发。

<jsp:forward page="/...">
    <%-- jsp:param标签用于向转发页面传递数据 --%>
    <jsp:param name="..." value="..."/>
    <jsp:param name="..." value="..."/>
    <jsp:param name="..." value="..."/>
</jsp:forward>

  例:

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        <!-- 请求转发 -->
        <jsp:forward page="/test.jsp">
            <%-- 向外部JSP文件传递数据 --%>
            <jsp:param name="name" value="Lishaoyin"/>
            <jsp:param name="number" value="1703181407"/>
        </jsp:forward>
    </body>
</html>

test.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        Hello world!<br/>
        <!-- 接收数据 -->
        <%=request.getParameter("name") %><br/>
        <%=request.getParameter("number") %>
    </body>
</html>

EL表达式

基础

  EL(Expression Language)用于替代JSP中的表达式脚本进行数据的输出,EL表达式主要用于输出JSP域对象中的数据。EL表达式输出示例:

<%
    /* 在pageContext域中添加属性 */
    pageContext.setAttribute("name","Lishaoyin");
%>
<%-- 获取域中的属性并输出 --%>
EL表达式输出的结果是:${name}

  JSP拥有四个不同作用范围的域对象,EL表达式按照JSP域对象的作用范围从小到大搜索指定数据,并输出第一个搜索到的数据:

<%
    /* pageContext的作用范围只在当前页面有效,是JSP中作用范围最小的域对象 */
    pageContext.setAttribute("key","pageContext");
    /* request的作用范围在一次请求中有效,包括请求转发至其它页面,作用范围大于pageContext */
    request.setAttribute("key","request");
    /* session的作用范围在一次会话中有效,作用范围大于request */
    session.setAttribute("key","session");
    /* application的作用范围在整个Web工程有效,是JSP中作用范围最大的域对象 */
    application.setAttribute("key","application");
%>
<%-- EL表达式按照JSP域对象的作用范围从小到大搜索指定数据,输出"pageContext" --%>
EL表达式输出的结果是:${key}

  EL表达式对null值的处理比JSP表达式脚本更加严谨:

<%
    pageContext.setAttribute("name",null);
%>
<%-- 输出"null"字符串 --%>
表达式脚本的输出结果是:<%=pageContext.getAttribute("name") %><br/>
<%-- 输出空串 --%>
EL表达式的输出结果是:${name}

  EL表达式输出对象:

<%
    Person person = new Person("Tony","m",1.75f,140);
    /* 域对象添加属性 */
    pageContext.setAttribute("person",person);
%>
${person.getName()}的名字是${person.getName()}<br/>
${person.getName()}的性别是${person.getSex()}<br/>
${person.getName()}的身高是${person.getHeight()}<br/>
${person.getName()}的体重是${person.getWeight()}

  EL表达式还可以调用对象中的私有属性,实际调用的是私有属性对应的getXXX()方法,因此必须为对象定义符合命名规范的get/set方法。

<%
    Person person = new Person("Tony","m",1.75f,140);
    /* 域对象添加属性 */
    pageContext.setAttribute("person",person);
%>
${person.name}的名字是${person.name}<br/>
${person.name}的性别是${person.sex}<br/>
${person.name}的身高是${person.height}<br/>
${person.name}的体重是${person.weight}

EL表达式运算

<%
    int a = 10;
    int b = 20;
    boolean c = true;
    boolean d = false;
    pageContext.setAttribute("a",a);
    pageContext.setAttribute("b",b);
    pageContext.setAttribute("c",c);
    pageContext.setAttribute("d",d);
%>

<%-- 关系运算,输出"true/false" --%>
a等于b?:${a == b}<br/>
a等于b?:${a eq b}<br/>

a不等b?:${a != b}<br/>
a不等b?:${a ne b}<br/>

a大于b?:${a > b}<br/>
a大于b?:${a gt b}<br/>

a小于b?:${a < b}<br/>
a小于b?:${a lt b}<br/>

a大于等于b?:${a >= b}<br/>
a大于等于b?:${a ge b}<br/>

a小于等于b?:${a <= b}<br/>
a小于等于b?:${a le b}<br/>

<%-- 逻辑运算,输出"true/false" --%>
c与d:${c && d}<br/>
c与d:${c and d}<br/>

c或d:${c || d}<br/>
c或d:${c or d}<br/>

非c:${!c}<br/>
非c:${not c}<br/>

<%-- 算数运算,输出运算结果 --%>
a+b=${a + b}<br/>
a-b=${a - b}<br/>
a*b=${a * b}<br/>

a/b=${a / b}<br/>
a/b=${a div b}<br/>

a%b=${a % b}<br/>
a%b=${a mod b}<br/>

<%-- empty运算,判断对象是否为空,输出"true/false" --%>
<%
    pageContext.setAttribute("null",null);
    pageContext.setAttribute("string","");
    pageContext.setAttribute("array",new Byte[0]);
    pageContext.setAttribute("list",new ArrayList<String>());
    pageContext.setAttribute("map",new HashMap<String,Object>());
%>
null值判断为空:${empty null}<br/>
空字符串判断空:${empty string}<br/>
数组长度为0判断为空:${empty array}<br/>
list无元素判断为空:${empty list}<br/>
map无元素判断为空:${empty map}<br/>

<%-- 三元运算,输出运算结果 --%>
求最大:${a>b ? a : b}

<%-- .运算,输出bean类的成员 --%>
<%
    pageContext.setAttribute("person",new Person("Tony","m",1.75f,140));
%>
输出属性(调get()方法):${person.name}<br/>
输出方法(输出方法返回):${person.getName()}<br/>

<%-- []运算,输出有序集合指定索引的成员值 --%>
<%
    pageContext.setAttribute("array",new String[]{"Tony","Steven","Jack"});
    ArrayList<String> list = new ArrayList<String>();
    list.add("Tony");
    list.add("Steven");
    list.add("Jack");
    pageContext.setAttribute("list",list);
%>
输出数组成员值:${array[1]}<br/>
输出集合成员值:${list[0]}

EL隐含对象

  EL表达式定义了一些隐含对象,可以在EL表达式中直接使用:

pageContext : PageContextImpl类型,用于获取JSP内置对象
pageScope : Map<String,Object>类型,用于获取pageContext域中的数据
requestScope : Map<String,Object>类型,用于获取request域中的数据
sessionScope : Map<String,Object>类型,用于获取session域中的数据
applicationScope : Map<String,Object>类型,用于获取application域中的数据
param : Map<String,String>类型,用于获取请求参数值
paramValues : Map<String,String[]>类型,用于获取请求参数值(一个参数有多个值时使用)
header : Map<String,String>类型,用于获取请求头值
headerValues : Map<String,String[]>类型,用于获取请求头值(一个请求头有多个值时使用)
cookie : Map<String,Cookie>类型,用于获取当前请求的Cookie信息
initParam : Map<String,String>类型,用于获取初始化参数,
    对应web.xml中的<init-param>或@WebServlet注解中的initParams

  获取域中的数据:

<%
    pageContext.setAttribute("key","pageContext");
    request.setAttribute("key","request");
    session.setAttribute("key","session");
    application.setAttribute("key","application");
%>
获取pageContext域的数据:${pageScope.key}<br/>
获取request域的数据:${requestScope.key}<br/>
获取session域的数据:${sessionScope.key}<br/>
获取application域的数据:${applicationScope.key}

  pageContext获取JSP内置对象(表达式脚本与EL表达式对比):

<%-- 获取request --%>
获取请求协议:<%=request.getScheme() %><br/>
获取请求协议:${pageContext.request.scheme}<br/>

获取服务器IP或域名:<%=request.getServerName() %><br/>
获取服务器IP或域名:${pageContext.request.serverName}<br/>

获取服务器端口号:<%=request.getServerPort() %><br/>
获取服务器端口号:${pageContext.request.serverPort}<br/>

获取工程路径:<%=request.getContextPath() %><br/>
获取工程路径:${pageContext.request.contextPath}<br/>

获取请求方法:<%=request.getMethod() %><br/>
获取请求方法:${pageContext.request.method}<br/>

获取客户端IP:<%=request.getRemoteHost() %><br/>
获取客户端IP:${pageContext.request.remoteHost}<br/>

获取客户端端口号:<%=request.getRemotePort() %><br/>
获取客户端端口号:${pageContext.request.remotePort}<br/>

获取会话ID:<%=session.getId() %><br/>
获取会话ID:${pageContext.session.id}<br/>

JSTL标签库

基础

  JSTL(JSP Standard Tag Library)用于替换JSP中的代码脚本。JSTL标签库:

功能范围URI前缀
核心标签库http://java.sun.com/jsp/jstl/corec
格式化标签库http://java.sun.com/jsp/jstl/fmtfmt
函数标签库http://java.sun.com/jsp/jstl/functionsfn

  使用JSTL需要先引入JSTL:

<%-- 引入核心标签库,prefix指定前缀,uri指定路径 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%-- 引入格式化标签库,prefix指定前缀,uri指定路径 --%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%-- 引入函数标签库,prefix指定前缀,uri指定路径 --%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

https://jakarta.ee/zh/specifications/tags/2.0
jakarta-servlet-jsp-jstl-xxx.jar

JSTL依赖包

  使用JSTL需要相关jar包的支持,可以在Tomcat(Tomcat10)的安装路径中获取相关jar包:

Tomcat安装(解压)路径/webapps/examples/WEB-INF/lib
    --taglibs-standard-impl-xxx-migrated-xxx.jar
    --taglibs-standard-spec-xxx-migrated-xxx.jar

JSTL-core标签库

  使用core标签库需要引入:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
set标签

  set标签用于向指定域中添加指定属性或修改已有属性。

<%-- scope属性指定域对象,var属性指定key,value属性指定value;
     set标签相当于:scope.setAttribute(var,value); --%>
<c:set scope="..." var="..." value="..." />

<%-- 示例 --%>

<%-- 添加或修改属性,scope可以取值:"page","request","session","application" --%>
<c:set scope="page" var="name" value="Lishaoyin"/>
<%-- 获取属性 --%>
你的名字是:${pageScope.name}
remove标签

  remove标签用于移除指定域中的属性。

<%-- scope属性指定域对象,var属性指定key,
     remove标签相当于scope.removeAttribute(key); --%>
<c:remove scope="..." var="..." />

<%-- 示例 --%>

<%-- 添加属性 --%>
<c:set scope="page" var="test" value="page" />
<c:set scope="request" var="test" value="request" />
<div>${pageScope.test}</div>
<div>${requestScope.test}</div>
<%-- 移除属性 --%>
<c:remove scope="request" var="test" />
<div>${test}</div>
if标签

  if标签用于进行单分支判断。

<%-- test属性传递EL表达式,if标签内部为HTML语句,
     test属性值为true,if内部的HTML语句展示,否则不展示 --%>
<c:if test="EL表达式">
    HTML内容
</c:if>

<%-- 示例 --%>

<%-- 添加属性 --%>
<c:set scope="page" var="test" value="100"/>
<c:if test="${pageScope.test > 50}">
    <div>test大于50</div>
</c:if>
<c:if test="${pageScope.test < 50}">
    <div>test小于50</div>
</c:if>
<c:if test="${pageScope.test == 50}">
    <div>test等于50</div>
</c:if>
choose标签

  choose标签用于进行多分支判断,相当于switch语句。

<%-- <c:when>标签指定判断分支,test属性传递EL表达式的值,
     test属性值为true则展示标签内的HTML语句,否则不展示;
     otherwise为其余判断情况 --%>
<c:choose>
    <c:when test="EL表达式1">HTML语句1</c:when>
    <c:when test="EL表达式2">HTML语句2</c:when>
    ...
    <c:otherwise>HTML语句</c:otherwise>
</c:choose>

<%-- 示例 --%>

<%-- 添加属性 --%>
<c:set scope="page" var="test" value="100"/>
<c:choose>
    <c:when test="${pageScope.test > 50}"><div>test大于50</div></c:when>
    <c:when test="${pageScope.test == 50}"><div>test等于50</div></c:when>
    <c:otherwise><div>test小于50</div></c:otherwise>
</c:choose>
forEach标签

  forEach标签用于遍历循环输出。

<%-- forEach标签的属性:
     var(String类型)指定遍历变量名,
     begin(int类型)指定遍历初始索引,
     end(int类型)指定遍历结束索引,
     step(int类型)指定每次增加的步长(默认为1),
     items(Object类型)指定被循环的数据 --%>

<%-- forEach用法1,相当于:
     for(int var=begin;var<=end;var+=step){...} --%>
<c:forEach var="..." begin="..." end="..." step="...">
    ...
</c:forEach>

<%-- 示例 --%>

<%
    String[] names = new String[]{"Tony","Tom","Steven","Ellen"};
    pageContext.setAttribute("array",names);
    /* 数组的长度 */
    pageContext.setAttribute("length",names.length);
%>
<c:forEach var="index" begin="0" end="${pageScope.length-1}" step="1">
    <%-- 使用EL表达式取值 --%>
    <div>name is ${array[index]}</div>
</c:forEach>

<%-- forEach用法2,相当于:
     for(Object var : items){...} --%>
<c:forEach var="..." items="...">
    ...
</c:forEach>

<%-- 示例 --%>

<%
    ArrayList<String> names = new ArrayList<String>();
    names.add("Tony");
    names.add("Tom");
    names.add("Steven");
    names.add("Ellen");
    pageContext.setAttribute("list",names);
%>
<%-- items使用EL表达式传入需要遍历的集合 --%>
<c:forEach var="name" items="${pageScope.list}">
    <%-- 使用EL表达式取值 --%>
    <div>name is ${name}</div>
</c:forEach>

JSTL-fmt标签库

  使用fmt标签库需要引入:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
formatNumber标签

  formatNumber标签用于将指定数字转换成指定格式或类型的数据,再将转换后的数据添加到指定域对象中。

<%-- value指定要显示的数字, 
     type指定需要转换为的数据类型,
         可取number(数字,默认值),percent(百分比),currency(货币类型,带¥/$符号)
     scope指定数据输出的域对象,
     var指定数据在域对象中引用的key,如果不设置var,
         标签会在JSP页面中直接展示转换结果,如果设置了var,需要使用域对象获取结果 --%>
<fmt:formatNumber value="..." type="..." scope="..." var="..."/>

<%-- 示例 --%>

<fmt:formatNumber value="3.1415" type="number" scope="page" var="PI"/>
<div>value of PI is ${pageScope.PI}</div>

<fmt:formatNumber value="0.1" type="percent" scope="page" var="per"/>
<div>percent is ${pageScope.per}</div>

<%-- setLocale标签用于设置时区,设置为"ch_CN"带上"¥",设置为"en_US"带上"$" --%>
<fmt:setLocale value="en_US"/>
<fmt:formatNumber value="1000000" type="currency" scope="page" var="money"/>
<div>money is ${pageScope.money}</div>
formateDate标签

  formatNumber标签用于将日期数据转换成指定格式,再将转换后的结果添加到指定域对象中。

<%-- value指定要显示的日期,
     type指定要显示的数据类型,可取"DATE"/"TIME"/"BOTH",
     dateStyle指定要显示的日期类型,可取"FULL"/"LONG"/"MEDIUM"/"SHORT"/"DEFAULT",
     timeStyle指定要显示的时间类型,可取"FULL"/"LONG"/"MEDIUM"/"SHORT"/"DEFAULT",
     pattern指定自定义时间格式,
     timeZone指定时区,
     scope指定数据输出的域对象,
     var指定数据在域对象中引用的key,如果不设置var,标签会在JSP页面中直接展示转换结果,
         如果设置了var,需要使用域对象获取结果 --%>
<fmt:formatDate value="..." 
                type="..." 
                dateStyle="..." 
                timeStyle="..." 
                pattern="..." 
                timeZone="..." 
                scope="..."
                var="..."/>

<%-- 示例 --%>

<%
    pageContext.setAttribute("myDate",new Date());
%>
<fmt:formatDate value="${myDate}" type="BOTH" var="date" scope="page"/>
<div>date is ${date}</div>
<%-- 使用pattern自定义日期时间格式 --%>
<fmt:formatDate value="${myDate}" pattern="yyyy-MM-dd" var="date" scope="page"/>
<div>date is ${date}</div>




Filter

  FilterJavaWeb的三大组件之一,作用是拦截请求,过滤响应。Filter可用于权限检查、事务管理等场景。FilterJavaEE的规范之一,具体为Filter接口。具有FilterWeb工程运行流程为:

请求->Filter->Servlet->响应->Filter->客户端

概述

Filter接口

package jakarta.servlet;
public interface Filter {
    /* Filter初始化时由Web容器调用 */
    void init(FilterConfig filterConfig) throws ServletException;
    /* Filter核心方法,客户端请求映射资源时由Web容器调用 */
    void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) 
        throws IOException,ServletException;
    /* Filter销毁前调用 */
    void destroy();
}

Filter演示

  实现以下功能:在工程中创建资源文件夹logged用于存储已登录用户可访问的资源,客户端请求其中的资源时,检查用户是否已登录,如果未登录则跳转至登录页面。使用Filter需要进行配置:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    ...
    <!-- 为Filter起名,指定Filter实现类的全类名 -->
    <filter>
        <filter-name>login-filter</filter-name>
        <filter-class>com.lishaoyin.servletdemo.LoginFilter</filter-class>
    </filter>
    <!-- 路径映射,url-pattern指定拦截资源路径 -->
    <filter-mapping>
        <filter-name>login-filter</filter-name>
        <!-- 拦截http://ip:port/project/logged下的所有资源 -->
        <url-pattern>/logged/*</url-pattern>
    </filter-mapping>
    ...
</web-app>

LoginFilter.java

public class LoginFilter implements filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }
    @Override
    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) 
        throws IOException,ServletException {
        /* session域用于存储会话信息,可使用session域中的状态变量判断用户的登录状态 */
        HttpSession session = ((HttpServletRequest)request).getSession();
        if(session.getAttribute("user") == null) {
            /* 如果未登录则将请求转发至登录页面 */
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        } else {
            /* 如果已登录则继续执行请求和响应 */
            chain.doFilter(request,response);
        }
    }
    @Override
    public void destroy() {
        
    }
}

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div>please login!</div>
    <form action="http://localhost:8080/ServletDemo/login" method="POST">
        <div>用户:<input type="text" name="user"/></div>
        <div>密码:<input type="password" name="password"/></div>
        <div><button type="submit">确认</button></div>
    </form>
</body>
</html>

LoginServlet.java

@WebServlet(value="/login")
public class LoginServlet extends HttpServlet {
    @Override 
    protected void service(HttpServletRequest request,HttpServletResponse response)
        throws ServletException,IOException {
        request.setCharacterEncoding("UTF-8");
        /* 获取表单数据 */
        String user = request.getParameter("user");
        String password = request.getParameter("password");
        if("Lishaoyin".equals(user) && "123456".equals(password)) {
            /* 设置登录标志 */
            request.getSession().setAttribute("user",user);
            /* 设置登录信息的过期时间 */
            request.getSession().setMaxInactiveInterval(60);
            /* 解决乱码 */
            response.setContentType("text/html;charset=UTF-8");
            response.getOutputStream().write("登录成功".getBytes("UTF-8"));
        }
    }
}

Filter深入

Filter的生命周期

1.Filter的创建:服务器启动时由服务器创建
    (1).调用构造器创建Filter
    (2).调用init()方法
2.Filter过滤:每次客户端发出请求时,服务器都会调用doFilter()方法对请求进行拦截
3.Web工程关闭,Filter被销毁

Filter的配置方法

使用配置文件

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    ...
    <!-- 为Filter起名,指定Filter实现类的全类名 -->
    <filter>
        <filter-name>login-filter</filter-name>
        <filter-class>com.lishaoyin.servletdemo.LoginFilter</filter-class>
        <!-- 可自定义多个Filter初始化参数 -->
        <init-param>
            <param-name>...</param-name>
            <param-value>...</param-value>
        </init-param>
        <init-param>
            <param-name>...</param-name>
            <param-value>...</param-value>
        </init-param>
        ...
    </filter>
    
    <!-- 路径映射,url-pattern指定拦截资源路径 -->
    <filter-mapping>
        <filter-name>login-filter</filter-name>
        <url-pattern>url1</url-pattern>
    </filter-mapping>
    <!-- 一个Filter可映射多个资源 -->
    <filter-mapping>
        <filter-name>login-filter</filter-name>
        <url-pattern>url2</url-pattern>
    </filter-mapping>
    ...
    
    ...
</web-app>
使用注解

  使用@WebFilter注解也可实现Filter的配置,与配置文件等效,并且可与配置文件一起使用。

WebFilter.java

public @interface WebFilter {
    String description() default "";
    String displayName() default "";
    /* Filter初始化参数 */
    WebInitParam[] initParams() default {};
    /* Filter名,与<filter-name>对应 */
    String filterName() default "";
    String smallIcon() default "";
    String largeIcon() default "";
    String[] servletNames() default {};
    /* 拦截路径,与<url-pattern>对应,与urlPatterns等效,但不能同时使用 */
    String[] value() default {};
    /* 拦截路径,与<url-pattern>对应,与value等效,但不能同时使用 */
    String[] urlPatterns() default {};
    DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST};
    boolean asyncSupported() default false;
}

  例:

@WebFilter(value="/logged/file.png") // 指定拦截路径为http://ip:port/project/logged/file.png
public class LoginFilter implements filter {...}

Filter相关的类

FilterConfig接口

  FilterConfig类用于封装Filter的配置信息,对应配置文件中的信息。

public interface FilterConfig {
    /* 获取Filter名,对应<filter-name> */
    String getFilterName();
    /* 获取Servlet上下文 */
    ServletContext getServletContext();
    /* 获取Filter初始化参数,对应<filter>标签中<init-param>中的内容 */
    String getInitParameter(String name);
    /* 获取所有Filter初始化参数名 */
    Enumeration<String> getInitParameterNames();
}
FilterChain接口
public interface FilterChain {
    /* 执行过滤器链,请求路径中可能包含映射的多个Filter,多个Filter从外层路径开始链式执行,
       如果有下一个Filter,执行下一个Filter的filterChain.doFilter();
       如果没有下一个Filter,执行资源请求 */
    void doFilter(ServletRequest request, ServletResponse response) 
        throws IOException,ServletException;
}

  doFilter()中编写在filterChain.doFilter()之前的代码称为前置代码,之后的代码成为后置代码,整个过滤器链执行的过程中,前置代码顺序执行,后置代码倒序执行:

Filter1.doFilter()->前置代码1->chainFilter.doFilter()->
    Filter2.doFilter()->前置代码2->chainFilter.doFilter()->
        ...
        Filtern.doFilter()->前置代码n->chainFilter.doFilter()->
        后置代码n->
    后置代码n-1->
    ...
后置代码1->执行客户端请求
FilterChain的特点
1.FilterChain中的所有Filter在同一个线程中执行,即内层Filter会阻塞外层Filter的运行
2.FilterChain中的所有filterChain.doFilter()的前置代码顺序执行,后置代码倒序执行
3.FilterChain中的所有Filter共享同一个request

Filter拦截路径

1.精确匹配:
    <!-- 拦截指定的一个资源 -->
    <url-pattern>http://ip:port/project/resource</url-pattern>
2.目录匹配:
    <!-- 拦截http://ip:port/project/dir下的所有资源 -->
    <url-pattern>/dir/*</url-pattern>
3.后缀匹配
    <!-- 拦截http://ip:port/project/dir下的所有后缀为.jsp的资源 -->
    <url-pattern>/dir/*.jsp</url-pattern>




Listener

  ListenerJavaWeb三大组件之一,用于监听对象的创建和销毁,或监听域对象的改变等。

ServletContextListener

  ServletContextListener用于监听Servlet上下文创建与销毁,可以简单理解为Web工程的开启与关闭。

ServletContextListener.java

package jakarta.servlet;
public interface ServletContextListener extends EventListener {
    /* Servlet上下文初始化时由Web容器调用,可以简单理解为Web工程开启时调用 */
    void contextInitialized(ServletContextEvent sce);
    /* Servlet上下文销毁时由Web容器调用,可以简单理解为Web工程关闭或服务器关闭时调用 */
    void contextDestroyed(ServletContextEvent sce);
}

ServletContextEvent

  ServletContextEvent类是ServletContextListener中监听上下文初始化和销毁方法的参数,常用方法有:

/* 获取ServletContext */
public ServletContext getServletContext();

配置

  使用配置文件配置:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    <listener>
        <!-- Listener的配置仅需指明实现类路径信息 -->
        <listener-class>com.lishaoyin.servletdemo.MyContextListener</listener-class>
    </listener>
</web-app>

  使用@WebListener注解配置:

@WebListener
public class MyContextListener implements ServletContextListener {
    ...
}

示例

@WebListener
public class MyContextListener implements ServletContextListener {
    /* Servlet上下文初始化时由Web容器调用,可以简单理解为Web工程开启时调用 */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("Web工程开启");
    }
    /* Servlet上下文销毁时由Web容器调用,可以简单理解为Web工程关闭或服务器关闭时调用 */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Web工程关闭");
    }
}

ServletRequestListener

  ServletRequestListener用于监听客户端请求的创建与销毁。

ServletRequestListener.java

package jakarta.servlet;
public interface ServletRequestListener extends EventListener {
    /* 客户端请求初始化时由Web容器调用,客户端发起请求时,请求被创建并初始化 */
    void requestInitialized(ServletRequestEvent sre);
    /* 客户端请求被销毁时由Web容器调用,服务器响应请求后,请求被销毁 */
    void requestDestroyed(ServletRequestEvent sre);
}

ServletRequestEvent

  ServletRequestEvent类是ServletRequestListener中监听request初始化和销毁方法的参数,常用方法有:

/* 获取ServletRequest对象 */
public ServletRequest getServletRequest();
/* 获取ServletContext对象 */
public ServletContext getServletContext();

配置

  使用配置文件配置:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    <listener>
        <!-- Listener的配置仅需指明实现类路径信息 -->
        <listener-class>com.lishaoyin.servletdemo.MyRequestListener</listener-class>
    </listener>
</web-app>

  使用@WebListener注解配置:

@WebListener
public class MyRequestListener implements ServletRequestListener {
    ...
}

示例

@WebListener
public class MyRequestListener implements ServletRequestListener {
    /* 客户端请求初始化时由Web容器调用,客户端发起请求时,请求被创建并初始化 */
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("客户端发起新的请求");
        ServletRequest request = sre.getServletRequest();
        try {
            /* 设置请求的编码 */
            request.setCharacterEncoding("UTF-8");
        } catch(Exception e) {
            e.printStackTrace();
        }  
    }
    
    /* 客户端请求被销毁时由Web容器调用,服务器响应请求后,请求被销毁 */
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("服务器已处理请求");
    }
}

HttpSessionListener

  HttpSessionListener用于监听session的创建和销毁。

HttpSessionListener.java

public interface HttpSessionListener extends EventListener {
    /* 新的session创建时由Web容器调用 */
    void sessionCreated(HttpSessionEvent se);
    /* session过期或被手动销毁时由web容器调用 */
    void sessionDestroyed(HttpSessionEvent se);
}

HttpSessionEvent

  HttpSessionEvent类是HttpSessionListener中监听session创建和销毁方法的参数,常用方法有:

/* 获取session */
public HttpSession getSession();

配置

  使用配置文件配置:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app ... >
    <listener>
        <!-- Listener的配置仅需指明实现类路径信息 -->
        <listener-class>com.lishaoyin.servletdemo.MySessionListener</listener-class>
    </listener>
</web-app>

  使用@WebListener注解配置:

@WebListener
public class MySessionListener implements HttpSessionListener {
    ...
}

示例

MySessionListener.java

@WebListener
public class MySessionListener implements HttpSessionListener {
    /* 统计在线人数 */
    private int onlineNumber = 0;
    /* 新的session创建时由Web容器调用 */
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("成员加入");
        onlineNumber++;
        /* 更新ServletContext域中的在线人数 */
        se.getSession().getServletContext().setAttribute("online-number",onlineNumber);
    }
    /* session过期或被手动销毁时由web容器调用 */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("成员离开");
        onlineNumber--;
        /* 更新ServletContext域中的在线人数 */
        se.getSession().getServletContext().setAttribute("online-number",onlineNumber);
    }
}

LoginServlet.java

@WebServlet(value="/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request,HttpServletResponse response)
            throws ServletException,IOException {
        request.setCharacterEncoding("UTF-8");
        /* 获取或创建Session */
        HttpSession session = request.getSession();
        /* 设置登录过期时间 */
        session.setMaxInactiveInterval(10);
        response.setContentType("text/html;charset=UTF-8");
        String message = "当前在线人数 : " 
            + session.getServletContext().getAttribute("online-number") 
            + "人";
        response.getOutputStream().write(message.getBytes("UTF-8"));
    }
}




JSON

  JSON(JavaScript Object Notation)是一种轻量级客户端与服务器之间的数据交换格式。

JSON格式

JSON对象

  JSON是一组键值对,可包含各种类型的数据。JSON可以是对象格式(JavaScript对象),也可以是字符串形式。JSON对象格式为:

/* 键与值之间使用":"分隔,多个键值对之间使用","分隔 */
var jsonObj = {
    key1:value,
    key2:value,
    ...
}
    
/* JSON的数据类型可以是普通类型、数组和JSON类型 */
var jsonObj = {
    key1:123,
    key2:"abc",
    key3:false,
    key4:[123,"abc"],
    key5:{
        key5_1:246,
        key5_2:"abc"
    }
}

/* JSON的访问:JSON对象.键 */
jsonObj.key1

  示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSON</title>
</head>
<body>
    <script type="text/javascript">
        var json = {
            name:"Lishaoyin",
            age:22,
            skill:["c","java","javascript"],
            department:{
                id:1,
                salary:1000000
            }
        }
        console.log(json.name);
        for(var i=0;i<json.skill.length;i++) {
            console.log(json.skill[i]);
        }
        console.log(json.department.salary);
    </script>
</body>
</html>

JSON字符串

  JSON为字符串形式时,JSON字符串必须以{}包括,key必须以""包括。

/* JSON为字符串形式时,JSON字符串必须以{}包括,key必须以""包括,\"是"的转义 */
String json = "{\"key1\":value,\"key2\":value...}";

/* 例 */
String json = "{\"username\":\"Lishaoyin\",\"password\":\"123456789\"}";

客户端JSON

  JSON对象形式在客户端(JavaScript)需要对数据进行处理时使用,字符串形式在客户端与服务器交换数据时使用。客户端(JavaScript)中JSON的内置对象名即为JSON.客户端JSON的常用方法:

/* 将JSON对象转换为JSON字符串 */
JSON.stringify(jsonObject);
/* 将JSON字符串转换为JSON对象 */
JSON.parse(jsonString);

/* JSON对象示例 */
var json = {
    name:"Lishaoyin",
    age:22,
    skill:["c","java","javascript"],
    department:{
        id:1,
        salary:1000000
    }
}

/* JSON字符串示例,
   JSON字符串必须使用"{}"包括,key必须使用""包括,\"为"的转义 */
var jsonString = "{\"key1\":value,\"key2\":value...}"

/* JSON对象转换为JSON字符串 */
var jsonString = JSON.stringify(json);
console.log(jsonString);

/* JSON字符串转换为JSON对象,\"是"的转义 */
var jsonObj = JSON.parse("{\"name\":\"Lishaoyin\",\"age\":22}");
console.log(jsonObj.name);

客户端发送JSON

  客户端发送JSON将在之后的AJAX章节介绍。

客户端接收JSON

  客户端接收JSON将在之后的AJAX章节介绍。

服务器JSON

  服务器(Java)使用JSON可以使用谷歌提供的GSON,也可以直接使用JSON字符串。

JSONJavaBean的转换

Person person = new Person("Tony",1.75f,140);
Gson gson = new Gson();
/* JavaBean对象转换为JSON字符串 */
String jsonString = gson.toJson(person);

/* JSON字符串转换为JavaBean对象 */
Person person_1 = gson.fromJson("{\"name\" : \"Tony\",\"height\" : 1.75,\"weight\" : 140}",
                                Person.class);

JSONList的转换

List<Person> list = new ArrayList<Person>();
list.add(new Person("Tony",1.75f,140));
list.add(new Person("Steven",1.85f,150));
list.add(new Person("Ellen",1.80f,130));

Gson gson = new Gson();
/* 将List转换为JSON字符串数组 */
String jsonString = gson.toJson(list);

String jsons = {"{\"name\":\"Tony\",\"height\":1.75,\"weight\":140}",
                "{\"name\":\"Steven\",\"height\":1.85,\"weight\":150}",
                "{\"name\":\"Ellen\",\"height\":1.80,\"weight\":130}"};
/* 将JSON字符串数组转换为泛型List,参数1传入JSON字符串数组,参数2传入Type类型 */
List<Person> persons = gson.fromJson(jsons,new PersonListType().getType());

  将JSON字符串数组转换为对应的泛型List需要定义一个空类,并继承TypeToken类:

/* PersonListType是一个中转工具类 */
public class PersonListType extends TypeToken<List<Person>> {
    // 空类
}

JSONMap的转换

Map<String,Person> map = new HashMap<String,Person>();
map.put("Tony",new Person("Tony",1.75f,140));
map.put("Steven",new Person("Steven",1.85f,150));
map.put("Ellen",new Person("Ellen",1.80f,130));

Gson gson = new Gson();
/* 将Map转换为JSON字符串数组 */
String jsonString = gson.toJson(map);

/* key对应Map的key */
String jsons = {"\"Tony\":{\"name\":\"Tony\",\"height\":1.75,\"weight\":140}",
                "\"Steven\":{\"name\":\"Steven\",\"height\":1.85,\"weight\":150}",
                "\"Ellen\":{\"name\":\"Ellen\",\"height\":1.80,\"weight\":130}"};
/* 将JSON字符串转换为Map,参数1传入JSON字符串,参数2传入Type类型 */
Map<String,Person> persons = gson.fromJson(jsons,new PersonMapType().getType());

  将JSON字符串数组转换为对应的Map需要定义一个空类,并继承TypeToken类:

/* PersonMapType是一个中转工具类 */
public class PersonMapType extends TypeToken<Map<String,Person>> {
    // 空类
}

服务器接收JSON

  JSON用于向服务器传递参数,JavaScript发送的JSON数据将以"?key1=value&key2=value..."的形式追加至请求url之后,因此服务器端相应的使用request.getParameter()方法获取JSON数据。

String username = request.getParameter("username");
String password = request.getParameter("password");

服务器响应JSON

  服务器只能使用字符串形式响应JSON,但可以指定响应内容的数据类型。

/* 设置响应数据类型为JSON并设置编码格式,
   "application"指定响应数据为应用类型,不在页面上直接展示 */
response.setContentType("application/json;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,key必须使用""包括,\"是"的转义 */
String json = "{\"username\":\"Lishaoyin\",\"password\":\"123456789\"}";
response.getOutputStream().write(json.getBytes("UTF-8"));

/* 设置响应数据类型为普通文本并设置编码格式,
   "application"指定响应数据为应用类型,不在页面上直接展示 */
response.setContentType("application/text;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,key必须使用""包括,\"是"的转义 */
String json = "{\"username\":\"Lishaoyin\",\"password\":\"123456789\"}";
response.getOutputStream().write(json.getBytes("UTF-8"));

  服务器端响应指定的数据类型必须与客户端一致。




AJAX

  AJAX(Asynchronous JavaScript And XML)是客户端与服务器交换数据的另一种格式,并且是目前最常用的服务器与客户端数据交换格式。AJAX的原理为浏览器通过JavaScript异步向服务器发起请求,因此可以在不刷新网页的情况下向服务器发送请求,并且可实现网页局部数据刷新。W3School-AJAX教程

原生AJAX

AJAX请求步骤

1.创建XMLHttpRequest对象
/* XMLHttpRequest对象是AJAX的核心,AJAX实际是XMLHttpRequest对象的别名 */
var xhr = new XMLHttpRequest(method,url,async)
/* 例 */
var xhr = new XMLHttpRequest();

2.调用XMLHttpRequest对象的open()方法设置请求参数
/* 设置请求参数, 
   参数method指定请求方法("GET"/"POST");
   url指定请求资源的地址;
   async指定是否为异步请求,为true则为异步请求 */
xhr.open(method,url,async);
/* 例 */
xhr.open("GET","http://ip:port/project?key1=value&key2=value",true);

3.绑定接收响应方法
/* 为onreadystatechange绑定函数,在绑定的函数中可以根据不同状态码实现不同的功能;
   readyState : 
       0 - 请求未初始化
       1 - 服务器连接已建立
       2 - 请求已接收
       3 - 请求处理中
       4 - 请求已完成,且响应已就绪
   status : 
       404 - 找不到指定资源
       500 - 服务器内部错误
       200 - "OK" */
xhr.onreadystatechange = function() {
    // 根据不同的状态码实现不同的功能
    if(xhr.readyState == 4 && xhr.status == 200) {
        // 接收响应数据
    }
}

4.调用XMLHttpRequest对象的send()方法发送请求
/* 例 */
xhr.send();

XMLHttpRequest中的重要方法

/* 取消当前请求 */
abort();

/* 以字符串形式返回所有响应头 */
getAllResponseHeaders();

/* 设置请求参数, 
   参数method指定请求方法("GET"/"POST");
   url指定请求资源的地址;
   async指定是否为异步请求,为true则为异步请求 */
open(method,url,async);
/* 设置请求参数同时指定用户名和密码 */
open(method,url,async,userName,password);

/* 发送请求 */
send();
/* 发送请求内容,请求内容可以是任何内容,仅用于POST请求 */
send(content);

/* 设置指定请求头的值 */
setRequestHeader(label,value);

AJAX请求参数

GET请求

  AJAX发送GET请求需要在请求的url之后追加请求参数。

/* 请求指定资源 */
xhr.open("GET","http://localhost:8080/ServletDemo/src",true);
/* 发送请求 */
xhr.send();

/* 设置请求参数 */
xhr.open("GET","http://localhost:8080/ServletDemo?name=Lishaoyin&password=123456",true);
/* 发送请求 */
xhr.send();
POST请求

  AJAX发送POST请求需要使用send()方法,并且需要设置请求头。

/* 设置请求地址 */
xhr.open("POST","http://localhost:8080/ServletDemo/src",true);
/* 设置请求头,设置请求内容类型 */
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
/* 发送请求参数 */
xhr.send("name=Lishaoyin&password=123456");

AJAX接收响应

xhr.onreadystatechange = function() {
    // 根据不同的状态码实现不同的功能
    if(xhr.readyState == 4 && xhr.status == 200) {
        /* 获取响应数据(非XML数据,例如JSON) */
        var text = console.log(xhr.responseText);
        /* 获取响应数据(XML数据) */
        var xml = console.log(xhr.responseXML);
    }
}

jQuery使用AJAX

  实际开发中不会使用原生AJAX,而是使用JavaScript框架实现的AJAX,例如jQuery.jQuery在线教程

基础

  jQuery使用AJAX的方式一:

/* 使用jQuery对象中的ajax()函数,函数中传入JSON对象 */
$.ajax({
    param1:value,
    param2:value,
    ...
    paramn:value
});

/* ajax()参数 */
type : 请求方式"GET"/"POST"
url : 请求地址
async : 是否异步请求,true/false,默认为true
data : 发送至服务器的数据,如果没有参数则不需要设置
dataType : 预期服务器返回的数据类型
contentType : 设置请求头
success : 请求成功回调函数
error : 请求失败回调函数

/* 示例 */
$.ajax({
    type:"GET",
    url:"http://localhost:8080/ServletDemo/src",
    async:true,
    /* 设置返回的数据类型为JSON,此时jQuery接收到数据后会将数据自动封装为JSON格式 */
    dataType:"json",
    /* JSON对象格式数据 */
    data:{
        name:"Lishaoyin",
        age:22
    },
    /* success回调函数的参数data为响应数据 */
    success:function(data) {
        var json = JSON.parse(data);
        console.log(json.name + " , " + json.age);
    }
});

  jQuery使用AJAX的方式二:

/* 使用"GET"方法请求指定资源,传入资源地址,请求参数和请求响应回调函数 */
$.get(url,param,function(data){
    // ...
});
/* 例:请求指定资源 */
$.get("http://localhost:8080/ServletDemo/file.png");
/* 例:请求指定动作 */
$.get("http://localhost:8080/ServletDemo/login",
      {user:"Lishaoyin",password:"123456"},
      function(data){
    // ...
});

/* 使用"POST"方法请求指定资源,传入资源地址,请求参数和请求响应回调函数 */
$.post(url,param,function(data){
    // ...
});
/* 例:请求指定资源 */
$.post("http://localhost:8080/ServletDemo/file.png");
/* 例:请求指定动作 */
$.post("http://localhost:8080/ServletDemo/login",
      {user:"Lishaoyin",password:"123456"},
      function(data){
    // ...
});

  jQuery使用AJAX的方式三:

/* 使用"GET"方法,要求回传数据为JSON格式,传入请求地址,请求参数和响应回调函数 */
$.getJSON(url,param,function(data){
    // ...
});

AJAX发送JSON

$.ajax()
$.ajax({
    ...
    url:"http://ip:port/project/src",
    /* 发起请求时data中的参数直接追加至url之后:
       "http://ip:port/project/src?key1=value&key2=value..." */
    data:{
        key1:value,
        key2:value,
        ...
    }
    ...
});
    
/* 例 */
    
$.ajax({
    type:"GET",
    url:"http://localhost:8080/ServletDemo/login",
    async:true,
    dataType:"json",
    /* 请求参数追加:
       "http://localhost:8080/ServletDemo/login?username=Lishaoyin&password=123456789" */
    data:{
        username:"Lishaoyin",
        password:"123456789"
    }
});
$.get()/post()/getJSON()
/* 发起请求时参数直接追加至url之后:
   "http://ip:port/project/src?key1=value&key2=value..." */
$.get("http://ip:port/project/src",{key1:value,key2:value...});

/* 例 */

/* 请求参数追加:
   "http://localhost:8080/ServletDemo/login?username=Lishaoyin&password=123456789" */
$.get("http://ip:port/project/src",{
    username:"Lishaoyin",
    password:"123456789"});

AJAX接收JSON

JSON对象形式

  服务器端发送数据:

/* 设置响应内容为JSON并设置编码格式,
   "application"指定响应内容为应用类型,不在页面上直接展示 */
response.setContentType("application/json;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,且key必须使用""包括 */
String json = "{\"key1\":value,\"key2\":value...}";
/* 发送响应 */
response.getOutputStream().write(json.getBytes("UTF-8"));

  客户端接收数据:

$.ajax({
    ...
    dataType:"json", 
    success(data){
        /* dataType指定为"json",jQuery直接将响应结果处理为JSON */
        console.log(data.key1);
        console.log(data.key2);
        ...
    },
    ...
});
JSON字符串形式

  服务器端发送数据:

/* 设置响应内容为普通文本并设置编码格式,
   "application"指定响应内容为应用类型,不在页面上直接展示 */
response.setContentType("application/text;charset=UTF-8");
/* JSON字符串必须使用"{}"包括,且key必须使用""包括 */
String json = "{\"key1\":value,\"key2\":value...}";
/* 发送响应 */
response.getOutputStream().write(json.getBytes("UTF-8"));

  客户端接收数据:

$.ajax({
    ...
    dataType:"text",
    success(data){
        /* dataType设置为"text",jQuery将原样接收响应字符串,需要将字符串转换为JSON */
        var json = JSON.parse(data);
        console.log(json.key1);
        console.log(json.key2);
        ...
    },
    ...
});

示例

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <!-- 远程jQuery -->
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <script type="text/javascript">
        $(function(){
            var usernameInput = $("input[name='username']");
            var passwordInput = $("input[name='password']");
            var usernameInputFinished = false;
            var passwordInputFinished = false;
            
            usernameInput.on("focusin",function(){usernameInputFinished = false;});
            passwordInput.on("focusin",function(){passwordInputFinished = false;});
            
            /* 提交结果 */
            function submit(username,password) {
                $.ajax({
                    type:"POST",
                    url:"http://localhost:8080/ServletDemo/login",
                    async:true,
                    /* 要求响应格式为JSON,如果响应格式不正确,将跳至error回调 */
                    dataType:"json",
                    data:{
                        username:username,
                        password:password
                    },
                    success:function(data){
                        console.log("request succeed");
                        if(data.message == "login success") {
                            alert("login success");
                        }
                        else if(data.message == "user not exists") {
                            alert("please check username");
                        }
                        else if(data.message == "password wrong") {
                            alert("please check password");
                        }
                    },
                    error:function(){
                        alert("please check the internet connection");
                    }
                });
            }
            
            usernameInput.on("focusout",function(){
                usernameInputFinished = true;
                /* 检查username非空 */
                if(!usernameInput.val()) {
                    usernameInputFinished = false;
                    alert("user name cannot be null");
                } 
                /* 检查是否同时输入完成了password */
                else if(passwordInputFinished) {
                    console.log("user finished input");
                    /* 提交数据 */
                    submit(usernameInput.val(),passwordInput.val());
                }
            });
            passwordInput.on("focusout",function(){
                passwordInputFinished = true;
                /* 检查password非空 */
                if(!passwordInput.val()) {
                    passwordInputFinished = false;
                    alert("password cannot be null");
                } 
                /* 检查是否同时输入完成了username */
                else if(usernameInputFinished) {
                    console.log("user finished input");
                    /* 提交数据 */
                    submit(usernameInput.val(),passwordInput.val());
                }
            });
        });
    </script>
    <style type="text/css">
        *{
            font-family: "Youyuan";
        }
    </style>
</head>
<body>
    <h1>请登录</h1><br/>
    账户<input name="username" type="text"/><br/>
    密码<input name="password" type="password"/>
</body>
</html>

LoginServlet.java

@WebServlet(value="/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request,HttpServletResponse response) 
        throws ServletException,IOException {
        /* 设置请求编码格式为UTF-8 */
        request.setCharacterEncoding("UTF-8");
        /* 设置响应内容为JSON及编码格式为UTF-8 */
        response.setContentType("application/json;charset=UTF-8");
        System.out.println("request in");
        /* 获取数据 */
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("username : " + username + " , " + "password : " + password + ".");
        if(username.equals("Lishaoyin")) {
            if(password.equals("123456789")) {
                /* 服务器端发送JSON字符串必须严格符合{"key1":value,"key2":value}格式,
                   "{}"必须加上,且双引号使用"\""转义 */
                response.getOutputStream().write(
                    "{\"message\":\"login success\"}".getBytes("UTF-8"));
                System.out.println("login success");
            } else {
                response.getOutputStream().write(
                    "{\"message\":\"password wrong\"}".getBytes("UTF-8"));
                System.out.println("password wrong");
            }
        } else {
            response.getOutputStream().write(
                "{\"message\":\"user not exists\"}".getBytes("UTF-8"));
            System.out.println("user not exists");
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值