jsp_jsplumb - CSDN
jsp 订阅
JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。JSP将Java代码和特定变动内容嵌入到静态的页面中,实现以静态页面为模板,动态生成其中的部分内容。JSP引入了被称为“JSP动作”的XML标签,用来调用内建功能。另外,可以创建JSP标签库,然后像使用标准HTML或XML标签一样使用它们。标签库能增强功能和服务器性能,而且不受跨平台问题的限制。JSP文件在运行时会被其编译器转换成更原始的Servlet代码。JSP编译器可以把JSP文件编译成用Java代码写的Servlet,然后再由Java编译器来编译成能快速执行的二进制机器码,也可以直接编译成二进制码。 展开全文
JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。JSP将Java代码和特定变动内容嵌入到静态的页面中,实现以静态页面为模板,动态生成其中的部分内容。JSP引入了被称为“JSP动作”的XML标签,用来调用内建功能。另外,可以创建JSP标签库,然后像使用标准HTML或XML标签一样使用它们。标签库能增强功能和服务器性能,而且不受跨平台问题的限制。JSP文件在运行时会被其编译器转换成更原始的Servlet代码。JSP编译器可以把JSP文件编译成用Java代码写的Servlet,然后再由Java编译器来编译成能快速执行的二进制机器码,也可以直接编译成二进制码。
信息
外文名
Java Server Pages
本    质
JSP
中文名
JAVA服务器页面
JSP特点
收起全文
精华内容
参与话题
  • 学会jsp环境的安装与配置,jsp基础,jsp的9大内置对象应用,servlet技术,几种乱码处理方法;javabean,数据库操作,连接池,自定义标签,标准标签库,监听器和过滤器,文件上传,分页技术,MVC开发模式应用,投票...
  • Jsp&Servlet学习笔记(一)

    千次阅读 多人点赞 2020-08-27 11:57:15
    Jsp&Servlet学习笔记(一) 第一章 问候 Jsp&Servlet 他大爷 第一节:Jsp 概述 第二节:B/S vs C/S B/S系统是 浏览器/服务器(瘦客户端); C/S系统是 客户端/服务器(胖客户端)。 第三节:...

    Jsp&Servlet学习笔记(一)

     

    第一章 问候 Jsp&Servlet 他大爷

    第一节:Jsp 概述

     

    第二节:B/S vs C/S

    B/S系统是 浏览器/服务器(瘦客户端); C/S系统是 客户端/服务器(胖客户端)。

     

    第三节:Tomcat 服务器

    简介:百度百科。

    安装及配置:百度。

    目录结构:

     

    第五节:问候他大爷

    第一个Jsp程序:

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

        pageEncoding="UTF-8"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

    <html>

    <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <title>问候Jsp他大爷</title>

    </head>

    <body>

    问候Jsp他大爷

    </body>

    </html>

    (在Tomcat服务器上运行,具体实现略)

     

    第二章 Jsp 基础语法

    第一节:page 指令介绍

    Language : 用来定义要使用的脚本语言;

    contentType:定义 JSP 字符的编码和页面响应的 MIME 类型;

    pageEncoding:Jsp 页面的字符编码

     

     

    第二节:scriptlet 标签

    通过 scriptlet 标签我们可以在 Jsp 里嵌入 Java 代码;

     

    脚本程序:可以包含任意量的Java语句、变量、方法或表达式。

    语法格式:<% 代码片段 %>

     

    JSP声明:一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。

    语法格式:<%! declaration; [ declaration; ]+ ... %>

    JSP表达式:一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方。

    语法格式:<%= 表达式 %>

     

    第三节:Jsp 注释

    <!-- --> Html 注释 客户端源码可见

    <%-- --%> Jsp 注释 客户端源码不可见

    // java 单行注释

    /* */ java 多行注释

     

     

    第四节:Jsp 包含指令

    <%@ include file=”要包含的文件”%> 静态包含:先包含,后编译处理;

    <h1>静态包含</h1>

    <%@ include file="common/head.jsp" %>

    <p>content</p>

    <%@ include file="common/foot.jsp" %>

    <jsp:include page=”要包含的文件”/> 动态包含:先编译处理,后包含;

    <h1>动态包含</h1>

    <jsp:include page="common/head.jsp"/>

    <p>content</p>

    <jsp:include page="common/foot.jsp"/>

    (建议开发用动态包含。)

     

    第五节:Jsp 跳转指令

    <jsp:forward page=” ”>

    <jsp:param value=”” name=””>

    </jsp:forward>

    服务器内部跳转,可带参数;

     

    <title>Jsp 跳转指令</title>

    </head>

    <body>

    <jsp:forward page="target.jsp">

        <jsp:param value="huang" name="userName"/>

        <jsp:param value="123" name="password"/>

    </jsp:forward>

    </body>

    -----------------------------------------------------------

    <title>Jsp 跳转后页面target</title>

    </head>

    <body>

    服务器内部跳转后的页面<br>

    userName:<%=request.getParameter("userName") %><br>

    password:<%=request.getParameter("password") %><br>

    </body>

    第三章 JSP 九大内置对象及四个作用域

     

    第一节:Jsp 九大内置对象及四大作用域概述

    在Jsp开发中,Jsp提供了9个内置对象,这些内置对象将由容器为用户进行实例化,用户直接使用即可。

    这9个内置对象分别是:

    pageContext,request,response,session,application,config,out,page,exception;常用的是前面 5个,需要熟练掌握。

    在Jsp开发中,可以保存数据,Jsp提供了四种数据保存范围;分别是 page,request,session,application。

     

     

    第二节:Jsp 四大作用域

    Page 范围:

    只在一个页面中保存数据; javax.servlet.jsp.PageContext(抽象类)

    <body>

    <%

        // 设置两个page范围的数据 key->value

        pageContext.setAttribute("name","page王二小");

        pageContext.setAttribute("age",12);

    %>

    <%

        // 取值

        String name=(String)pageContext.getAttribute("name");

        int age=(Integer)pageContext.getAttribute("age");

    %>

    <font>姓名:<%=name %></font>

    <font>年龄:<%=age %></font>

     

    Request 范围:

    只在一个请求中保存数据; javax.servlet.http.HttpServletRequest(接口)

    RequestScope.jsp

    <body>

    <%

        // 设置两个request范围的数据 key-> value

        request.setAttribute("name","request王二小");

        request.setAttribute("age",12);

    %>

    <jsp:forward page="requestTarget.jsp"></jsp:forward>

    </body>

    RequestTarget.jsp

    <body>

    <%

        // 取值

        String name=(String)request.getAttribute("name");

        int age=(Integer)request.getAttribute("age");

    %>

    <font>姓名:<%=name %></font>

    <font>年龄:<%=age %></font>

    </body>

     

    Session 范围:

    在一次会话范围中保存数据,仅供单个用户使用;

    javax.servlet.http.HttpSession(接口)

    sessionScope.jsp

    <body>

    <%

        // 设置两个session范围的数据 key-> value

        session.setAttribute("name","session王二小");

        session.setAttribute("age",12);

    %>

    <h1>session值设置完毕!</h1>

    </body>

    sessionTarget.jsp

    <%

        // 取值

        String name=(String)session.getAttribute("name");

        int age=(Integer)session.getAttribute("age");

    %>

    <font>姓名:<%=name %></font>

    <font>年龄:<%=age %></font>

     

    Application 范围:

    在整个服务器上保存数据,所有用户共享;javax.servlet.ServletContext(接口)

    applicationScope.jsp

    <body>

    <%

        // 设置两个application范围的数据 key-> value

        application.setAttribute("name","application王二小");

        application.setAttribute("age",12);

    %>

    <h1>application值设置完毕!</h1>

    </body>

     

    applicationTarget.jsp

    <body>

    <%

        // 取值

        String name=(String)application.getAttribute("name");

        int age=(Integer)application.getAttribute("age");

    %>

    <font>姓名:<%=name %></font>

    <font>年龄:<%=age %></font>

    </body>

     

     

    第三节:response 对象

    response内置对象和request内置对象是相对应的,response内置对象用于响应客户请求,向客户端输出信息;javax.servlet.HttpServletResponse 接口。

    1. 自动刷新应用

    <body>

    <%

        // 每隔一秒刷新一次页面

        response.setHeader("refresh","1");

        // 获取当前时间

        Date myDate=new Date();

    %>

    当前时间:<%= myDate.toLocaleString() %>

    </body>

    2,页面重定向应用客户端跳转

    <%

        // 重定向,客户端跳转,网址会变

        response.sendRedirect("index.html");

    %>

    3,操作cookie应用  post、get方法比较:post放数据包里;get放Url后面,get数据量小,不安全;

    //response03.jsp

    <title>登录页面</title>

    <script type="text/javascript">

        function resetValue(){

           document.getElementById("userName").value="";

           document.getElementById("pwd").value="";

        }

    </script>

    <%

        String userName=null;

        String pwd=null;

        Cookie[] cookies=request.getCookies();

        for(int i=0;cookies!=null &&i<cookies.length;i++){

           if(cookies[i].getName().equals("userNameAndPwd")){

               userName=cookies[i].getValue().split("-")[0];

               pwd=cookies[i].getValue().split("-")[1];

           }

        }

       

        if(userName==null){

           userName="";

        }

       

        if(pwd==null){

           pwd="";

        }

    %>

    </head>

    <body>

    <form action="userLogin.jsp" method="post">

        <table>

           <tr>

               <td>用户名:</td>

               <td><input type="text" id="userName" name="userName" value="<%=userName%>"/></td>

           </tr>

           <tr>

               <td>密码:</td>

               <td><input type="password" id="pwd" name="pwd" value="<%=pwd %>" /></td>

           </tr>

           <tr>

               <td>记住密码:</td>

               <td><input type="checkbox" id="remember" name="remember" value="remember-me"/></td>

           </tr>

           <tr>

               <td><input type="submit" value="登录"/></td>

               <td><input type="button" value="重置" onclick="resetValue()"/></td>

           </tr>

        </table>

    </form>

    </body>

    //userLogin.jsp

    <body>

    <%

        String userName=request.getParameter("userName");  // 获取用户名

        String pwd=request.getParameter("pwd");  // 获取密码

        String remember=request.getParameter("remember");  // 获取记住密码

       

        if("remember-me".equals(remember)){

           Cookie userNameAndPwd=new Cookie("userNameAndPwd",userName+"-"+pwd);

           userNameAndPwd.setMaxAge(1*60*60*24*7); // cookie记录一个星期

           response.addCookie(userNameAndPwd);  // 保存cookie

           System.out.println("设置Cookie成功");

        }

        // response.sendRedirect("response03.jsp");

    %>

    登录成功

    </body>

    4,cookie和session的比较:cookie信息是存客户端的,session 信息是存服务器的;

     

    第四节:out 对象

    Out 内置对象主要用来向客户端输出各种类型的数据,同时还可以管理应用服务器上的输出缓冲区。所以out内置对象的方法是向客户端输出数据和管理缓冲区; 底层javax.servlet.jsp.JspWriter 抽象。

    <body>

    <%

        int totalbuffer=out.getBufferSize();  // 获取总缓冲区的大小

        int available=out.getRemaining(); // 获取未使用的缓冲区的大小

        int user=totalbuffer-available;  // 获取使用的缓冲区大小

        out.println("总共缓冲区的大小:"+totalbuffer);

        out.println("未使用的缓冲区的大小:"+available);

        out.println("使用的缓冲区大小:"+user);

    %>

    </body>

     

    第五节:config 对象

    config 内置对象是JSP页面通过JSP容器进行初始化时被传递的对象。javax.servlet.ServletConfig 。

    在Servlet初始化的时候,JSP引擎通过config 向它传递信息。这种信息可以是属性名和属性值匹配的参数,也可以是通过ServletContext对象传递的服务器的有关信息。

    //web.xml

    <servlet>

        <servlet-name>init</servlet-name>

        <jsp-file>/sysInit.jsp</jsp-file>

        <init-param>

           <param-name>jdbcName</param-name>

           <param-value>com.mysql.jdbc.Driver</param-value>

        </init-param>

        <init-param>

           <param-name>dbUrl</param-name>

           <param-value>jdbc:mysql://localhost:3306/db_xx</param-value>

        </init-param>

      </servlet>

     

      <servlet-mapping>

        <servlet-name>init</servlet-name>

        <url-pattern>/init</url-pattern>

      </servlet-mapping>

     

    //sysInit.jsp

    <body>

    <%

        String jdbcName=config.getInitParameter("jdbcName");

        String dbUrl=config.getInitParameter("dbUrl");

    %>

    <h1>驱动名称:<%=jdbcName %></h1>

    <h1>连接地址:<%=dbUrl %></h1>

    </body>

     

    请求地址:http://localhost:8081/JspServlet/init

     

    第六节:exception 对象

    exception 内置对象用来处理JSP文件在执行时发生的所有异常,它是java.lang.Throwable 类的一个对象。

    //exception01.jsp

    <%@ page errorPage="error.jsp"%>

    ...

    <body>

    <%

        int a=1;

        int b=0;

        out.println(a/b);

    %>

    </body>

     

    //error.jsp

    <%@ page isErrorPage="true"%>

    ...

    <body>

    <%

        if(exception!=null){

           out.println("程序错误信息:");

           out.println(exception.getMessage());

        }

    %>

    </body>

    第七节:pageContext 对象

    pageContext 内置对象是一个比较特殊的对象。它相当于页面中所有对象功能的集合,即使用它可以访问到本页面中所有对象。pageContext 内置对象由Jsp 容器创建并初始化,pageContext 对象提供了对JSP 页面所有对象及控件的访问。

    <body>

    <%

        pageContext.setAttribute("name0", "pageInfo");

        request.setAttribute("name1", "requestInfo");

        session.setAttribute("name2", "sessionInfo");

        application.setAttribute("name3", "applicationInfo");

       

        out.println("使用pageContext<br/>");

        out.println("page中的属性值:"+pageContext.getAttribute("name0")+"<br/>");

        out.println("request中的属性值:"+pageContext.getRequest().getAttribute("name1")+"<br/>");

        out.println("session中的属性值:"+pageContext.getSession().getAttribute("name2")+"<br/>");

        out.println("application中的属性值:"+pageContext.getServletContext().getAttribute("name3")+"<br/>");

    %>

    </body>

     

    第四章 JavaBean组件

    第一节:Javabean 组件引入

    JavaBean 是使用Java 语言开发的一个可重用的组件,在JSP 开发中可以使用JavaBean 减少重复代码,使整个JSP 代码的开发更简洁。

     

    第二节:jsp:useBean 创建javabean

    <jsp:useBean id="实例化对象名称" scope="保存范围" class="类完整名称"/>

    Scope一共有page,request,session和application 4个属性范围,默认是page;

    <jsp:useBean id="student" scope="page" class="com.java1234.model.Student"/>

    <%

        student.setName("王二小2");

        student.setAge(12);

    %>

    <h1>姓名:<%=student.getName() %></h1>

    <h1>年龄:<%=student.getAge() %></h1>

    </body>

    第三节:jsp:setProperty 设置javabean 属性值

    <jsp:setProperty property="属性名称" name="实例化对象的名称" value="属性值" param="参数名称"/>

    当 property=”*” 时,自动匹配所有属性。

    <body>

    <% request.setCharacterEncoding("utf-8"); %>

    <jsp:useBean id="student" scope="page" class="com.java1234.model.Student"/>

    <jsp:setProperty property="*" name="student"/>   <!--  自动匹配所有属性 -->

    <h1>姓名:<%=student.getName() %></h1>

    <h1>年龄:<%=student.getAge() %></h1>

    </body>

    当属性名不匹配时,使用param属性

    <body>

    <% request.setCharacterEncoding("utf-8"); %>

    <jsp:useBean id="student" scope="page" class="com.java1234.model.Student"/>

    <jsp:setProperty property="name" name="student" param="userName"/>

    <jsp:setProperty property="age" name="student" value="100"/>

    <h1>姓名:<%=student.getName() %></h1>

    <h1>年龄:<%=student.getAge() %></h1>

    </body>

     

    第四节:jsp:getProperty 获取javabean 属性值

    <jsp:getProperty property="属性名称" name="实例化对象的名称"/>

    <body>

    <jsp:useBean id="student" scope="page" class="com.java1234.model.Student"/>

    <%

        student.setName("王二小2");

        student.setAge(12);

    %>

    <h1>姓名:<jsp:getProperty property="name" name="student"/></h1>

    <h1>年龄:<jsp:getProperty property="age" name="student"/></h1>

    </body>

     

    第五节:javabean 的保存范围

    Javabean 的保存范围有page,request,session.application,默认是page;

    举例request范围:

    <body>

    <jsp:useBean id="student" scope="request" class="com.java1234.model.Student"/>

    <jsp:setProperty property="name" name="student" value="王八蛋"/>

    <jsp:setProperty property="age" name="student" value="12"/>

    <jsp:forward page="target01.jsp"/>

    </body>

    //target01.jsp

    <body>

    <jsp:useBean id="student" scope="request" class="com.java1234.model.Student"/>

    <h1>姓名:<jsp:getProperty property="name" name="student"/></h1>

    <h1>年龄:<jsp:getProperty property="age" name="student"/></h1>

    </body>

    第六节:javabean 删除

    page 范围:pageContext.removeAttribute(“javaBean Name”);

    request 范围:request.removeAttribute(“javaBean Name”);

    session 范围:session.removeAttribute(“javaBean Name”);

    application 范围:application.removeAttribute(“javaBean Name”);

    <body>

    <jsp:useBean id="student" scope="session" class="com.java1234.model.Student"/>

    <jsp:setProperty property="name" name="student" value="王八蛋"/>

    <jsp:setProperty property="age" name="student" value="12"/>

    <h1>Session数据设置完毕!</h1>

    </body>

    //javabeanDelete.jsp

    <body>

    <% session.removeAttribute("student"); %>

    <h1>javabean已删除!</h1>

    </body>

     

    第五章Servlet 开发

    第一节:问候servlet 他大爷

    Web.xml配置servlet名称、映射等,此处略。

    package com.java1234.web;

     

    import java.io.IOException;

    import java.io.PrintWriter;

     

    import javax.servlet.ServletException;

    import javax.servlet.http.HttpServlet;

    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpServletResponse;

     

    public class HelloWorldServlet extends HttpServlet{

     

        /**

         *

         */

        private static final long serialVersionUID = 1L;

     

        @Override

        protected void doGet(HttpServletRequest request, HttpServletResponse response)

               throws ServletException, IOException {

           System.out.println("get");

           this.doPost(request, response);

        }

     

        @Override

        protected void doPost(HttpServletRequest request, HttpServletResponse response)

               throws ServletException, IOException {

           response.setCharacterEncoding("gbk");

           PrintWriter out=response.getWriter();

           out.println("<html>");

           out.println("<head><title>问候大爷</title></head>");

           out.println("Servlet大爷你好!");

           out.println("</html>");

           out.close();

        }

     

    }

    第二节:servlet 生命周期

    Servlet 的生命周期,简单的概括这就分为四步:servlet 类加载--->实例化--->服务--->销毁。

     

    第三节:客户端跳转VS 服务器端跳转

    1,在Servlet 中获取session,application

    2,客户端跳转response.sendRedirect("目标地址");

    //web.xml配置同上

        @Override

        protected void doPost(HttpServletRequest request, HttpServletResponse response)

               throws ServletException, IOException {

           request.setAttribute("requestKey", "request");

           HttpSession session=request.getSession();  // 获取session

           session.setAttribute("sessionKey", "session");

           ServletContext application=this.getServletContext(); // 获取application

           application.setAttribute("applicationKey", "application");

           response.sendRedirect("target.jsp"); // 客户端跳转/重定向

        }

    //target.jsp

    <body>

    <h1>目标地址</h1>

    request值:<%=request.getAttribute("requestKey") %><br/>

    session值:<%=session.getAttribute("sessionKey") %><br/>

    application值:<%=application.getAttribute("applicationKey") %><br/>

    </body>

    (sendRedirect重定向不携带request参数)

    3,服务器跳转:RequestDispatcher rd=request.getRequestDispatcher("目标地址"); rd.forward(request, response);

        @Override

        protected void doPost(HttpServletRequest request, HttpServletResponse response)

               throws ServletException, IOException {

           request.setAttribute("requestKey", "request");

           HttpSession session=request.getSession();  // 获取session

           session.setAttribute("sessionKey", "session");

           ServletContext application=this.getServletContext(); // 获取application

           application.setAttribute("applicationKey", "application");

            RequestDispatcher rd=request.getRequestDispatcher("target.jsp");

           rd.forward(request, response); // 服务器跳转/转发

        }

    //target.jsp

    <body>

    <h1>目标地址</h1>

    request值:<%=request.getAttribute("requestKey") %><br/>

    session值:<%=session.getAttribute("sessionKey") %><br/>

    application值:<%=application.getAttribute("applicationKey") %><br/>

    </body>

    (携带request参数)

    第四节:Jsp&Servlet 用户登录功能实现

    代码太多…

    第五节:Servlet 过滤器

    过滤用户(非法)请求

    Web,xml配置filter,此处略。下面贴核心代码:

    public class LoginFilter implements Filter {

     

        @Override

        public void destroy() {

           // TODO Auto-generated method stub

          

        }

     

        @Override

        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)

               throws IOException, ServletException {

           // TODO Auto-generated method stub

           HttpServletRequest request=(HttpServletRequest)servletRequest;

           HttpSession session=request.getSession();

           Object o=session.getAttribute("currentUser");

           String path=request.getServletPath();  //获取请求路径

           if(o==null&&path.indexOf("login")<0){  // 排除登录请求的过滤

               request.getRequestDispatcher("login.jsp").forward(servletRequest, servletResponse);

           }else{

               filterChain.doFilter(servletRequest, servletResponse);

           }

        }

     

        @Override

        public void init(FilterConfig arg0) throws ServletException {

           // TODO Auto-generated method stub

          

        }

    }

     

    第六节:Servlet 监听器

    监听web 事件;如application,session,request

    示例(session)

    Web.xml配置

    <listener>

      <listener-class>com.java1234.listener.SessionAttributeListener</listener-class>

    </listener>

     

    // SessionAttributeListener.java

    package com.java1234.listener;

     

    import javax.servlet.http.HttpSessionAttributeListener;

    import javax.servlet.http.HttpSessionBindingEvent;

     

    public class SessionAttributeListener implements HttpSessionAttributeListener{

     

        public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {

           // TODO Auto-generated method stub

           System.out.println("添加的属性名:"+httpSessionBindingEvent.getName()+",属性值:"+httpSessionBindingEvent.getValue());

        }

     

        public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {

           // TODO Auto-generated method stub

           System.out.println("删除的属性名:"+httpSessionBindingEvent.getName()+",属性值:"+httpSessionBindingEvent.getValue());

        }

     

        public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

           // TODO Auto-generated method stub

          

        }

    }

     

    展开全文
  • JSP介绍(一)

    万次阅读 多人点赞 2018-09-05 07:17:43
    一、JSP是什么,它有什么作用? Java Server Page,它是用于展示信息操作。 为了servlet展示信息的不方便,引入了JSPJSP本质上也是一个servlet!!!!!! JSP就是在HTML页面中嵌入了java代码。 二、JSP...

    一、JSP是什么,它有什么作用?

    Java Server Page,它是用于展示信息操作。

    为了servlet展示信息的不方便,引入了JSP。

    JSP本质上也是一个servlet!!!!!!

    JSP就是在HTML页面中嵌入了java代码。

    二、JSP原理

    在Tomcat中的web.xml中,有一个默认的servlet,处理不可以处理的请求,还有一个JSPservlet,又叫servlet引擎,所有后缀为.jsp的文件都处理。

    当浏览器访问http://localhost:8080/day9_1/index.jsp。服务器发现后缀为.jsp,它会根据路径找到index.jsp文件,会将index.jsp翻译成index_jsp.java文件,对这个java文件进行编译,产生一个index_jsp.class文件,将class文件加载运行。将JSP翻译成java文件,它是将JSP中的所有的HTML代码通过流进行输出,也就是说最终翻译成class,被虚拟机加载,它本质是servlet,它就会往回响应,响应回去就是把JSP中的HTML代码以流的方式写回浏览器。所以在JSP中展示出了HTML代码。

    当请求过来,这是第一步,到服务器端,服务器端处理了,找到hello.jsp,服务器怎么处理的,就是在Tomcat中的web.xml配置了一个JSP servlet引擎,根据后缀名,处理了,处理之后会找到这个JSP,然后对这个JSP做一个翻译的过程,把它翻译成了一个java文件,这个java文件会进行编译,编译以后变成class,而后被加载,加载后就执行,就把原来JSP里面的代码,即HTML代码以流的方式写回浏览器。

    work --->  catalina -----> localhost ------> day9_1 ------> index_JSP.java

    三、怎么在JSP中书写java代码

    标签 语法 用途
    声明标签 <%! 变量或方法声明%> 声明JSP内所使用的全局变量或方法或内部类(它用于定义成员)(写的内容在成员位置上)
    表达式 <%= 表达式 %> 输出java中变量或者表达式的值到页面上(它用于输出) out.print(内容)
    程序代码标签 <%程序代码%> 编写java程序代码,但不可以定义代码(它用于定义局部)(声明的变量在局部位置_jspService方法中)
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <%! int i = 10; %> <!-- 成员变量 -->
    <%! public void show(){} %> <!-- 成员方法 -->
    <% int i = 100; %>
    <%= "hello" %>
    </body>
    </html>

    注意:<%! %>中的内容,作为类的成员变量和成员函数,写在类的成员位置。

    <% %>中的内容,写在_jspService函数中。

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <%! 
    	int i = 10;
    %> <!-- 成员变量 -->
    <%! 
    	public void show(){} 
    %> <!-- 成员方法 -->
    <% int i = 100; %>
    输出函数中的i:<%=i%>
    输出成员变量i:<%=this.i %>
    </body>
    </html>

    上面声明,下面可以直接用,因为被编译到一个类中,知道被编译的位置,就可以直接正确使用。

    如果把一个数据存在request域中(因为_jspService方法参数就是request和response,所以可以直接用),取值操作如下:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <%request.setAttribute("name","tom"); %>
    <%=request.getAttribute("name")==null ? "" : request.getAttribute("name") %><br>
    ${requestScope.name}
    </body>
    </html>

    java代码和JSP代码一起使用,在开发中不要这样写,不便于维护

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <%
    	for(int i = 0; i < 10; i++){
    		%>
    		hello&nbsp;<%=i %><br>
    		<%
    	}
    %>
    </body>
    </html>
    <%
    String path=request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%
    String path=request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
    %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <%=path %><br>
    <%=basePath %>
    <form action="<%=path %>/login">
    	<input type="submit">
    </form>
    <form action="<%=basePath%>login">
    	<input type="submit">
    </form>
    </body>
    </html>

    建议使用<%=path%>/login

     

    展开全文
  • JSP的基本使用

    万次阅读 多人点赞 2018-09-03 18:25:00
    今天给大家分享一下,修真院官网JAVA任务二,扩展思考中的知识点——JSP基本知识 一、背景介绍   JSP(Java Server Pages),其根本是一个简化的Servlet设计,它实现了在Java中使用HTML标签。JSP是一种动态网页技术...

    大家好,我是IT修真院郑州分院第十期学员,一枚正直纯洁善良的JAVA程序员。

    今天给大家分享一下,修真院官网JAVA任务二,扩展思考中的知识点——JSP基本知识

    一、背景介绍  

           JSP(Java Server Pages),其根本是一个简化的Servlet设计,它实现了在Java中使用HTML标签。JSP是一种动态网页技术标准,也是JavaEE的标准。JSP和Servlet一样,是在服务器端执行的。JSP是在Servlet技术发展之后为了让开发者写html标签更方便而发展起来的技术,JSP实际上就是Servlet。

           人们通常把Servlet作为Web应用中的控制组件来使用,只负责响应请求产生数据,并把数据通过转发技术带给jsp,而把jsp技术作为数据显示模板来使用。这样使程序结构更清晰,可读性和可维护性更高。

    二、知识剖析(JSP页面元素构成)

          1.JSP页面的三大指令

         (1)<%@ page ... %>    定义网页依赖属性,比如脚本语言、error页面、缓存需求等等;

    <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"  import="java.util.Date"  import="java.text.SimpleDateFormat"%>

           page指令一般在表头位置,用来设置本页面的语法,编码格式,导包等信息,page指令可以合并在一行,也可以分为多行,这个可以根据自己的需求编写。

         (2)<%@ include ... %>    包含其他文件(静态包含);

           有两个页面一个是index.jsp(左)页面,一个是include.jsp(右)页面,在同级目录,如下所示;

          初始页面:

             

          显示:

             

          在index.jsp中加入include指令:

          

          显示:

          

         (3)taglib指令:引入标签库的定义。

          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
           prefix:指定标签前缀,这个东西可以随意起名
           uri:指定第三方标签库的uri(唯一标识)

          2.表达式

          在JSP页面中执行的表达式 <%=表达式%>,注意表达式不以分号结束。例如,当前时间:<%= new java.util.Date() %>;

          在页面输出指定格式的当前时间:

    <%=new SimpleDateFormat("yy年MM月dd日 HH:mm:ss").format(new Date())%>

     

          3.脚本片段

          在JSP页面中插入多行java代码 <% Java代码 %>,JSP引擎在翻译JSP页面时,会将JSP脚本片段中的Java代码原封不动的放到Servlet的_jspServlet方法中,所以,<% %>中的Java代码必须严格遵循java语法。

        脚本即 Scriptlet , 也就是 JSP 中的代码部分 , 是一段 Java 代码 , 几乎可以使用任何 Java 语法 , 它是在请求时期执行的 , 可以使用 JSP 页面所定义的变量、方法、表达式或 JavaBean . 脚本定义的变量和方法在当前整个页面内有效 , 但不会被其他线程共享 , 用户对该变量的作用不会影响其他用户 , 当变量所在页面关闭时 , 该变量会被销毁

      语法格式如下 :

      <%scriptlet%>

    <%! String str ="为中华之崛起而读书";%>
    <%
        if(true){
    %>
            <%=str%>
    <%
        }
    %>

          4.注释(3种方式)

         (1) HTML注释:<!--html注释,且客户端可见-->;

         (2)JSP注释:<%--JSP注释,客户端不可见--%>;

         (3)JSP脚本注释:即java注释 //单行 , /*多行 */。

          5.声明:

          在JSP页面中,可以声明一个或者多个合法的变量和方法 , 声明后的变量和方法可以在本 JSP 页面的任何位置使用 , 并将在 JSP 页面初始化时被初始化。

     语法格式如下 :

      <!%declaration; [declaration;] ...%>

         在声明时需要注意 :

    1. 声明以 "<%!" 开头 , 以 "%>" 结尾 , "%!" 必须紧挨
    2. 变量和方法的命名规则应与 Java 的命名规则相同
    3. 可以直接使用在 "<@page %>" 中被包含进来的已经声明的变量和方法 , 不需要对其进行重新声明
    4. 一个声明仅在一个页面有效 , 如果想每个页面都用到一些声明 , 可以将它们写成一个单独的文件 , 用 "<%@ include%>" 指令 或 "<jsp:include>" 动作包含进来

    三、常见问题

          html和jsp的区别(见扩展思考)

    四、编码实战(见知识剖析)

    五、扩展思考

          html和jsp的区别:

          HTML(Hypertext Markup Language)文本标记语言,它是静态页面,和JavaScript一样解释性语言,为什么说是解释性语言呢?因为,只要你有一个浏览器那么它就可以正常显示出来,而不需要指定的编译工具,只需在TXT文档中写上HTML标记就OK。

          JSP(Java Server Page)是Java服务端的页面,所以它是动态的,它是需要经过JDK编译后把内容发给客户端去显示,我们都知道,Java文件编译后会产生一个class文件,最终执行的就是这个class文件,JSP也一样,它也要编译成class文件!JSP不止要编译,它还得要转译,首先把JSP转译成一个Servlet文件,然后在编译成class文件。当用户访问JSP时就执行了class文件

          1.HTML能直接打开,jsp只能发布到Tomact等服务器上才能打开;

          2.定义上HTML页面是静态页面可以直接运行,JSP页面是动态页它运行时需要转换成servlet;

          3.他们的表头不同,这个是JSP的头“ <%@ page language="java" import="java.util.*" pageEncoding="gbk"%>”在表头中有编码格式和导入包等;

          4.在jsp中用<%%>就可以写Java代码了,而html没有

     六、参考文献

          本文主要摘自https://www.cnblogs.com/rocomp/p/4822419.htmlhttps://www.cnblogs.com/yuanmiemie/p/6196906.html

    七、更多讨论

         1.jsp页面如何设置没有缓存?

        (1)清空tomcat缓存。

           server->tomcat->clean tomcat work directory.

        (2)设置页面Nocache方式,即每次访问此页面,均需要从服务器重新读取,而不是使用缓存中存在的此页面。

          在jsp页面的<head></head>中间添加如下代码。

    <meta http-equiv="pragma" content="no-cache"> 
    
    <meta http-equiv="cache-control" content="no-cache"> 
    
    <meta http-equiv="expires" content="0">   

        2.jsp怎么去关掉session?

          session.removeAttribute("username"); //注销session中的username对象
          session.removeAttribute("id"); //注销session中的id对象
          session.invalidate(); //关闭session

        3.jsp的内置对象有哪几个?

          JSP中一共预先定义了9个这样的对象,分别为:request、response、session、application、out、pagecontext、config、page、exception

         (1)request对象

          request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求。

         (2)response对象

          response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。

          (3)session对象

           session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。

          (4)application对象

           application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。

         (5)out 对象

          out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。

         (6)pageContext 对象

          pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等 对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。

          (7)config 对象

          config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。

          (8)page 对象

          page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。

          (9)exception 对象

           exception 对象的作用是显示异常信息,只有在包含 isErrorPage="true" 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。

    八、视频教程

    https://v.qq.com/x/page/f0761gtmhhp.html

    快点加入我们吧:http://www.jnshu.com/login/1/23284132

    展开全文
  • JSP开发环境的搭建,工具的使用,基础语法、变量、常量、数据类型、运算符、if语句、while语句、表单数据的获取、在线编辑器的使用、上传的使用等 开发工具:intellij idea 2020 开发环境:jdk1.8. + tomcat-8.5
  •   JSP的本质就是Servlet,而Web容器会将JSP编译成对应的Servlet。自MVC规范出现后,Servlet的责任开始明确下来,仅仅作为控制器使用,不再需要生成页面标签,也不再作为视图层角色使用。 1.1 Servlet的开发   ...

    一、Servlet

      JSP的本质就是Servlet,而Web容器会将JSP编译成对应的Servlet。自MVC规范出现后,Servlet的责任开始明确下来,仅仅作为控制器使用,不再需要生成页面标签,也不再作为视图层角色使用。

    1.1 Servlet的开发

      Servlet是运行在服务器端的程序,用于处理及响应客户端的请求。Servlet是个特殊的Java类必须继承HttpServlet,每个Servlet可以响应客户端的请求。Servlet提供不同的方法用于响应客户端请求:

    • doGet:用于响应客户端的GET请求
    • doPost:用于响应客户端的POST请求
    • doPut&doDelete:基本不用

      事实上,客户端的请求通常只有GET和POST两种,Servlet为了响应这两种请求,必须重写doGet()和doPost()两个方法。

      大部分时候,Servlet对于所有请求的响应都是完全一样的。此时,可以采用重写一个方法来替代上面的几个方法:只需重写service()方法即可响应客户端的所有请求。

    另外,HttpServlet还包含两个方法:

    • init(ServletConfig config):创建Servlet实例时,调用该方法的初始化Servlet资源
    • destroy():销毁Servlet实例时,自动调用该方法的回收资源。

    通常无须重写上面的两个方法,除非需要在初始化Servlet时,完成某些资源初始化的方法,才考虑冲洗init方法。如果需要在销毁Servlet之前,先完成某些资源的回收,比如关闭数据库连接等,才需要重写destroy方法。

    注意:如果重写init()方法,则应该在重写该方法的第一行调用super.init(config)。

    Servlet和JSP的区别在于:

    • Servlet中没有内置对象,原来JSP的内置对象都必须有程序显示创建
    • 对于静态的HTML标签,Servlet都必须使用页面输出流逐行输出

    1.2 Servlet的配置

      编辑好的Servlet源文件并不能响应用户请求, 还必须将其编译成class文件,如果需要直接使用javac命令编译Servlet类,则必须将Servlet API接口和类添加到系统的CLASSPATH环境变量里。也就是将Tomcat安装目录下的servlet-api.jar和jsp-api.jar添加到CLASSPATH环境变量中,当然使用IDE这些都会自动完成了。

      为了让Servlet能响应用户请求,还必须将Servlet配置在Web应用中。配置Servlet时需要修改web.xml文件。

    从Servlet3.0开始,配置Servlet有两种方式:

    • 在Servlet类中使用@WebServlet注解进行配置
    • 通过在web.xml文件中进行配置

    开发Servlet类时使用了@WebServlet注解修饰该Servlet,其有如下常用的属性(全都是可选的):

    • name:指定该Servlet的名称
    • urlPatterns / value:这两个属性的作用完全相同,都是指定该Servlet处理的URL
    • asyncSupported:指定该Servlet是否支持异步操作模式
    • display:指定该Servlet的显示名
    • initParams:用于为该Servlet配置参数
    • loadOnStartup:用于将该Servlet配置成load-on-startup的Servlet

    如果打算使用注解来配置Servlet,有两点需要指出:

    1. 不要在web.xml文件的根元素(<web-app../>)中指定metadata-complete=“true”
    2. 不要在web.xml文件中配置该Servlet

    如果打算使用web.xml文件来配置Servlet,则需要配置如下两个部分:

    • 配置Servlet的名字:对应web.xml文件中的<servlet/>元素
    • 配置Servlet的URL:对应web.xml文件中的<servlet-mapping/>元素。这一步是可选的,但如果没有为是Servlet配置URL,则该Servlet不能响应用户请求
    <!-- 配置Servlet的名字 -->
    <servlet>
    	<!-- 指定Servlet的名字, 相当于指定@WebServlet的name属性 -->
    	<servlet-name>firstServlet</servlet-name>
    	<!-- 指定Servlet的实现类 -->
    	<servlet-class>lee.FirstServlet</servlet-class>
    </servlet>
    <!-- 配置Servlet的URL -->
    <servlet-mapping>
    	<!-- 指定Servlet的名字 -->
    	<servlet-name>firstServlet</servlet-name>
    	<!-- 指定Servlet映射的URL地址, 相当于指定@WebServlet的urlPatterns属性 -->
    	<url-pattern>/aa</url-pattern>
    </servlet-mapping>

    1.3 JSP/Servlet的声明周期

      JSP本质是Servlet,JSP页面将有Web容器编译成对应的Servlet,当Servlet容器中运行时,其实例的创建及销毁等都不是由程序员决定的,而是有Web容器进行控制的。

    创建Servlet实例有两个时机:

    • 客户端第一次请求某个Servlet时,系统创建该Servlet的实例:大部分的Servlet都是这种Servlet
    • Web应用启动时立即创建Servlet实例,即load-on-startup Servlet

    1.4 load-on-startup Servlet

      这总方式的Servlet在Web应用启动时立即创建Servlet实例,通常是用于某些后台服务的Servlet,或者需要拦截很多请求的Servlet;这种Servlet通常作为应用的基础Servlet使用,提供重要的后台服务。

    配置load-on-startup的Servlet有两种方式:

    • 在web.xml文件中通过<servlet../>元素的<load-on-startup../>子元素进行配置
    • 通过@WebServlet注解的loadOnStartup属性指定

    <load-on-startup../>元素或loadOnStartUp属性都只接收一个整型值,这个整型值越小,Servlet就越优先实例化。

    示例:

    <servlet>
    	<!-- Servlet名 -->
    	<servlet-name>timerServlet</servlet-name>
    	<!-- Servlet的实现类 -->
    	<servlet-class>lee.TimerServlet</servlet-class>
    	<!-- 配置应用启动时,创建Servlet实例 ,相当于指定@WebServlet的loadOnStartup属性 -->
    	<load-on-startup>1</load-on-startup>
    </servlet>

    1.5 访问Servlet的配置参数

      配置Servlet时,还可以增加额外的配置参数。通过使用配置参数,可以实现提供更好的可移植性,避免将参数以硬编码方式写在程序代码中。

    为Servlet配置参数有两种方式:

    • 通过@WebServlet的initParams属性来指定
    • 通过在web.xml文件的<servlet.../>元素中添加<init-param.../>子元素来指定

    示例:

    @WebServlet(name = "testServlet", urlPatterns = { "/testServlet" }, initParams = {
    		@WebInitParam(name = "driver", value = "com.mysql.jdbc.Driver"),
    		@WebInitParam(name = "url", value = "jdbc:mysql://localhost:3306/javaee"),
    		@WebInitParam(name = "user", value = "root"),
    		@WebInitParam(name = "pass", value = "32147") })

    访问Servlet配置参数通过ServletConfig独享完成,ServletConfig提供getInitParameter()来获取初始化参数,如下:

    // 获取ServletConfig对象
    ServletConfig config = getServletConfig();
    // 通过ServletConfig对象获取配置参数:dirver
    String driver = config.getInitParameter("driver");
    // 通过ServletConfig对象获取配置参数:url
    String url = config.getInitParameter("url");
    // 通过ServletConfig对象获取配置参数:user
    String user = config.getInitParameter("user");
    // 通过ServletConfig对象获取配置参数:pass
    String pass = config.getInitParameter("pass");

      ServletConfig获取配置参数的方法和ServletContext获取配置参数的方法完全一样,只是ServletConfig是获得当前Servlet的配置参数,而ServletContext是获取整个Web应用的配置参数。
    在web.xml中为Servlet配置参数(与上面的功能相同):

    <servlet>
    	<!-- 配置Servlet名 -->
    	<servlet-name>testServlet</servlet-name>
    	<!-- 指定Servlet的实现类 -->
    	<servlet-class>lee.TestServlet</servlet-class>
    	<!-- 配置Servlet的初始化参数:driver -->
    	<init-param>
    		<param-name>driver</param-name>
    		<param-value>com.mysql.jdbc.Driver</param-value>
    	</init-param>
    	<!-- 配置Servlet的初始化参数:url -->
    	<init-param>
    		<param-name>url</param-name>
    		<param-value>jdbc:mysql://localhost:3306/javaee</param-value>
    	</init-param>
    	<!-- 配置Servlet的初始化参数:user -->
    	<init-param>
    		<param-name>user</param-name>
    		<param-value>root</param-value>
    	</init-param>
    	<!-- 配置Servlet的初始化参数:pass -->
    	<init-param>
    		<param-name>pass</param-name>
    		<param-value>32147</param-value>
    	</init-param>
    </servlet>

    1.6 使用Servlet作为控制器

    使用Servlet作为表现层有如下三个劣势:

    1. 开发效率低,所有的HTML标签都需使用页面输出流完成
    2. 不利于团队协作开发,美工人员无法参与Servlet界面的开发
    3. 程序可维护性差,即使修改一个按钮的标题,都必须重新编辑Java代码,并重新编译

      在标准的MVC模式中,Servlet仅作为控制器使用。Java EE应用框架正式遵循MVC模式的,对于遵循MVC模式的Java EE应用而言,JSP仅作为表现层(View)技术,其作用有两点:

    1. 负责收集用户请求参数
    2. 将应用的处理结果、状态数据呈现给用户

      Servlet则仅充当控制器(Controller)角色,它的作用类似于调度员:所有用户请求都发送给Servlet,Servlet调用Model来处理用户请求,并调用JSP来呈现处理结果;或者Servlet直接调用JSP将应用的状态数据呈现给用户。

    二、JSP2的自定义标签

    JSP2规范简化了标签的开发,在JSP2中开发标签库只需要如下几个步骤:

    1.  开发自定义标签处理类
    2.  建立一个*.tld文件,每个*.tld文件对应一个标签库,每个标签库可包含多个标签
    3. 在JSP文件中使用自定义标签

      标签库是非常重要的技术,通常来说,初学者、普通开发人员都不会编写自己的标签库,因为像MVC框架,如Struts2、SpringMVC、JSF等都提供了丰富的自定义标签。

    2.1 开发自定义标签类

    自定义标签类应该继承一个父类:javax.servlet.jsp.tagext.SimpleTagSupport,除此之外,JSP自定义标签类还有如下要求:

    • 如果标签类包含属性,每个属性都有对应的getter和setter方法
    • 重写doTag()方法,这个方法负责生成页面内容
    public class HelloWorldTag extends SimpleTagSupport {
    	// 重写doTag()方法,该方法为标签生成页面内容
    	public void doTag() throws JspException, IOException {
    		// 获取页面输出流,并输出字符串
    		getJspContext().getOut().write("Hello World " + new java.util.Date());
    	}
    }

    2.2 建立TLD文件

      TLD是Tag Library Definition的缩写,即标签库定义,文件的后缀是tld,每个TLD文件对应一个标签库,一个标签库中可包含多个标签。TLD文件也称为标签库定义文件。标签库定义文件的根元素是taglib,他可以包含多个tag子元素,每个tag子元素都定义了一个标签。通常可以到Web容器下复制一个标签库定义文件,在其基础上进行修改即可。例如Tomcat中,在webapps\examples\WEB-INF\jsp2路径下包含了一个示例文件。
      我们可以将tld文件复制到Web应用的WEB-INF路径下(可以是任意子路径下)。

    注意:Tomcat 8 所带的示例的TLD文件的根元素所采用的的是标签库2.0规范。

    标签库2.0根元素:

    <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
        http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">

    标签库2.1根元素:

    <taglib 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-jsptaglibrary_2_1.xsd"
    	version="2.1">

    taglib下有如下三个子元素:

    • tlib-version:指定该标签库实现的版本,这是一个作为标识的内部版本号,对程序没有太大的作用
    • short-name:该标签库的默认短名,该名称通常也没有太大的用处
    • uri:这个属性非常重要,它指定该标签库的URI,相当于指定该标签库的唯一标识。JSP页面中使用标签库时就是根据该URI属性来定位标签库的

    taglib元素下可以包含多个tag元素,每个tag元素定义一个标签,tag元素下允许出现如下常用子元素:

    • name:该标签的名称,这个子元素很重要,JSP页面中就是根据该名称来使用此标签的
    • tag-class:指定标签的处理类
    • body-content:这个子元素也很重要,它指定标签体内容。其值可以为:
      • tagdependent:指定标签处理类自己负责处理标签体
      • empty:指定该标签只能作用空标签使用
      • scriptless:指定该标签的标签体可以是静态HTML元素、表达式语言,但不允许出现JSP脚步
      • JSP:指定该标签的标签体可以使用JSP脚本

    注:JSP2规范中body-content元素的值不可以是JSP。

    注:标签库文件放在Web应用的WEB-INF路径或任意子路径下,Java Web规范会自动加载该文件,则该文件定义的标签库也将生效。

    2.3 使用标签库

    在JSP页面中确定指定的标签需要两点:

    1. 标签库URI:确定使用哪个标签库
    2. 标签名:确定使用哪个标签

    使用标签库分成以下两个步骤:

    1. 导入标签库:使用taglib编译指令导入标签库,就是将标签库和指定前缀关联起来
    2. 使用标签:在JSP页面中使用标签

    2.3.1 导入标签库

    导入时的语法格式如下:

    <%@ taglib uri="tagLibUri" prefix="tagPrefix"%>

    其中uri属性指定标签库的URI,这个URI可以确定一个标签库,这个URI就是TLD文件中定义的uri属性对应。而prefix属性指定标签库前缀,即所有使用该前缀的标签将由此标签库处理。

    2.3.2 使用标签库

    使用标签的语法格式如下:

    <tagprefix:tagName tagAttribute="tagValue"...>
    <tagBody />
    </tagprefix:tagName>

    如果该标签没有标签体,则可以使用如下语法格式:

    <tagprefix:tagName tagAttribute="tagValue".../>
    

    注:tagprefix其实就是上面定义的prefix属性所定义的值。

    示例:

    标签库类定义在上面,在tld中定义如下:

    <taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">
    	<tlib-version>1.0</tlib-version>
    	<short-name>mytaglib</short-name>
    	<!-- 定义该标签库的URI -->
    	<uri>http://www.crazyit.org/mytaglib</uri>
    
    	<!-- 定义一个标签 -->
    	<tag>
    		<!-- 定义标签名 -->
    		<name>helloWorld</name>
    		<!-- 定义标签处理类 -->
    		<tag-class>lee.HelloWorldTag</tag-class>
    		<!-- 定义标签体为空 -->
    		<body-content>empty</body-content>
    	</tag>
    </taglib>

    JSP中导入标签库:

    <!-- 导入标签库,指定mytag前缀的标签,
    	由URI为http://www.crazyit.org/mytaglib的标签库处理 -->
    <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>

    JSP中使用标签:

    <mytag:helloWorld/>

    2.4 带属性的标签

      前面的简单标签既没有属性,也没有标签体,用法和功能比较简单。需要注意的是带属性标签必须为每个属性提供对应的setter和getter方法,而带属性标签的配置方法与简单标签也略有差别。示例如下:

    自定义带属性的标签类:

    public class QueryTag extends SimpleTagSupport {
    	// 定义成员变量来代表标签的属性
    	private String driver;
    	private String url;
    	private String user;
    	private String pass;
    	private String sql;
    
    	// 执行数据库访问的对象
    	private Connection conn = null;
    	private Statement stmt = null;
    	private ResultSet rs = null;
    	private ResultSetMetaData rsmd = null;
    	
    	//省略各成员变量的setter和getter方法
    	...
    
    	public void doTag() throws JspException, IOException {
    		try {
    			// 注册驱动
    			Class.forName(driver);
    			// 获取数据库连接
    			conn = DriverManager.getConnection(url, user, pass);
    			// 创建Statement对象
    			stmt = conn.createStatement();
    			// 执行查询
    			rs = stmt.executeQuery(sql);
    			rsmd = rs.getMetaData();
    			// 获取列数目
    			int columnCount = rsmd.getColumnCount();
    			// 获取页面输出流
    			Writer out = getJspContext().getOut();
    			// 在页面输出表格
    			out.write("<table border='1' bgColor='#9999cc' width='400'>");
    			// 遍历结果集
    			while (rs.next()) {
    				out.write("<tr>");
    				// 逐列输出查询到的数据
    				for (int i = 1; i <= columnCount; i++) {
    					out.write("<td>");
    					out.write(rs.getString(i));
    					out.write("</td>");
    				}
    				out.write("</tr>");
    			}
    		} catch (ClassNotFoundException cnfe) {
    			cnfe.printStackTrace();
    			throw new JspException("自定义标签错误" + cnfe.getMessage());
    		} catch (SQLException ex) {
    			ex.printStackTrace();
    			throw new JspException("自定义标签错误" + ex.getMessage());
    		} finally {
    			// 关闭结果集
    			try {
    				if (rs != null)
    					rs.close();
    				if (stmt != null)
    					stmt.close();
    				if (conn != null)
    					conn.close();
    			} catch (SQLException sqle) {
    				sqle.printStackTrace();
    			}
    		}
    	}
    }

      该标签类包含了5个属性,而为标签处理类定义成员变量即可代表标签的属性,而且程序需要为这5个属性提供setter()和getter()方法。该标签输出的内容依然由doTag()方法决定,该方法会根据SQL语句查询数据库,并将查询结果显示在当前页面中。

      对于有属性的标签,需要在tld中为<tag../>元素增加<attribute../>子元素,每个<attribute../>定义一个标签属性。<attribute../> 子元素通常还需要指定如下几个子元素:

    • name:设置属性名,子元素的值是字符串内容
    • required:设置该属性是否为必须属性,该子元素的值时true或false
    • fragment:设置该属性是否支持JSP脚本、表达式等动态内容,子元素的值true或false

    为了配置上面的QueryTag标签,需要在tld文件中增加如下配置片段:

    <tag>
    	<!-- 定义标签名 -->
    	<name>query</name>
    	<!-- 定义标签处理类 -->
    	<tag-class>lee.QueryTag</tag-class>
    	<!-- 定义标签体为空 -->
    	<body-content>empty</body-content>
    	<!-- 配置标签属性:driver -->
    	<attribute>
    		<name>driver</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    	<!-- 配置标签属性:url -->
    	<attribute>
    		<name>url</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    	<!-- 配置标签属性:user -->
    	<attribute>
    		<name>user</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    	<!-- 配置标签属性:pass -->
    	<attribute>
    		<name>pass</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    	<!-- 配置标签属性:sql -->
    	<attribute>
    		<name>sql</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    </tag>

    配置完毕后,就可在页面中使用标签了,先导入标签库,然后使用标签,代码如下:

    <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
    <!-- 使用标签 ,其中mytag是标签前缀,根据taglib的编译指令,
    	mytag前缀将由http://www.crazyit.org/mytaglib的标签库处理 -->
    ......
    <mytag:query
    	driver="com.mysql.jdbc.Driver"
    	url="jdbc:mysql://localhost:3306/javaee"
    	user="root"
    	pass="32147"
    	sql="select * from news_inf"/>

    2.5 带标签体的标签(重点)

      带标签体的标签,可以在标签内嵌入其他内容(包括静态的HTML内容和动态的JSP内容),通常用于完成一些逻辑运算。示例如下:

    标签处理类:

    public class IteratorTag extends SimpleTagSupport {
    	// 标签属性,用于指定需要被迭代的集合
    	private String collection;
    	// 标签属性,指定迭代集合元素,为集合元素指定的名称
    	private String item;
    
    	// 省略get与set...
    	// 省略get与set...
    	// 标签的处理方法,标签处理类只需要重写doTag()方法
    	public void doTag() throws JspException, IOException {
    		// 从page scope中获取名为collection的集合
    		Collection<?> itemList = (Collection<?>) getJspContext().getAttribute(collection);
    		// 遍历集合
    		for (Object s : itemList) {
    			// 将集合的元素设置到page范围内
    			getJspContext().setAttribute(item, s);
    			// 输出标签体
    			getJspBody().invoke(null);
    		}
    	}
    
    	// 标签的处理方法,标签处理类只需要重写doTag()方法
    	public void doTag() throws JspException, IOException {
    		// 从page scope中获取名为collection的集合
    		Collection<?> itemList = (Collection<?>) getJspContext().getAttribute(collection);
    		// 遍历集合
    		for (Object s : itemList) {
    			// 将集合的元素设置到page范围内
    			getJspContext().setAttribute(item, s);
    			// 输出标签体
    			getJspBody().invoke(null);
    		}
    	}
    }

      该标签处理类的doTag()方法首先从page范围内获取了指定名称的Collection对象,然后遍历Collection对象的元素,每次遍历都调用getJspBody()方法,该方法返回该标签所包含的标签体:JSPFragment对象,执行该对象的invoke()方法,即可输出标签体内容。该标签的作用是:遍历指定集合,每遍历一个集合元素,技术处标签体一次。

      因为该标签的标签体不为空,配置该标签时指定body-content为scriptless,该标签的配置代码如下:

    <tag>
    	<!-- 定义标签名 -->
    	<name>iterator</name>
    	<!-- 定义标签处理类 -->
    	<tag-class>lee.IteratorTag</tag-class>
    	<!-- 定义标签体不允许出现JSP脚本 -->
    	<body-content>scriptless</body-content>
    	<!-- 配置标签属性:collection -->
    	<attribute>
    		<name>collection</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    	<!-- 配置标签属性:item -->
    	<attribute>
    		<name>item</name> 
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    </tag>

      上面的配置指定该标签的标签体可以是静态HTML内容,也可以是表达式语言,但不允许出现JSP脚本。下面是测试代码:(导入代码省略)

    <body>
    	<h2>带标签体的标签-迭代器标签</h2>
    	<hr />
    	<%
    		//创建一个List对象
    		List<String> a = new ArrayList<String>();
    		a.add("疯狂Java");
    		a.add("www.crazyit.org");
    		a.add("www.fkit.org");
    		//将List对象放入page范围内
    		pageContext.setAttribute("a", a);
    	%>
    	<table border="1" bgcolor="#aaaadd" width="300">
    		<!-- 使用迭代器标签,对a集合进行迭代 -->
    		<mytag:iterator collection="a" item="item">
    			<tr>
    				<td>${pageScope.item}</td>
    			</tr>
    		</mytag:iterator>
    	</table>
    </body>

    测试结果如下:


      从输出结果看,使用iterator标签遍历集合元素比使用JSP脚本遍历集合元素要优雅得多,这就是自定义标签的魅力。实际上JSTL标签库提供了一套功能强大的标签,如果有现成的轮子就没必要再造轮子了。

      可能你有所疑惑,这个JSP页面自己先把多个字符串添加到ArrayList,然后再使用这个iterator标签进行迭代输出,好像没有意义。实际上这个标签的用处非常大,在严格的MVC规范下,JSP页面只负责显示数据——而数据通常由控制器(Servlet)放入request范围内,而JSP页面就通过iterator标签迭代出输出request范围内的数据。

    2.6 以页面片段作为属性的标签(暂不理解)

      JSP2规范的自定义标签还允许直接将一段“页面片段”作为属性,这种方式给自定义标签提供了更大的灵活性。以“页面片段”为属性的标签与与普通标签区别并不大,只有两个简单的改变:

    1.  标签处理类中定义类型为JSPFragment的属性,该属性代表了“页面片段”
    2. 使用标签库,通过<jsp:attribute.../>动作指令为标签的属性指定值

    定义一个标签处理类,示例:

    public class FragmentTag extends SimpleTagSupport {
    	private JspFragment fragment;
    	//省略fragment的setter和getter方法
    	@Override
    	public void doTag() throws JspException, IOException {
    		JspWriter out = getJspContext().getOut();
    		out.println("<div style='padding:10px;border:1px solid black;" + ";border-radius:20px'>");
    		out.println("<h3>下面是动态传入的JSP片段</h3>");
    		// 调用、输出“页面片段”
    		fragment.invoke(null);
    		out.println("</div");
    	}
    }

      上面的程序中定义了fragment成员变量,该成员变量代表了使用该标签时的“页面片段”,配置该标签与配置普通标签并无任何区别,增加如下配置片段即可。

    <tag>
    	<!-- 定义标签名 -->
    	<name>fragment</name>
    	<!-- 定义标签处理类 -->
    	<tag-class>lee.FragmentTag</tag-class>
    	<!-- 指定该标签不支持标签体 -->
    	<body-content>empty</body-content>
    	<!-- 定义标签属性:fragment -->
    	<attribute>
    		<name>fragment</name>
    		<required>true</required>
    		<fragment>true</fragment>
    	</attribute>
    </tag>

      这个自定义标签并没有任何特别之处,就是一个普通的带属性标签,该标签的标签体为空。由于该标签需要一个fragment属性,该属性的类型为JspFragment,因此使用该标签时需要使用<jsp:attribute../>动作指令来设置属性值,如下所示:

    <!-- 导入标签库,指定mytag前缀的标签,
    由http://www.crazyit.org/mytaglib的标签库处理 -->
    <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
    .....
    <mytag:fragment>
    	<jsp:attribute name="fragment">
    <%-- 使用jsp:attribute标签传入fragment参数(该注释不能放在fragment内) -->
    	<%-- 下面是动态的JSP页面片段 --%>
    	<mytag:helloWorld />
    </jsp:attribute>
    </mytag:fragment>
    <br />
    <mytag:fragment>
    	<jsp:attribute name="fragment">
    	<%-- 下面是动态的JSP页面片段 --%>
    	${pageContext.request.remoteAddr}
    </jsp:attribute>
    </mytag:fragment>
    

    注意:由于程序指定了fragment标签的标签体为empty,因此程序中fragment开始标签和fragment结束标签之间只能使用<jsp:attribute.../>子元素,不允许出现其他内容,甚至连注释都不允许。

    2.7 动态属性的标签(终点)

      前面的标签的属性个数是确定的,绝大部分情况下这种带属性的标签能处理得很好,但在某些特殊情况下,需要传入自定义标签的属性个数是不确定的,属性名也不确定,这就需要借助于动态属性的标签了。

    动态属性标签比普通标签多了两个额外需求:

    1.  标签处理类还需要实现DynamicAttributes接口
    2.  配置标签时通过<dynamic-attributes.../>子元素指定该标签支持动态属性

    动态属性标签的处理类,示例:

    public class DynaAttributesTag extends SimpleTagSupport implements DynamicAttributes {
    	// 保存每个属性名的集合
    	private ArrayList<String> keys = new ArrayList<String>();
    	// 保存每个属性值的集合
    	private ArrayList<Object> values = new ArrayList<Object>();
    
    	@Override
    	public void doTag() throws JspException, IOException {
    		JspWriter out = getJspContext().getOut();
    		// 此处只是简单地输出每个属性
    		out.println("<ol>");
    		for (int i = 0; i < keys.size(); i++) {
    			String key = keys.get(i);
    			Object value = values.get(i);
    			out.println("<li>" + key + " = " + value + "</li>");
    		}
    		out.println("</ol>");
    	}
    
    	@Override
    	public void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
    		// 添加属性名
    		keys.add(localName);
    		// 添加属性值
    		values.add(value);
    	}
    }

      上面的标签处理类实现了DynamicAttributes接口,就是动态属性标签处理类必须实现的接口,实现该接口必须实现setDynamicAttribute()方法,该方法用于为该标签处理类动态地添加属性名和属性值。标签处理类使用ArrayList<String>类型的Keys属性来保存标签的所有属性名,使用ArrayList<Object>类型的values属性来保存标签的所有属性值。
      配置该标签时需要额外地指定<dynamic-attributes.../>子元素,表明该标签时带动态属性的标签,如下:

    <!-- 定义接受动态属性的标签 -->
    <tag>
    	<name>dynaAttr</name>
    	<tag-class>lee.DynaAttributesTag</tag-class>
    	<body-content>empty</body-content>
    	<!-- 指定支持动态属性 -->
    	<dynamic-attributes>true</dynamic-attributes>
    </tag>

      一旦定义了动态属性的标签,接下来在页面中使用该标签时将十分灵活,完全可以为该标签设置任意的属性,如下:

    <!-- 导入标签库,指定mytag前缀的标签,
    	由http://www.crazyit.org/mytaglib的标签库处理 -->
    <%@ taglib uri="http://www.crazyit.org/mytaglib" prefix="mytag"%>
    ...
    <h2>下面显示的是自定义标签中的内容</h2>
    <h4>指定两个属性</h4>
    <mytag:dynaAttr name="crazyit" url="crazyit.org" />
    <br />
    <h4>指定四个属性</h4>
    <mytag:dynaAttr 书名="疯狂Java讲义" 价格="99.0" 出版时间="2008年" 描述="Java图书" />

    三、Filter

      Filter主要用于对用户请求进行预处理,也可对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

    Filter有如下几个用处:

    • 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletResponse
    • 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据
    • 在HttpServletResponse到达客户端之前,拦截HttpServletResponse
    • 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据

    Filter有如下几个种类:

    • 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求
    • 日志Filter:详细记录某些特殊的用户请求
    • 负责解码的Filter:包括对非标准编码的请求解码
    • 能改变XML内容的XSLT Filter等
    • Filter可负责拦截多个请求或相应:一个请求或相应也可被多个Filter拦截

    创建一个Filter只需要两个步骤:

    1.  创建Filter处理类
    2.  web.xml文件中配置Filter

    3.1 创建Filter类

    创建Filter必须实现javax.Servlet.Filter接口,在该接口中定义了如下三个方法:

    • void init(FilterConfig config):用于完成Filter的初始化
    • void destroy():用于Filter销毁前,完成某些资源的回收
    • void doFilter(ServletRequest request , ServletResponse response , FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理

      终点在doFilter方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理——它们的分界线为是否调用了chain.doFilter(request, response);方法,执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。

    示例:(使用注解配置,详细介绍在下面)

    @WebFilter(filterName = "log", urlPatterns = { "/*" })
    public class LogFilter implements Filter {
    	// FilterConfig可用于访问Filter的配置信息
    	private FilterConfig config;
    	// 实现初始化方法
    	public void init(FilterConfig config) {
    		this.config = config;
    	}
    	// 实现销毁方法
    	public void destroy() {
    		this.config = null;
    	}
    	// 执行过滤的核心方法
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    			throws IOException, ServletException {
    		// ---------下面代码用于对用户请求执行预处理---------
    		// 获取ServletContext对象,用于记录日志
    		ServletContext context = this.config.getServletContext();
    		long before = System.currentTimeMillis();
    		System.out.println("开始过滤...");
    		// 将请求转换成HttpServletRequest请求
    		HttpServletRequest hrequest = (HttpServletRequest) request;
    		// 输出提示信息
    		System.out.println("Filter已经截获到用户的请求的地址: " + hrequest.getServletPath());
    		// Filter只是链式处理,请求依然放行到目的地址
    		chain.doFilter(request, response);//放行用户请求
    		// ---------下面代码用于对服务器响应执行后处理---------
    		long after = System.currentTimeMillis();
    		// 输出提示信息
    		System.out.println("过滤结束");
    		// 输出提示信息
    		System.out.println("请求被定位到" + hrequest.getRequestURI() + "   所花的时间为: " + (after - before));
    	}
    }

      这个Filter的作用是拦截所有的请求,然后输出请求地址,然后放行这个请求,然后,输出响应地址,并且输出整个响应过程所需要的时间。这只是个简单的拦截器,我们完全也可以在Filter中根据用户请求的HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用chain.doFilter(request, response);方法。

    3.2 配置Filter

      Filter可以认为是Servlet的“增强版”,因此配置Filter与配置Servlet非常相似,都需要配置如下两个部分:

    • 配置Filter名
    • 配置Filter拦截URL模式

      区别在于:Servlet通常指配置一个URL,而Filter可以同时拦截多个请求的URL。因此,在配置Filter的URL模式时通常会使用模式字符串,使得Filter可以拦截多个请求。与配置Servlet相似的是,配置Filter同样有两种方式:

    • 在Filter类中通过注解进行配置
    • 在web.xml文件中通过配置文件进行配置

      上面Filter类代码使用@WebFilter配置该Filter的名字为log,它会拦截向/*发送的所有请求。而用web.xml文件配置其作用相同,如下:

    <!-- 定义Filter -->
    <filter>
    	<!-- Filter的名字,相当于指定@WebFilter
    		的filterName属性 -->
    	<filter-name>log</filter-name>
    	<!-- Filter的实现类 -->
    	<filter-class>lee.LogFilter</filter-class> 
    </filter>
    <!-- 定义Filter拦截的URL地址 -->
    <filter-mapping>
    	<!-- Filter的名字 -->
    	<filter-name>log</filter-name>
    	<!-- Filter负责拦截的URL,相当于指定@WebFilter
    		的urlPatterns属性 -->
    	<url-pattern>/*</url-pattern>
    </filter-mapping>

    3.2.1 @WebFilter注解常用属性(都是非必须)

    • filterName:指定该Filter的名称
    • urlPatterns / value:这两个属性的作用完全相同。都指定该Filter所拦截的URL
    • initParams:用于为该Filter配置参数
    • servletNames:该属性值指定多个Servlet的名称,用于指定该Filter仅对这几个Servlet执行过滤
    • displayName:指定该Filter的显示名
    • asyncSupported:指定该Filter是否支持异步操作模式。
    • dispatcherTypes:指定该Filter仅对那种dispatcher模式的请求进行过滤。该属性支持ASYNC、ERROR、FORWARD、INCLUDE、REQUEST这5个值的任何组合。默认值为同时过滤5种模式的请求。

    3.2.2 使用示例

      假设系统有包含多个Servlet,这些Servlet都需要进行一些的通用处理:比如权限控制、记录日志等,这将导致在这些Servlet的service方法中有部分代码是相同的——为了解决这种代码重复的问题,可以考虑把这些通用处理提取到Filter中完成,这样各Servlet中剩下的只是特定请求相关的处理代码,而通过处理则交给Filter完成。

      由于Filter和Servlet如此相似,所以Filter和Servlet具有完全相同的生命周期行为,且Filter也可以通过<init-param.../>元素或@WebFilter的initParams属性来配置初始化参数,获取Filter的初始化参数则使用FilterConfig的getInitParameter()方法。

      下面我们定一个较为实用的Filter,该Filter对用户请求进行过滤,Filter将通过doFilter方法来设置request编码的字符集,从而避免每个JSP、Servlet都需要设置;而且还会验证用户是否登录,如下:

    Filter源码:

    @WebFilter(filterName = "authority", urlPatterns = { "/*" }, initParams = {
    		@WebInitParam(name = "encoding", value = "GBK"), @WebInitParam(name = "loginPage", value = "/login.jsp") })
    public class AuthorityFilter implements Filter {
    	// FilterConfig可用于访问Filter的配置信息
    	private FilterConfig config;
    	// 实现初始化方法
    	public void init(FilterConfig config) {
    		this.config = config;
    	}
    	// 实现销毁方法
    	public void destroy() {
    		this.config = null;
    	}
    	// 执行过滤的核心方法
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    			throws IOException, ServletException {
    		// 获取该Filter的配置参数
    		String encoding = config.getInitParameter("encoding");
    		String loginPage = config.getInitParameter("loginPage");
    		// 设置request编码用的字符集
    		request.setCharacterEncoding(encoding); 
    		HttpServletRequest requ = (HttpServletRequest) request;
    		HttpSession session = requ.getSession(true);
    		// 获取客户请求的页面
    		String requestPath = requ.getServletPath();
    		// 如果session范围的user为null,即表明没有登录
    		// 且用户请求的既不是登录页面,也不是处理登录的页面
    		if (session.getAttribute("user") == null && !requestPath.endsWith(loginPage)) {
    			// forward到登录页面
    			request.setAttribute("tip", "您还没有登录");
    			request.getRequestDispatcher(loginPage).forward(request, response);
    		}
    		// "放行"请求
    		else {
    			chain.doFilter(request, response);
    		}
    	}
    }

      当然也可以使用web.xml文件中配置该Filter,如下:

    <!-- 定义Filter -->
    <filter>
    	<!-- Filter的名字 -->
    	<filter-name>authority</filter-name>
    	<!-- Filter的实现类 -->
    	<filter-class>lee.AuthorityFilter</filter-class>
    	<!-- 下面两个init-param元素配置了两个参数 -->
    	<init-param>
    		<param-name>encoding</param-name>
    		<param-value>GBK</param-value>
    	</init-param>
    	<init-param>
    		<param-name>loginPage</param-name>
    		<param-value>/login.jsp</param-value>
    	</init-param>
    </filter>
    <!-- 定义Filter拦截的URL地址 -->
    <filter-mapping>
    	<!-- Filter的名字 -->
    	<filter-name>authority</filter-name>
    	<!-- Filter负责拦截的URL -->
    	<url-pattern>/*</url-pattern>
    </filter-mapping>

    login.jsp(可以写几个其它页面,以作测试)

    <body>
    	<h2>登录页面</h2>
    	<%
    		if (request.getAttribute("tip") != null) {
    			out.println("<font color='red'>" + request.getAttribute("tip") + "</font>");
    		}
    	%>
    	<form method="post" action="proLogin.jsp">
    		用户名:<input type="text" name="name" /><br /> <input type="submit"
    			value="登录" />
    	</form>
    </body>

      实现的效果是,如果没有的登录,则只能访问/login.jsp,反之则可以自由访问其他页面。

    四、使用URL Rewrite实现网站伪静态

      大部分搜索引擎都会优先考虑收录静态的HTML页面,而不是这种动态的*.jsp、*.php页面。但实际上绝大多数网站都是动态的,不可能全部是静态的HTML页面,因此互联网上的大部分网站都会考虑使用伪静态——就是*.jsp、*.php这种动态URL伪装成静态的HTML页面。

      对于Java Web应用来说,要实现伪静态非常简单:可以通过Filter拦截所有发向*.html请求,然后按某种规则将请求forward到实际的*.jsp页面即可。现有的URL Rewrite开源项目为这种思路提供了实现,使用URL Rewrite实现网站伪静态也很简单。

    4.1 利用URL Rewrite实现网站伪静态

    <1> 下载URL Rewrite jar包

    <2>  添加urlrewritefilter-4.0.3.jar 到 WEB-INF/lib目录下,或者添加Maven依赖如下:

    <dependency>
        <groupId>org.tuckey</groupId>
        <artifactId>urlrewritefilter</artifactId>
        <version>4.0.3</version>
    </dependency>

    <3> 在web.xml文件中配置启用URL Rewrite Filter,在web.xml文件中添加如下配置:

    <filter>
        <filter-name>UrlRewriteFilter</filter-name>
        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>UrlRewriteFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    注意:需要在所有的servlet mappings的上面。
    <4> 在应用的WEB-INF路径下增加urlrewrite.xml文件,该文件定义了伪静态映射规则,这份伪静态规则是基于正则表达式。

    添加响应的urlrewrite.xml伪静态规则文件如下:

    <?xml version="1.0" encoding="GBK"?>
    <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN"
    	"http://tuckey.org/res/dtds/urlrewrite3.2.dtd">
    <urlrewrite>
    	<rule>
    		<!-- 所有配置如下正则表达式的请求 -->
    		<from>/userinf-(\w*).html</from>
    		<!-- 将被forward到如下JSP页面,其中$1代表
    			上面第一个正则表达式所匹配的字符串 -->
    		<to type="forward">/userinf.jsp?username=$1</to>
    	</rule>
    </urlrewrite>

      上面的规则文件中只定义了一个简单的规则:所有发向/userinf-(\w*).html的请求都将被forward到userinf.jsp,并将(\w*)正则表达式所匹配的内容作为username参数值。根据这个伪静态规则,需要为该应用提供了一个userinf.jsp页面,该页面只是一个模拟了一个显示用户信息的页面,页面代码如下:

    <%@ page contentType="text/html; charset=GBK" language="java"
    	errorPage=""%>
    <%
    	// 获取请求参数
    	String user = request.getParameter("username");
    %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title><%=user%>的个人信息</title>
    <meta name="website" content="http://www.crazyit.org" />
    </head>
    <body>
    	<%
    		// 此处应该通过数据库读取该用户对应的信息
    		// 此处只是模拟,因此简单输出:
    		out.println("现在时间是:" + new java.util.Date() + "<br/>");
    		out.println("用户名:" + user);
    	%>
    </body>
    </html>

    五、Listener介绍

      Servlet API提供了大量监听器来监听Web项目的内部事件,从而允许当Web内部事件发生时回调事件监听器内的方法。使用Listener只需要两个步骤:

    1. 定义Listener实现类
    2. 通过注解或web.xml文件中配置Listener

    5.1 实现Listener类

      常用的Web事件监听器接口有如下几个:

    • ServletContextListener:用于监听Web应用的启动和关闭
    • ServletContextAttributeListener:用于监听ServletContext范围(application)内属性的改变
    • ServletRequestListener:用于监听用户请求
    • ServletRequestAttributeListener:用于监听ServletRequest范围(request)内属性的改变
    • HttpSessionListener:用于监听用户session的开始和结束
    • HttpSessionAttributeListener:用于监听HttpSession范围(session)内属性的改变

    5.2 配置Listener

      配置Listener只要向Web应用注册Listener实现类即可,无须配置参数之类的东西,因此十分简单。为Web应用配置Listener也有两种方式:

    • 使用@WebListener修饰Listener实现类即可
    • 在web.xml文档中使用<listener../>元素进行配置

      使用@WebListener时通常无须指定任何属性,只要使用该注解修饰Listener实现类即可向Web应用注册该监听器。在web.xml中使用<listener../>元素进行配置时只要配置如下子元素即可。

    • listener-class:指定Listener实现类

    5.3 使用ServletContextListener

      ServletContextListener 用于监听Web应用的启动和关闭,该接口包含如下两个方法:

    • contextInitialized(ServletContextEvent sce):启动Web应用时,系统调用Listener的该方法
    • contextDestroyed(ServletContextEvent sce):关闭Web应用时,系统调用Listener的该方法

    示例:

    @WebListener
    public class GetConnListener implements ServletContextListener {
    	// 应该启动时,该方法被调用。
    	public void contextInitialized(ServletContextEvent sce) {
    		try {
    			// 取得该应用的ServletContext实例
    			ServletContext application = sce.getServletContext();
    			// 从配置参数中获取驱动
    			String driver = application.getInitParameter("driver");
    			// 从配置参数中获取数据库url
    			String url = application.getInitParameter("url");
    			// 从配置参数中获取用户名
    			String user = application.getInitParameter("user");
    			// 从配置参数中获取密码
    			String pass = application.getInitParameter("pass");
    			// 注册驱动
    			Class.forName(driver);
    			// 获取数据库连接
    			Connection conn = DriverManager.getConnection(url, user, pass);
    			// 将数据库连接设置成application范围内的属性
    			application.setAttribute("conn", conn);
    		} catch (Exception ex) {
    			System.out.println("Listener中获取数据库连接出现异常" + ex.getMessage());
    		}
    	}
    
    	// 应该关闭时,该方法被调用。
    	public void contextDestroyed(ServletContextEvent sce) {
    		// 取得该应用的ServletContext实例
    		ServletContext application = sce.getServletContext();
    		Connection conn = (Connection) application.getAttribute("conn");
    		// 关闭数据库连接
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException ex) {
    				ex.printStackTrace();
    			}
    		}
    	}
    }

    需要在web.xml文件中配置连接数据库的几个参数:

    <!-- 配置第一个参数:driver -->
    <context-param>
    	<param-name>driver</param-name>
    	<param-value>com.mysql.jdbc.Driver</param-value>
    </context-param>
    <!-- 配置第二个参数:url -->
    <context-param>
    	<param-name>url</param-name>
    	<param-value>jdbc:mysql://localhost:3306/ceshi</param-value>
    </context-param>
    <!-- 配置第三个参数:user -->
    <context-param>
    	<param-name>user</param-name>
    	<param-value>root</param-value>
    </context-param>
    <!-- 配置第四个参数:pass -->
    <context-param>
    	<param-name>pass</param-name>
    	<param-value>root</param-value>
    </context-param>
    
    <listener>
    	<!-- 指定Listener的实现类(配置注解就不需要了) -->
    	<listener-class>lee.GetConnListener</listener-class>
    </listener>

    5.4 使用ServletContextAttributeListener

      ServletContextAttributeListener用于监听ServletContext(application)范围内属性的变化,实现该接口的监听器需要实现如下三个方法:

    • attributeAdded(ServletContextAttributeEvent event):当程序把一个属性存入application范围时触发该方法
    • attributeRemoved(ServletContextAttributeEvent event):当程序把一个属性从application范围删除时触发该方法
    • attributeReplaced(ServletContextAttributeEvent event):当程序替换application范围内的属性时将触发该方法

    示例:

    @WebListener
    public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    	// 当程序向application范围添加属性时触发该方法
    	public void attributeAdded(ServletContextAttributeEvent event) {
    		ServletContext application = event.getServletContext();
    		// 获取添加的属性名和属性值
    		String name = event.getName();
    		Object value = event.getValue();
    		System.out.println(application + "范围内添加了名为" + name + ",值为" + value + "的属性!");
    	}
    
    	// 当程序从application范围删除属性时触发该方法
    	public void attributeRemoved(ServletContextAttributeEvent event) {
    		ServletContext application = event.getServletContext();
    		// 获取被删除的属性名和属性值
    		String name = event.getName();
    		Object value = event.getValue();
    		System.out.println(application + "范围内名为" + name + ",值为" + value + "的属性被删除了!");
    	}
    
    	// 当application范围的属性被替换时触发该方法
    	public void attributeReplaced(ServletContextAttributeEvent event) {
    		ServletContext application = event.getServletContext();
    		// 获取被替换的属性名和属性值
    		String name = event.getName();
    		Object value = event.getValue();
    		System.out.println(application + "范围内名为" + name + ",值为" + value + "的属性被替换了!");
    	}
    }

    5.5 使用ServletRequestListener和ServletRequestAttributeListener

      ServletRequestListener用于监听用户请求的到达,实现该接口的监听器需要实现如下两个方法:

    • requestInitialized(ServletRequestEvent sre):用户请求到达、被初始化时触发该方法
    • requestDestroyed(ServletRequestEvent sre):用户请求结束、被销毁时触发该方法

      ServletRequestAttributeListener则用于监听ServletRequest(request)范围内属性的变化,实现该接口的监听器需要实现attributeAdded()、attributeRemoved()、attributeReplaced()三个方法。ServletRequestAttributeListener与ServletContextAttributeListener的作用相似,都用于监听属性的改变,只是ServletRequestAttributeListener监听request范围内属性的改变。

     

    @WebListener
    public class RequestListener implements ServletRequestListener, ServletRequestAttributeListener {
    	// 当用户请求到达、被初始化时触发该方法
    	public void requestInitialized(ServletRequestEvent sre) {
    		HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
    		System.out.println("----发向" + request.getRequestURI() + "请求被初始化----");
    	}
    
    	// 当用户请求结束、被销毁时触发该方法
    	public void requestDestroyed(ServletRequestEvent sre) {
    		HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
    		System.out.println("----发向" + request.getRequestURI() + "请求被销毁----");
    	}
    
    	// 当程序向request范围添加属性时触发该方法
    	public void attributeAdded(ServletRequestAttributeEvent event) {
    		ServletRequest request = event.getServletRequest();
    		// 获取添加的属性名和属性值
    		String name = event.getName();
    		Object value = event.getValue();
    		System.out.println(request + "范围内添加了名为" + name + ",值为" + value + "的属性!");
    	}
    
    	// 当程序从request范围删除属性时触发该方法
    	public void attributeRemoved(ServletRequestAttributeEvent event) {
    		ServletRequest request = event.getServletRequest();
    		// 获取被删除的属性名和属性值
    		String name = event.getName();
    		Object value = event.getValue();
    		System.out.println(request + "范围内名为" + name + ",值为" + value + "的属性被删除了!");
    	}
    
    	// 当request范围的属性被替换时触发该方法
    	public void attributeReplaced(ServletRequestAttributeEvent event) {
    		ServletRequest request = event.getServletRequest();
    		// 获取被替换的属性名和属性值
    		String name = event.getName();
    		Object value = event.getValue();
    		System.out.println(request + "范围内名为" + name + ",值为" + value + "的属性被替换了!");
    	}
    }

    5.6 使用HttpSessionListener 和HttpSessionAttributeListener 

      HttpSessionListener 用于监听用户session的创建和销毁,实现该接口的监听器需要实现如下两个方法:

    • sessionCreated(HttpSessionEvent se)
    • sessionDestroyed(HttpSessionEvent se)

    示例:

    @WebListener
    public class OnlineListener implements HttpSessionListener {
    	// 当用户与服务器之间开始session时触发该方法
    	public void sessionCreated(HttpSessionEvent se) {
    		HttpSession session = se.getSession();
    		ServletContext application = session.getServletContext();
    		// 获取session ID
    		String sessionId = session.getId();
    		// 如果是一次新的会话
    		if (session.isNew()) {
    			String user = (String) session.getAttribute("user");
    			// 未登录用户当游客处理
    			user = (user == null) ? "游客" : user;
    			Map<String, String> online = (Map<String, String>) application.getAttribute("online");
    			if (online == null) {
    				online = new Hashtable<String, String>();
    			}
    			// 将用户在线信息放入Map中
    			online.put(sessionId, user);
    			application.setAttribute("online", online);
    		}
    	}
    
    	// 当用户与服务器之间session断开时触发该方法
    	public void sessionDestroyed(HttpSessionEvent se) {
    		HttpSession session = se.getSession();
    		ServletContext application = session.getServletContext();
    		String sessionId = session.getId();
    		Map<String, String> online = (Map<String, String>) application.getAttribute("online");
    		if (online != null) {
    			// 删除该用户的在线信息
    			online.remove(sessionId);
    		}
    		application.setAttribute("online", online);
    	}
    }

    六、JSP2特性

    JSP2主要增加了如下新特性:

    • 直接配置JSP属性
    • 表达式语言
    • 简化的自定义标签API
    • Tag文件语法

      如果需要使用JSP2语法,其web.xml文件必须使用Servlet2.4以上版本的配置文件。Servlet2.4配置文件的根元素写法如下:

    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"  
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
              xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee  
              http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"  
              version="2.4"> 
    </web-app> 

      我在写博客的时候常用的是Servlet3.1,其根元素写法可参考:web.xml部署描述符的例子

    6.1 配置JSP属性

    JSP属性定义使用<jsp-property-group/>元素配置,主要包括如下4个方面:

    • 是否允许使用表达式语言:使用<el-ignored/>元素确定,默认值为false,即允许使用表达式语言
    • 是否允许使用JSP脚本:使用<scripting-invalid/>元素确定,默认值为false,即允许使用JSP脚本
    • 声明JSP页面的编码:使用<page-encoding/>元素确定,配置该元素后,可以代替每个页面里page指令contentType属性的charset部分
    • 使用隐士包含:使用<include-prelude/>和<include-coda/>元素确定,可以代替在每个页面里使用include编译指令来包含其他页面

    注:此处隐式包含的作用与JSP提供的静态包含的作用相似。

    6.2 表达式语言&Tag File

    6.3 Servlet3.0新特性

    6.3.1 Servlet3.0的注解

    Servlet3.0规范在javax.servlet.annotation包下提供了如下注解:

    • @WebServlet:用于修饰一个Servlet雷,用于部署Servlet类
    • @WebInitParam:用于与@WebServlet或@WebFilter一起使用,为Servlet、Filter配置参数
    • @WebListener:用于修饰Listener类,用于部署Listener类
    • @WebFilter:用于修饰Filter类,用于部署Filter类
    • @MultipartConfig:用于修饰Servlet,指定该Servlet将会负责处理multipart/form-data类型的请求(主要用于文件上传)
    • @ServletSecurity:这是一个与JAAS有关的注解,修饰Servlet指定该Servlet的安全与授权控制
    • @HttpConstraint:用于与@ServletSecurity一起使用,用于指定该Servlet的安全与授权控制
    • @HttpMethodConstraint:用于与@ServletSecurity一起使用,用于指定该Servlet的安全与授权控制

    6.3.2 Servlet 3.0 的Web模块支持

      Servlet 3.0为模块化开发提供了良好的支持,Servlet 3.0规范不再要求所有Web组件(如Servlet、Listener、Filter)都部署在web.xml文件中,而是允许采用“Web模块”来部署、管理它们。

    一个Web模块通常对应于一个JAR包,这个JAR包有如下文件结构:

      从上面的文件结构可以看出,Web模块与普通JAR的最大区别在于需要在META-INF目录下添加一个Web-fragment.xml文件,这个文件也被称为Web模块部署描述符。

      web-fragment.xml文件与web.xml文件的作用、文件结构都基本相似,因为它们都用于部署、管理各种Web组件。只是web-fragment.xml用于部署、管理Web模块而已,但web-fragment.xml可以指定多个下面的两个元素:

    • <name.../>:用于指定该模块的名称
    • <ordering.../>:用于指定加载该Web模块的相对顺序

      Web应用除了可按web-fragment.xml文件中指定的加载顺序来加载Web模块之外,还可以通过web.xml文件指定个Web模块加载的绝对顺序。在web.xml文件中指定的加载顺序将会覆盖Web模块中web-fragment.xml文件所指定的加载顺序。

      假如在Web应用的web.xml文件中增加如下配置片段:

    <absolute-ordering>
    	<!-- 指定Web模块按如下顺序加载 -->
    	<name>moudle-name</name>
    	<name>moudle-name</name>
    </absolute-ordering>

      Servlet 3.0的Web模块支持为模块化开发、框架使用提供了巨大的方便,例如需要在Web应用中使用Web框架,这就只要将该框架的JAR包复制到Web应用中即可。因为这个JAR包的META-INF目录下可以通过web-fragment.xml文件来配置该框架所需要的Servlet、Listener、Filter等,从而避免修改Web应用的web.xml文件。Web模块支持对于模块化开发也有很大的帮助,开发者可以将不同模块的Web组件部署在不同的web-fragment.xml文件中,从而避免所有模块的配置、部署信息都写在web.xml文件中,这对以后的升级、维护将更加方便。

    6.3.3 Servlet3.0提供的异步处理

      在之前的Servlet规范中,如果Servlet作为控制器调用一个耗时的业务方法,nameServlet必须等到业务方法完全返回之后才会生成响应,这将使得Servlet对业务方法的调用变成一种阻塞式的调用,因此效率比较低。

      Servlet 3.0规范引入了异步处理来解决这个问题,异步处理允许Servlet重新发起一条新线程去调用耗时的业务方法,这样就可避免等待。

      Servlet 3.0的异步处理是通过AsyncContext类来处理的,Servlet可通过ServletRequest的如下连个方法开启异步调用、创建AsyncContext对象:

    • AsyncContext startAsync()
    • AsyncContext startAsync(ServletRequest,ServletResponse)

      重复调用上面的方法将得到同一个AsyncContext对象,AsyncContext对象代表异步处理的上下文,它提供了一些工具方法,可完成设置异步调用的超时时长,dispatch用于请求、启动后台线程、获取request、response对象等功能。

    示例:

    创建异步处理的Servlet类:

    @WebServlet(urlPatterns = "/async", asyncSupported = true)
    public class AsyncServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	@Override
    	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    		response.setContentType("text/html;charset=GBK");
    		PrintWriter out = response.getWriter();
    		out.println("<title>异步调用示例</title>");
    		out.println("进入Servlet的时间:" + new java.util.Date() + ".<br/>");
    		// 创建AsyncContext,开始异步调用
    		AsyncContext actx = request.startAsync();
    		// 设置异步调用的超时时长
    		actx.setTimeout(60 * 1000);
    		// 启动异步调用的线程,该线程以异步方式执行
    		actx.start(new GetBooksTarget(actx));
    		out.println("结束Servlet的时间:" + new java.util.Date() + ".<br/>");
    		out.flush();
    	}
    }

    下面创建线程执行体代码:

    public class GetBooksTarget implements Runnable {
    	private AsyncContext actx = null;
    	public GetBooksTarget(AsyncContext actx) {
    		this.actx = actx;
    	}
    	public void run() {
    		try {
    			// 等待5秒钟,以模拟业务方法的执行
    			Thread.sleep(5 * 1000);
    			ServletRequest request = actx.getRequest();
    			List<String> books = new ArrayList<String>();
    			books.add("疯狂Java讲义");
    			books.add("轻量级Java EE企业应用实战");
    			books.add("疯狂Ajax讲义");
    			request.setAttribute("books", books);
    			actx.dispatch("/async.jsp");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }

      该线程体让线程暂停5秒来模拟调用耗时的业务方法,最后调用AsyncContext的dispatch方法把请求dispatch到指定JSP页面。

      被异步请求dispatch的目标页面需要指定session=“false”,表明该页面不会重新创建session。下面是async.jsp页面代码:

    <%@ page contentType="text/html; charset=GBK" language="java"
    	session="false"  isELIgnored="false"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <ul>
    	<c:forEach items="${books}" var="book">
    		<li>${book}</li>
    	</c:forEach>
    </ul>
    <%
    	out.println("业务调用结束的时间:" + new java.util.Date());
    	if (request.isAsyncStarted()) {
    		// 完成异步调用
    		request.getAsyncContext().complete();
    	}
    %>

    上面使用注解的方式已经配置好,如果要使用web.xml配置,则需要如下配置:

    <servlet> 
    	<servlet-name>async</servlet-name> 
    	<servlet-class>lee.AsyncServlet</servlet-class> 
    	<async-supported>true</async-supported> 
    </servlet> 
    <servlet-mapping> 
    	<servlet-name>async</servlet-name> 
    	<url-pattern>/async</url-pattern> 
    </servlet-mapping>

      对于支持异步调用的Servlet来说,当Servlet以异步方式启用新线程之后,该Servlet的执行不会被阻塞,该Servlet将可以向客户端浏览器生成响应——当新线程执行完成后,新线程生成的响应再次被送往客户端浏览器。

    输出如下:


      当Servlet启用异步调用的线程之后,该线程的执行过程对开发者是透明的。但在有些情况下,开发者需要了解该异步线程的执行细节,并针对特定的执行结果进行针对性处理,这可借助于Servlet3.0提供的异步监听器来实现。
      异步监听器需要实现AsyncListener接口,实现该接口的监听器类需要实现如下4个方法:

    • onStartAsync(AsyncEvent event):当异步调用开始时触发该方法
    • onComplete(AsyncEvent event):当异步调用完成时触发该方法
    • onError(AsyncEvent event):当异步调用错误时触发该方法
    • onTimeout(AsyncEvent event):当异步调用超时时触发该方法

      设计好异步监听器之后,需要通过AsyncContext来注册监听器,调用该对象的addListener()方法即可注册监听器。

      Filter与Servlet具有很大的相似性,因此Servlet3.0规范支持在Filter中使用异步调用。在Filter中进行异步调用与在Servlet中进行异步调用的效果完全相似,因此不进行叙述。

    6.3.4 改进的Servlet API

    重大的改进包括:

    • HttpServletRequest增加了对文件上传的支持
    • ServletContext允许通过编程的方式动态注册Servlet、Filter

    HttpServletRequest提供了如下两个方法来处理文件上传:

    • Part getPart(String name):根据名称来获取文件上传域
    • Collection<Part> getParts():获取所有的文件上传域

      上面两个方法的返回值都涉及一个API:Part,每个Part对象对应于一个文件上传域,该对象提供了大量方法来访问上传文件的文件类型、大小、输入流等,并提供了一个write(String file)方法将上传文件写入服务器磁盘。

      为了向服务器上传文件,需要在表单里使用<input type="file" ../>文件域,这个文件域会在HTML页面上产生一个单行文本框和一个“浏览”按钮,浏览者可通过该按钮选择需要上传的文件。除此之外,上传文件一定要为表单域设置enctype属性。

    表单的enctype属性指定的是表单数据的编码方式,该属性有如下三个值:

    • application/x-www-form-urlencoded:这是默认的编码,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式
    • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容封装到请求参数里
    • text/plain:这种编码方式当表单的action属性为mailto:URL的形式时比较方便,这种方式主要适用于直接通过表单发送邮件的形式

      如果将enctype设置为application/x-www-form-urlencoded,或不设置enctype属性,提交表单时只会发送文件域的文本框里的字符串,也就是浏览者所选择文件的绝对路径,对服务器获取该文件在客户端上的绝对路径没有任何作用,因为服务器不可能访问客户机的文件系统。

    示例如下:

    创建上传页面upload.jsp

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

      上面的页面中的表单需要设置enctype=“multipart/form-data”,这表明该表单可用于上传文件。上面表单中定义了两个表单域:一个普通的文本框,它将生成普通请求参数;一个文件上传域,它用于上传文件。对于传统的文件上传需要借助于common-fileupload等工具,处理起来极为复杂,借助于Servlet 3.0的API,处理文件上传将变得十分简单。如下面的Servlet代码:

    @WebServlet(name = "upload", urlPatterns = { "/upload" })
    @MultipartConfig
    public class UploadServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    		response.setContentType("text/html;charset=GBK");
    		PrintWriter out = response.getWriter();
    		request.setCharacterEncoding("GBK");
    		// 获取普通请求参数
    		String name = request.getParameter("name");
    		out.println("普通的name参数为:" + name + "<br/>");
    		// 获取文件上传域
    		Part part = request.getPart("file");
    		// 获取上传文件的文件类型
    		out.println("上传文件的的类型为:" + part.getContentType() + "<br/>");
    		// 获取上传文件的大小。
    		out.println("上传文件的的大小为:" + part.getSize() + "<br/>");
    		// 获取该文件上传域的Header Name
    		Collection<String> headerNames = part.getHeaderNames();
    		// 遍历文件上传域的Header Name、Value
    		for (String headerName : headerNames) {
    			out.println(headerName + "--->" + part.getHeader(headerName) + "<br/>");
    		}
    		// 获取包含原始文件名的字符串
    		String fileNameInfo = part.getHeader("content-disposition");
    		// 提取上传文件的原始文件名
    		String fileName = fileNameInfo.substring(fileNameInfo.indexOf("filename=\"") + 10, fileNameInfo.length() - 1);
    		// 将上传的文件写入服务器
    		part.write(getServletContext().getRealPath("/uploadFiles") + "/" + fileName); // ①
    	}
    }

      上面Servlet使用了@MultipartConfig修饰,处理文件上传的Servlet应该使用该注解修饰。接下来该Servlet中HttpServletRequest就可通过getPart(String name)方法来获取文件上传域——就像获取普通请求参数一样。

    注:上面的Servlet中将会把上传的文件保存到Web应用的根路径下的uploadFiles目录下,因此我们还需要在该Web应用的根目录下创建uploadFiles目录。
      上面Servlet上传时保存的文件名直接使用了上传文件的原始文件名,在实际项目中一般不会这么做,因为可能多个用户可能上传同名的文件,这样将导致后面用户上传的文件覆盖前面用户上传的文件。在实际项目中可借助于java.util.UUID工具类生成文件名。

      ServletContext则提供了如下方法来动态地注册Servlet、Filter,并允许动态设置Web应用的初始化参数:

    • 多个重载的addServlet()方法:动态地注册Servlet
    • 多个重载的addFilter()方法:动态地注册Filter
    • 多个重载的addListener():动态地注册Listener
    • setInitParameter(String name,String value)方法:为Web应用设置初始化参数

    6.4 Servlet 3.1 新增的非阻塞式IO

      Servlet 3.1提供的非阻塞IO进行输入、输出,可以更好地提升性能:

    • ServletInputStream:Servlet用于读取数据的输入流
    • ServletOutputStream:Servlet用于输出数据的输出流

      传统读取方式采用阻塞式IO——当Servlet读取浏览器提交的数据时,如果数据暂时不可用,或数据没有读取完成,Servlet当前所在线程将会被阻塞,无法继续向下执行。
      从Servlet 3.1 开始,ServletInputStream新增了一个setReadListener(ReadListener readListener)方法,该方法允许以非阻塞IO读取数据,实现ReadListener监听器需要实现如下三个方法:

    • onAllDataRead():当所有数据读取完成时激发该方法
    • onDataAvailable():当有数据可用时激发该方法
    • onError(Throwable t):读取数据出现错误时激发该方法

    类似地ServletOutputStream也提供了SetWriterListener()方法。

    在Servlet中使用费阻塞IO非常简单,主要按如下步骤进行即可:

    • 调用ServletRequest的startAsync()方法开启异步模式
    • 通过ServletRequest获取ServletInputStream,并为ServletInputStream设置监听器(ReadListener实现类)
    • 实现ReadListener接口来实现监听器,在该监听器的方法中以非阻塞方式读取数据。

    示例如下:

    创建Servlet类

    @WebServlet(urlPatterns = "/async", asyncSupported = true)
    public class AsyncServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    		response.setContentType("text/html;charset=GBK");
    		PrintWriter out = response.getWriter();
    		out.println("<title>非阻塞IO示例</title>");
    		out.println("进入Servlet的时间:" + new java.util.Date() + ".<br/>");
    		// 创建AsyncContext,开始异步调用
    		AsyncContext context = request.startAsync();
    		// 设置异步调用的超时时长
    		context.setTimeout(60 * 1000);
    		ServletInputStream input = request.getInputStream();
    		// 为输入流注册监听器
    		input.setReadListener(new MyReadListener(input, context));
    		out.println("结束Servlet的时间:" + new java.util.Date() + ".<br/>");
    		out.flush();
    	}
    }

    创建实现ReadListener接口的类

    public class MyReadListener implements ReadListener {
    	private ServletInputStream input;
    	private AsyncContext context;
    
    	public MyReadListener(ServletInputStream input, AsyncContext context) {
    		this.input = input;
    		this.context = context;
    	}
    
    	@Override
    	public void onDataAvailable() {
    		System.out.println("数据可用!!");
    		try {
    			// 暂停5秒,模拟读取数据是一个耗时操作。
    			Thread.sleep(5000);
    			StringBuilder sb = new StringBuilder();
    			int len = -1;
    			byte[] buff = new byte[1024];
    			// 采用原始IO方式读取浏览器向Servlet提交的数据
    			while (input.isReady() && (len = input.read(buff)) > 0) {
    				String data = new String(buff, 0, len);
    				sb.append(data);
    			}
    			System.out.println(sb);
    			// 将数据设置为request范围的属性
    			context.getRequest().setAttribute("info", sb.toString());
    			// 转发到视图页面
    			context.dispatch("/async.jsp");
    		} catch (Exception ex) {
    			ex.printStackTrace();
    		}
    	}
    
    	@Override
    	public void onAllDataRead() {
    		System.out.println("数据读取完成");
    	}
    
    	@Override
    	public void onError(Throwable t) {
    		t.printStackTrace();
    	}
    }

    下面创建页面:

    form.html

    <form action="async" method="post">
    	用户名:<input type="text" name="name"/><br/>
    	密码:<input type="text" name="pass"/><br/>
    	<input type="submit" value="提交">
    	<input type="reset" value="重设">
    </form>

    async.jsp

    <%@ page contentType="text/html; charset=GBK" language="java"
    	session="false"%>
    <div style="background-color: #ffffdd; height: 80px;">
    	浏览器提交数据为:${info}<br />
    	<%=new java.util.Date()%>
    </div>

    6.5 Tomcat 8的WebSock支持(暂未完成)

    赞赏

    展开全文
  • JSP & Servlet学习笔记(第2版)

    千次阅读 2012-12-12 11:01:47
    书名: JSP & Servlet学习笔记(第2版)  作者: (台湾)林信良 ISBN: 978-7-302-28366-9 定价: 58.00元 出版日期: 2012.5 图书分类: 程序设计/ Java编程/JSP 图书样章试读:...
  • JSP

    万次阅读 多人点赞 2018-09-17 16:16:44
    jsp简介 JSP(Java Server Pages)是JavaWeb服务器端的动态资源,它与html页面的作用是相同的,显示数据和获取数据。 jsp: 作为请求发起页面,例如显示表单、超链接。 : 作为请求结束页面,例如显示数据。 ...
  • jsp简介及工作原理

    万次阅读 2017-08-05 07:52:31
    什么是jsp JSP全名为Java Server Pages,中文名叫java服务器页面,是一种动态页面技术,逻辑从servlet中分离出来。在传统的网页HTML(标准通用标记语言的子集)文件(*.htm,*.html)中插入Java程序段(Scriptlet)和JSP...
  • JSP快速入门教程——全十讲

    万次阅读 多人点赞 2008-03-12 19:26:00
    第一讲(参考《Java Web程序设计基础教程》第1章)1 JSP 和 Java的关系 一般Java指的标注版 Java SE 另外两个版本:Java EE 和 Java ME JSP属于Java EE的一部分。 Java EE: 组件:Web层组件(JSP+Servlet)+...
  • jsp页面中把多个商品的详细信息传到另一个jsp页面中并显示该怎么做
  • JSP之换行的实现...

    万次阅读 2012-05-13 22:22:42
    JSP换行的实现,其实是通过html换行标签来实现的,这也说明了,JSP页面经服务器转换后, 最终变成了html页面... 效果截图如下: 代码如下: //输出数据库的路径,然后换行 out.println("数据库路径为:"+conn....
  • 一: <script> $(function () { location.href="${pageContext.request.contextPath}/pages/main.jsp"; }); </script>...jsp:forward page="/pages/main.jsp"></jsp:forward&g...
  • 如何在Jsp页面中导入JAVA类。

    万次阅读 2004-09-15 11:36:00
    BBS0010AF BBS0010 = (BBS0010AF) session.getAttribute("BBS0010");%>
  • jsp页面中如何使用超链接

    万次阅读 2017-04-01 16:16:10
    以下是一个例子 注册
  • eclipse 不让验证jsp 设置

    万次阅读 2013-03-05 21:41:16
    windows->preferences->MyEclipse->Validation 勾选JSP Syntax Validator为Manual(去掉Build的勾) 如想验证语法时用在JSP文件中右键MyEclipse->Run Validation
  • JSP中大于号小于号的转义字符

    万次阅读 2010-08-18 16:04:00
    JSP 中大于号、小于号、单引号 分别为: > < " 
  • jsp页面给字体加颜色

    万次阅读 2018-02-27 15:19:51
    jsp页面给字体加颜色&lt;span style="color:red"&gt;补件完成&lt;/span&gt;
  • 从一个jsp页面向另一个jsp页面传值

    万次阅读 2012-10-12 15:32:39
    只要以下做法就可以实现:A.jsp :通过post 和get、连接都可以传 post和get 就不提了。 连接的: '>传递到B页面 B.jsp : B页面通过如下代码接收 String name=request.getParameter("name"); out.println("接收到:...
  • JSP中引用JS文件的三种方法

    万次阅读 2016-11-11 10:57:24
    JSP中引用JS文件的三种方法:1、如果是直接访问JSP,则使用相对于JSP页面的相对路径:当项目目录如图所示时,则在NewFile.jsp中访问jquery-1.9.1.min.js的方法为:...则使用相对于W
1 2 3 4 5 ... 20
收藏数 926,606
精华内容 370,642
关键字:

jsp