05_JavaEE

1.工程目录介绍

在这里插入图片描述

三层架构

在这里插入图片描述

2.Servlet

手动实现

实现Servlet接口,如果servlet报红,为模块添加web框架

  1. 编写一个类去实现 Servlet 接口
  2. 实现 service 方法,处理请求,并响应数据
  3. 到 web.xml 中去配置 servlet 程序的访问地址
public class Test_Servlet 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() {

    }
}
<servlet>
    <!--起别名,一般是类名-->
    <servlet-name>Test_Servlet</servlet-name>
    <!--是servlet程序全类名-->
    <servlet-class>com.example._2_Servlet.Test_Servlet</servlet-class>
</servlet>
<servlet-mapping>
    <!--告诉服务器,当前配置地址给哪个Servlet用-->
    <servlet-name>Test_Servlet</servlet-name>
    <!--配置访问地址-->
    <url-pattern>/test</url-pattern>
</servlet-mapping>

获取请求

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("被访问了");
    HttpServletRequest hr = (HttpServletRequest) servletRequest;
    String method = hr.getMethod();
    if("GET".equals(method)){
        doGet();
    }
    else if("POST".equals(method)){
        doPost();
    }
}
<form action="http://localhost:8080/_2_Servlet_war_exploded/test" method="get"><input type="submit"></form>
<hr>
<form action="http://localhost:8080/_2_Servlet_war_exploded/test" method="post"><input type="submit"></form>

通过HttpServlet实现

  1. 编写一个类去继承 HttpServlet 类
  2. 根据业务需要重写 doGet 或 doPost 方法
  3. 到 web.xml 中的配置 Servlet 程序的访问地址
public class HelloServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        System.out.println("HelloServlet2 的 doGet 方法");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        System.out.println("HelloServlet2 的 doPost 方法");
    }
}

ServletConfig

三大作用

  1. 可以获取 Servlet 程序的别名 servlet-name 的值

  2. 获取初始化参数 init-param

  3. 获取 ServletContext 对象

    在这里插入图片描述

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
    <!--init-param 是初始化参数-->
    <init-param>
        <!--是参数名-->
        <param-name>username</param-name>
        <!--是参数值-->
        <param-value>root</param-value>
    </init-param>
    <!--init-param 是初始化参数-->
    <init-param>
        <!--是参数名-->
        <param-name>url</param-name>
        <!--是参数值-->
        <param-value>jdbc:mysql://localhost:3306/test</param-value>
    </init-param>
</servlet>
@Override
public void init(ServletConfig servletConfig) throws ServletException {
    System.out.println("2 init 初始化方法");
    // 1、可以获取 Servlet 程序的别名 servlet-name 的值
    System.out.println("HelloServlet 程序的别名是:" + servletConfig.getServletName());
    // 2、获取初始化参数 init-param
    System.out.println("初始化参数 username 的值是;" + servletConfig.getInitParameter("username"));
    System.out.println("初始化参数 url 的值是;" + servletConfig.getInitParameter("url"));
    // 3、获取 ServletContext 对象
    System.out.println(servletConfig.getServletContext());
}

ServletContext

上下文参数

<!--context-param 是上下文参数(它属于整个 web 工程)-->
<context-param>
    <param-name>username</param-name>
    <param-value>context</param-value>
</context-param>
<!--context-param 是上下文参数(它属于整个 web 工程)-->
<context-param>
    <param-name>password</param-name>
    <param-value>root</param-value>
</context-param>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
    // 1、获取 web.xml 中配置的上下文参数 context-param
    ServletContext context = getServletConfig().getServletContext();
    String username = context.getInitParameter("username");
    System.out.println("context-param 参数 username 的值是:" + username);
    System.out.println("context-param 参数 password 的值是:" +
                       context.getInitParameter("password"));
    // 2、获取当前的工程路径,格式: /工程路径
    System.out.println( "当前工程路径:" + context.getContextPath() );
    // 3、获取工程部署后在服务器硬盘上的绝对路径
    // 斜杠被服务器解析地址为:http://ip:port/工程名/ 映射到 IDEA 代码的web 目录<br/>*/
    System.out.println("工程部署的路径是:" + context.getRealPath("/"));
    System.out.println("工程下 css 目录的绝对路径是:" + context.getRealPath("/css"));System.out.println("工程下 imgs 目录 1.jpg 的绝对路径是:" + context.getRealPath("/imgs/1.jpg"));}

可以像Map一样存储数据

public class ContextServlet1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
        // 获取 ServletContext 对象
        ServletContext context = getServletContext();
        System.out.println(context);
        System.out.println("保存之前: Context1 获取 key1 的值是:"+ context.getAttribute("key1"));
        context.setAttribute("key1", "value1");
        System.out.println("Context1 中获取域数据 key1 的值是:"+ context.getAttribute("key1"));}
}

HttpServletRequest

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到Request 对象中。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。

在这里插入图片描述

乱码解决
//get
// 获取请求参数
String username = req.getParameter("username");
//1 先以 iso8859-1 进行编码
//2 再以 utf-8 进行解码
username = new String(username.getBytes("iso-8859-1"), "UTF-8");

//post
// 设置请求体的字符集为 UTF-8,从而解决 post 请求的中文乱码问题
req.setCharacterEncoding("UTF-8");
System.out.println("-------------doPost------------");
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
请求转发
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
// RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");
// 走向 Sevlet2
requestDispatcher.forward(req,resp);
base标签

在这里插入图片描述

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh_CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <!--base 标签设置页面相对路径工作时参照的地址href 属性就是参数的地址值-->
        <base href="http://localhost:8080/07_servlet/a/b/">
    </head>
    <body>
        这是 a 下的 b 下的 c.html 页面<br/>
        <a href="../../index.html">跳回首页</a><br/>
    </body>
</html>

HttpServletResponse

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个Response对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置

  • 字节流 getOutputStream(); 常用于下载(传递二进制数据)
  • 字符流 getWriter(); 常用于回传字符串(常用)
回传数据
// 要求 : 往客户端回传 字符串 数据。
PrintWriter writer = resp.getWriter();
writer.write("response's content!!!");
解决乱码
// 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8");
请求重定向
resp.sendRedirect("http://localhost:8080");

3.JSP

原理

jsp 的本质 ,其实是一个 Servlet 程序。

在这里插入图片描述

我们打开 index_jsp.java 文件查看里面的内容不难发现。jsp 中的 html 页面内容都被翻译到Servlet 中的service方法中直接输出。

jsp 的语法

page指令

在这里插入图片描述

三种脚本
声明脚本
声明脚本格式如下:
<%! java 代码 %>
1. 我们可以定义全局变量。 
2. 定义 static 静态代码块 
3. 定义方法 
4. 定义内部类 
5. 几乎可以写在类的内部写的代码,都可以通过声明脚本来实现
表达式脚本 ※
<%= 表达式 %>
表达式脚本 用于向页面输出内容。
表达式脚本 翻译到 Servlet 程序的 service 方法中 以 out.print() 打印输出
out 是 jsp 的一个内置对象,用于生成 html 的源代码
注意:表达式不要以分号结尾,否则会报错

表达式脚本可以输出任意类型。
比如:
1.输出整型
2.输出浮点型
3.输出字符串
4.输出对象
代码脚本 ※※
<% java 代码 %>
代码脚本里可以书写任意的 java 语句。
代码脚本的内容都会被翻译到 service 方法中。
所以 service 方法中可以写的 java 代码,都可以书写到代码脚本中
九大内置对象

在这里插入图片描述

九大内置对象,都是我们可以在【代码脚本】中或【表达式脚本】中直接使用的对象。

四大域对象

在这里插入图片描述

out 输出和response.getWriter输出的区别
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <%
        // out 输出
        out.write("这是 out 的第一次输出<br/>");
        // out flush 之后。会把输出的内容写入 writer 的缓冲区中
        out.flush();
        // 最后一次的输出,由于没有手动 flush,会在整个页面输出到客户端的时候,自动写入到writer缓冲区
        out.write("这是 out 的第二次输出<br/>");
        // writer 的输出
        response.getWriter().write("这是 writer 的第一次输出<br/>");
        response.getWriter().write("这是 writer 的第二次输出<br/>");
        %>
    </body>
</html>

在这里插入图片描述

由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下。我们在jsp 页面中统一使用out 来进行输出。避免打乱页面输出内容的顺序。

  • out.write() 输出字符串没有问题
  • out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)

深入源码,浅出结论:在 jsp 页面中,可以统一使用 out.print()来进行输出

常用标签
//静态包含--常用
<%@ include file="" %>
1、静态包含不会翻译被包含的 jsp 页面。
2、静态包含其实是把被包含的 jsp 页面的代码拷贝到包含的位置执行输出。

//动态包含--很少用
<jsp:include page=""></jsp:include>
1、动态包含会把包含的 jsp 页面也翻译成为 java 代码
2、动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out,false);
3、动态包含,还可以传递参数
<jsp:include page="/include/footer.jsp">
    <jsp:param name="username" value="bbj"/>
    <jsp:param name="password" value="root"/>
</jsp:include>

//页面转发--常用
<jsp:forward page=""></jsp:forward>
九九乘法表
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Title</title>
        <style type="text/css">
            table{
                width: 650px;
            }
        </style>
    </head>
    <body>
        <%-- 练习一:在 jsp 页面中输出九九乘法口诀表 --%>
        <h1 align="center">九九乘法口诀表</h1>
        <table align="center">
            <%-- 外层循环遍历行 --%>
            <% for (int i = 1; i <= 9; i++) { %>
            <tr>
                <%-- 内层循环遍历单元格 --%>
                <% for (int j = 1; j <= i ; j++) { %>
                <td><%=j + "x" + i + "=" + (i*j)%></td>
                <% } %>
            </tr>
            <% } %>
        </table>
    </body>
</html>

4.监听器

Listener 监听器

Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener监听器。

ServletContextListener 监听器

  • ServletContextListener 它可以监听 ServletContext 对象的创建和销毁。
  • ServletContext 对象在 web 工程启动的时候创建,在 web 工程停止的时候销毁。
  • 监听到创建和销毁之后都会分别调用 ServletContextListener 监听器的方法反馈。

使用步骤如下:

  1. 编写一个类去实现 ServletContextListener
  2. 实现其两个回调方法
  3. 到 web.xml 中去配置监听器
public class MyServletContextListenerImpl implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext 对象被创建了");
    }
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext 对象被销毁了");
    }
}
<!--配置监听器-->
<listener>
    <listener-class>com.atguigu.listener.MyServletContextListenerImpl</listener-class>
</listener>

5.el表达式

EL 表达式的什么作用:EL 表达式主要是代替 jsp 页面中的表达式脚本在 jsp 页面中进行数据的输出。因为 EL 表达式在输出数据的时候,要比 jsp 的表达式脚本要简洁很多。

EL 表达式的格式是:${表达式}

EL 表达式在输出 null 值的时候,输出的是空串。jsp 表达式脚本输出 null 值的时候,输出的是null 字符串。

<body>
    <%
    request.setAttribute("key","值");
    %>
    表达式脚本输出 key 的值是:
    <%=request.getAttribute("key1")==null?"":request.getAttribute("key1")%><br/>
    EL 表达式输出 key 的值是:${key1}
</body>

输出顺序

EL 表达式主要是在 jsp 页面中输出数据。 主要是输出域对象中的数据。

当四个域中都有相同的 key 的数据的时候,EL 表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出。

empty运算

empty 运算可以判断一个数据是否为空,如果为空,则输出 true,不为空输出false。

以下几种情况为空:

  1. 值为 null 值的时候,为空
  2. 值为空串的时候,为空
  3. 值是 Object 类型数组,长度为零的时候
  4. list 集合,元素个数为零
  5. map 集合,元素个数为零
${ empty xxx }

11 个隐含对象

EL 个达式中 11 个隐含对象,是 EL 表达式中自己定义的,可以直接使用。

在这里插入图片描述

在这里插入图片描述

6.JSTL 标签库

EL 表达式主要是为了替换 jsp 中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个jsp页面变得更佳简洁。

在这里插入图片描述

在 jsp 标签库中使用 taglib 指令引入标签库

CORE 标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
XML 标签库
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
FMT 标签库
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
SQL 标签库
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
FUNCTIONS 标签库
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

使用步骤

//1、先导入 jstl 标签库的 jar 包。
taglibs-standard-impl-1.2.1.jar
taglibs-standard-spec-1.2.1.jar

//第二步,使用 taglib 指令引入标签库。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

core 核心库使用

<%-- <c:set />(使用很少)--%>
<c:set scope="session" var="abc" value="abcValue"/>

<%-- <c:if />判断  test 属性表示判断的条件(使用 EL 表达式输出) --%>
<c:if test="${ 12 == 12 }">
    <h1>12 等于 12</h1>
</c:if>

<%-- <c:choose> <c:when> <c:otherwise>多路判断 --%>
<c:choose>
    <c:when test="${ requestScope.height > 190 }">
        <h2>小巨人</h2>
    </c:when>
    <c:when test="${ requestScope.height > 180 }">
        <h2>很高</h2>
    </c:when>
    <c:when test="${ requestScope.height > 170 }">
        <h2>还可以</h2>
    </c:when>
    <c:otherwise>
        <c:choose>
            <c:when test="${requestScope.height > 160}">
                <h3>大于 160</h3>
            </c:when>
            <c:when test="${requestScope.height > 150}">
                <h3>大于 150</h3>
            </c:when>
            <c:when test="${requestScope.height > 140}">
                <h3>大于 140</h3>
            </c:when>
            <c:otherwise>
                其他小于 140
            </c:otherwise>
        </c:choose>
    </c:otherwise>
</c:choose>

<%-- <c:forEach /> --%>
<c:forEach begin="1" end="10" var="i">
    <tr>
        <td>第${i}行</td>
    </tr>
</c:forEach>

//Object
<c:forEach items="${ requestScope.arr }" var="item">
    ${ item } <br>
</c:forEach>

//Map
<c:forEach items="${ requestScope.map }" var="entry">
    <h1>${entry.key} = ${entry.value}</h1>
</c:forEach>

//List
<c:forEach begin="2" end="7" step="2" varStatus="status" items="${requestScope.stus}" var="stu"><tr>
    <td>${stu.id}</td>
    <td>${stu.username}</td>
    <td>${stu.password}</td>
    <td>${stu.age}</td>
    <td>${stu.phone}</td>
    <td>${status.step}</td>
    </tr>
</c:forEach>

7.文件上传

在这里插入图片描述

commons-fileupload.jar

commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入。

commons-fileupload-1.2.1.jar
commons-io-1.4.jar
//常用类
ServletFileUpload 类,用于解析上传的数据。
    FileItem 类,表示每一个表单项。
    
    boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);
	判断当前上传的数据格式是否是多段的格式。
        
    public List<FileItem> parseRequest(HttpServletRequest request)
    解析上传的数据
        
    boolean FileItem.isFormField()
    判断当前这个表单项,是否是普通的表单项。还是上传的文件类型。
    true 表示普通类型的表单项
    false 表示上传的文件类型
        
    String FileItem.getFieldName()
    获取表单项的 name 属性值
        
    String FileItem.getString()
    获取当前表单项的值。
        
    String FileItem.getName();
	获取上传的文件名
    
    void FileItem.write( file );
	将上传的文件写到 参数 file 所指向抽硬盘位置 。

上传步骤

<form action="http://192.168.31.74:8080/09_EL_JSTL/uploadServlet" method="post"
      enctype="multipart/form-data">
    用户名:<input type="text" name="username" /> <br>
    头像:<input type="file" name="photo" > <br>
    <input type="submit" value="上传">
</form>
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    //1 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
    if (ServletFileUpload.isMultipartContent(req)) {
        // 创建 FileItemFactory 工厂实现类
        FileItemFactory fileItemFactory = new DiskFileItemFactory();
        // 创建用于解析上传数据的工具类 ServletFileUpload 类
        ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);try {
            // 解析上传的数据,得到每一个表单项 FileItem
            List<FileItem> list = servletFileUpload.parseRequest(req);
            // 循环判断,每一个表单项,是普通类型,还是上传的文件
            for (FileItem fileItem : list) {
                if (fileItem.isFormField()) {
                    // 普通表单项
                    System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
                    // 参数 UTF-8.解决乱码问题
                    System.out.println("表单项的 value 属性值:" + fileItem.getString("UTF-8"));} 
                else {
                    // 上传的文件
                    System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
                    System.out.println("上传的文件名:" + fileItem.getName());
                    //写入
                    fileItem.write(new File("e:\\" + fileItem.getName()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

下载步骤

//下载常用API
servletContext.getMimeType();
servletContext.getResourceAsStream();
response.getOutputStream();
response.setContentType();
//下载文件名
String downloadfile = "xxx";
//通知客户端
ServletContext sc = getServletContext();
String minetype = sc.getMimeType("/file/" + downloadfile);
res.setContentType(minetype)
resp.setHeader("Content-Disposition", "attachment; fileName=" + downloadfile);
//读取下载内容
InputStream is = sc.getResourceAsStream("/file/" + downloadfile);
//获取响应输出流
OutputStream os = resp.getOutputStream();
//输入流写给输出流
IOUtils.copy(is,os);

解决附件乱码

//ie,chrome
// 把中文名进行 UTF-8 编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg","UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);

//firefox
// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?"+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) +"?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);
//通用
String ua = request.getHeader("User-Agent");
// 判断是否是火狐浏览器
if (ua.contains("Firefox")) {
    String str = "attachment; fileName=" + "=?utf-8?B?"
        + new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) +"?=";
    response.setHeader("Content-Disposition", str);
} else {
    String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg","UTF-8");
    response.setHeader("Content-Disposition", str);
}

8.cookie

cookie使用

//创建cookie
protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    //1 创建 Cookie 对象
    Cookie cookie = new Cookie("key4", "value4");
    //2 通知客户端保存 Cookie
    resp.addCookie(cookie);
    //1 创建 Cookie 对象
    Cookie cookie1 = new Cookie("key5", "value5");
    //2 通知客户端保存 Cookie
    resp.addCookie(cookie1);
    resp.getWriter().write("Cookie 创建成功");
}
//接收cookie Cookie[] 
req.getCookies()
//修改cookie 一种直接创建,同名cookie会覆盖,一种修改值
cookie.setValue("newValue2");
//生存时间
cookie.setMaxAge(60 * 60); // 设置 Cookie 一小时之后被删除。无效
//删除
cookie.setMaxAge(0);
//默认的会话级别的 Cookie
protected void defaultLife(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    Cookie cookie = new Cookie("defalutLife","defaultLife");
    cookie.setMaxAge(-1);//设置存活时间
    resp.addCookie(cookie);
}

有效路径

在这里插入图片描述

//Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。path 属性是通过请求的地址来进行有效的过滤。
protected void testPath(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    Cookie cookie = new Cookie("path1", "path1");
    // getContextPath() ===>>>> 得到工程路径
    cookie.setPath( req.getContextPath() + "/abc" ); // ===>>>> /工程路径/abc
    resp.addCookie(cookie);
    resp.getWriter().write("创建了一个带有 Path 路径的 Cookie");
}

9.session

session使用

//第一次调用是:创建 Session 会话,之后调用都是:获取前面创建好的 Session 会话对象。
request.getSession();

//判断到底是不是刚创建出来的(新的)true 表示刚创建 false 表示获取之前创建
isNew(); 

//每个会话都有一个身份证号。也就是 ID 值。而且这个 ID 是唯一的。  得到 Session 的会话 id 值。
getId()
//创建
protected void setAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    req.getSession().setAttribute("key1", "value1");
    resp.getWriter().write("已经往 Session 中保存了数据");
}

//获取
protected void getAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    Object attribute = req.getSession().getAttribute("key1");
    resp.getWriter().write("从 Session 中获取出 key1 的数据是:" + attribute);
}

生命周期

public void setMaxInactiveInterval(int interval);
//设置 Session 的超时时间(以秒为单位),超过指定的时长,Session就会被销毁。
//值为正数的时候,设定 Session 的超时时长。
//负数表示永不超时(极少使用)
public int getMaxInactiveInterval()//获取 Session 的超时时间
    
public void invalidate() //让当前 Session 会话马上超时无效。
//Session 默认的超时时间长为 30 分钟。可以在xml中设置
<session-config>
    <session-timeout>20</session-timeout>
</session-config>

10.验证码解决重复提交

表单重复提交有三种常见的情况:

一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键F5,就会发起最后一次的请求。造成表单重复提交问题。解决方法:使用重定向来进行跳转

二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,就会着急,然后多点了几次提交操作,也会造成表单重复提交。

三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。

在这里插入图片描述

谷歌 kaptcha 图片验证码的使用

  1. 导入谷歌验证码的 jar 包 kaptcha-2.3.2.jar
  2. 在 web.xml 中去配置用于生成验证码的 Servlet 程序
<servlet>
    <servlet-name>KaptchaServlet</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>KaptchaServlet</servlet-name>
    <url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
<form action="http://localhost:8080/tmp/registServlet" method="get">
    用户名:<input type="text" name="username" > <br>
    验证码:<input type="text" style="width: 80px;" name="code">
    <img src="http://localhost:8080/tmp/kaptcha.jpg" alt="" style="width: 100px; height:28px;"><br><input type="submit" value="登录">
</form>

3.比较使用

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    // 获取 Session 中的验证码,第一次是正确的code,第二次是空
    String token = (String) req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
    // 删除 Session 中的验证码
    req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
    String code = req.getParameter("code");
    // 获取用户名
    String username = req.getParameter("username");
    //不为空且验证码对的
    if (token != null && token.equalsIgnoreCase(code)) {
        System.out.println("保存到数据库:" + username);
        resp.sendRedirect(req.getContextPath() + "/ok.jsp");
    } else {
        System.out.println("请不要重复提交表单");
    }
}

切换验证码

// 给验证码的图片,绑定单击事件
$("#code_img").click(function () {
    // 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的dom对象
    // src 属性表示验证码 img 标签的 图片路径。它可读,可写
    // alert(this.src);
    this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});

11.过滤器filter

基本使用

public class AdminFilter implements Filter {
    //doFilter 方法,专门用于拦截请求。可以做权限检查
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest hr = (HttpServletRequest) servletRequest;
        HttpSession session = hr.getSession();
        Object user = session.getAttribute("user");
        // 如果等于 null,说明还没有登录
        if (user == null) {
            hr.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
            return;
        } else {
            // 让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
}
<filter>
    <!--给 filter 起一个别名-->
    <filter-name>AdminFilter</filter-name>
    <!--配置 filter 的全类名-->
    <filter-class>com.atguigu.filter.AdminFilter</filter-class>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
    <!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
    <filter-name>AdminFilter</filter-name>
    <!--url-pattern 配置拦截路径 -->
    <url-pattern>/admin/*</url-pattern>
</filter-mapping>

FilterConfig

Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。

  1. 获取 Filter 的名称 filter-name 的内容
  2. 获取在 Filter 中配置的 init-param 初始化参数
  3. 获取 ServletContext 对象
@Override
public void init(FilterConfig filterConfig) throws ServletException {
    // 1、获取 Filter 的名称 filter-name 的内容
    System.out.println("filter-name 的值是:" + filterConfig.getFilterName());
    // 2、获取在 web.xml 中配置的 init-param 初始化参数
    System.out.println("初始化参数 username 的值是:" + filterConfig.getInitParameter("username"));
    System.out.println("初始化参数 url 的值是:" + filterConfig.getInitParameter("url"));
    // 3、获取 ServletContext 对象
    System.out.println(filterConfig.getServletContext());
}
<filter>
    <filter-name>AdminFilter</filter-name>
    <filter-class>com.atguigu.filter.AdminFilter</filter-class>
    <init-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
    </init-param>
    <init-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost3306/test</param-value>
    </init-param>
</filter>

FilterChain 过滤器链

在这里插入图片描述

Filter 的拦截路径

//精确匹配
<url-pattern>/target.jsp</url-pattern>

//目录匹配
<url-pattern>/admin/*</url-pattern>

//后缀名匹配
<url-pattern>*.html</url-pattern>
以上配置的路径,表示请求地址必须以.html 结尾才会拦截到
<url-pattern>*.do</url-pattern>
以上配置的路径,表示请求地址必须以.do 结尾才会拦截到
<url-pattern>*.action</url-pattern>
以上配置的路径,表示请求地址必须以.action 结尾才会拦截到

Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!

Filter 过滤器统一给所有的 Service 方法都加上try-catch

通过filter调用,后置代码进行提交,如果出现异常进行回滚

在这里插入图片描述

public class TransactionFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,FilterChainfilterChain) throws IOException, ServletException {
        try {
            filterChain.doFilter(servletRequest,servletResponse);
            JdbcUtils.commitAndClose();// 提交事务
        } catch (Exception e) {
            JdbcUtils.rollbackAndClose();//回滚事务
            e.printStackTrace();
        }
    }
}
<filter>
    <filter-name>TransactionFilter</filter-name>
    <filter-class>com.atguigu.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>TransactionFilter</filter-name>
    <!-- /* 表示当前工程下所有请求 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

一定要记得把 BaseServlet(方法调用类) 中的异常往外抛给 Filter 过滤器

public abstract class BaseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        doPost(req, resp);
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        // 解决 post 请求中文乱码问题
        // 一定要在获取请求参数之前调用才有效
        req.setCharacterEncoding("UTF-8");
        String action = req.getParameter("action");
        try {
            // 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
            Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class,HttpServletResponse.class);
            // System.out.println(method);
            // 调用目标业务 方法
            method.invoke(this, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);// 把异常抛给 Filter 过滤器
        }
    }
}

12.ThreadLocal

  • ThreadLocal 的作用,它可以解决多线程的数据安全问题。
  • ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)
  1. ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)
  2. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal 对象实例。
  3. 每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型
  4. ThreadLocal 中保存数据,在线程销毁后。会由 JVM 虚拟自动释放。

数据关联

使用Hashtable,保证线程安全

public class Test_ThreadLocal {
    public static Map<String,Object> data  = new Hashtable<String ,Object>();
    public static Random random = new Random();
    public static void main(String[] args) {

        for(int i = 0; i<3 ;i++){
            new Thread(new Task()).start();
        }
    }
}

class Task implements Runnable{
    @Override
    public void run() {
        Integer i = Test_ThreadLocal.random.nextInt(1000);
        String name = Thread.currentThread().getName();
        System.out.println(name +" " + i);
        Test_ThreadLocal.data.put(name,i);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        Object o = Test_ThreadLocal.data.get(name);
        System.out.println("结束"+name + " " + o);
    }
}

使用ThreadLocal

class OrderService {
    public void createOrder(){
        String name = Thread.currentThread().getName();
        System.out.println("OrderService 当前线程[" + name + "]中保存的数据是:" +
                           Test_ThreadLocal2.threadLocal.get());
        new OrderDao().saveOrder();
    }
}
class OrderDao {
    public void saveOrder(){
        String name = Thread.currentThread().getName();
        System.out.println("OrderDao 当前线程[" + name + "]中保存的数据是:" +
                           Test_ThreadLocal2.threadLocal.get());
    }
}
public class Test_ThreadLocal2 {
    // public static Map<String,Object> data = new Hashtable<String,Object>();
    public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
    private static Random random = new Random();
    public static class Task implements Runnable {
        @Override
        public void run() {
            // 在 Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为key 保存到map中
            Integer i = random.nextInt(1000);
            // 获取当前线程名
            String name = Thread.currentThread().getName();
            System.out.println("线程["+name+"]生成的随机数是:" + i);
            // data.put(name,i);
            threadLocal.set(i);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new OrderService().createOrder();
            // 在 Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作// Object o = data.get(name);
            Object o = threadLocal.get();
            System.out.println("在线程["+name+"]快结束时取出关联的数据是:" + o);
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++){
            new Thread(new Task()).start();
        }
    }
}

ThreadLocal 来确保所有 dao 操作都在同一个Connection连接对象中完成,每次dao操作不新new一个connection对象

13.友好的错误页面

在 web.xml 中我们可以通过错误页面配置来进行管理。

<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
    <!--error-code 是错误类型-->
    <error-code>500</error-code>
    <!--location 标签表示。要跳转去的页面路径-->
    <location>/pages/error/error500.jsp</location>
</error-page>
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
    <!--error-code 是错误类型-->
    <error-code>404</error-code>
    <!--location 标签表示。要跳转去的页面路径-->
    <location>/pages/error/error404.jsp</location>
</error-page>

14.json

  • JSON.stringify( json ); 此方法可以把一个 json 对象转换成为json 字符串
  • JSON.parse( jsonString ); 此方法可以把一个 json 字符串转换成为json 对象

javaBean与json相互转换

// 要把复杂的 json 字符串转换成为 java 对象。需要继承 TypeToken 类。// 并把返回的类型当成 TypeToken 的泛型注入
static class PersonType extends TypeToken<List<Person>> {
}
public static void main(String[] args) {
    // json 操作,一定要先 new 一个 gson 对象。
    Gson gson = new Gson();
    // java 对象--json
    Person person = new Person(12, "wzg168");
    // 把对象转成为 json 字符串
    String personjson = gson.toJson(person);
    System.out.println(personjson);
    // 把 json 字符串转换成为 java 对象
    Person p = gson.fromJson(personjson, Person.class);
    System.out.println(p);
    System.out.println("------------------------------------------");
    // 2、java 对象 list 集合和 json 的转换
    List<Person> list = new ArrayList<Person>();
    for (int i = 0; i < 3; i++) {
        list.add(new Person(10 * i, "name-" + i));
    }
    String jsonListString = gson.toJson(list);
    System.out.println(jsonListString);
    // 把 json 数组转换成为 List 对象
    // List<Person> ps = gson.fromJson(jsonListString, new PersonType().getType());
    // 我们也可以使用匿名内部类
    List<Person> ps = gson.fromJson(jsonListString, new TypeToken<List<Person>>(){}.getType());
    System.out.println(ps);
    System.out.println("------------------------------------------");
    // 3、map 对象和 json 的转换
    Map<String, Person> mapPerson = new HashMap<String, GsonTest.Person>();// 添加 person 到 map 中
    mapPerson.put("p1", new Person(1, "person-1"));
    mapPerson.put("p2", new Person(2, "person-2"));
    // 把 map 转换成为 json 对象
    String jsonMapString = gson.toJson(mapPerson);
    System.out.println(jsonMapString);
    // 通过使用匿名内部类的方式
    Map<String, Person> map = gson.fromJson(jsonMapString,
                                            new TypeToken<HashMap<String, Person>>() {}.getType());
    System.out.println(map);
}
}


其他问题

Idea中Tomcat服务器乱码

  • tomcat中logging.properties,所有encoding改为UTF-8
  • IDEA ->帮助->编辑自定义VM选项->添加 -Dfile.encoding=UTF-8
  • IDEA->编辑tomcat配置->虚拟机选项->添加 -Dfile.encoding=UTF-8
  • 重启idea

jsp方法使用不了

要手动导入tomcat中lib的jsp-api

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值