JavaWeb学习笔记(五)—— Servlet

JavaWeb学习笔记(五)—— Servlet,第1张

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

【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
  • 可以通过标签进行欢迎页。(在tomcatweb.xml中设置,或者在自己项目的web.xml中设置)

    <welcome-file-list>
            <welcome-file>index.htmlwelcome-file>
            <welcome-file>index.htmwelcome-file>
            <welcome-file>index.jspwelcome-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>Titletitle>
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】


<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>AddServletservlet-name>
        <servlet-class>com.javaweb.servlets.addServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>AddServletservlet-name>
        <url-pattern>/addurl-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、继承关系

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();
  2. 默认情况下:

    • 第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(init()),然后服务(service())。
    • 从第二次请求开始,每一次都是服务
    • 当容器关闭时,其中的所有Servlet实例会被销毁,调用destory()
  3. 通过案例发现:

    • Tomcat只会创建一个Servlet实例,所有的请求都是这个实例去响应。

    • 默认情况下,第一次请求时,Tomcat才会去实例化,初始化,然后再服务。

      • 优点:提高系统的启动速度
      • 缺点:第一次请求时,耗时较长
    • 因此得出结论:

      • 如果需要提高系统的启动速度,当前默认情况就是这样。
      • 如果需要提高响应速度,我们需要设置servlet的初始化时机。
  4. Servlet的初始化时机:

    • 默认第一次接收请求时,实例化,初始化

    • 可以通过来设置Servlet启动的先后顺序,数字越小,启动越靠前,最小为0。

      【web.xml】

      <servlet>
          <servlet-name>Demo02Servletservlet-name>
          <servlet-class>com.javaweb.servlets.Demo02Servletservlet-class>
          
          <load-on-startup>1load-on-startup>
      servlet>
      <servlet-mapping>
          <servlet-name>Demo02Servletservlet-name>
          <url-pattern>/demo02url-pattern>
      servlet-mapping>
      
  5. Servlet在容器中是:单例的、线程不安全的

    • 单例:所有的请求都是一个实例去响应。

    • 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断,但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生变化。

    线程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
  2. 请求(消息)头

    请求消息头中包含了很多客户端需要告诉服务器的信息。

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

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

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

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

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

    三种情况:

    • Get方式:没有请求体,但是有一个queryString
    • Post方式:有请求体,form data
    • Json格式:有请求体,request payload
5.6.3、响应报文

包含三个部分:

  1. 响应(状态)行

    包含三个信息:

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

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

  3. 响应体

    响应的实际内容(比如添加add.html页面时,响应的内容就是

    …)

5.7、Session
  1. HTTP是无状态的。

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

  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():强制性让会话立即失效
  2. session保存作用域

  • session保存作用域是和具体的某一个session对应的
  • 常用的API:
    • void session.setAttribute(key,value)
    • Object session.getAttribute(key)
    • void removeAttribute(key)
5.8、服务器内部转发以及客户端重定向
  1. 服务器内部转发:

    request.getRequestDispatcher("...").forward(request,response);

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

    response.sendRedirect("...");

    • 两次请求响应的过程。客户端肯定知道请求URL有变化。
    • 地址栏有变化
5.9、Thymeleaf——试图模板技术 5.9.1、Thymeleaf概述
  1. Thymeleaf优势

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

5.9.2、Thymeleaf的使用
  1. 添加thymeleafjar

  2. web.xml文件中添加配置

    【web.xml】

    
        <context-param>
            <param-name>view-prefixparam-name>
            <param-value>/param-value>
        context-param>
        <context-param>
            <param-name>view-suffixparam-name>
            <param-value>.htmlparam-value>
        context-param>
    
    • 配置前缀:view-prefix
    • 配置后缀:view-suffix
  3. 新建一个servletViewBaseServlet

    【ViewBaseServlet.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`的标签
5.10、保存作用域
  1. 原始情况下,保存作用域有四个:

    • page:页面级别,现在几乎不用
    • request:请求级别,一次请求响应范围
    • session:会话级别,一次会话范围
    • application:应用级别,整个应用程序范围
  2. request:一次请求响应范围

    • 重定向

    • 内部转发

代码演示:

【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、路径问题

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/736223.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存