Javaweb

Javaweb三大组件:Servlet程序、Filter过滤器、Listener监听器

三大组件启动顺序:Listener-->Filter-->Servlet

一、Servlet

1.1 Servlet生命周期

1、执行 Servlet 构造器方法

2、执行 init 初始化方法 第一、二步,是在第一次访问,的时候创建 Servlet 程序会调用

3、执行 service 方法 第三步,每次访问都会调用

4、执行 destroy 销毁方法 第四步,在 web工程停止的时候调用

1.2 HttpServletRequest 类

1.2.1 HttpServletRequest 类作用

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

1.2.2 HttpServletRequest 类的常用方法

i.     getRequestURI() 获取请求的资源路径
ii.    getRequestURL() 获取请求的统一资源定位符(绝对路径)
iii.   getRemoteHost() 获取客户端的 ip 地址
iv.   getHeader() 获取请求头
v.    getParameter() 获取请求的参数
vi.   getParameterValues() 获取请求的参数(多个值的时候使用)
vii.  getMethod() 获取请求的方式 GET 或 POST
viii. setAttribute(key, value); 设置域数据
ix.   getAttribute(key); 获取域数据
x.    getRequestDispatcher() 获取请求转发对象

1.2.3 获取请求参数

<body>
<form action="http://localhost:8080/07_servlet/parameterServlet" method="get">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="js">JavaScript<br/>
<input type="submit">
</form>
</body>
public class ParameterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        // 获取请求参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");
        System.out.println(" 用户名:" + username);
        System.out.println(" 密码:" + password);
        System.out.println(" 兴趣爱好:" + Arrays.asList(hobby));
    }
}

 doGet请求中文乱码处理

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

doPost请求中文乱码处理

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    // 设置请求体的字符集为 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");
    System.out.println(" 用户名:" + username);
    System.out.println(" 密码:" + password);
    System.out.println(" 兴趣爱好:" + Arrays.asList(hobby));
}

1.2.4 请求转发

      请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发。

1.2.5 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>

1.2.6 Web 中的相对路径和绝对路径

在 javaWeb 中,路径分为相对路径和绝对路径两种
相对路径是:
. 表示当前目录
.. 表示上一级目录
资源名 表示当前目录/资源名

绝对路径
http://ip:port/工程路径/资源路径
在实际开发中,路径都使用绝对路径,而不简单地使用相对路径。
1、绝对路径
2、base+相对

1.2.7 web 中 / 斜杠的不同意义

    在 web 中 / 斜杠 是一种绝对路径。

  • / 斜杠 如果被浏览器解析,得到的地址是:http://ip:port/
<a href="/">斜杠</a>
  •  / 斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径
<url-pattern>/servlet1</url-pattern>
servletContext.getRealPath(“/”);
request.getRequestDispatcher(“/”);

特殊情况: response.sendRediect(“/”); 把斜杠发送给浏览器解析,得到 http://ip:port/   。

1.3 HttpServletResponse 类

1.3.1 HttpServletResponse 类的作用

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

1.3.2 两个输出流

字节流 getOutputStream(); 常用于下载(传递二进制数据)
字符流 getWriter(); 常用于回传字符串常用

注意: 两个流同时只能使用一个。使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。

1.3.3 往客户端回传数据

public class ResponseIOServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//        System.out.println( resp.getCharacterEncoding() );//默认ISO-8859-1

//        // 设置服务器字符集为UTF-8
//        resp.setCharacterEncoding("UTF-8");
//        // 通过响应头,设置浏览器也使用UTF-8字符集
//        resp.setHeader("Content-Type", "text/html; charset=UTF-8");

        // 它会同时设置服务器和客户端都使用UTF-8字符集,还设置了响应头
        // 此方法一定要在获取流对象之前调用才有效
        resp.setContentType("text/html; charset=UTF-8");

//        System.out.println( resp.getCharacterEncoding() );

//        要求 : 往客户端回传 字符串 数据。
        PrintWriter writer = resp.getWriter();
        writer.write("春江潮水连海平!");
    }
}

1.3.4 解决响应中文乱码问题

      推荐使用该方式:

// 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8");

1.3.5 重定向

         客户端给服务器发请求,服务器给客户端新地址,让客户端去新地址访问,叫请求重定向(因为之前的地址可能已经被废弃)。

 请求重定向推荐使用:resp.sendRedirect("http://localhost:8080");

public class Response1 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("曾到此一游 Response1 ");

        req.setAttribute("key1", "value1");

        // 设置响应状态码302 ,表示重定向,(已搬迁)
//        resp.setStatus(302);
        // 设置响应头,说明 新的地址在哪里
//        resp.setHeader("Location", "http://localhost:8080/07_servlet/response2");
//        resp.setHeader("Location", "http://localhost:8080");

        //推荐使用
        resp.sendRedirect("http://localhost:8080");
    }
}

二、JavaEE三层架构

 

        MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用( 目的还是为了降低耦合度)。MVC 全称:Model 模型、 View 视图、 Controller 控制器。

  • View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——JSP/HTML。
  • Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。转到某个页面,或者是重定向到某个页面。
  • Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo。

三、jsp

3.1 本质

        jsp 的全称是 java server pages,是java 的服务器页面。主要作用是代替 Servlet 程序回传 html 页面的数据。jsp页面本质上是一个servlet程序。

3.2 九大内置对象

  1. request 对象              请求对象,可以获取请求信息
  2. response 对象            响应对象。可以设置响应信息
  3. pageContext 对象      当前页面上下文对象。可以在当前上下文保存属性信息
  4. session 对象              会话对象。可以获取会话信息。
  5. exception 对象           异常对象。只有在 jsp 页面的 page 指令中设置 isErrorPage="true" 的时候才会存在
  6. application 对象         ServletContext 对象实例,可以获取整个工程的一些信息。
  7. config 对象                 ServletConfig 对象实例,可以获取 Servlet 的配置信息
  8. out 对象                     输出流。
  9. page 对象                  表示当前 Servlet 对象实例(无用,用它不如使用 this 对象)。

3.3 四大域对象

        域对象可以存取数据

  1. pageContext                             当前 jsp 页面中使用
  2. request                                     一次请求内有效
  3. session                                    一个会话范围内有效(浏览器从打开到关闭)
  4. application(ServletContext)      整个web工程范围内有效(web工程不停止,数据都在)

        四个域在使用的时候,优先顺序是从小到大的范围的顺序:
        pageContext ——> request——> session——> application

3.4 out 输出和 response.getWriter 输出的区别

         由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下在 jsp 页面中统一使用 out 来进行输出,避免打乱页面输出内容的顺序。
        out.write() 输出字符串没有问题
        out.print() 输出任意数据都没有问题(原因:都转换成为字符串后调用的 write 输出)
        深入源码,浅出结论:在 jsp 页面中,可以统一使用 out.print()来进行输出。

3.5 EL表达式

        EL 表达式的全称是:Expression Language,是表达式语言。

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

        EL 表达式的格式是:${表达式}
        EL 表达式在输出 null 值的时候,输出的是空串。jsp 表达式脚本输出 null 值的时候,输出的是 null 字符串。(EL表达式的优势!null多难看!)

        EL 表达式主要是在 jsp 页面中输出域对象中的数据当四个域中都有相同的 key 的数据的时候,EL 表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出

3.6 JSTL标签库

        JSTL 标签库 全称是指 JSP Standard Tag Library JSP 标准标签库,是一个不断完善的开放源代码的 JSP 标签库。EL 表达式主要是为了替换 jsp 中的表达式脚本,而标签库则是为了替换代码脚本,这样使得整个 jsp 页面变得更加简洁。

3.6.1 使用步骤:

        1. 先导入 jstl 标签库的 jar 包。

        taglibs-standard-impl-1.2.1.jar
        taglibs-standard-spec-1.2.1.jar

        2. 使用 taglib 指令引入标签库

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

 3.6.2 使用示例

保存之前:${ sessionScope.abc } <br>
<c:set scope="session" var="abc" value="abcValue"/>
保存之后:${ sessionScope.abc } <br>
<%--
ii.<c:if />
if 标签用来做 if 判断。
test 属性表示判断的条件(使用 EL 表达式输出)
--%>
<c:if test="${ 12 == 12 }">
    <h1>12 等于 12</h1>
</c:if>
<c:if test="${ 12 != 12 }">
    <h1>12 不等于 12</h1>
</c:if>
<%--
iii.<c:choose> <c:when> <c:otherwise> 标签
作用:多路判断。跟 switch ... case .... default 非常接近
choose 标签开始选择判断
when 标签表示每一种判断情况
test 属性表示当前这种判断情况的值
otherwise 标签表示剩下的情况
<c:choose> <c:when> <c:otherwise> 标签使用时需要注意的点:
1 、标签里不能使用 html 注释,要使用 jsp 注释
2 、 when 标签的父标签一定要是 choose 标签
--%>

<%
    request.setAttribute("height", 180);
%>
<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 begin="1" end="10" var="i">
    <tr>
        <td>第${i}行</td>
    </tr>
</c:forEach>

<%
    request.setAttribute("arr", new String[{"18610541354","18688886666","18699998888"});
%>
<c:forEach items="${ requestScope.arr }" var="item">
    ${ item } <br>
</c:forEach>


<%
    Map<String,Object> map = new HashMap<String, Object>();
    map.put("key1", "value1");
    map.put("key2", "value2");
    map.put("key3", "value3");
    request.setAttribute("map", map);
%>
<c:forEach items="${ requestScope.map }" var="entry">
    <h1>${entry.key} = ${entry.value}</h1>
</c:forEach>

<%
    List<Student> studentList = new ArrayList<Student>();
    for (int i = 1; i <= 10; i++) {
        studentList.add(new Student(i,"username"+i ,"pass"+i,18+i,"phone"+i));
    }
    request.setAttribute("stus", studentList);
%>
<table>
    <tr>
        <th>编号</th>
        <th>用户名</th>
        <th>密码</th>
        <th>年龄</th>
        <th>电话</th>
        <th>操作</th>
    </tr>
<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>
</table>

四、分页

import java.util.List;

/**
 * Page是分页的模型对象
 * @param <T> 是具体的模块的javaBean类
 */
public class Page<T> {
    public static final Integer PAGE_SIZE = 4;
    // 当前页码
    private Integer pageNo;//用Integer而不用int的原因:包装类的默认值直接为null
    // 总页码
    private Integer pageTotal;
    // 当前页显示数量
    private Integer pageSize = PAGE_SIZE;
    // 总记录数
    private Integer pageTotalCount;
    // 当前页数据
    private List<T> items;
    // 分页条的请求地址
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Integer getPageNo() {
        return pageNo;
    }

    public void setPageNo(Integer pageNo) {
        /* 数据边界的有效检查 */
        if (pageNo < 1) {
            pageNo = 1;
        }
       if (pageNo > pageTotal) {
            pageNo = pageTotal;
        }

        this.pageNo = pageNo;
    }

    public Integer getPageTotal() {
        return pageTotal;
    }

    public void setPageTotal(Integer pageTotal) {
        this.pageTotal = pageTotal;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }

    public Integer getPageTotalCount() {
        return pageTotalCount;
    }

    public void setPageTotalCount(Integer pageTotalCount) {
        this.pageTotalCount = pageTotalCount;
    }

    public List<T> getItems() {
        return items;
    }

    public void setItems(List<T> items) {
        this.items = items;
    }

    @Override
    public String toString() {
        return "Page{" +
                "pageNo=" + pageNo +
                ", pageTotal=" + pageTotal +
                ", pageSize=" + pageSize +
                ", pageTotalCount=" + pageTotalCount +
                ", items=" + items +
                ", url='" + url + '\'' +
                '}';
    }
}

xxxDaoImpl:

@Override
    public Integer queryForPageTotalCount() {
        String sql = "select count(*) from t_book";
        Number count = (Number) queryForSingleValue(sql);
        return count.intValue();
    }

@Override
    public List<Book> queryForPageItems(int begin, int pageSize) {
        String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book limit ?,?";
        return queryForList(Book.class,sql,begin,pageSize);
    }

xxxServiceImpl:

@Override
    public Page<Book> page(int pageNo, int pageSize) {
        Page<Book> page = new Page<Book>();

        // 设置每页显示的数量
        page.setPageSize(pageSize);
        // 求总记录数
        Integer pageTotalCount = bookDao.queryForPageTotalCount();
        // 设置总记录数
        page.setPageTotalCount(pageTotalCount);
        // 求总页码
        Integer pageTotal = pageTotalCount / pageSize;
        if (pageTotalCount % pageSize > 0) {
            pageTotal += 1;
        }
        // 设置总页码
        page.setPageTotal(pageTotal);

        // 设置当前页码
        page.setPageNo(pageNo);

        // 求当前页数据的开始索引
        int begin = (page.getPageNo() - 1) * pageSize;
        // 求当前页数据
        List<Book> items = bookDao.queryForPageItems(begin, pageSize);
        // 设置当前页数据
        page.setItems(items);

        return page;
    }

xxxServlet:

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);
//            System.out.println(this);
            // 调用目标业务 方法
            method.invoke(this, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
public class XxxServlet extends BaseServlet{

    private XxxService xxxService = new XxxServiceImpl();

    /**
     * 处理分页
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、获取请求的参数 pageNo , pageSize
        int pageNo = WebUtils.parseInt(req.getParameter("pageNo"),1);
        int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
        //2. 调用XxxService.page(pageNo,pageSize),得到Page对象
        Page<Xxx> page = xxxService.page(pageNo,pageSize);

        page.setUrl("manager/xxxServlet?action=page");
        //3. 保存Page对象到Request域中
        req.setAttribute("page",page);
        //4. 请求转发到pages/manager/book_manager.jsp
        req.getRequestDispatcher("/pages/manager/xxx_manager.jsp").forward(req,resp);
    }
}
public class WebUtils {

    /**
     * 将字符串转换成为int类型的数据
     *
     * @param strInt
     * @param defaultValue
     * @return
     */
    public static int parseInt(String strInt, int defaultValue) {
        try {
            return Integer.parseInt(strInt);
        } catch (NumberFormatException e) {
//            e.printStackTrace();
        }
        return defaultValue;
    }

}

分页条page_nav.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--分页条的开始--%>
<div id="page_nav">
    <%--大于首页,才显示--%>
    <c:if test="${requestScope.page.pageNo > 1}">
        <a href="${ requestScope.page.url }&pageNo=1">首页</a>
        <a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageNo-1}">上一页</a>
    </c:if>
    <%--页码输出  开始--%>
    <c:choose>
        <%--情况1:如果总页码小于等于5的情况,页码的范围是:1-总页码--%>
        <c:when test="${ requestScope.page.pageTotal <= 5 }">
            <c:set var="begin" value="1"/>
            <c:set var="end" value="${requestScope.page.pageTotal}"/>
        </c:when>
        <%--情况2:总页码大于5的情况--%>
        <c:when test="${requestScope.page.pageTotal > 5}">
            <c:choose>
                <%--小情况1:当前页码为前面3个:1,2,3的情况,页码范围是:1-5.--%>
                <c:when test="${requestScope.page.pageNo <= 3}">
                    <c:set var="begin" value="1"/>
                    <c:set var="end" value="5"/>
                </c:when>
                <%--小情况2:当前页码为最后3个,8,9,10,页码范围是:总页码减4 - 总页码--%>
                <c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
                    <c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
                    <c:set var="end" value="${requestScope.page.pageTotal}"/>
                </c:when>
                <%--小情况3:4,5,6,7,页码范围是:当前页码减2 - 当前页码加2--%>
                <c:otherwise>
                    <c:set var="begin" value="${requestScope.page.pageNo-2}"/>
                    <c:set var="end" value="${requestScope.page.pageNo+2}"/>
                </c:otherwise>
            </c:choose>
        </c:when>
    </c:choose>

    <c:forEach begin="${begin}" end="${end}" var="i">
        <c:if test="${i == requestScope.page.pageNo}">
            【${i}】
        </c:if>
        <c:if test="${i != requestScope.page.pageNo}">
            <a href="${ requestScope.page.url }&pageNo=${i}">${i}</a>
        </c:if>
    </c:forEach>
    <%--页码输出  结束--%>


    <%-- 如果已经 是最后一页,则不显示下一页,末页 --%>
    <c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
        <a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageNo+1}">下一页</a>
        <a href="${ requestScope.page.url }&pageNo=${requestScope.page.pageTotal}">末页</a>
    </c:if>

    共${ requestScope.page.pageTotal }页,${ requestScope.page.pageTotalCount }条记录
    到第<input value="${param.pageNo}" name="pn" id="pn_input"/>页
    <input id="searchPageBtn" type="button" value="确定">

    <script type="text/javascript">

        $(function () {
            // 跳到指定的页码
            $("#searchPageBtn").click(function () {

                var pageNo = $("#pn_input").val();

                <%--var pageTotal = ${requestScope.page.pageTotal};--%>
                <%--alert(pageTotal);--%>

                // javaScript语言中提供了一个location地址栏对象
                // 它有一个属性叫href.它可以获取浏览器地址栏中的地址
                // alert(location.href);
                // href属性可读,可写
                location.href = "http://localhost:8080/book1/manager/bookServlet?action=page&pageNo=" + pageNo;
                <%--location.href = "${pageScope.basePath}${ requestScope.page.url }&pageNo=" + pageNo;--%>

            });
        });
    </script>
</div>
<%--分页条的结束--%>
<%--静态包含分页条--%>
<%@include file="/pages/common/page_nav.jsp"%>

五、Cookie和Session

        Session 在服务器端,Cookie 在客户端(浏览器)。【如:Cookie用来自动登录,Session用来保持登录状态】

session机制:

        客户端第一次请求服务端时,服务端会产生一个session对象(用于保存该客户的信息),并且每个session对象都会有一个唯一的 sessionId( 用于区分其他session);服务端会产生一个cookie,并且该cookie的name=JSESSIONID ,value=服务端sessionId的值。然后服务端会在响应客户端的同时将该cookie发送给客户端,至此客户端就有了 一个cookie(JSESSIONID)。因此,客户端的cookie就可以和服务端的session一一对应(JSESSIONID - sessionID)

        客户端第二/n次请求服务端时,服务端会先用客户端cookie中的JSESSIONID去服务端的session中匹配sessionid,如果匹配成功(cookie 的 jsessionid和sesion 的 sessionid),
说明此用户不是第一次访问,无需登录。

举例:

        客户端:               顾客(客户端)
        服务端: 存包处   -  商场(服务端)

        顾客第一次存包:商场 判断此人是 之前已经存过包(通过你手里是否有钥匙)。
        如果是新顾客(没钥匙) ,分配一个钥匙给该顾客; 钥匙 会和 柜子 一一对应;

         第二/n次 存包:商场 判断此人是 之前已经存过包(通过你手里是否有钥匙)
         如果是老顾客(有钥匙),则不需要再分配;该顾客手里的钥匙会和柜子自动一一对应。

六、谷歌验证码

6.1 导入谷歌验证码kaptcha的jar

        kaptcha-2.3.2.jar

6.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>

6.3 在表单中使用 img 标签去显示验证码图片并使用

<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>

6.4 在服务器获取谷歌生成的验证码和客户端发过来的验证码比较使用

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
    // 获取 Session 中的验证码
    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("请不要重复提交表单");
    }
}

6.5 前端页面点击图片刷新验证码

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

七、Filter过滤器

7.1 Filter过滤器简介

          Filter 过滤器它是 JavaWeb 的三大组件之一,它的作用是:拦截请求,过滤响应。拦截请求常见的应用场景有:权限检查、日志操作、事务管理等等。

7.2 代码实现(简单)

Filter代码:

public class AdminFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * doFilter方法专门用于拦截请求,过滤响应。可以做【权限检查】
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpSession session = httpServletRequest.getSession();
        Object user = session.getAttribute("user");
        if(user == null) {//若用户没有登录,则跳到登录页面
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
            return;
        }else{//让程序继续访问用户的目标资源
            filterChain.doFilter(servletRequest,servletResponse);
        }

    }

    @Override
    public void destroy() {

    }
}

配置web.xml:

<!--    配置filter过滤器-->
    <filter>
<!--        给filter起别名-->
        <filter-name>AdminFilter</filter-name>
<!--        配置filter全类名-->
        <filter-class>com.atguigu.filter.AdminFilter</filter-class>
    </filter>
<!--    配置filter过滤器的拦截路径-->
    <filter-mapping>
<!--        当前的拦截路径给哪个filter使用-->
        <filter-name>AdminFilter</filter-name>
<!--        拦截路径     / 表示请求地址为:http://ip:port/工程路径/    映射到IDEA的web目录-->
<!--                    /admin/* 表示请求地址为:http://ip:port/工程路径/admin/*-->
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>

7.3 Filter生命周期

  1. 构造器方法
  2. init 初始化方法  第 1,2 步,在 web 工程启动的时候执行(Filter 已经创建)
  3. doFilter 过滤方法 第 3 步,每次拦截到请求,就会执行
  4. destroy 销毁 第 4 步,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)

7.4 FilterChain

按照web.xml中配置filter的顺序执行(下图为先配置的Filter1):

 7.5 拦截路径 

  • 精确匹配

 <url-pattern>/target.jsp</url-pattern>

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp

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

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*

  • 后缀名匹配

 <url-pattern>*.html</url-pattern>

以上配置的路径,表示请求地址必须以.html 结尾才会拦截到

 <url-pattern>*.do</url-pattern>

以上配置的路径,表示请求地址必须以.do 结尾才会拦截到

 <url-pattern>*.action</url-pattern>

以上配置的路径,表示请求地址必须以.action 结尾才会拦截到

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

7.6 ThreadLocal

        ThreadLocal 可以解决多线程的数据安全问题。 ThreadLocal 可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

        ThreadLocal 的特点:

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

7.7 使用 Filter 和 ThreadLocal 组合管理事务

7.7.1 使用 ThreadLocal 确保所有 Dao 操作都在同一个 Connection 连接对象中完成

 JdbcUtils工具类修改为:

public class JdbcUtils {

    private static DruidDataSource dataSource;
    private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();

    static {
        try {
            Properties properties = new Properties();
            // 读取 jdbc.properties属性配置文件
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            // 从流中加载数据
            properties.load(inputStream);
            // 创建 数据库连接 池
            dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取数据库连接池中的连接
     *
     * @return 如果返回null, 说明获取连接失败<br />有值就是获取连接成功
     */
    public static Connection getConnection() {
        Connection conn = conns.get();
        if (conn == null) {
            try {
                conn = dataSource.getConnection();//从数据库连接池中获取连接
                conns.set(conn);//将获取到的连接conn保存到ThreadLocal对象中,供后面的JDBC对象使用
                conn.setAutoCommit(false);//设置手动提交事务
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }

    /**
     * 提交事务并关闭释放连接
     */
    public static void commitAndClose() {
        Connection connection = conns.get();
        if (connection != null) {//如果获取到的connection不是null,说明之前使用过该连接操作数据库
            try {
                connection.commit();//提交事务
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close();//关闭连接,资源
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        //一定要执行remove操作!!!否则会出错(Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }

    /**
     * 回滚事务并关闭释放连接
     */
    public static void rollbackAndClose() {
        Connection connection = conns.get();
        if (connection != null) {//如果获取到的connection不是null,说明之前使用过该连接操作数据库
            try {
                connection.rollback();//回滚事务
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close();//关闭连接,资源
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        //一定要执行remove操作!!!否则会出错(Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }
}

BaseDao修改为:(在每一个catch抛出运行时异常给需要的地方捕获处理!)

public abstract class BaseDao {

    //使用DbUtils操作数据库
    private QueryRunner queryRunner = new QueryRunner();

    /**
     * update() 方法用来执行:Insert\Update\Delete语句
     *
     * @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
     */
    public int update(String sql, Object... args) {
        System.out.println("BaseDao在【"+Thread.currentThread().getName()+"】中");
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.update(connection, sql, args);
        } catch (SQLException e) {
            e.printStackTrace();
            //把异常往外抛。因为不在DAO层关闭connection了,就需要在提交事务(或者回滚)并关闭连接的地方捕获异常
            //如果不抛出去,需要的地方捕获不到异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询返回一个javaBean的sql语句
     *
     * @param type 返回的对象类型
     * @param sql  执行的sql语句
     * @param args sql对应的参数值
     * @param <T>  返回的类型的泛型
     * @return
     */
    public <T> T queryForOne(Class<T> type, String sql, Object... args) {
        Connection con = JdbcUtils.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
            //把异常往外抛。因为不在DAO层关闭connection了,就需要在提交事务(或者回滚)并关闭连接的地方捕获异常
            //如果不抛出去,需要的地方捕获不到异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询返回多个javaBean的sql语句
     *
     * @param type 返回的对象类型
     * @param sql  执行的sql语句
     * @param args sql对应的参数值
     * @param <T>  返回的类型的泛型
     * @return
     */
    public <T> List<T> queryForList(Class<T> type, String sql, Object... args) {
        Connection con = JdbcUtils.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanListHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
            //把异常往外抛。因为不在DAO层关闭connection了,就需要在提交事务(或者回滚)并关闭连接的地方捕获异常
            //如果不抛出去,需要的地方捕获不到异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行返回一行一列的sql语句
     * @param sql   执行的sql语句
     * @param args  sql对应的参数值
     * @return
     */
    public Object queryForSingleValue(String sql, Object... args){

        Connection conn = JdbcUtils.getConnection();

        try {
            return queryRunner.query(conn, sql, new ScalarHandler(), args);
        } catch (SQLException e) {
            e.printStackTrace();
            //把异常往外抛。因为不在DAO层关闭connection了,就需要在提交事务(或者回滚)并关闭连接的地方捕获异常
            //如果不抛出去,需要的地方捕获不到异常
            throw new RuntimeException(e);
        }
    }
}

7.7.2 使用 Filter 过滤器统一给所有的Service方法都加上 try-catch,进行实现的管理

 TransactionFilter 类代码:

public class TransactionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            filterChain.doFilter(servletRequest,servletResponse);
            JdbcUtils.commitAndClose();
        } catch (Exception e) {
            JdbcUtils.rollbackAndClose();
            e.printStackTrace();
            throw new RuntimeException(e);//把异常抛给Tomcat管理,展示出错后的友好界面
        }
    }

    @Override
    public void destroy() {

    }
}

web.xml 中的配置:

    <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 过滤器:(在catch中抛出运行时异常)

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");
        //解决中文响应乱码
        resp.setContentType("text/html;charset=UTF-8");
        String action = req.getParameter("action");
        try {
            // 获取action业务鉴别字符串,获取相应的业务 方法反射对象
            Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
//            System.out.println(method);
//            System.out.println(this);
            // 调用目标业务 方法
            method.invoke(this, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);//把异常抛给Filter过滤器
        }
    }

}

7.7.3 将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误信息页面

在web.xml中配置:

<!--    配置服务器出错后自动跳转的页面-->
    <error-page>
<!--        error-code错误类型-->
        <error-code>404</error-code>
<!--        location表示跳转路径-->
        <location>/pages/error/error404.jsp</location>
    </error-page>

    <error-page>
        <error-code>500</error-code>
        <location>/pages/error/error500.jsp</location>
    </error-page>

八、JSON和AJAX

8.1 JSON

        JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式(轻量级指的是跟 xml 做比较数据交换指的是客户端和服务器之间业务数据的传递格式),易于阅读和编写,同时也易于机器解析和生成。JSON 采用完全独立于语言的文本格式,而且很多语言都提供了对 json 的支持(包括 C, C++, C#, Java, JavaScript, Perl, Python 等), 这样就使得 JSON 成为理想的数据交换格式。

8.1.1 JSON 在 JavaScript 中的使用

        json 是由键值对组成,并且由花括号(大括号)包围。每个键由引号引起来,键和值之间使用冒号进行分隔, 多组键值对之间用逗号进行分隔。

JSON定义及访问:

json 的存在有两种形式:

        一种以对象的形式存在,称为 json 对象

        一种以字符串的形式存在,称为 json 字符串。

        要操作 json 中的数据时,需要 json 对象的格式;

        要在客户端和服务器之间进行数据交换的时候,使用 json 字符串。

  • JSON.stringify() 把 json 对象转换成为 json 字符串
  • JSON.parse() 把 json 字符串转换成为 json 对象
<script type="text/javascript">
        // json的定义
        var jsonObj = {
            "key1": 12,
            "key2": "abc",
            "key3": true,
            "key4": [11, "arr", false],
            "key5": {
                "key5_1": 551,
                "key5_2": "key5_2_value"
            },
            "key6": [{
                "key6_1_1": 6611,
                "key6_1_2": "key6_1_2_value"
            }, {
                "key6_2_1": 6621,
                "key6_2_2": "key6_2_2_value"
            }]
        };

        alert(typeof (jsonObj));// object  json就是一个对象
        alert(jsonObj.key1); //12
        alert(jsonObj.key2); // abc
        alert(jsonObj.key3); // true
        alert(jsonObj.key4);// 得到数组[11,"arr",false]
        //  // json 中 数组值的遍历
        for (var i = 0; i < jsonObj.key4.length; i++) {
            alert(jsonObj.key4[i]);
        }
        alert(jsonObj.key5.key5_1);//551
        alert(jsonObj.key5.key5_2);//key5_2_value
        alert(jsonObj.key6);// 得到json数组

        //取出来每一个元素都是json对象
        var jsonItem = jsonObj.key6[0];
        alert(jsonItem.key6_1_1); //6611
        alert(jsonItem.key6_1_2); //key6_1_2_value

        alert(jsonObj);

        // 把json对象转换成为 json字符串
        var jsonObjString = JSON.stringify(jsonObj); //stringify() 特别像 Java中对象的toString
        alert(jsonObjString)
        // 把json字符串。转换成为json对象
        var jsonObj2 = JSON.parse(jsonObjString);
        alert(jsonObj2.key1);// 12
        alert(jsonObj2.key2);// abc
    </script>

8.1.2 JSON 在 Java 中的使用

        1. javaBean 和 json 的互转

    @Test
    public void test1(){
        Person person = new Person(1,"周涛");
        // 创建Gson对象实例
        Gson gson = new Gson();
        // toJson方法可以把java对象转换成为json字符串
        String personJsonString = gson.toJson(person);
        System.out.println("personJsonString"+personJsonString);
        // fromJson把json字符串转换回Java对象
        // 第一个参数是json字符串
        // 第二个参数是转换回去的Java对象类型
        Person person1 = gson.fromJson(personJsonString, Person.class);
        System.out.println(person1);
    }

        2. List和json的互转

    @Test
    public void test2() {
        List<Person> personList = new ArrayList<>();

        personList.add(new Person(1, "周涛"));
        personList.add(new Person(2, "总监"));

        Gson gson = new Gson();

        // 把List转换为json字符串
        String personListJsonString = gson.toJson(personList);
        System.out.println("personListJsonString:"+personListJsonString);

//        List<Person> list = gson.fromJson(personListJsonString, new PersonListType().getType());
        //不创建获取list类的方法直接使用匿名类对象
        List<Person> list = gson.fromJson(personListJsonString, new TypeToken<ArrayList<Person>>(){}.getType());
        System.out.println(list);
        Person person = list.get(0);
        System.out.println(person);
    }

        3. Map和json的互转

    @Test
    public void test3(){
        Map<Integer,Person> personMap = new HashMap<>();

        personMap.put(1, new Person(1, "周涛"));
        personMap.put(2, new Person(2, "总监"));

        Gson gson = new Gson();
        // 把 map 集合转换成为 json字符串
        String personMapJsonString = gson.toJson(personMap);
        System.out.println("personMapJsonString:"+personMapJsonString);

//        Map<Integer,Person> personMap2 = gson.fromJson(personMapJsonString, new PersonMapType().getType());
        //使用匿名内部类 匿名对象
        Map<Integer,Person> personMap2 = gson.fromJson(personMapJsonString, new TypeToken<HashMap<Integer,Person>>(){}.getType());

        System.out.println(personMap2);
        Person p = personMap2.get(1);
        System.out.println(p);
    }

8.2 AJAX

8.2.1 AJAX请求

        AJAX 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术。 ajax 是一种浏览器通过 js 异步发起请求,局部更新页面的技术。 ajax 请求的局部更新,浏览器地址栏不会发生变化,局部更新不会舍弃原来页面的内容

8.2.2 jQuery中Ajax请求

1.    $.ajax 方法

url 表示请求的地址

type 表示请求的类型 GET 或 POST 请求

data 表示发送给服务器的数据 格式有两种:

        一:name=value&name=value

        二:{key:value}

success 请求成功,响应的回调函数

dataType 响应的数据类型

        常用的数据类型有:

                text 表示纯文本

                xml 表示 xml 数据

                json 表示 json 对象

            // ajax请求
            $("#ajaxBtn").click(function () {
                $.ajax({
                    url: "http://localhost:8080/16_json_ajax__il8n/ajaxServlet",
                    // data:"action=jQueryAjax",
                    data: {action: "jQueryAjax"},
                    type: "GET",
                    success: function (data) {
                        alert("服务器返回的数据是" + data);
                        // var jsonObj = JSON.parse(data);要把传回来的json字符串转为json对象
                        // $("#msg").html("编号:"+jsonObj.id+",姓名:"+jsonObj.name);
                        $("#msg").html("ajax  编号:" + data.id + ",姓名:" + data.name);
                    },
                    // dataType:"text"//设置传回来的数据类型为text文本类型
                    dataType: "json"//设置传回来的数据类型为json对象,可以直接取值
                });
                alert("ajax btn");
            });

2.     $.get 方法和$.post 方法

url 请求的 url 地址

data 发送的数据

callback 成功的回调函数

type 返回的数据类型

            // ajax--get请求
            $("#getBtn").click(function () {
                $.get("http://localhost:8080/16_json_ajax__il8n/ajaxServlet", "action=jQueryGet", function (data) {
                    $("#msg").html("get  编号:" + data.id + ",姓名:" + data.name);
                }, "json");
                alert(" get btn ");
            });

            // ajax--post请求
            $("#postBtn").click(function () {
                $.post("http://localhost:8080/16_json_ajax__il8n/ajaxServlet", "action=jQueryPost", function (data) {
                    $("#msg").html("post  编号:" + data.id + ",姓名:" + data.name);
                }, "json");
                alert("post btn");
            });

3.       $.getJSON 方法

url 请求的 url 地址

data 发送给服务器的数据

callback 成功的回调函数

            // ajax--getJson请求
            $("#getJSONBtn").click(function () {
                $.getJSON("http://localhost:8080/16_json_ajax__il8n/ajaxServlet", "action=jQueryGetJSON", function (data) {
                    $("#msg").html("getJSON  编号:" + data.id + ",姓名:" + data.name);
                });
                alert("getJSON btn");
            });

         表单序列化方法serialize()可以把表单中所有表单项的内容都获取到,并以name=value&name=value的形式进行拼接。

            // ajax请求  把参数序列化
            $("#submit").click(function () {
                alert($("#form01").serialize());

                $.getJSON("http://localhost:8080/16_json_ajax__il8n/ajaxServlet", "action=jQuerySerialize&" + $("#form01").serialize(), function (data) {
                    $("#msg").html("jQuerySerialize  编号:" + data.id + ",姓名:" + data.name);
                });
                alert("serialize()");
            });

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值