WEB由浅入深-02篇

140 阅读6分钟

学习.jpg

前篇

今天是持续学习WEB的第二天,希望每天进步一点点!

英语雅思考试进击中...以后每天分享自己的随记! 耶!

中篇

关于前端向后端传输中文数据的事儿。

remark:tomcat8之前的版本底层采用的编码集为ISO-8859-1

前端传输中文数据.png

Servlet的继承关系

继承结构.png

相关方法

javax.servlet.Servlet接口.png

Servlet的生命周期

说到生命周期,先解释一下什么是生命周期?

从出生到死亡的过程就是生命周期,在servlet中对应到了三个方法:init()、service()、destory()

在默认情况下,Tomcat容器接收到Socket请求时,容器会将对应的servlet进行实例化(容器通过反射调用构造构造方法)、

初始化(调用init方法)、服务(调用service方法)、

销毁(随着容器的关闭调用destory方法)

当对这个servlet进行第二次请求时,只会调用service方法,因为在第一次请求的时候容器已经完成了这个servlet的实例化和初始化的操作。

当容器关闭时,其中的所有servlet实例会被销毁,调用对应servlet的销毁方法。

以上,就是一个servlet的生命周期(创建和销毁的整个过程)。

Tomcat容器与Servlet的关系?

在默认情况下,第一次请求时,对应的Servlet才会被Tomcat容器去实例化、初始化、然后再服务;这样的好处是什么?可以提高系统启动的速度。 这样做的缺点是什么?第一次请求时,耗时较长。

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

Servlet的初始化时机

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

我们可以通过<load-on-startup>来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0

<servlet>
    <servlet-name>Demo01Servlet</servlet-name>
    <servlet-class>com.crystal.test.Demo01Servlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

Servlet的特点

Servlet在容器中是:单例的、线程不安全的

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

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

结论

我们已经知道了servlet是线程不安全的!所以: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么:①不要去修改成员变量的值 ②不要去根据成员变量的值做一些逻辑判断

Http协议

Http : Hyper Text Transfer Protocol 超文本传输协议

Http最大的作用就是规定了请求和响应数据的格式

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

Http.png

请求方式.png

会话

Http是无状态的,这里的无状态是指:服务器无法判断两次请求是同一个客户端发过来的,还是不同的客户端发过来的;如果不能解决这个问题,就会导致混乱,比如:A客户要结账,刷新了之后,再次请求之后,服务器就分不清到底是谁了。

类似于生活场景:比如一个盲人要与很多人进行对话交流,此时他要分清楚每一个第一次与他进行对话的人,记住他们各自的特征,方便第二次进行交谈的时候,能把话题无缝衔接,而不是重新要问"我们接下来要聊些什么呢?"而此时的会话就相当于一个连接客户端与服务器交互的专属通道,也就是盲人要与特定的人进行特定对话的一个标记或特征。

通过会话跟踪技术可以来解决无状态的问题。

会话跟踪技术

客户端第一次发请求给服务器,服务器会获取请求中携带的session(在客户端指的是cookie),获取不到,就会创新新的,然后响应给客户端。 下次客户端给服务器发请求时,会携带第一次服务器分配的sessionID,服务器就能获取到了。 常用的API:

request.getSession(); 获取当前的会话,没有则创建一个新的会话 request.getSession(true); 与不带参数的一样 request.getSession(false);获取当前的会话,没有则返回null,不会创建新的

session.getId();获取sessionID

session.isNew();判断当前session是否是第一次创建

session.getMaxInactiveInterval();session的非激活间隔时长,默认为1800秒

session.invalidate();强制性让会话立即失效

session保存作用域

session保存作用域:服务器端会有专门的内存区域,去保存某一个session中的数据,这个session就是以上所说的"专属通道"。

常用的API:

void session.setAttribute(k,v);

Object session.getAttribute(k);

void removeAttribute(k);

每一个请求都有自己唯一的sessionID,且对应唯一的session保存作用域,第二次请求会带有自己的sessionID,对应到自己的session保存作用域,获取对应的数据。

资源跳转技术:服务端内部转发和客户端重定向

服务端内部转发:一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的,URL没有发生变化request.getRequestDispatcher("...").forward(request,response);

客户端重定向:两次请求响应的过程。客户端肯定知道,请求URL有变化 response.sendRedirect("...");

浏览器会显示302状态码,表示该请求会发生重定向

Thymeleaf——视图模板技术

Thymeleaf的使用.png

总结

1.servlet的生命周期:

初始化(tomcat通过反射进行实例化)、init、service(一旦前端访问servlet,容器首先会调用servlet类中的service方法,然后去判断请求方式,如果方式不匹配,就会调用Servlet类中对应的do方法,浏览器报出405错误)、destory(随着容器的关闭所有servlet会销毁:容器会去调用各自的destory方法)

2.http协议: 8种不同的请求方式和协议具体内容(关注http详篇)

3.会话和保存作用域

4.Thymeleaf的简单使用

小试牛刀

demo: 使用最"原始"的技术将单表的数据查询出来显示在html表格中

  1. 数据库:mysql8.0
  2. 数据库连接技术:JDBC
  3. 视图渲染:Thymeleaf
package com.test;

import com.test.dao.FruitDAO;
import com.test.dao.impl.FruitDAOImpl;
import com.test.myssm.ViewBaseServlet;
import com.test.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;

/**
 * @author crystal
 * @create 2022-12-13 10:17
 */
@WebServlet("/fruit.do")
public class IndexServlet extends ViewBaseServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        FruitDAO fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getAll();
        HttpSession session = req.getSession();
        session.setAttribute("fruitList",fruitList);
        super.processTemplate("index",req,resp);
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>水果库存管理</title>
    <link rel="stylesheet" href="css/index.css">
</head>
<body>
        <h1>水果库存管理系统</h1>
        <table>
            <input type="hidden" name="operate" value="fruit"/>
            <tr>
                <th>名称</th>
                <th>单价</th>
                <th>库存</th>
                <th>备注</th>
            </tr>
            <tr th:if="${#lists.isEmpty(session.fruitList)}">
                <td colspan="4">对不起,库存为空!</td>
            </tr>
            <tr th:unless="${#lists.isEmpty(session.fruitList)}"  th:each="fruit : ${session.fruitList}">
                <td th:text="${fruit.fname}">apple</td>
                <td th:text="${fruit.price}">12</td>
                <td th:text="${fruit.fcount}">3</td>
                <td><a>删除</a></td>
            </tr>
        </table>
</body>
</html>

辅助代码

package com.test.myssm;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author crystal
 * @create 2022-12-03 23:25
 */
public abstract class BaseDAO<T> {

//    读取配置文件,获取连接
    public Connection getConn(){
//        获取类加载器
        try {
//            InputStream inputStream = BaseDAO.class.getResourceAsStream("jdbc.properties");
            InputStream inputStream = BaseDAO.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties pros = new Properties();
            pros.load(inputStream);
            String driverClass = pros.getProperty("driverClass");
//        注册驱动
            Class.forName(driverClass);
            String url = pros.getProperty("url");
            String user = pros.getProperty("user");
            String password = pros.getProperty("password");
            Connection connection = DriverManager.getConnection(url, user, password);
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void closeResource(Connection conn,PreparedStatement ps,ResultSet rs){
        try {
            if (rs != null)
                rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (ps != null)
                ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null)
                conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

//    执行update操作
    public void update( String sql,Object...args){
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1,args[i]);
            }
            ps.execute();
            closeResource(conn,ps,null);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    Class<?> clazz = null;
    public BaseDAO(){
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        Type[] arguments = parameterizedType.getActualTypeArguments();
        clazz = (Class<T>) arguments[0];
    }

//    查询,返回单条记录
    public  <T> T queryOne(String sql,Object ...args){
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[i]);
            }
            ResultSet rs = ps.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            if (rs.next()){
                T t = (T) clazz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    Object columnValue = rs.getObject(i + 1);
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                closeResource(conn,ps,rs);
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

//    查询,返回多条记录
    public <T> List<T> queryList(String sql,Object ...args){
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1,args[0]);
            }
            ResultSet rs = ps.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            List<T> list = new ArrayList<>();
            while (rs.next()){
                T t = (T) clazz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    Object columnValue = rs.getObject(i + 1);
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,columnValue);
                }
                list.add(t);
            }
            closeResource(conn,ps,rs);
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.test.myssm;

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());
    }
}
<?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">
    <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>
</web-app>