Jsp与Servlet(二)之Servlet

这里写目录标题

  • 一、Servlet 基本使用
  • 二、重定向与转发
  • 三、生命周期与创建时机
  • 四、模糊映射
  • 五、过滤器
  • 六、session 超时
  • 七、监听器
  • 八、注解
  • 九、文件上传与下载
  • 十、jsp 安全模式

一、Servlet 基本使用

  Servlet 是运行在 Web 服务器端的 Java 应用程序,它使用 Java 语言编写,具有 Java 语言的优点。与 Java 程序的区别是,Servlet 对象主要封装了对 HTTP 请求的处理,并且它的运行需要 Servlet 容器(tomcat)的支持,在 Java Web 应用方面,Servlet 的应用占有十分重要的地位,它在 Web 请求的处理功能方面也非常强大。

  Servlet 的创建十分简单,创建一个普通的 Java 类,使这个类继承 HttpServlet 类并根据需要重写父类的 doGet()或 doPost()方法,再通过手动配置 web.xml 文件注册 Servlet 对象,以告知 Web 容器哪一个请求调用哪一个 Servlet 对象处理。

  Servlet 的配置包含在 web.xml文件中,主要通过以下两步进行设置。

<!--声明Servlet-->
<servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>cn.hxzy.MyServlet</servlet-class>
</servlet>
<!--映射Servlet-->
<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/my</url-pattern>
</servlet-mapping>

  声明 Servlet 对象在 web.xml 文件中,通过标签声明一个 Servlet 对象。在此标签下包含两个主要子元素,分别为 其中,元素用于指定 Servlet 的名称,该名称可以为自定义的名称;元素用于指定 Servlet 对象的完整位置,包含 Servlet 对象的包名与类名。

  在 web.xml 文件中声明了 Servlet 对象后,需要映射访问 Servlet 的 URL 该操作使用标签进行配置。 标签包含两个子元素,分别为 。其中,元素与 标签中的 元素相对应,不可以随意命名。 元素用于映射访问 URL。

  案例:将 jsp 页面的登录过程用 Servlet 实现一下

public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(rep, resp);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        String pwd = request.getParameter("pwd");        
        if ("admin".equals(name) && "123".equals(pwd)){
            request.getSession().setAttribute("user",login);
            response.sendRedirect("index.jsp");
        }else {
            response.sendRedirect("login.jsp");
        }
    }
}

  案例:Servlet 直接向浏览器响应 HTML 字符串

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class TestServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        writer.write("

hello world!

"
); } }

  Servlet 是使用 Java Servlet 接口运行在 Web 应用服务器上的 Java 程序,其功能十分强大,它不但可以处理 HTTP 请求中的业务逻辑,而且还可以输出 HTML 代码来显示指定页面。而 JSP 是一 种在 Servlet 规范之上的动态网页技术,在 JSP 页面中,同样可以编写业务逻辑处理 HTTP 请求,也可以通过 HTML 代码来编辑页面,在实现功能上,Servlet 与 JSP 貌似相同,实质存在一定的区别,主要表现在以下几方面。

  1.角色不同:JSP 页面可以存在 HTML 代码与 Java 代码并存的情况,而 Servlet 需要承担客户请求与业务处理的中间角色,只有调用固定的方法才能将动态内容输出为静态的 HTML,所以,JSP 更具有显示层的角色。
  2.编程方法不同:Servlet 与 JSP 在编程方法上存在很大的区别,使用 Servlet 开发 Web 应用程序需要遵循 Java 的标准,而 JSP 需要遵循一定脚本语言规范。
  3.Servlet 需要编译后运行:Servlet 需要在 Java 编译器编译后才可以运行,如果 Servlet 在编写完成或修改后没有被重新编译, 则不能运行在 Web 容器中。而 JSP 则与之相反,JSP 由 JSP Container 对其进行管理,它的编辑过程也由 JSP Container 对 JSP 进行自动编译,所以,无论 JSP 文件被创建还是修改,都不需要对其编译即可运行。
  4.速度不同:由于 JSP 页面由 JSP Container 对其进行管理,在每次执行不同内容的动态 JSP 页面时,JSP Container 都要对其自动编译,所以,它的效率低于 Servlet 的执行效率。而 Servlet 在编译完成之后,则不需要再次编译,可以直接获取及输出动态内容。在 JSP 页面中的内容没有变化的情况下,JSP 页面的编译完成之后,JSP Container 不会再次对 JSP 进行编译。

  注意:在响应过程中如果出现中文乱码,需要设置response.setCharacterEncoding(“UTF-8”);和 response.setContentType(“text/html;charset=UTF-8”);关于请求的方法,点击 a 标签跳转,或直接在浏览器地址栏输入地址并回车,这样的请求方式是 get 请求。 post 请求一般有表单提交且表单指定请求方式为 post 或者 ajax 请求。

练习:

1.注册校验练习:创建项目 demo3 并编写一个注册模块,注册页 register.jsp,jsp 里面有 username,password,repsssword,telephone。提交之后转到 servlet。servlet 判断 password 与 repassword 是否相同和电话号码为 11 位。如果都符合,显示"注册成功"。否则显示"密码与确认密码不一致或手机号不合法"。

参考代码:

register.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>注册</title>
</head>
<body>
<form action="register" method="post">
    <input name="username">
    <br>
    <input name="password">
    <br>
    <input name="repassword">
    <br>
    <input name="telephone">
    <br>
    <input type="submit" value="提交">
</form>
</body>
</html>

RegisterServlet

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RegisterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String repassword = request.getParameter("repassword");
        String telephone = request.getParameter("telephone");
        if (repassword != null && repassword.equals(password) && telephone != null && telephone.length() == 11) {
            response.getWriter().write("

注册成功

"
); return; } response.getWriter().write("

密码与确认密码不一致或手机号不合法

"
); } }

二、重定向与转发

  重定向表示当前访问的资源没有权限获取操作成功后需要浏览器跳转到指定的 jsp 或 servlet,这种跳转是由于后台返回了一个 code 为 302 的响应头,并且在响应头中告知浏览器跳转地址。浏览器主动跳转。 使用如下语法即可完成重定向:

response.sendRedirect("login.jsp");

  转发是描述 jsp 与 servlet 之间的关系,该转发的逻辑如下,当我们访问 jsp页面时,页面没有相关的数据展示,于是我们的请求便指向 servlet 的地址,servlet 调用相关组件获取数据,然后将数据存入 request 中,jsp 页面再将 request 中的数据渲染出一张有数据的 html 页面。这样各司其职也正是 java 提出的高内聚、低耦合的编程思想。使用如下语法即可完成请求转发:

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

注意:转发时浏览器地址不发生变化,重定向时由于浏览器发送了两次请求,所以不能在 request 里面存放数据。

案例:转发传值

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("id", 12);
        request.setAttribute("name", "张三");
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }
}

jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>INDEX</title>
</head>
<body>
<p>id:<%=request.getAttribute("id")%>
</p>
<p>name:${name}
</p>
</body>
</html>

案例:登录表单回填

login.jsp

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>LOGIN</title>
</head>
<body>
<form action="login">
    <input name="name" type="text"
           value="${name}">
    <input name="password" type="text"
           value="<%=request.getAttribute("password")==null?"":request.getAttribute("password")%>">
    <input type="submit" value="提交">
</form>
</body>
</html>

servlet

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        if ("123".equals(password)) {
            request.getSession().setAttribute("userInfo", name);
            response.sendRedirect("index.jsp");
            return;
        }
        request.setAttribute("name", name);
        request.setAttribute("password", password);
        request.getRequestDispatcher("login.jsp").forward(request, response);
    }
}

练习:

1.创建项目 demo4 编写一个 index.jsp 页面,输入圆的半径以 post 请求提交给一个 Servlet。Servlet 对半径进行判定,若为 null 或 <=0 的数,重定向到 index.jsp 页面,若为 >0 的值,则转发到 success.jsp 页面,并显示其面积、周长。

参考代码:

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
<form action="check" method="post">
    <input name="number">
    <input type="submit" value="提交">
</form>
</body>
</html>

CheckServlet

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/check")
public class CheckServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String number = request.getParameter("number");
        if (number != null && number != "") {
            try {              
                int i = Integer.parseInt(number);
                if (i > 0) {
                    request.setAttribute("area", Math.PI * i * i);
                    request.setAttribute("perimeter", Math.PI * i * 2);
                    request.getRequestDispatcher("success.jsp").forward(request, response);
                    return;
                }                
            } catch (Exception e) {
            }
        }
        response.sendRedirect("index.jsp");
    }
}

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>计算成功</title>
</head>
<body>
<p>周长:<%=request.getAttribute("perimeter")%></p>
<p>周长:${perimeter}</p>
<p>面积:${area}</p>
</body>
</html>

三、生命周期与创建时机

  Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  1.Servlet 初始化后调用 init () 方法。
  2.Servlet 调用 doGet() 或 doPost() 方法来处理客户端的请求。
  3.Servlet 销毁前调用 destroy() 方法。
  4.最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
  在一些课外书上通常也有使用 service() 来响应请求的,service() 是类 GenericServlet 中最重要的方法,每次客户向服务器发出请求时,服务器就会调用这个方法。程序员覆盖这个方法,并在这个方法中加入自己的代码来实现对客户的响应。service() 的方法参数和用法与 doGet()、doPost() 相同。

  注意:继承了 HttpServlet 类并重写了该类的 service()、doPost() 和 doGet() 三个方法时,java 只会执行 service() 方法,而其他的两种方法不会被执行。

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServlet extends HttpServlet {
    @Override//处理get请求
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override//处理post请求
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

    @Override//销毁调用
    public void destroy() {
        super.destroy();
    }

    @Override//初始化调用
    public void init() throws ServletException {
        super.init();
    }
}

  上述代码显示了一个 Servlet 对象的代码结构, MyServlet 类通过继承 HttpServlet 类被声明为一个 Servlet 对象。该类中包含 4 个方法,其中 init() 方法与destroy() 方法为 Servlet 初始化与生命周期结束所调用的方法,其余的 2 个方法为 Servlet 针对处理不同的 HTTP 请求类型所提供的方法,其作用如注释中所示。
  在创建 Servlet 时一般还可以指定创建的时机

<servlet>
    <servlet-name>secondServlet</servlet-name>
    <servlet-class>com.hw.javaweb.SecondServlet</servlet-class>
    <!-- 可以指定 Servlet 被创建的时机 -->
    <load-on-startup>2</load-on-startup>
</servlet>

  load-on-startup:可以指定 Serlvet 被创建的时机。若为负数,则在第一次请求时被创建。若为 0 或正数,则在当前 WEB 应用被 Serlvet 容器加载时创建实例,且数值越小越早被创建。

四、模糊映射

  同一个 Servlet 可以被匹配映射到多个 URL 上,即多个元素的子元素的设置值可以是同一个 Servlet 的注册名。Servlet 的匹配分为精确匹配和模糊匹配。

精确匹配
<servlet-mapping>
    <servlet-name>loginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>userAddServlet</servlet-name>
    <url-pattern>/user/add</url-pattern>
</servlet-mapping>
模糊匹配
<servlet-mapping>
    <servlet-name>allServlet</servlet-name>
    <url-pattern>/*


    userAllServlet
    /user/*


    actionServlet
    *.action

Jsp与Servlet(二)之Servlet_第1张图片
参考代码:

配置映射:

<servlet-mapping>
    <servlet-name>findById</servlet-name>
    <url-pattern>/findById/*

获取参数:

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String requestURI = request.getRequestURI();
    String[] split = requestURI.split("/");
    request.setAttribute("id", split[split.length - 1]);
    request.getRequestDispatcher("/index.jsp").forward(request, response);
}

五、过滤器

  Servlet 过滤器与 Servlet 十分相似,对于程序开发人员而言,过滤器实质上就是在 Web 应用服务器上的一个 Web 应用组件,用于拦截客户端(浏览器)与目标资源(Servlet)的请求,并对这些请求进行一定过滤处理再发送给目标资源。Servlet 过滤器可以改变请求中的内容,来满足实际开发中的需要。

  过滤器(Filter)核心对象放置在 javax.servlet 包中,其名称为 Filter,它是一个接口。除这个接口外,与过滤器相关的对象还有 FilterConfig(过滤器的配置对象)对象与 FilterChain(过滤器传递对象),在实际开发中,定义过滤器对象只需要直接或间接地实现 Filter 接口即可。

  每一个过滤器对象都要直接或间接地实现 Filter接口,在 Fiter 接口中定义了3个方法,分别为 init(过滤器初始化方法,该方法在过滤器初始化时调用)、doFilter(对请求进行过滤处理)和destroy(销毁方法,以便释放资源)。

  在 doFilter 方法中调用 filterChain.doFilter(servletRequest, servletResponse); 方法即可完成放行操作,若没有放行操作。客户端浏览器本次请求将接收不到任何数据,浏览器一般表现为白屏。

案例:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

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

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤器");
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (request.getRequestURI().endsWith("login.jsp")) {
            filterChain.doFilter(servletRequest, servletResponse);//放行
        } else {
            response.sendRedirect("login.jsp");
        }
    }

    @Override
    public void destroy() {
    }
}

  过滤器与 Servlet 十分相似,在创建之后同样需要对其进行配置,过滤器的配置主要分为两个步骤,分别为声明过滤器对象和创建过滤器映射。

<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>cn.hxzy.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/*

  标签用于声明过滤器对象,在这个标签中必须配置两个子元素,分别为过滤器的名称与过滤器完整类名,其中用于定义过滤器的名称,用于指定过滤器的完整类名。标签用于创建过滤器的映射,它的主要作用就是指定 Web 应用中,哪些 URL 应用哪一个过滤器进行处理。在标签中需要指定过滤器的名称与过滤器的 URL 映射,其中用于定义过滤器的名称,用于指定过滤器应用的 URL。

案例:过滤器实现登录拦截

删除首页的如下代码:

<%
    if (session.getAttribute("userInfo") == null) {//如果 session 里面没有用户信息
        response.sendRedirect("login.jsp");//重定向到 login.jsp
    }
%>

并使用过滤器完成用户不登录,不能访问 index.jsp

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

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        //由于ServletRequest是HttpServletRequest父类,无法获取请求的 URI 所以强制转换为 HttpServletRequest
        HttpServletResponse response= (HttpServletResponse) servletResponse;
        if (request.getRequestURI().endsWith("login") || request.getRequestURI().endsWith("login.jsp")) {
            filterChain.doFilter(request,response);//放行
            return;
        }else {
            if (request.getSession().getAttribute("user")==null) {
                response.sendRedirect("login.jsp");//未登录,重定向到登录页
            }else {
                filterChain.doFilter(request,response);//放行
                return;
            }
        }
    }

    @Override
    public void destroy() {
    }
}

注意:过滤器配置时需要考虑静态资源,如:css、js、图片等的放行,否则将不能访问静态资源。

案例:过滤器参数

在配置 web.xml 时增加参数如下。

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>cn.hxzy.FirstFilter</filter-class>
    <init-param>
        <param-name>param</param-name>
        <param-value>参数</param-value>
    </init-param>
</filter>

在过滤器初始化方法中获取参数。

@Override
public void init(FilterConfig filterConfig) throws ServletException {
    String param = filterConfig.getInitParameter("param");
    System.out.println(param);
}

练习:

1.创建项目 demo5 实现一个 IP 过滤器,比如本地有 localhost、127.0.0.1 还有局域 IP 地址,把同桌的 IP 加入黑名单测试。如果不在黑名单中则放行,否则页面弹窗“权限不足”。

2.创建项目 demo6 并编写一个过滤器链,第一个过滤器负责矫正请求和响应的字符编码,第二个负责检查请求地址中有一个 token 的参数,该参数是一个md5 加密后的 123456。如果满足需求则放行,否则重定向到 index.jsp

参考代码

1题

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class CheckFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setCharacterEncoding("utf-8");
        String remoteHost = request.getRemoteHost();
        if (remoteHost.equals("192.168.8.223")) {
            PrintWriter writer = response.getWriter();
            writer.write("");
            return;
        }
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

2题过滤器1

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CharEncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("text/html;charset=UTF-8");
        request.setCharacterEncoding("utf-8");
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

过滤器2

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class TokenFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String token = request.getParameter("token");
        if ("E10ADC3949BA59ABBE56E057F20F883E".equals(token)) {
            filterChain.doFilter(request, response);
            return;
        }
        response.sendRedirect("index.jsp");
    }

    @Override
    public void destroy() {
    }
}

六、session 超时

  在 Java Web 开发中,Session 为我们提供了很多方便,Ssession 是由浏览器和服务器之间维护的。Session 超时理解为:浏览器和服务器之间创建了一个Session,由于客户端长时间(默认 30 分钟)没有与服务器交互,服务器将此 Session 销毁,客户端再一次与服务器交互时之前的 Session 就不存在了。
  在正常使用 session 过程中调用 invalidate() 也可以将当前的客户端浏览器的会话清除。

session.invalidate();

  一般用于用户退出时销毁 session。清除 session 后再次请求网站,tomcat 服务器又会为客户端浏览器分配新的 session 空间,并将 session 的 ID 通过响应头发送给客户端浏览器并设置到 cookie 里面。
  而在实际开发中,需要监听 session 的创建和销毁,以及往 session 中存入的值等。这些功能都只能通过监听来完成。
  设置 Session 超时时间通常在 web.xml 中设置 session-config

<session-config>
      <session-timeout>2</session-timeout>
</session-config>

  如上客户端连续两次与服务器交互间隔时间最长为 2 分钟,2 分钟后 session.getAttribute() 获取的值为空。

注意:

  1.若访问服务器 session 超时(本次访问与上次访问时间间隔大于 session 最大的不活动的间隔时间)了,即上次会话结束服务端会清除 session,在客户端会又发生请求时,服务端会产生一个新的会话,将之前的 sessionId 替换掉。从而保证数据安全。

  2.记录 session 的 cookie 使用周期一般为浏览器关闭前,若用户关闭客户端浏览器,则上次登录的 cookie 将失效,从而无法获取 session,此时服务端的 session 还是存在的,只是客户端浏览器无法访问该空间。所以 session 常见销毁方法有:在某个请求周期内调用了 session.invalidate()方法,此请求周期结束后,session 被销毁;或者是 session 超时后自动销毁;或者关闭客户端浏览器;重启 tomcat 所有 session 都会清空。

案例:退出案例

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getSession().invalidate();//清除当前客户端的session
        response.sendRedirect("login.jsp");
    }
}

七、监听器

  监听器的作用是监听 Web 容器的有效期事件,因此它是由容器管理的。利用 Listener 接口监听在容器中的某个执行程序,并且根据其应用程序的需求做出适当的响应。
  监听通常有下面三类 8 种监听,分别监听 ServletContext(jsp 里面的 application)变化、session 变化和 request 变化。
1.ServletContextListener:完成 Servlet 上下文整体监听,主要有以下方法:

  • contextInitialized(ServletContextEvent event)监听 application 上下文初始化。
  • contextDestroyed(ServletContextEvent event)监听 application 上下文被载出,即关闭。

2.ServletContextAttibuteListener:完成 Servlet 上下文内容监听,主要有以下方法:

  • attibuteAdded(ServletContettributeEevent event)监听对象加入 application 的范围时。
  • atributeReplaced(ServetContexttributeEvent event)监听在 application 的范围有对象取代另一个对象时。
  • atributeRemoved(erleCotettributcEvent event)监听对象从 application 的范围移除时。

3.HttpSessionListener:完成 Session 整体监听,主要有以下方法:

  • sessionCreated(HttpSessionEvent event)监听 session 初始化。
  • sessionDetoyed(HtpSessionEvent event)监听 session 销毁。

4.HttpSessionActivationListener:完成 Session 状态监听,主要有以下方法:

  • sessionDidActivate(HtpSessionEvent event)监听 session 变为有效状态。
  • sessionWillPassivae(HttpSessionEvent event)监听 session 变为无效状态。

5.HttpSessionAttributeListener:完成 Session 内容监听,主要有以下方法:

  • attributeAdded(HttpSessionBindingEvent event)监听对象加入 session 中。
  • attributeReplaced(HttpSessionBindingEvent event)监听 session 中有对象取代另一个对象时。
  • attributeRemoved(HttpSessionBindingEvent event)监听对象从 session 中移除时。

6.HttpSessionBindingListener:完成 Session 绑定监听,主要有以下方法:

  • valueBound(HttpSessionBindingEvent event)监听对象加入 session 中。
  • valueUnBound(HttpSessionBindingEvent event)监听对象从 session 中移除。

7.ServletRequestListener:完成 ServletRequest 监听,主要有以下方法:

  • requestInitalized(ServletRequestEvent event)监听 ServletRequest 已经被初始化。
  • requestDestroyed(ServletRequestEvent event)监听 ServletRequest 已经被销毁。

8.ServletRequestAttributeListener:完成 ServletRequest 内容监听,主要有以下方法:

  • attributeAdded(ServletRequestAttributeEvent event)监听对象加入 request 中。
  • attributeReplaced(ServletRequestAttributeEvent event)监听 request 中有对象取代另一个对象。
  • attributeRemoved(ServletRequestAttributeEvent event)监听对象从 request 中移除时。

监听能够有效的向我们展示各个对象的创建与销毁,内容的变化等。使用时也和 Servlet 一样需要在 web.xml 中配置。

<listener>
    <listener-class>cn.hxzy.MyListener</listener-class>
</listener>

案例:servletContext(jsp 中的 application)全局域

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyServletContextListener implements ServletContextListener, ServletContextAttributeListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("应用初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("应用销毁");
    }

    @Override
    public void attributeAdded(ServletContextAttributeEvent event) {
        System.out.println("添加键:" + event.getName());
        System.out.println("添加值:" + event.getValue());
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent event) {
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent event) {
    }
}

案例:session 会话域

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyHttpSessionListener implements HttpSessionListener, HttpSessionAttributeListener {
    @Override
    public void sessionCreated(HttpSessionEvent event) {
        System.out.println("session 创建" + event.getSession().getId());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        System.out.println("session 销毁" + event.getSession().getId());
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        System.out.println("session 里面增加键值" + event.getSession().getId());
        System.out.println("session 里面键" + event.getName());
        System.out.println("session 里面值" + event.getValue());
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {
    }
}

案例:request 请求域

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class MyServletRequestListener implements ServletRequestListener, ServletRequestAttributeListener {
    @Override
    public void requestDestroyed(ServletRequestEvent event) {
        System.out.println("ServletRequest 销毁");
    }

    @Override
    public void requestInitialized(ServletRequestEvent event) {
        System.out.println("ServletRequest 初始化");
    }

    @Override
    public void attributeAdded(ServletRequestAttributeEvent event) {
        System.out.println("往 request 里面存数据,键:" + event.getName());
        System.out.println("往 request 里面存数据,值:" + event.getValue());
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent event) {
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent event) {
    }
}

案例:在线人数统计

@WebListener
public class SessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println(httpSessionEvent.getSession().getId());
        ServletContext context = httpSessionEvent.getSession().getServletContext();
        if (context.getAttribute("count") != null) {
            context.setAttribute("count", (int) context.getAttribute("count") + 1);
        }else {
            context.setAttribute("count",  1);
        }

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println(httpSessionEvent.getSession().getId());
        ServletContext context = httpSessionEvent.getSession().getServletContext();
        if (context.getAttribute("count") != null) {
            context.setAttribute("count", (int) context.getAttribute("count") - 1);
        }
    }
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
在线人数:${count}
<a href="logout">退出</a>
</body>
</html>

LogoutServlet

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getSession().invalidate();
    }
}

练习:

1.创建项目 demo7 并通过监听器实现一个在线用户的显示。用户登录时将用户名添加到 HttpSession 会话。登录后跳转到在线首页面,显示当前登录的用户名称、所有在线人的名称和个数。每个用户可以发送公开消息。

参考代码:

监听代码

@WebListener
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        if (event.getName().equals("userInfo")) {
            ServletContext context = event.getSession().getServletContext();
            Object list = context.getAttribute("userList");
            List userList;
            if (list != null) {
                userList = (List) list;
            } else {
                userList = new ArrayList();
            }
            userList.add(event.getValue());
            context.setAttribute("userList", userList);
        }
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {
    }
}

登录 Servlet

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       request.setCharacterEncoding("utf-8");
        String name = request.getParameter("name");
        if (name != null && name != "") {
            request.getSession().setAttribute("userInfo",name);
        }
        response.sendRedirect("index.jsp");
    }
}

发送消息 Servlet

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        String message = req.getParameter("message");
        ServletContext context = req.getSession().getServletContext();
        Object list = context.getAttribute("messageList");
        List userList;
        if (list != null) {
            userList = (List) list;
        } else {
            userList = new ArrayList();
        }
        userList.add(message);
        context.setAttribute("messageList", userList);
        resp.sendRedirect("index.jsp");
    }
}

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<form action="login" method="post">
    <input name="name">
    <input value="提交" type="submit">
</form>
</body>
</html>

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%
    if (session.getAttribute("userInfo") == null) {
        response.sendRedirect("login.jsp");
    }
%>
在线用户:${userList}
<br>
聊天记录:${messageList}
<br>
<form method="post" action="message">
    <textarea name="message">

    </textarea>
    <input type="submit" value="发送">
</form>
</body>
</html>

八、注解

新增注解是 Servlet3.0 中的重大革新之一。通过使用注释就无须在 web.xml 文件中对 Servlet 或者过滤器进行配置。Servlet3.0 新增的注释有 @WebServlet、@WebFilter、@WebListener 和 @WebInitParam 等,下面分别进行介绍。

1.@WebServlet

注解定义在 Servlet 的类声明之前,用于定义 Servlet 组件。使用该注解,就无须在 web.xml 文件中对 Servlet 进行配置。@WebServlet 注解包含如下属性:

name(String)             等价于<servlet-name>,如果没有显式指定,则为类的全限定名
value(String[])          该属性等价于urlPatterm属性。两个属性不能同时使用
urlPatterns(String[])    等价于<urlpattern>标签
loadOnStartup(int)       指定Servlet的加载顺序,等价于<load-on-startup>标签
initParams(WebInitParam[])指定一组Servlet初始化参数,等价于<init-param>标签
asyncSupported(boolean)  声明Servlet是否支持异步操作模式,等价于<async suppored>标签
description(String)Servlet的描述信息,等价于<description>标签

2.@WebFilter

注解用于声明过滤器,该注解将会在部署时被容器处理,容器根据具体的属性配置将相应的类部署为过滤器。该注解也包含如下属性:

filterName(String)        指定过滤器的name属性,等价于<filter-name>
value(String[])           该属性等价于urlPatterns属性。但是两者不应该同时使用
urlPattemns(String[])     指定一组过滤器的URL匹配模式。等价于<url-patterm>标签
servletNames(String[])    指定过滤器将应用于哪些 Servlet
initParams(WebInitParam]) 指定一组过滤器初始化参数,等价于<init-param>标签
asyncSupported(Boolean)   声明过滤器是否支持异步操作模式,等价于<async-supported>标签
description(String)       该过滤器的描述信息,等价于<description>标签

3.@WebListener

该注释用于声明监听器,还可以用于充当给定 Web 应用上下文中各种 Web 应用事件的监听器的类。可以使用 @WebListener 来标注一个实现ServletContextListener、ServletContextAttributeListener、ServletRequestListener、ServletRequestAttributeListener、HttpSessionListener 和HttpSessionAttributeListener 的类。@WebListener 注释有一个 value 的属性,该属性为可选属性,用于描述监听器信息。使用该注释就不需要在 web.xml 文件中配置标签了。

4.@WebInitParam

该注解等价于 web.xml 文件中的和的子标签该注解通常不单独使用,而是配合 @WebServlet 或者 @WebFilter 使用。它的作用是为 Servlet 或者过滤器指定初始化参数。

@WebServlet(value = "/my",initParams = {@WebInitParam(name = "a",value = "1")})

5.@MultipartConfig

该注解主要是为了从 request 对象中获取 Part 文件对象。一般用于文件上传的 Servlet 上。

九、文件上传与下载

JSP 可以与 HTML form 标签一起使用,来允许用户上传文件到服务器。上传的文件可以是文本文件或图像文件或任何文档。

文件上传时 servlet 需要用注解 @MultipartConfig 修饰。同时前段表单需要指定 enctype=“multipart/form-data”。

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        Part file = request.getPart("file");
        if (file != null && file.getSize() != 0) {
            String fileName = file.getSubmittedFileName();
            System.out.println(fileName);
            file.write("E:/ideaProject/jsp/web/aa.png");
            response.getWriter().write("上传成功");
        }
    }
}

前端 jsp

<form action="upload" enctype="multipart/form-data" method="post">
    选择文件<input type="file" name="file1"/>
    <input type="submit" name="upload" value="上传"/>
</form>

关于文件上传的位置建议使用相对路径并将其挂载到 tomcat,否则上传文件无法预览。
Jsp与Servlet(二)之Servlet_第2张图片
文件下载

一般如果文件存放在 tomcat 中,则直接请求文件路径即可获取文件。所以本节文件是在 tomcat 之外,无法通过 tomcat 直接获取的。使用 Servlet 下载一般要指定响应的 ContentType,以及下载的文件名,代码如下:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

@WebServlet("/down")
public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("application/x-msdownload");
        //文件下载时必须设置响应头如上
        String fileName = "中文.txt";
        //文件下载时浏览器端的文件名
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        OutputStream outputStream = response.getOutputStream();
        InputStream inputStream = new FileInputStream("C:\\ProgramData\\DisplaySessionContainer1.log");
        byte[] bytes = new byte[1024];
        int read;
        while ((read = inputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, read);
        }
        outputStream.close();
        inputStream.close();
    }
}

练习:

1.创建项目 demo8 ,并完成文件上传功能。在实际开发中文件名都不可能与上传时相同,否则会导致文件覆盖问题,一般使用 UUID 命名文件,后缀保持不变。使用 jquery 完成文件上传回显,并使用 bootstrap 美化。

参考代码:

html 代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>图片上传回显示</title>
    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<input type="file" id="file">
<img>
<script>
    $("#file").change(function () {
        let formData = new FormData();  //得到一个FormData对象
        let file = $(this).get(0).files[0];  //得到file对象
        formData.append('file', file);  //用append方法添加键值对
        $.ajax({
            url: "upload",
            type: "post",
            data: formData,
            processData: false,
            contentType: false,
            success: function (response) {
                $("img").attr("src",response)
            }
        });
    });
</script>
</body>

Servlet 代码

@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        Part ap = request.getPart("file");
        if (null != ap) {
            String[] split = ap.getSubmittedFileName().split("\\.");
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            String fileName = uuid + "." + split[split.length - 1];
            ap.write("E://IdeaProjects/testweb/image/" + fileName);
            out.write("/image/" + fileName);
        }
    }
}

十、jsp 安全模式

在实际开发中,jsp 一般不能直接访问,因为其页面未携带数据。一张空的 jsp 意义并不大。通常都是请求对应的 servlet 获取数据,转发到对应的 jsp。

就算页面不需要数据也可以通过 servlet 转发。这样做的好处还有一个好处是请求地址不再包含 .jsp 后缀。这对隐藏后台框架也是一种很好的保护。

通常将 jsp 放入 WEB-INF 目录下即可保护 jsp 页面。访问 jsp 必须经过 servlet 转发才能访问。这样就可以在 servlet 中校验权限等。

jsp 安全模式开发守则:
  1.每个 jsp 对应一个 Servlet,如:index.jsp 对应 IndexServlet、login.jsp 对应 LoginServlet。
  2.任何重定向操作都不能重定向 jsp 页面,而是重定向 Servlet,也不能随意用转发代替重定向。
  3.编写系统的顺序为:jsp -> servlet ->filter,不能一开始就把 filter 写了拦截了所有请求导致页面或 Servlet 无法访问。
  4.不能在 jsp 页面编写大量的 java 代码,jsp 从本质上理解为展示层面的组件。

案例:

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("aa","数据");
        request.getRequestDispatcher("WEB-INF/jsp/index.jsp").forward(request,response);
    }
}

欢迎页配置:web.xml 中默认欢迎页为 index.html 和 index.jsp ,如果需要配置为 Servlet 地址也可以使用如下方式。在项目启动后或打开项目的根路径自动调用该 Servlet。

<welcome-file-list>
    <welcome-file>index</welcome-file>
</welcome-file-list>

章节练习:(默写 30 分钟)
1.使用 安全 jsp 开发用户登录、退出、显示当前登录界面。

参考代码:

IndexServlet:

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("WEB-INF/jsp/index.jsp").forward(req, resp);
    }
}

LoginServlet:

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        if ("admin".equals(name) && "123".equals(password)) {
            req.getSession().setAttribute("userInfo", name);
            resp.sendRedirect("index");
        } else {
            req.setAttribute("name", name);
            req.setAttribute("password", password);
            req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
        }
    }
}

LogoutServlet

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getSession().invalidate();
        resp.sendRedirect("index");
    }
}

MyFilter

@WebFilter("/*")
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String uri = request.getRequestURI();
        if (uri.endsWith("login")) {
            filterChain.doFilter(request, response);
            return;
        }
        if (request.getSession().getAttribute("userInfo") != null) {
            filterChain.doFilter(request, response);
            return;
        }
        response.sendRedirect("login");

    }

    @Override
    public void destroy() {

    }
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>欢迎${userInfo}登录</h1>
<a href="logout">退出</a>
</body>
</html>

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form method="post" action="login">
    <input name="name" value="${name}">
    <input name="password" value="${password}">
    <input type="submit" value="提交">
</form>
</body>
</html>

你可能感兴趣的:(Jsp与Servlet,Jsp与Servlet,jsp,servlet)