JavaWeb知识点总结

Lou.Chen
大约 44 分钟

一、Servlet作用

  • 接收请求
  • 处理请求
  • 完成响应

二、Servlet创建和使用流程

1、创建类继承HttpServlet

public class OneServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
}

2、在web.xml配置Servlet位置和地址映射

<?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">
     <!--tomcat初始化打开的页面-->
    <welcome-file-list>
        <welcome-file>/index.html</welcome-file>
        <welcome-file>/index.jsp</welcome-file>
    </welcome-file-list>
    <!--将servlet接口实现类类路径地址交给tomcat    -->
    <servlet>
        <!-- 为我们自定义的Servlet起一个名称-->
        <servlet-name>OneServlet</servlet-name>
        <!--        该servlet具体的包下的地址-->
        <servlet-class>org.lc.controller.OneServlet</servlet-class>
        <!--  load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
        当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载。
        正数的值越小,该Servlet的优先级就越高,应用启动时就优先加载。
        当值相同的时候,容器就会自己选择优先加载。-->
        <!--tomcat启动时就会创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--    为自定义servlet配置地址映射-->
    <servlet-mapping>
        <!--        指定servlet-->
        <servlet-name>OneServlet</servlet-name>
        <!--        指定请求地址-->
        <url-pattern>/one</url-pattern>
    </servlet-mapping>
</web-app>

三、Servlet生命周期

1、初始化init()。Servlet对象只会初始化一次

  • 若Servlet指定load-on-startup的配置,则在tomcat服务器启动时 该Servlet即被初始化

  • 若Servlet没有指定load-on-startup配置,则在第一次请求时初始化Servlet

    • <servlet>
          <!-- 为我们自定义的Servlet起一个名称-->
          <servlet-name>OneServlet</servlet-name>
          <!--        该servlet具体的包下的地址-->
          <servlet-class>org.lc.controller.OneServlet</servlet-class>
          <!--  load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。
          当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载。
          正数的值越小,该Servlet的优先级就越高,应用启动时就优先加载。
          当值相同的时候,容器就会自己选择优先加载。-->
          <!--tomcat启动时就会创建 -->
          <load-on-startup>1</load-on-startup>
      </servlet>
      

2、调用service()方法,处理post或者get及其他请求。每次请求都会调用该service方法

3、调用destory()方法,当服务区关闭时就会调用此方法。该方法只会在服务器结束时调用一次

注意:

  • Servlet为单实例多线程,所有不要在Servlet实例中写共享变量。

四、HttpServletResponse和HttpServletRequest

HttpServletResponse封装服务端返回给浏览器的数据

HttpServletRequest封装客户端请求的数据

1、HttpServletResponse

1)向客户端返回中文数据

浏览器根据不同的响应体中的ContentType类型类解析不同的二进制数据

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //根据响应体对象返回数据给浏览器 注意:必须先指定字符编码 然后获取PrintWriter流对象。
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter printWriter = response.getWriter();
        printWriter.write("hello world<br/>");
        printWriter.write("你好世界!");
        printWriter.flush();
        printWriter.close();
    }
2)重定向其它地址

浏览器会

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //重定向到我们项目下的指定页面
        resp.sendRedirect("success.html");
        //重定向到我们指定的地址
        //resp.sendRedirect("http://www.baidu.com");
    }
3)若禁用cookie,可采用编码重定向

详细参考cookie禁用

使用response中的encodeRedirectURL()方法优化其请求路径。自动判断,如果禁用cookie则帮我们携带上session否则不会携带

  • <a href="<%=response.encodeRedirectURL("sessiontest.jsp")%>">去其他页面</a>
4)获取响应流对象

一般用作文件下载。详细参考文件下载

2、HttpServletRequest

1)获取请求参数
<form action="/one" method="get">
    用户名<input type="text" name="username"><br/>
    <input type="submit" value="提交">
</form>
<h1>POST请求</h1>
<form action="/one" method="post">
    用户名<input type="text" name="username"><br/>
    爱好: 篮球<input type="checkbox" name="interest" value="lanqiu">
    足球<input type="checkbox" name="interest" value="zuqiu">
    排球<input type="checkbox" name="interest" value="paiqiu">
    性别:
    男:<input type="radio" name="gender" value="male">
    女:<input type="radio" name="gender" value="female">
    <input type="submit" value="提交">
</form>
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        //1、获取单个值 文本框,单选框
        String username = req.getParameter("username");
        String gender = req.getParameter("gender");
        System.out.println(username);
        System.out.println(gender);
        //2、获取某个参数的多个值,例如多选框
        String[] interests = req.getParameterValues("interest");
        System.out.println(Arrays.toString(interests));
        //3、获取请求头信息
        System.out.println(req.getHeader("Host"));
        System.out.println(req.getHeader("Content-Type"));
        System.out.println(req.getHeader("Accept"));
    }
}
2)get和post请求处理乱码方式
  • GET请求

①浏览器地址栏默认使用ISO-8859-1进行编码,所以我们需要用对应的编码格式解码

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决get请求乱码
        System.out.println(new String(req.getParameter("username").getBytes("ISO-8859-1"),"utf-8"));
    }

②修改tomcat的解码规则

找到tomcat安装路径下的conf/server.xml文件:

在下列标签加入:URIEncoding="utf-8" 即可

 <Connector connectionTimeout="30000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="utf-8" />
  • PSOT请求
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决请求乱码  重新设置请求的编码格式,浏览器和我们服务器的编码和解码的格式不一致,所以我们需要在获取请求参数之前,同一设置编码格式
        req.setCharacterEncoding("utf-8");
        
        //第一种方式:设置文本类型并设置编码格式
        req.setContentType("text/html;charset=utf-8");
        //第二种方式:设置响应头
        //req.addHeader("Content-Type","text/html;charset=utf-8");
        //必须在获得PrintWriter流之前设置请求编码   
        PrintWriter printWriter = req.getWriter();
    }

在我们设置响应的编码格式时:只需要设置内容类型编码格式就行。

3)请求转发
@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //转发到指定路径或请求
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("success.html");
        //若转发到page/success.html页面,则:
        //req.getRequestDispatcher("page/success.html");
        //并转发请求和响应参数
        requestDispatcher.forward(req, resp);
    }
3)获得项目路径
//  /servlet_test
String contextPath1 = req.getContextPath();
4)获取请求地址

request.getRequestURI(); 获取请求的URI 不包含项目路径

request.getRequestURL(); 获取请求的URL 包含项目路径(全路径)

5)获取请求流对象
  • 若请求的enctytpe类型为 enctype="application/x-www-form-urlencoded"
    • 则流ServletInputStream流中存储的为 请求参数和值。
  • 若请求的enctytpe类型为 enctype="multipart/form-data"
    • 则流ServletInputStream中存储为 一段段数据部分。
ServletInputStream inputStream1 = request.getInputStream(); 

3、转发和重定向的区别

  • 转发(HttpServletRequest对象)

    • 地址栏的路径不会更改
    • 请求只会发生一次,即getRequestDispatcher("success.html")中的路径不会请求,只转发相应的请求和响应对象即可。
    • 服务器内部完成
    • 可以共享请求域数据(HttpServletRequest和HttpServletResponse)
    • 必须是当前服务器的资源
    • 可以访问WEB-IN下的资源
  • 重定向(HttpServletResponse对象)

    • 地址栏会变成getRequestDispatcher("success.html")中指定的转发路径
    • 会发生两次请求
    • 浏览器完成
    • 不可以共享请求域数据(HttpServletRequest和HttpServletResponse)
    • 可以是外部的资源
    • 不可以访问WEB-IN下的资源

注意:我们只能一个请求对应一个响应,而且先处理请求操作再处理响应操作,不能有多个响应或者请求转发操作。

五、项目路径问题

我们一般新建javaweb选择,选择打包的方式:

上面的Application context:/servlet_test 默认为项目名称。可以把其设置为 / 即可

我们的Servlet在进行地址解析的时候web.xml中的地址解析为:

    <servlet-mapping>
        <servlet-name>TwoServlet</servlet-name>
        <!--其中的 / 代表的是当前项目的路径 即http://localhost:8081/servlet_test/ 这也是为什么我们无需在页面访问时加 / 的原因了-->
        <url-pattern>/two</url-pattern>
    </servlet-mapping>

1、项目路径和服务器路径的区别:

项目路径:http://localhost:8081/servlet_test/open in new window 携带项目名称

服务器路径:http://localhost:8081/open in new window 以我们tomcat服务器的路径,不携带项目名称

2、页面路径访问问题(在页面请求中访问servlet也如此)

  • 若设置了路径项目名称

  • 若没有设置项目路径名称,则请求加不加 / 都无所谓

3、Servlet中的路径问题

  • 重定向路径

    • 因为重定向是请求路径交给浏览器去访问,所以在Servlet中使用重定向时加 / 也代表当前服务器路径即 http://localhost:8081/open in new window, 需要加项目名 。用法和在页面访问路径一致

    • response.sendRedirect("/servlet_test/success.html");
      
  • 转发路径

    • 因为转发是在服务器内部完成的,所有在Servlet中使用重定向时 / 代表当前项目路径即 http://localhost:8081/servlet_test/ ,所有我们使用 / 开头作为路径时无需加项目路径

      • req.getRequestDispatcher("/success.html").forward(req, resp);
        
      • //或者
        req.getRequestDispatcher("success.html").forward(req, resp);
        
  • 但是我们频繁的写项目路径不够灵活

    • 使用ServletContext 中的 getContextPath 获取项目名称

      •     @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                ServletContext context=getServletContext();
                //  /servlet_test
                String contextPath = context.getContextPath();
                resp.sendRedirect(contextPath+"/success.html");
            }
        
    • 使用HttpServletRequest中的getContextPath方法获取项目名称

      • 		//  /servlet_test
        String contextPath1 = req.getContextPath();
        

六、JSP介绍

JSP其实就是一个Servlet, 服务器将jsp中的内容,从上到下依次逐行通过write("页面元素和标签")流一行一行写入到浏览器的。

1、jsp脚本片段,编写java代码

在jsp中的声明的java代码都会加载到 Servlet 中的service中执行

  • 打印到控制台:
<%
   //在控制台打印 hello
    System.out.println("hello");
%>
  • 打印到页面:使用out对象
<%
   //在页面打印 hello
    out.println("hello");
%>

2、jsp表达式,在页面输出内容

在jsp中声明的jsp表达式会被加载到service中的write()流方法中当做参数输出

<!--会在头部导入指定包-->
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<!--会在页面输出日期-->
<%=new Date()%>

3、声明成员(少用)

在jsp中以<%! %> 中声明的代码相当于声明在 Servlet的service方法外面。当做成员参数或成员方法。

<%!private String name="张三";%>

<!--在页面输出 '张三'-->
<%=name%>

4、jsp注释

<%-- --%>

<%-- 我是注释 --%>

5、jsp指令

<%@ 指令名 属性名=属性值 %>

1)page指令

定义页面是如何解析

<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2)include指令

静态包含。要包含的文件不需要翻译和编译的。静态包含的内容一般不动

<%@include file="register.html"%>
3)taglib指令

在页面引入标签库

<%@ taglib 属性名=属性值 %>

4)jsp指令

<jsp:指令名 属性名=属性值>

<!--动态包含 需要翻译和编译,常用于经常变动的页面。--> 
<jsp:include page="register.jsp"></jsp:include>
<!--请求转发到指定页面-->
<jsp:forward page="register.jsp"></jsp:forward>
<!--请求并指定参数-->
<jsp:forward page="register.jsp">
    <jsp:param name="username" value="张三"/>
</jsp:forward>

6、jsp九大隐含对象

可以在jsp中直接使用

1)隐含对象
  • Throwable exception=null 代表捕获异常对象

  • ServletConfig config=null 代表Servlet配置信息

    • 获得当前jsp对应servlet的详细信息
    • 获取servlet名称 config.getServletName();
  • JspWriter out=null 代表在页面输出数据的out对象

    • out.print("你好");
  • Object page=this; 代表当前jsp

    • 主要使用this, page仅代表Object
  • **HttpServletResponse **response 代表当前的响应对象

    • 向页面输出内容 response.getWriter().write("hello");
2)四大域对象

域对象用来在其他资源共享数据。都可以通过setAttribute(K,V)的形式设置数据。通过getAttribute(K)的形式拿到数据

  • PageContext pageContext=null 代表当前页面对象

    • 获取所有的其他的隐含对象

      • ServletRequest request1 = pageContext.getRequest();
        Object page1 = pageContext.getPage();
        
    • 作为域对象共享数据。只能在当前页面共享数据,离开页面无法共享

      • //设置 键-值 参数
        pageContext.setAttribute("username", "张三");
        //通过 键 获取值
        System.out.println(pageContext.getAttribute("username"));
        
  • HttpSession session=null 代表会话对象

  • 同一次会话共享数据, 浏览单打开--开始会话。 浏览器关闭--结束会话。使用不同web应用(Chrom,IE,Egde),就是不同的会话

  • HttpServletRequest request代表当前封装的请求对象

  • 在同一个请求对象中共享数据,只要是同一次请求,就可以共享数据。转发也可以共享数据

  • ServletContent application =null 代表整个web应用

    • application 代表当前web应用。只要在同一个web应用都可以共享数据。web应用只要不卸载都可以访问
域对象作用范围起始时间结束时间
pageContext当前jsp页面页面加载离开页面
request同一个请求收到请求响应
session同一个会话开始会话结婚会话
application当前web应用web应用加载web应用卸载

7、EL表达式

EL表达式都是通过属性的get方法获取的。而不是属性。所以命名要以按照javabean的规范编写

EL表达式可以存在html标签中的属性中。或者任意hmtl标签内

1)获取页面显示域中的属性值${属性名}

2)要获取某个属性的值,直接${属性名.属性}

3)若el表达式中有多个属性名相同则会从四个域从小到大寻找,找到即停止

4)当有多个属性名相同时,则我们使用:

只能取出通过setAttribute()设置的值

  • pageContext域中的数据使用pageScope(封装了pageContext中的所有的属性并封装为map)获取属性 ${pageScope.属性}

  • request域中的数据使用requestScope(封装了requestScope中的所有的属性并封装为map)获取属性 ${requestScope.属性}

  • session域中的数据使用sessionScope(封装了sessionScope中的所有的属性并封装为map)获取属性 ${sessionScope.属性}

  • application域中的数据使用applicationScope(封装了applicationScope中的所有的属性并封装为map)获取属性 ${applicationScope.属性}

5)el表达式可以取出指定的11隐含对象中的值,自己在<% %>定义的不能够取出。

<%
    String s = "jj";
%>
<!--不能够取出-->    
el字符串s的值为: ${s}  <br/>

<!--能够取出-->    
字符串s的值为: <%=s%> <br/>

6)当我们的属性名取出的名称出现歧义时我们可以使用['属性']的方式取出

可以通过${pageScope['属性名']['属性']} 或者${pageScope['属性名']}

7)使用el表达式中的的pageContext获取其他隐含对象,若要获取隐含对象中的其他属性,使用 . 即可

${pageContext.request.getAttribute("user")}

8)el获取其他隐含对象的值

  • param 保存的请求参数是 key -- value (value只有一个)
    • $
  • paramsValues 保存的请求参数是 key --value (value有多个)
    • $
  • cookie 保存cookie信息
    • ${cookie.键.value} 获取指定cookie键的value

9)el表达式运算

empty 判断是否为空,为空则为true,否则会false

${empty "你好"}

也可以在el表达式中写其他表达式的元素,三门运算符等

10)获取请求的上下文路径

${pageContext.request.contextPath}

<%@ page import="org.lc.entity.User" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    User user = new User(1, "admin", "123456");
    String s = "jj";
%>
el字符串s的值为: ${s}  <br/>
字符串s的值为: <%=s%> <br/>
   pageContext:<%pageContext.setAttribute("pageContextAttr", "pageContextValue");%><br/>
   user:<%pageContext.setAttribute("user", user);%><br/>
   request:<%request.setAttribute("user", "requestValue");%><br/>
   session:<%session.setAttribute("user", "sessionValue");%><br/>
   application:<%application.setAttribute("user", "applicationValue");%><br/>
   <hr/>
<%--   以前获取共享数据的方式--%>
<%--   pageContext:<%=pageContext.getAttribute("pageContextAttr")%><br/>--%>
<%--   现在使用el表达式--%>
<%--通过${属性值}--%>
   pageContext:${pageContextAttr}<br/>
<%--直接获取属性对应的对象值--%>
   user:${user}<br/>
<%--获取对象的属性--%>
   user:${user.password}<br/>
   user:${pageScope.user.username}<br/>
   request:${requestScope.user}<br/>
   session:${sessionScope.user}<br/>
   application:${applicationScope.user}<br/>
${pageContext.request.getAttribute("user")}
${pageContext.request.contextPath}
</body>
</html>

七、Cookie

HTTP是无状态协议

  • 无状态:服务器无法分辨每次请求来自谁

所以使用cookie是让浏览器去保存一份数据,以后每次访问的时候带上相应的数据。Cookie是服务器发给浏览器保存的

1、创建cookie

cookie由我们的服务器发送给浏览器创建并发送给浏览器设置保存,每次请求时都会携带此cookie。

在servlet中创建cookie

    protected void create(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //可创建多个cookie
        //创建cookie  键 - 值
        Cookie cookie=new Cookie("username","louchen");

        //把cookie发送给浏览器
        response.addCookie(cookie);

        response.getWriter().write("cookie发给你了...");
    }

我们请求创建cookie后,发现在响应头中有Set-Cookie信息,则为Cookie信息

我们发现保存在浏览器的Cookie中,发现其过期时间Expire/Max-Age 为session,即cookie为一次会话的时间。

只要不关闭当前浏览器都是当前会话。若打开其他浏览器或关闭此浏览器则cookie失效。

当我们每次进行其他任何请求时,都会携带此cookie。如下请求头:

2、获取cookie

servlet获取cookie

    protected void get(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //cookie可能有多个
        Cookie[] cookies = request.getCookies();
        PrintWriter writer = response.getWriter();
        for (Cookie cookie : cookies) {
            writer.write("cookie键:"+cookie.getName()+"-值:"+cookie.getValue()+"<br/>");
        }
        writer.close();
    }
}

3、删除cookie

  • cookie默认的生命周期为session,即当前会话(浏览器一直不关则一直有效)

  • 修改cookie的默认存活时间

    • cooke.setMaxAge(0) 参数为秒
      • 为负数时,浏览器根本不会存储此cookie。 注意:不一定有效,根据不同浏览器的规则来实现
      • 为0时,删除此cookie
      • > 0 时, 例如:setMaxAge(3600) ,则过期时间为3600秒后

servlet实现删除

    protected void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie[] cookies = request.getCookies();
        PrintWriter writer = response.getWriter();
        Cookie cookieR=null;
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals("username")) {
                //设置过期时间为0,则删除
                cookie.setMaxAge(0);
                cookieR=cookie;
            }
        }
        //响应此修改后的cookie给浏览器
        response.addCookie(cookieR);
        writer.write("已删除cookie...");
        writer.close();
    }

我们发现响应头中的Set-Cookie的Expires已经设置为我们最大生成时间0秒

并且发现浏览器中存储的Cookie也已经删除。

4、设置cookie有效时间

servlet实现

    protected void livetime(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Cookie[] cookies = request.getCookies();
        PrintWriter writer = response.getWriter();
        Cookie cookieR=null;
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals("username")) {
                //设置请求的最大生命时间为3600s
                cookie.setMaxAge(3600);
                cookieR=cookie;
            }
        }
        //响应此修改后的cookie给浏览器
        response.addCookie(cookieR);
        writer.write("已持久化cookie 3600秒...");
        writer.close();
    }

我们发现在请求头中的Set-Cookie过期时间Expiress已经向后延期了3600秒

5、设置cookie请求路径

默认我们的每一次请求都会携带cookie。但是我们的请求往往不是每一次都需要携带cookie,所以我们可以设置cookie的请求路径

servlet设置cookie请求路径

    protected void cookiePath(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置cookie
        Cookie cookie=new Cookie("cookieppp","lc");
        //设置cookie访问的路径 即只有携带该路径 此cookie才会被存储
        // /代表服务器根路径 http://localhost:8080/
        //所以我们每次访问必须以  http://localhost:8080/hello/** 开始
        cookie.setPath("/hello");
        response.addCookie(cookie);
        response.getWriter().write("cookie路径已被修改");
    }

我们设置后浏览器发送给服务器一个cookie,但是我们此时并没有访问cookie路径,所以此时cooke并不会存储。

当我们请求以服务器的路径开始请求时 即 http://localhost:8080/helloopen in new window 我们的浏览器才会存储此cookie,并且携带此cookie进行请求。

但我们的服务器请求其他非cookie路径时,请求不会携带cookie,浏览器也不会存储其cookie

5、修改cookie

当我们要修改cookie的value值或者cookie的生存周期,我们可以通过遍历的方式通过cookie名称拿到此cookie,这样做可以,但是很麻烦。我们可以直接通过新建一个cookie来覆盖掉之前的cookie即可。

  • 默认cookie的value值不能为中文,若为中文,则需要将中文编码。读取时将值进行解码
    • 编码 String value = URLEncoder.encode("张三", "utf-8"); %E5%BC%A0%E4%B8%89
    • 解码 String decode = URLDecoder.decode("%E5%BC%A0%E4%B8%89", "utf-8"); 张三
    protected void updateCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //默认cookie的value值不能为中文,若为中文,则需要将中文编码。读取时将值进行解码
        String value = URLEncoder.encode("张三", "utf-8");
        Cookie cookie = new Cookie("username", value);
        cookie.setMaxAge(300);
        response.addCookie(cookie);
        response.getWriter().write("cookie值已被修改");
    }

八、Session

服务器保存的技术,域对象(在整个会话期间保存的数据在任意资源都可以取到)

作用:域对象共享数据。

session就是当前会话对象,它可以共享当前会话的数据。服务器会为每个会话(重新开启浏览器,或者打开不同的浏览器)创建独立的session。只会为每个会话创建一个session,第一次获取的session是新创建的,以后获取的都是之前创建的。

  • 当我们的请求第一次使用session时,若session没有被创建,那么服务器会创建一个session,并生成一个sessionId发送个浏览器,浏览器在cookie中保存此sessionid。每次请求时并携带此sessionId.

    • 当我们的服务器没有session时,第一次请求服务器会为我们的浏览器再响应头中传入一个JSESSIONID保存在浏览器的cookie中.发现session的默认生命周期即为一次会话

1、创建session对象并获取session id

在servlet中实现

    protected void get(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //从请求中获取session
        HttpSession session = request.getSession();
        //判断该session是否为新创建的
        boolean sessionNew = session.isNew();
        //获取session id
        String id = session.getId();
        response.getWriter().write("已经获取到session对象...是否为新创建的:"+sessionNew);
        response.getWriter().write("<br/>session的id为:"+id);
    }

2、设置sessiond的属性并获取session的属性值

servlet中设置

    protected void set(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取session
        HttpSession session = req.getSession();
        //给session设置属性
        session.setAttribute("user", "louchen");
        resp.getWriter().write("已经向session设置属性user...");
    }

servlet 获取

    protected void getValue(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        //获取sessions属性值
        String user = (String) session.getAttribute("user");
        response.getWriter().write("session中属性user的值为:"+user);
    }

3、获取session的有效时间

servlet中获取

    protected void getTime(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        //以秒为单位  默认有效是时间为30分钟
        int maxInactiveInterval = session.getMaxInactiveInterval();
        response.getWriter().write("session的有效时间为:"+maxInactiveInterval);
    }

为啥是30分钟,我们的浏览器再次关闭时,再打开时为啥还是失效?。

因为我们的sessionid是保存在cookie中,当我们关闭浏览器时,cookie即失效。再一次请求时cookie为空,所以session失效

  • 我们可以在tomcat的配置中\apache-tomcat-7.0.40\conf\web.xml查看session的有效时间 .单位为分钟

        <session-config>
            <session-timeout>30</session-timeout>
        </session-config>
    

4、设置session有效时间

1)在tomcat中的web.xml中配置。 或者在项目下的web.xml中配置 单位为分钟

    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

2)延期失效 单位为秒

servlet设置

    protected void updateTime(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        int i=3;
        //单位秒  距离最后一次使用session开始计时 
        //为负数时:永不过期
        //为正数时:过期时间单位为秒
        session.setMaxInactiveInterval(i);
        response.getWriter().write("已设置session的有效期,时间为:"+i);
    }

3)立即失效

servlet设置

    protected void invalid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        //session立即失效。不会延期
        session.invalidate();
        response.getWriter().write("session已失效...");
    }

5、设置session持久化

将sessionid存在cookie中并设置过期时间。即使关闭浏览器也不会失效。

    protected void persistence(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        String id = session.getId();
        //把session手动存在cookie中
        Cookie cookie = new Cookie("JSESSIONID", id);
        //设置过期时间 24小时
        cookie.setMaxAge(60 * 60 * 24);
        response.addCookie(cookie);
        response.getWriter().write("session已持久化");
    }

九、禁用cookie重写url

​ 我们每次请求浏览器时,当服务器没有创建session,服务器会创建session对象并向我们的浏览器发送一份sessionid存储在cookie中,若我们禁用浏览器的cookie,那么浏览器并不会存储此sessionid,导致我们在请求服务器时服务器都会创建一份新的sessionid。,即从服务器无法获取session中设置的属性值

解决方法: 在请求指定session对象中的值时在地址栏中传入sessionid即可

bancookie.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    //设置session属性
    session.setAttribute("sessionAttr", "sessionValue");
%>
<a href="sessiontest.jsp">去其他页面</a>
</body>
</html>

sessiontest.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<!--获取session属性值-->
session中的值:${sessionAttr}
</body>
</html>

请求地址:·http://localhost:8080/sessiontest.jsp;jsessionid=9C592B93BFF112050536DDD98BFD6D7B

jsessionid=9C592B93BFF112050536DDD98BFD6D7B 该sessionid为在页面bancookie.jsp 页面上获取的sessionid

  • 使用response中的encodeRedirectURL()方法优化其请求路径。自动判断,如果禁用cookie则帮我们携带上session否则不会携带
    • <a href="<%=response.encodeRedirectURL("sessiontest.jsp")%>">去其他页面</a>

十、session的钝化和活化

现象:服务器关闭重新启动,只要浏览器没关,还是能获取到session的内容

钝化:服务器关闭后,会将session(序列化)保存到硬盘中,可以在项目中查看生成SESSION.ser的文件

活化:服务器再次启动时,把之前序列化好的文件加载进来。就会再次加载之前保存的SESSION.ser文件包含了session域中的内容

session域中的对象要能同session一起钝化到磁盘中必须实现serializable接口

十一、Filter 过滤器

Filter是用来执行过滤任务的一个接口对象,作用于:

  • 请求一个资源(动态资源Servlet,JSP,静态资源)

  • 来自一个资源的响应

  • 两个都可以。既可以过滤请求,也可以过滤响应

Filter过滤请求和响应:

  • filter可以拦截请求(request),可以修改请求头,请求内容等
  • filter可以拦截来自服务端的响应(response),可以修改响应头和响应内容
  • 放行请求

1、过滤器执行流程

实现Filter接口:

只有请求地址为 http://localhost:8080/filtertest.jsp的 请求才能被该过滤器所过滤

请求参数携带 key 的才能被放行执行

//实现Filter接口
public class RoomFilter implements Filter {

//    销毁的方法
    @Override
    public void destroy() {
    }

//    执行过滤的具体方法
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("进入过滤器...");
        String key = req.getParameter("key");
        if (key == null || key.equals("")) {
            //说明没拿到key 不放行  拦截所有页面内容。只会输出响应的值
            resp.getWriter().write("stop in....");
        }else{
            System.out.println("放行之前...");
            //放行该请求
            chain.doFilter(req, resp);
            //继续执行后面代码
            System.out.println("放行之后...");
            //我是经过过滤器的响应  响应对象做修改操作
            resp.getWriter().write("我是给页面的响应...");
        }
    }
//    初始化方法
    @Override
    public void init(FilterConfig config) throws ServletException {

    }
}

配置Filer请求路径

和servlet配置类似

<url-pattern>/*</url-pattern> 过滤所有请求

    <!-- 配置filter路径-->
    <filter>
        <!-- 配置filer名称-->
        <filter-name>RoomFilter</filter-name>
        <!-- filer具体类路径-->
        <filter-class>org.lc.controller.RoomFilter</filter-class>
    </filter>
    <!-- 配置该filter过滤的路径-->
    <filter-mapping>
        <!-- 指定哪个filter-->
        <filter-name>RoomFilter</filter-name>
        <!-- 过滤的路径  只要是以 '项目路径/filtertest.jsp' 的请求即被过滤  -->
        <url-pattern>/filtertest.jsp</url-pattern>
    </filter-mapping>

2、过滤器生命周期

//实现Filter接口
public class RoomFilter implements Filter {

//    销毁的方法
    @Override
    public void destroy() {
    }
//    执行过滤的具体方法
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

//    初始化方法
    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}

1)初始化

  • 服务器启动的时候即初始化。创建filter对象调用 init()方法 。该过滤器为单例多线程,即只创建一个单例对象

2)每次拦截请求都执行 doFilter()方法

3)服务器停止执行destroy()方法销毁过滤器

3、过滤器拦截路径规则

1)精确匹配
  • 写要拦截的资源的详细资源路径

    • 只拦截某一个具体的指定资源。 / 代表 项目路径(若配置项目名):http://localhost:8080/项目名/

    • <url-pattern>/filtertest.jsp</url-pattern> 只拦截 /filtertest.jsp 的请求路径

    • <url-pattern>/page/filtertest.jsp</url-pattern> 只拦截 项目page文件夹下的 /page/filtertest.jsp

2)路径匹配
  • 要拦截的请求路径,或者资源路径
    • <url-pattern>/page/*</url-pattern> 拦截page开头的所有请求,或者拦截page下的所有资源
    • <url-pattern>/*</url-pattern> 拦截所有请求和所有资源
3)后缀匹
  • *.后缀名

    • <url-pattern>*.jsp</url-pattern> 拦截所有请求以 .jsp 结尾的资源
  • 若要实现拦截 page文件下的所有以 *.jsp结尾的资源

    •     <filter-mapping>
              <filter-name>PatternFilter</filter-name>
              <!--先拦截page下的所有请求-->
              <url-pattern>/page/*</url-pattern>
          </filter-mapping>
      

      通过请求拿到对应的请求路径,然后判断请求是否以指定规则结尾的,是则拦截,否则放行

      HttpServletRequest request= (HttpServletRequest) req;ServletRequest请求强制转为HttpServletRequest 请求

      request.getRequestURI(); 获取请求的URI 不包含项目路径

      request.getRequestURL(); 获取请求的URL 包含项目路径

    •     @Override
          public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
              System.out.println("执行doFilter...");
              HttpServletRequest request= (HttpServletRequest) req;
              //  /page/index.jsp
              String requestURI = request.getRequestURI();
              //  http://localhost:8080/page/success.jsp
              StringBuffer requestURL = request.getRequestURL();
              System.out.println(requestURI);
              System.out.println(requestURL);
              if (requestURL.toString().endsWith(".jsp")) {
                  //拦截 以 .jsp结尾的
              }else{
                  //放行其他的
                  chain.doFilter(req, resp);
              }
          }
      
  • 错误写法:

    • <url-pattern>/page/*.jsp</url-pattern> ×××
4)一次过滤多个拦截规则
    <filter-mapping>
        <filter-name>LoginFilter</filter-name>
        <!--拦截/shop2开头的所有请求-->
        <url-pattern>/shop2/*</url-pattern>
         <!--拦截/page开头的所有请求-->
        <url-pattern>/page/*</url-pattern>
    </filter-mapping>
5)拦截指定servlet的所有请求
    <filter-mapping>
        <filter-name>PatternFilter</filter-name>
        <!--servlet的名称-->
        <servlet-name>UserServlet</servlet-name>
    </filter-mapping>

4、filter响应中文乱码问题

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        	//我们在执行放行方法之前若写入响应数据,则会造成乱码。因为resp中的响应头字符编码的头为空,所以需要手动设置字符编码
        	 resp.setContentType("text/html;charest=utf-8");
            resp.getWriter().write("执行过滤器doFilter之前");
            chain.doFilter(req, resp);
        	//而执行doFilter方法后,响应头的中的字符编码jsp已经为我们设置,所以后续执行不乱码
            resp.getWriter().write("我是给页面的响应...");
        
    }

上述response封装完后,从上到下依次渲染数据,在doFilter之前的渲染数据若没有配置字符编码则会乱码,在doFilter之后的数据不会乱码,因为在doFilter中已经对字符完成编码

5、各种ServletConfig,ServletContext,FilterConfig初始化参数

1)ServletConfig

在该servlet创建的时候调用init()方法

  • 一个servlet对应一个 ServletConfig 存放该servlet的配置信息

web.xml配置

    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>org.lc.controller.UserServlet</servlet-class>
        <!--配置初始化参数-->
        <init-param>
             <!--参数名-->
            <param-name>user</param-name>
             <!--参数值-->
            <param-value>lc</param-value>
        </init-param>
    </servlet>
    @Override
    public void init(ServletConfig config) throws ServletException {
        //获取初始化参数 
        String user = config.getInitParameter("user");
        //lc
        System.out.println(user);
    }
2)ServletContextFilterConfig

服务器启动时即初始filter调用init()方法

ServletContext 对应整个web应用

通过FilterConfig 或者ServletConfig 获得 ServletContext

  • 一个filter对应一个FilterConfig ,存放该filer的配置信息
<!-- application初始化参数-->
    <context-param>
        <param-name>user</param-name>
        <param-value>applicationValue</param-value>
    </context-param>

    <filter>
        <!-- 配置filer名称-->
        <filter-name>RoomFilter</filter-name>
        <!-- filer具体类路径-->
        <filter-class>org.lc.controller.RoomFilter</filter-class>
        <init-param>
            <!--filter初始化参数-->
            <param-name>user</param-name>
            <param-value>filterValue</param-value>
        </init-param>
    </filter>
//    初始化方法
    @Override
    public void init(FilterConfig config) throws ServletException {
        //获取当前application参数  (代表整个web服务器)
        ServletContext servletContext = config.getServletContext();
        //获取application初始化参数
        String user = servletContext.getInitParameter("user");
        //applicationValue
        System.out.println(user);

        //获取过滤器初始化参数
        String user1 = config.getInitParameter("user");
        //filterValue
        System.out.println(user1);
        String filterName = config.getFilterName();
        //该filter的别名
        System.out.println(filterName);
    }

6、过滤器链

若多个过滤器过滤相同的请求,则会从web.xml中从上到下依次加载过滤器,优先执行先加载的过滤器

过滤器AFilter和BFilter依次过滤 a.jsp的页面请求

        <filter-name>AFilter</filter-name>
        <filter-class>org.lc.controller.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/a.jsp</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>BFilter</filter-name>
        <filter-class>org.lc.controller.BFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/a.jsp</url-pattern>
    </filter-mapping>

AFilter

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("顺序 1");
        //执行doFilter后,若还有相同请求的过滤器未执行则继续执行其他过滤器
        chain.doFilter(req, resp);
        System.out.println("顺序 2");
    }

BFilter

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("顺序 3");
        chain.doFilter(req, resp);
        System.out.println("顺序 4");
    }

a.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    System.out.println("顺序 a.jsp");
%>
  <h1>我是a.jsp页面</h1>
</body>
</html>

依次打印:

顺序 1
顺序 3
顺序 a.jsp
顺序 4
顺序 2

由上可以过滤器相当于在doFilter中执行递归调用

7、dispatcher

告诉服务器都拦截哪些方式到达的资源

FORWARD : 拦截转发过来的,转发到这个地址,会被拦截

INCLUDE:拦截包含的,包含这个地址的会被拦截,拦截动态包含

REQUEST(默认的):直接请求的,直接请求这个地址会被拦截,post,get请求

ERROR:发送错误,去全局配置一个错误页面会被拦截。不是errorPage指向的页面。指的是全年局的配置

    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/a.jsp</url-pattern>
        <!--FORWARD,INCLUDE, REQUEST, ERROR-->
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

十二、JDBC使用步骤

1、加载驱动

Class.forName("com.mysql.jdbc.Driver");

2、获得连接

Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456")

3、创建运行对象

4、运行sql

5、处理运行结果

6、释放资源

十三、AJAX

1、简介

AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。

2、js发送ajax请求

  • onreadystatechange 事件 获取响应过程状态信息

当请求被发送到服务器时,我们需要执行一些基于响应的任务。

每当 readyState 改变时,就会触发 onreadystatechange 事件。

readyState 属性存有 XMLHttpRequest 的状态信息。

下面是 XMLHttpRequest 对象的三个重要的属性:

属性描述
onreadystatechange存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
readyState存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
status200: "OK"
404: 未找到页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--通过ajax请求--%>
<script>
    function btnAjax() {
        //1、创建xhr对象,用这个对象来向服务器发送请求
        let xhr = new XMLHttpRequest();

        //2、定义请求发送格式
        //请求类型; 请求地址; 是否异步(默认true)
        //请求地址可以携带参数
        xhr.open("GET", "ajax?username=zs&pw=123456", true);

        //3、发送请求
        //若请求有请求体,则将请求体的数据发送(仅用于 POST 请求)
        // xhr.send(body);
        xhr.send();

        //4、获取响应的数据
        //xhr中的属性
        //responseText获得字符串形式的响应数据。
        //responseXML 获得XML形式的响应数据。
        //当请求的处理状态为4 并且 status状态为200时 代表响应处理完成。
        xhr.onreadystatechange = function () {
            if (!(xhr.readyState == 4 && xhr.status == 200)) {
                let v = document.getElementById("text");
                v.innerText = xhr.responseText;
            }
        }
    }
</script>
<button onclick="btnAjax()">发送请求</button>
<div id="text"></div>
</body>
</html>

3、解决ajax请求缓存问题 (验证码)

生成一个随机值携带参数一同发送给服务器

 $.get('ajax?username=lc&pw=123'+Math.random(),function (data) { },"text");

4、jquery发送$.post()/$.get()请求

  • $.get(URL,data,function(data,status,xhr),dataType)

    • 发送get请求,在url中携带的参数和在data中传入的参数 都会被放到url地址栏中传输
  • $.post(URL,data,function(data,status,xhr),dataType)

    • 发送post请求,在utl中携带的参数还是在url地址上,在data中的参数会被放在请求体中。
参数描述
URL必需。规定您需要请求的 URL。
data可选。规定连同请求发送到服务器的数据。带key-value的参数
function(data,status,xhr)可选。规定当请求成功时运行的函数。
额外的参数:
data - 包含来自请求的结果数据
status(可选) - 包含请求的状态("success"、"notmodified"、"error"、"timeout"、"parsererror")
xhr(可选) - 包含 XMLHttpRequest 对象
dataType可选。规定预期的服务器响应的数据类型。
"xml" - 一个 XML 文档
html" - HTML 作为纯文本
"text" - 纯文本字符串
"script" - 以 JavaScript 运行响应,并以纯文本返回
"json" - 以 JSON 运行响应,并以 JavaScript 对象返回
"jsonp" - 使用 JSONP 加载一个 JSON 块,将添加一个 "?callback=?" 到 URL 来规定回调
1)发送get请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
    function jqueryAjax() {
        $.get('ajax?user=zs&pw=123&r='+Math.random(),{"id":1111,username:"lc",password:123456},function (data) {
            let parse = JSON.parse(data);
            $("#text").text(parse.username+parse.id);
        },'text');
    }
</script>
<button onclick="jqueryAjax()">jquery发送get请求</button>
<div id="text"></div>
</body>
</html>

servlet为例

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("收到请求了");
        request.setCharacterEncoding("utf-8");
        String user1 = request.getParameter("user");
        String id = request.getParameter("id");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("url中的参数:"+user1);
        System.out.println("data中的参数:"+id);
        System.out.println("data中的参数:"+username);
        System.out.println("data中的参数:"+password);
        response.setContentType("text/html;charset=utf-8");
        User user = new User(1001, "张三", "123456");
        Gson gson=new Gson();
        //将javabean转为字符串 方便在请求中传输
        String s = gson.toJson(user);
        response.getWriter().write(s);
    }

我们可以发现get请求在url中的参数和在data中的参数都被放在在url地址栏中,后台也能获取到全部数据

2)发送post请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
    function jqueryPost() {
        $.post('ajax?user=zs&pw=123&r='+Math.random(),{"id":1111,username:"lc",password:123456},function (data) {
            let parse = JSON.parse(data);
            $("#text").text(parse.username+parse.id);
        },'text');
    }
</script>
<button onclick="jqueryPost()">jquery发送post请求</button>
<div id="text"></div>
</body>
</html>

后端就是 上面的get请求的后端

我们可以发现发送post请求,地址栏中的参数还是在地址栏中,但是data中的数据会方法在请求体中,不会方在url中,这意味着我们的post请求可以发送大量数据。

5、$.ajax()更灵活的其他方式

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
    function jqueryOther() {
        $.ajax({
            //1、请求地址
            url:'ajax?user=zs&pw=123&r='+Math.random(),

            //2、请求类型
            //请求类型(默认为get)  post,get,delete,put
            //注意get没有请求体只能在url中传输参数,其他类型有请求体,参数可以在请求体中
            type:'get',

            //3、请求参数。
            //注意请求参数必须为js对象形式,不能为字符串
            //例如:错误写法"{"id":"19321","username":"louc","password":"123456"}" 这样服务端会获取不到值
            //允许的形式 data:'id=19321&username=louc&password=123456' 可以为参数形式
            data:{"id":"19321","username":"louc","password":"123456"},
            // data:'id=19321&username=louc&password=123456',

            //4、是否异步 默认为异步
            async:true,

            //5、成功的回调
            success: function (data) {
                //一般data类型为字符串
                let p = JSON.parse(data);
                $("#text").text(p.username+""+p.id)
            },
            //6、失败的回调 404 403等
            error(data) {
                //打印错误信息
                alert("错误"+data)
            }
        })
    }
</script>
<button onclick="jqueryOther()">jquery发送其他格式请求</button>
<div id="text"></div>
</body>
</html>

6、表单序列化

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<script src="/webjars/jquery/3.3.1-2/jquery.min.js"></script>
<script>
    function subForm() {
        //传统获取表单属性值
        // let id=$("input[name='id']").val();
        // let username=$("input[name='username']").val();
        // let password=$("input[name='password']").val();
        // alert(id+username+password)
        
        //序列化表单后,得到的字符串为 'id=19321&username=louc&password=123456' 形式
        let formdata = $("#form1").serialize();
        alert(formdata)+
        $.ajax({
            url:"ajax?user=zs&pw=123&r='+Math.random()",
            data:formdata,
            async:true,
            type:'post',
            success: function (data) {
                //一般data类型为字符串
                let p = JSON.parse(data);
                $("#text").text(p.username+p.id)
            },
            error(data) {
                alert(data)
            }
        })
    }
</script>
<form action="#" id="form1">
    id <input type="text" name="id"><br>
   用户名 <input type="text" name="username"><br>
    密码<input type="text" name="password"><br>
    <button onclick="subForm()">提交</button>
</form>
<div id="text"></div>
</body>
</html>

十四、JSON

JSON就是表示对象的方法,格式就是按照js声明对象的格式来. json的编写必须要符合js规范。

  • 在js中:属性加和不加双引号都可以。值为必须为js符合的类型
  • 在java中:json字符串必须符合json规范,所有属性必须加双引号。

转换为:JSON格式要求

  • key(属性) 必须有双引号
  • value : 基本类型或者引用类型(对象)
    //js对象写法 (也可以直接转为字符串,自动为属性加上引号 )
    // let json={name:"张三", age: 22};
    //json写法 属性必须加字符串
    let json={"name":"张三", "age": 22};

    //将json对象转为字符串
    let str = JSON.stringify(json);
    //{"name":"张三", "age": 22}
    alert(str)

    //将字符串转为json对象/js对象
    let jsonstr = JSON.parse(str);
	//获取js对象属性值
    //张三
    alert(jsonstr.name)

1、Gson的使用

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>
1)javabean和字符串的转换
        Gson gson=new Gson();
        String str="{id:1002,username:'zs','password':123456}";
        //字符串转javabean
        User user = gson.fromJson(str, User.class);
		//User(id=1002, username=zs, password=123456)
        System.out.println(user);

        //javabean转字符串
        String s = gson.toJson(user);
		//{"id":1002,"username":"zs","password":"123456"}
        System.out.println(s);
2)List集合和字符串的转换
        Gson gson=new Gson();
        List<User> list=new ArrayList<>();
        list.add(new User(1001,"zs","123456"));
        list.add(new User(1001,"ls","1234"));
        list.add(new User(1001,"ww","12356"));

        //集合转字符串
        String s = gson.toJson(list);
        //[{"id":1001,"username":"zs","password":"123456"},{"id":1001,"username":"ls","password":"1234"},{"id":1001,"username":"ww","password":"12356"}]
        System.out.println(s);

        //字符串转集合
        List<User> list1 = gson.fromJson(s, new TypeToken<List<User>>() {
        }.getType());
        //[User(id=1001, username=zs, password=123456), User(id=1001, username=ls, password=1234), User(id=1001, username=ww, password=12356)]
        System.out.println(list1);
3)Map集合和字符串的转换
        Gson gson=new Gson();
        HashMap<String,User> hashMap=new HashMap<>();
        User user1 = new User(1001, "zs", "123456");
        User user2 = new User(1001, "zs", "123456");
        hashMap.put("level1", user1);
        hashMap.put("level2", user2);

        //map集合转为字符串
        String s = gson.toJson(hashMap);
        //{"level1":{"id":1001,"username":"zs","password":"123456"},"level2":{"id":1001,"username":"zs","password":"123456"}}
        System.out.println(s);

        //字符串转为集合
        HashMap<String,User> o = gson.fromJson(s, new TypeToken<HashMap<String, User>>() {
        }.getType());
        //{level1=User(id=1001, username=zs, password=123456), level2=User(id=1001, username=zs, password=123456)}
        System.out.println(o);

十五、监听器

​ 监听器,字面上的理解就是监听观察某个事件(程序)的发生情况,当被监听的事件真的发生了的时候,事件发生者(事件源) 就会给注册该事件的监听者(监听器)发送消息,告诉监听者某些信息,同时监听者也可以获得一份事件对象,根据这个对象可以获得相关属性和执行相关操作。

1、声明周期监听器。监听三个对象的生命周期(创建到消耗)

1)监听请求 ServletRequestListener

来一个请求就会执行一次初始化和销毁方法

public class RequestLifeListener implements ServletRequestListener {
    /**
     * 请求来的时候 调用requestInitialized初始化方法
     * @param sre
     */
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("请求创建...");
        //获取application对象 代表整个web应用
        ServletContext servletContext = sre.getServletContext();
        //获取当前请求对象
        ServletRequest servletRequest = sre.getServletRequest();

    }
    /**
     * 请求结束,完成响应 调用requestDestroyed销毁方法
     * @param sre
     */
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("请求销毁");
    }
}

web.xml配置该监听器:

    <listener>
        <listener-class>org.lc.listener.RequestLifeListener</listener-class>
    </listener>
2)监听整个web应用 ServletContextListener

项目启动时调用一次初始化方法 。项目停止时调用销毁方法

public class ApplicationLifeListener implements ServletContextListener {
    /**
     * 项目启动时调用此初始化方法
     * @param servletContextEvent
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ApplicationLifeListener 初始化");
        //获取当前web对象
        ServletContext servletContext = servletContextEvent.getServletContext();
    }

    /**
     * 项目停止时调用此销毁方法
     * @param servletContextEvent
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ApplicationLifeListener销毁方法...");
    }
}
    <listener>
        <listener-class>org.lc.listener.ApplicationLifeListener</listener-class>
    </listener>
3)监听会话 HttpSessionListener

第一次使用session才会创建,session 失效(强制实现,超时)则调用相应的销毁方法

public class SessionLifeListener implements HttpSessionListener {
    /**
     *第一次使用session的时候 初始化sessionCreated
     * @param httpSessionEvent
     */
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("SessionLifeListener创建...");
        //获取当前session对象
        HttpSession session = httpSessionEvent.getSession();
    }

    /**
     * session失效的时候调用此 sessionDestroyed方法
     * @param httpSessionEvent
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("SessionLifeListener销毁...");
    }
}

2、属性监听器。监听三个对象中的属性

1)监听请求属性 ServletRequestAttributeListener
  • 当请求中的属性增加时调用attributeAdded方法

  • 当请求中的属性修改时调用attributeReplaced方法

  • 当请求中的属性移除时调用attributeRemoved方法


public class RequestAttrListener implements ServletRequestAttributeListener {

    /**
     * 属性增加
     * @param servletRequestAttributeEvent
     */
    @Override
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("添加属性...");
        //获取增加的属性key
        String name = servletRequestAttributeEvent.getName();
        //获取增加的属性值
        Object value = servletRequestAttributeEvent.getValue();
        System.out.println("增加的属性 key:"+name+" value:"+value);
    }

    /**
     * 属性移除
     * @param servletRequestAttributeEvent
     */
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("删除属性...");
        //获取删除的属性key
        String name = servletRequestAttributeEvent.getName();
        //获取删除的属性值
        Object value = servletRequestAttributeEvent.getValue();
        System.out.println("删除的属性 key:"+name+" value:"+value);
    }

    /**
     * 属性修改
     * @param servletRequestAttributeEvent
     */
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("修改属性...");
        //获取修改之前的属性key
        String name = servletRequestAttributeEvent.getName();
        //获取修改之前的属性值
        Object value = servletRequestAttributeEvent.getValue();

        //获取请求对象
        ServletRequest servletRequest = servletRequestAttributeEvent.getServletRequest();
        //获取修改之后的属性值
        Object parameter =servletRequest.getAttribute(name);
        System.out.println("修改之前的属性 key:"+name+" value:"+value);
        System.out.println("修改之后的属性 key:"+name+" value:"+parameter);
    }
}

    <listener>
        <listener-class>org.lc.listener.RequestAttrListener</listener-class>
    </listener>
2)监听session属性 HttpSessionAttributeListener

和监听请求的用法一致。

public class SessionAttrListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}
3)监听web应用属性 ServletContextAttributeListener

和监听请求的用法一致。

public class ServletContextAttrListener implements ServletContextAttributeListener {

    @Override
    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
        
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {

    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {

    }
}

3、sessions固有监听器

1) 监听sessions活化钝化 HttpSessionActivationListener
public class SessionActivationListener implements HttpSessionActivationListener {


    @Override
    public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {

    }

    @Override
    public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {

    }
}

2)监听一个对象是否绑定到session中(保存在session中) HttpSessionBindingListener
public class SessionBindingListener implements HttpSessionBindingListener {
    
    @Override
    public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
        
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}

十六、文件上传与下载

文件上传的必要条件:

  • 指定input的类型typefile

    • <input type="file" name="img">
  • 指定表单编码类型enctypemultipart/form-data 多部分上传 默认为编码类型为application/x-www-form-urlencoded

  • 表单提交请求类型必须为POST

<form action="/upload" method="post" enctype="multipart/form-data">
    用户名 <input type="text" name="username"/><br>
    头像:<input type="file" name="img">
    <input type="submit" value="上传">
</form>

我们发现请求类型为多部分表单数据类型。并且各部分之间的分隔符对应的为 boundary后的内容。每个input项代表一个部分,部分直接由浏览生成一个分隔符分割。

后端将请求流转换为字符串形式的数据。 我们可以发现请求中所有的数据被包含在流中,无法通过getParamter()获取参数

------WebKitFormBoundaryveRHUB7FOGYYBQ3y //分隔符
Content-Disposition: form-data; name="username"  //每个部分的类型和详细信息
//空行后对应的为 每个部分的值
admin 
------WebKitFormBoundaryveRHUB7FOGYYBQ3y
Content-Disposition: form-data; name="img"; filename="1.jpg"
Content-Type: image/jpeg
//文件流的数据
??? JFIF      ?? C	#,%!*!&4'*./121%6:60:,010?? C		

1、流和文件操作工具类

commons-io 处理IO
commons-fileupload 处理文件
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-io</artifactId>
        <version>1.3.2</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>

2、文件上传

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    用户名 <input type="text" name="username"/><br>
    头像:<input type="file" name="img">
    <input type="submit" value="上传">
</form>
</body>
</html>

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //不能通过 request.getParameter("username")获取参数,因为请求已经被作为流传输分成一段一段的数据部分(表单的所有项,部件)
        //获取请求流对象
        //ServletInputStream inputStream = request.getInputStream();

        //1、创建工厂实例
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //2、创建处理文件上传的servlet对象
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        //3、解析文件上传请求
        try {
            //4、使用该servlet将请求解析为 多个数据部件对象。即每一项代表一个数据项。
            List<FileItem> fileItems = fileUpload.parseRequest(request);
            //遍历该数据部件
            fileItems.forEach((fileItem)->{
                //如果该项为 input标签中普通的 kye-value参数形式的部件
                if (fileItem.isFormField()) {
                    //获取表单项的name值
                    String username = fileItem.getFieldName();
                    try {
                        //获取表单项的value值 并指定编码
                        String value = fileItem.getString("utf-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    //获取表单项的文件名称 (为null) 普通input只有key-value
                    String filename = fileItem.getName();
                }else{
                    //否则为 文件对象
                    //获取input 标签name值
                    String fieldName = fileItem.getFieldName();
                    //获取文件名
                    String filename = fileItem.getName();
                    System.out.println("文件name值:"+fieldName);
                    System.out.println("文件名:"+filename);
                    try {
                        //得到该文件的 数据流部分
                        InputStream inputStream = fileItem.getInputStream();
                        //生成随机文件名
                        String s = UUID.randomUUID().toString().replaceAll("-", "");
                        String newfileName=s+"_"+filename;
                        //获取相对路径
                        //设置存放指定文件夹的绝对路径:
                        String realPath = request.getServletContext().getRealPath("/uploads");
                        //F:\桌面内容\springboot2\Spring+SpringMVC\servlet01\target\servlet01\uploads
                        System.out.println(realPath);
                        //创建输出流并指定文件路径
                        //  / 代表tomcat安装的盘符  即 D:
                        //  . 代表tomcat安装目录下的 bin文件夹下
                        FileOutputStream fileOutputStream = new FileOutputStream(realPath+"/"+newfileName);
                        //将输入流写到输出流中
                        IOUtils.copy(inputStream, fileOutputStream);
                        //关流
                        fileOutputStream.close();
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            });

        } catch (FileUploadException e) {
            e.printStackTrace();
        }
        response.sendRedirect("/fileupload.jsp");
    }

直接在服务器访问地址: http://localhost:8080/uploads/70f974b4c5544bae8ec3c55eea4c891a_1.jpg

3、获取指定文件或指定文件夹的绝对路径

String realPath = request.getServletContext().getRealPath("/uploads");
//F:\桌面内容\springboot2\Spring+SpringMVC\servlet01\target\servlet01\uploads

4、文件下载

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="download">下载图片</a>
</body>
</html>
 	@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         //本质就是告诉浏览器我给你资源别直接打开 给我下载
        //把要下载的资源传给浏览器

        ServletContext servletContext = request.getServletContext();
        //获取该图片的绝对路径
        String realPath = servletContext.getRealPath("/uploads/哈哈.jpg");

        //动态获取文件类型
        String mimeType = servletContext.getMimeType(realPath);
        //image/jpeg
        System.out.println("文件类型"+mimeType);

        //设置响应头类型(这里我们是图片类型)
        response.setContentType(mimeType);


        // 若文件名为中文 则会无法识别 (万能解决方案)
        String s = new String("哈哈".getBytes("gbk"), "iso8859-1");

        //设置浏览器处理的方式 即以附件形式打开
        // Content-Disposition   处置方式
        // attachment    附件
        // filename=文件名
        response.setHeader("Content-Disposition","attachment;filename="+s);

        //设置文件大小(可选) 一般不设置
//        response.setContentLength();

        //得到输入流
        FileInputStream fileInputStream = new FileInputStream(realPath);
        ServletOutputStream outputStream = response.getOutputStream();
        //将输入流拷贝到响应输出流
        IOUtils.copy(fileInputStream, outputStream);
        //关流
        outputStream.close();
        fileInputStream.close();
    }

点击下载,发现此时并没有打开,而是直接以附件形式打开

我们查看响应头

十七、javaweb项目部署tomcat

1、一般我们通过maven打成war包部署

2、生成的war包在指定的项目的target目录下

3、将项目的war包 servlet01.war直接复制到tomcat安装目录下的webapp目录下

4、进入tomcat安装目录下的bin目录下执行startup.bat 批处理指令

5、执行后会把webapp下的servlet01.war的war包自动解压生成项目文件夹

6、控制台启动成功,输入浏览器访问

注意:这里访问必须携带项目文件夹名称,所有在项目中有路径访问时,必须要动态获取项目文件,否则会找不到。

http://localhost:8080/servlet01/downloadimg.jsp

十八、创建自定义服务

我们每次手动执行启动startup.bat文件时不免麻烦。我们可以为此该批处理命令创建一个服务

1、以管理员身份打开cmd窗口

2、进入tomcat下的bin目录

3、使用service bat install 服务名 创建服务(Tomcat7.0)

4、启动服务

这里我们需要进入到任务管理器中才能查看到服务

5、删除服务

sc delete 服务名