05、servlet

5.1、idea创建JavaWeb项目及相关问题

java window 编码_java

【idea】

1、新建项目 - 新建module

2、在module中添加web

3、创建artifact - 部署包

4、libartifact之间的关系

  • 先有artifact,后来才添加的mysql.jar,此时,这个jar包并没有添加到部署包中。
  • 那么在Project Structure中有一个problems提示,点击fix,选择add to artifact
  • 另外,可以直接把lib文件夹直接新建在WEB-INF下。
  • 缺点:这个lib只能是当前这个module独享,如果有第二个module,我们需要再次重复的新建lib

5、在deployment,修改application Context,然后在会带server选项卡,检查URL的值。

  • URL的值指的是,Tomact启动完成后自动打开所指定的浏览器,其默认访问的网址。
  • 启动后,报404错误
  • 404:找不到指定的资源
  • 如果我们的网址是:http://localhost:8080/webPro1/,那么表明我们访问的是index.html
  • 可以通过<welcome-file-list>标签进行欢迎页。(在tomcatweb.xml中设置,或者在自己项目的web.xml中设置)

<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list>

6、405问题:当前请求的方法不支持

  • 如:我们表单method=post,那么Servlet必须对应doPost,否则报405错误。

7、空指针或者是NumberFormatException,因为有价格和库存,如果价格获取不到,结果你想对null进行Integer.parseInt()就会报错。错误的原因大部分是因为name = "price"此处写错了,结果在Servlet端还是使用request.getParameter("price")去获取;

5.2、servlet获取参数

add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="add" method="post">
        名称:<input type="text" name="fname"/></br>
        单价:<input type="text" name="price"/></br>
        库存:<input type="text" name="fcount"/></br>
        备注:<input type="text" name="remark"/></br>
        <input type="submit" value="添加"/>
    </form>
</body>
</html>

addServlet.java

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 addServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post方式下,设置编码:防止中文乱码
        request.setCharacterEncoding("UTF-8");
        //1、获取用户(客户端)发送的数据
        String fname = request.getParameter("fname");
        String priceStr = request.getParameter("price");
        Integer price = Integer.parseInt(priceStr);
        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        String remark = request.getParameter("remark");

        //System.out.println("fname:"+fname);
        FruitDAO fruitDAO = new FruitDAOImpl();
        boolean flag = fruitDAO.addFruit(new Fruit(fname,price,fcount,remark));
        System.out.println(flag == true ?"添加成功" : "添加失败");
    }
}

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">  
    <servlet>
        <servlet-name>AddServlet</servlet-name>
        <servlet-class>com.javaweb.servlets.addServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AddServlet</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>
</web-app>
  • 过程
  1. 用户发请求,action = post
  2. 项目中,web.xml中找到 url-pattern = /add ->第12行
  3. 找第11行的servlet-name = AddServlet
  4. 找和servlet-mapping中的servlet-name一致的servlet ->第7行
  5. 找第8行中的servlet-class -> com.javaweb.servlets.addServlet
  6. 用户发送的post请求(method = post),因此 tomcat 会执行AddServlet中的doPost方法

5.3、处理servlet请求参数中文乱码问题

5.3.1、Post请求方式

post方式下,设置编码为UTF-8:防止中文乱码

request.setCharacterEncoding("UTF-8");

5.3.2、Get请求方式

1、get方式目前不需要设置编码(基于tomcat8

2、如果是get请求发送的中文数据,转码稍微有点麻烦(tomcat之前

String fname = request.getParameter("fanme");
//1.将字符串打散成字节数组
byte[] bytes = fname.getBytes("ISO-8859-1");
//2.将字节数组按照设定的编码方式重新组装成字符串
fname = new String(bytes, "UTF-8");

注意:设置编码这一句代码必须在所有的获取参数动作之前

5.4、Servlet的继承关系——重点:查看服务方法(service())

5.4.1、继承关系

java window 编码_java-ee_02

5.4.2、相关方法

  • javax.servlet.Servlet 接口:
  • void init(config):初始化方法
  • void service(request, response):服务方法
  • void destory():销毁方法
  • javax.servlet.GenericServlet 抽象类:
  • void service(request, response):服务方法,仍然是抽象的
  • javax.servlet.http.HttpServlet 抽象子类:
  • void service(request, response):服务方法,不是抽象的
  1. String method = req.getMethod();获取请求的方式
  2. 各种if判断,根据请求方式不同,决定去调用不同的do方法
  3. 在HttpServlet`这个抽象类中,do方法都差不多
    如:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }
    }

service()

  • 当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
  • HttpServlet中去分析请求的方式:getpostheadputdelete
  • 然后决定调用哪个do开头的方法
  • 那么在HttpServlet中这些do方法默认都是405的实现风格——要我们子类去实现对应的方法,否则默认会报405错误。

因此,在新建Servlet时,要去考虑请求方法,从而决定重写哪个do方法。

5.5、Servlet的生命周期

  1. 生命周期:从出生到死亡的过程就是生命周期。
    对应Servlet中的三个方法:
  • init();
  • service();
  • destory();
  1. 默认情况下:
  • 第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(init()),然后服务(service())。
  • 从第二次请求开始,每一次都是服务
  • 当容器关闭时,其中的所有Servlet实例会被销毁,调用destory()
  1. 通过案例发现:
  • Tomcat只会创建一个Servlet实例,所有的请求都是这个实例去响应。
  • 默认情况下,第一次请求时,Tomcat才会去实例化,初始化,然后再服务。
  • 优点:提高系统的启动速度
  • 缺点:第一次请求时,耗时较长
  • 因此得出结论:
  • 如果需要提高系统的启动速度,当前默认情况就是这样。
  • 如果需要提高响应速度,我们需要设置servlet的初始化时机。
  1. Servlet的初始化时机:
  • 默认第一次接收请求时,实例化,初始化
  • 可以通过<load-on-startup>来设置Servlet启动的先后顺序,数字越小,启动越靠前,最小为0。
    web.xml
<servlet>
    <servlet-name>Demo02Servlet</servlet-name>
    <servlet-class>com.javaweb.servlets.Demo02Servlet</servlet-class>
    <!--数字越小(最好不要是负数),启动的顺序越靠前-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Demo02Servlet</servlet-name>
    <url-pattern>/demo02</url-pattern>
</servlet-mapping>
  1. Servlet在容器中是:单例的、线程不安全的
  • 单例:所有的请求都是一个实例去响应。
  • 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断,但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生变化。

java window 编码_java-ee_03

线程1访问该代码块,获取了变量num的值为1,正准备将num=1送进判断之前,线程2访问该代码块,并将num值改变,此时线程1获取的num值也发生了改变,从而导致线程1的执行路径发生变化。

启发:尽量不要在Servlet中定义成员变量。如果不得不定义成员变量,那么不要去根据成员变量的值做一些逻辑判断。

5.6、HTTP协议

5.6.1、介绍

1、HTTPHyper Text Transfer 超文本传输协议。

2、HTTP是无状态的。

3、HTTP最大的作用就是确定了请求和响应数据的格式。

  • 浏览器发送给服务器的数据:请求报文;
  • 服务器返回给浏览器的数据:响应报文。

5.6.2、请求报文

包含三个部分:

  1. 请求行

作用:展示当前请求的最基本信息

  • 请求方式
  • 请求的URL:访问地址
  • 请求的HTTP协议的版本(一般是HTTP1.1
  1. 请求(消息)头
    请求消息头中包含了很多客户端需要告诉服务器的信息。

作用:通过具体的参数对本次请求进行详细的说明

格式:键值对,键和值之间用冒号隔开

相对比较重要的请求消息头:

名称功能Host服务器的主机地址Accept声明当前请求能够接受的媒体类型Refer当前请求来源页面的地址Content-Length请求体内容的长度Content-Type请求体的内容类型,这一项的具体值是媒体类型中的某一种Cookie浏览器访问服务器时携带的Cookie数据

  1. 请求体

作用:作为请求的主题,发送数据给服务器,具体来说其实就是POST请求方式下的请求参数。

三种情况:

  • Get方式:没有请求体,但是有一个queryString
  • Post方式:有请求体,form data
  • Json格式:有请求体,request payload

5.6.3、响应报文

包含三个部分:

  1. 响应(状态)行

包含三个信息:

  • 协议
  • 响应状态码
  • 响应状态
  1. 响应(消息)头

包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)

  1. 响应体

响应的实际内容(比如添加add.html页面时,响应的内容就是<html><head><body><form>…)

5.7、Session

  1. HTTP是无状态的。
  • HTTP 无状态:服务器无法判断请求是同一个客户端发过来的,还是不同的客户端发过来的。
  • 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱。
  • 通过会话跟踪技术来解决无状态的问题

java window 编码_idea_04

  1. 会话跟踪技术
  • 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的session,然后响应给客户端。
  • 下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,服务器判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端。
  • 常用的API:
  • request.getSession():获取当前的会话,没有则创建一个新的会话
  • request.getSession(true):效果和不带参数相同
  • request.getSession(false):获取当前会话,没有则返回null,不会创建新的
  • session.getId():获取sessionID
  • session.isNew():判断当前的session是否是新的
  • session.getMaxInactiveInterval():获取session的非激活间隔时长,默认1800秒。
  • session.setMaxInactiveInterval():获取session的非激活间隔时长
  • session.invalidate():强制性让会话立即失效
  1. session保存作用域

java window 编码_java-ee_05

  • session保存作用域是和具体的某一个session对应的
  • 常用的API:
  • void session.setAttribute(key,value)
  • Object session.getAttribute(key)
  • void removeAttribute(key)

5.8、服务器内部转发以及客户端重定向

  1. 服务器内部转发:
    request.getRequestDispatcher("...").forward(request,response);

java window 编码_java window 编码_06

  • 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的。
  • 地址栏无变化
  1. 客户端重定向:
    response.sendRedirect("...");
  • 两次请求响应的过程。客户端肯定知道请求URL有变化。
  • 地址栏有变化

5.9、Thymeleaf——试图模板技术

5.9.1、Thymeleaf概述

  1. Thymeleaf优势
  • SpringBoot官方推荐使用的试图模板技术,和SpringBoot完美整合。
  • 不经过服务器运算仍然可以直接查看原始值,对前端工程师更友好。
  1. 流程:

java window 编码_java window 编码_07

5.9.2、Thymeleaf的使用

  1. 添加thymeleafjar
  2. web.xml文件中添加配置
    web.xml
<!--配置上下文参数-->
    <context-param>
        <param-name>view-prefix</param-name>
        <param-value>/</param-value>
    </context-param>
    <context-param>
        <param-name>view-suffix</param-name>
        <param-value>.html</param-value>
    </context-param>
  • 配置前缀:view-prefix
  • 配置后缀:view-suffix
  1. 新建一个servletViewBaseServletViewBaseServlet.java
package com.fruit.myssm.myspingmvc;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

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 ViewBaseServlet extends HttpServlet {

    private TemplateEngine templateEngine;

    @Override
    public void init() throws ServletException {

        // 1.获取ServletContext对象
        ServletContext servletContext = this.getServletContext();

        // 2.创建Thymeleaf解析器对象
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);

        // 3.给解析器对象设置参数
        // ①HTML是默认模式,明确设置是为了代码更容易理解
        templateResolver.setTemplateMode(TemplateMode.HTML);

        // ②设置前缀
        String viewPrefix = servletContext.getInitParameter("view-prefix");

        templateResolver.setPrefix(viewPrefix);

        // ③设置后缀
        String viewSuffix = servletContext.getInitParameter("view-suffix");

        templateResolver.setSuffix(viewSuffix);

        // ④设置缓存过期时间(毫秒)
        templateResolver.setCacheTTLMs(60000L);

        // ⑤设置是否缓存
        templateResolver.setCacheable(true);

        // ⑥设置服务器端编码方式
        templateResolver.setCharacterEncoding("utf-8");

        // 4.创建模板引擎对象
        templateEngine = new TemplateEngine();

        // 5.给模板引擎对象设置模板解析器
        templateEngine.setTemplateResolver(templateResolver);

    }

    protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 1.设置响应体内容类型和字符集
        resp.setContentType("text/html;charset=UTF-8");

        // 2.创建WebContext对象
        WebContext webContext = new WebContext(req, resp, getServletContext());

        // 3.处理模板数据
        templateEngine.process(templateName, webContext, resp.getWriter());
    }
}

4.使自己新建的servlet继承ViewBaseServlet

import com.fruit.dao.FruitDAO;
import com.fruit.dao.impl.FruitDAOImpl;
import com.fruit.myssm.myspingmvc.ViewBaseServlet;
import com.fruit.pojo.Fruit;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;


//servlet从3.0版本开始支持注解方式的注册
@WebServlet("/index")
public class indexServlet extends ViewBaseServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        FruitDAO fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList();

        //保存到session作用域
        HttpSession session = request.getSession();
        session.setAttribute("fruitList", fruitList);

        //此处的视图名称是 index
        //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称 上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
        //所以真实的视图名称:/index.html
        super.processTemplate("index",request,response);
    }
}

5.根据逻辑视图名称,得到物理视图名称

  • 视图名称是 index
  • 那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称 上去
  • 逻辑视图名称:index
  • 物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
  • 所以真实的视图名称:/index.html

5.9.3、thymeleaf`的标签

  • <th:if>
  • <th:unless>
  • <th:each>
  • <th:text>

5.10、保存作用域

  1. 原始情况下,保存作用域有四个:
  • page:页面级别,现在几乎不用
  • request:请求级别,一次请求响应范围
  • session:会话级别,一次会话范围
  • application:应用级别,整个应用程序范围
  1. request:一次请求响应范围
  • 重定向
  • 内部转发

java window 编码_idea_08

代码演示:

【demo01.java】

import javax.servlet.ServletException;
  import javax.servlet.annotation.WebServlet;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.io.IOException;
  
  @WebServlet("/demo01")
  public class Demo01Servlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //1.向request保存作用域保存数据
          request.setAttribute("uname","lili");
          //2.重定向
          //response.sendRedirect("demo02");
         
          //3.服务器端转发
          request.getRequestDispatcher("demo02").forward(request,response);
      }
  }

demo02.java

import javax.servlet.ServletException;
  import javax.servlet.annotation.WebServlet;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.io.IOException;
  
  @WebServlet("/demo02")
  public class Demo02Servlet extends HttpServlet {
  
      @Override
      protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          //1.获取request保存作用域保存的数据
          Object obj = request.getAttribute("uname");
          System.out.println("unameObj = " + obj);
      }
  }
  1. session:一次会话范围有效,同一个客户端有效

    此时,第二个客户端请求,服务器端无法打印出lili
  2. application:一次应用程序范围有效

    demo5.java
//演示application保存作用域
@WebServlet("/demo05")
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.向application保存作用域保存数据

        //ServletContext:Servlet上下文
        ServletContext application = request.getServletContext();
        application.setAttribute("uname","lili");
        //2.重定向
        //response.sendRedirect("demo02");
        //3.服务器端转发
        request.getRequestDispatcher("demo02").forward(request,response);
    }
}

demo06.java

@WebServlet("/demo06")
public class Demo06Servlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取application保存作用域保存的数据
        ServletContext application = request.getServletContext();
        Object obj = application.getAttribute("uname");
        System.out.println("unameObj = " + obj);
    }
}

5.11、路径问题

java window 编码_java-ee_09