《轻量级JavaEE-企业应用实战-Spring+Structs2+Hibernate》(二)

        • Filter
          • Filter配置
          • Filter的用途
          • 获取Filter初始化参数
        • Listener
          • Listener的配置
        • JSP2.0特性
          • 配置Jsp属性
        • Jsp中表达式语言
          • el表达式支持的逻辑运算和算术运算
          • el表达式的内置对象
        • el表达式的自定义函数
          • 定义表达式自定义函数步骤
          • 表达式自定义函数例子
        • TagFile
        • Servlet 3.0 web模块支持
          • 例子
        • Servlet3.0异步处理
          • 实现例子
          • 异步监听器
            • 实现例子
        • 改进的Serlvet3.0特性
          • 上传文件的实例

Filter

Filter是Servlet 的一种“加强版”,它主要用于对用户请求进行预处理,也可以对FilterHttpServletResponse 进行后处理,是个典型的处理链。Filter 也可对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter 向用户请求生成响应。

使用Filter 完整的流程是: Filter 对用户请求进行预处理,接着将请求交给Servlet 进行处理并生成响应,最后Filter 再对服务器响应进行后处理。

Filter 有如下几个用处。

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

Filter 有如下几个种类:

  1. 用户授权的Filter: Filter 负责检查用户请求,根据请求过滤用户非法请求。
  2. 日志Filter: 详细记录某些特殊的用户请求。
  3. 负责解码的Filter; 包括对非标准编码的请求解码。
  4. 能改变XML 内容的XSLT Filter 等。

Filter 可负责拦截多个请求或响应;- 一个请求或响应也可被多个Filter 拦截。

public class LoginFilter implements Filter{

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        //释放资源

    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest)arg0;//将request强转成http的request
        System.out.println("已经拦截到用户的url");
        long start = System.currentTimeMillis();
        arg2.doFilter(arg0, arg1);//只是链式处理,请求还是要到目的地址,如果是重定向直接就不要这句。
        long end = System.currentTimeMillis();
        System.out.println("请求的时间"+(end-start));


    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
        //初始化配置
    }

}

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

doFilter0方法里多了一个FilterChain 的参数,通过该参数可以控制是否放行用户请求。

Filter配置
  • 在Filter 类中通过Annotation 进行配置。
@WebFilter(filterName="LoginFilter",urlPatterns="/*")

《轻量级JavaEE-企业应用实战-Spring+Structs2+Hibernate》(二)_第1张图片

  • 在web.xml 文件中通过配置文件进行配置。
  <filter>
        <filter-name>LoginFilterfilter-name>
        <filter-class>com.example.test.Filter.LoginFilterfilter-class>
    filter>
    <filter-mapping>
        <filter-name>LoginFilterfilter-name>
        /*
    filter-mapping>
Filter的用途

实际上Filter 和Servlet 极其相似,区别Filter 里doFilter(方法里的代码就是从多个Servlet 的service(方法里抽取的通用代码,通过使用Filter 可以实现更好的代码复用。

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

《轻量级JavaEE-企业应用实战-Spring+Structs2+Hibernate》(二)_第2张图片

获取Filter初始化参数
package com.example.test.Filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@WebFilter(filterName="LoginFilter",urlPatterns="/*")
public class LoginFilter implements Filter{
      private FilterConfig config;
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        //释放资源

    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {
        // TODO Auto-generated method stub
        String encoding = config.getInitParameter("encoding");
        String loginpage = config.getInitParameter("loginpage");
        String prologin = config.getInitParameter("prologin");
        HttpServletRequest request = (HttpServletRequest)arg0;
        HttpSession seesion = request.getSession();
        String requestpath = request.getRequestURI();
        String username = (String) seesion.getAttribute("username");
        if(username==null&&!requestpath.endsWith(loginpage)&&!requestpath.endsWith(prologin)){
            request.getRequestDispatcher(loginpage).forward(arg0, arg1);
        }else{
            arg2.doFilter(arg0, arg1);//放行。
        }



    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
        //初始化配置
        this.config = arg0;
    }

}
  <filter>
        <filter-name>LoginFilterfilter-name>
        <filter-class>com.example.test.Filter.LoginFilterfilter-class>
        param>
          <param-name>encodingparam-name>
          <param-value>GBKparam-value>
        param>
         param>
          <param-name>loginpageparam-name>
          <param-value>/login.jspparam-value>
        param>
         param>
          <param-name>prologinparam-name>
          <param-value>/prologin.jspparam-value>
        param>
    filter>

Listener

当Web 应用在Web 容器中运行时,Web 应用内部会不断地发生各种事件: 如Web 应用被启动、Web 应用被停止,用户session 开始、用户session 结束、用户请求到达等,通常来说,这些Web 事件对开发者是透明的。

实际上,ServletAPI 提供了大量监听器来监听Web 应用的内部事件,从而允许当Web 内部事件发生时回调事件监听器内的方法。

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

对于监听对象的不同,只要相应的实现以上的接口,实现方法即可。

Listener的配置
  • 使用@WebListener 修饰Listener 实现类即可。
@WebListener
  • 在web.xml 文档中使用
    <listener>
        <listener-class>com.example.listener.MyListenerlistener-class>
    listener>

在上面的我们已经知道了要监听什么,就实现什么接口,但是我们知道Java是多实现的,我们可以通过一个类实现多个接口,从而实现监听多个对象。

注意实现Seesion对象监听,一定要注意浏览器的缓存影响。

JSP2.0特性

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

如果需要使用JSP 2 语法,其web.xml 文件必须使用Servlet 2.4 以上版本的配置文件。现在正常Servlet版本都是Servlet3.0以上。

查看Servlet的版本,到web.xml中:

这里写图片描述

配置Jsp属性
  <jsp-config>
  <jsp-property-group>
  <url-pattern>*.jsp</url-pattern> 对应的哪些jsp文件配置,可以使用正则表达式
  <el-ignored>true</el-ignored>   是否允许el表达式,false为可以使用el表达式,如果true,这输出表达式,不会计算。
  <page-encoding>GBK</page-encoding>  jsp页面的编码
  <scripting-invalid>true</scripting-invalid>  是否允许用java脚本,true是不可以使用,如果jsp包含java脚本,则运行报错。
  <include-prelude></include-prelude>  隐式导入页面头
  <include-coda></include-coda>  隐式导入页面尾
  </jsp-property-group>
  </jsp-config>

主要包括如下4 个方面。

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

需要注意的地方:

  • 在使用配置jsp信息不能和原有的jsp页面page指令信息冲突。
  • 多个jsp配置可以用多个jsp-property-group。
  • 导入其他的jsp页面,需要注意新建一个jsp文件,但是修改文件后缀名为jspf,另外涉及导入的jsp文件中,删除如下几句:
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

 <base href="<%=basePath%>">

在要主页面中,必须删除以上代码,使用这种导入方法,导入的jsp中如果有这两句代码,会出现重复,如果没有,在导入之后,会自动产生一个,也会重复。

Jsp中表达式语言

表达式语言(Expression Language) 是一种简化的数据访问方式。使用表达式语言可以方便地访问JSP 的隐含对象
和JavaBeans 组件,在JSP 2 规范中,建议尽量使用表达式语言使JSP 文件的格式一致,避免使用Java 脚本。

el表达式支持的逻辑运算和算术运算
   ${1+2}//3
   ${2*4}//8
   ${'a'>'b'}//false
el表达式的内置对象

使用表达式语言可以直接获取请求参数值,可以获取页面中JavaBean 的指定属性值,获取请求头
及获取page、request.session 和application 范围的属性值等,这些都得益于表达式语言的内置对象。

表达式语言包含如下11个内置对象。

  • pageContext: 代表该页面的pageContext 对象,与JSP 的pageContext 内置对象相同。
  • pageScope: 用于获取page 范围的属性值。
  • requestScope: 用于获取request 范围的属性值。
  • sessionScope:用于获取session 范[ 围的属性值。
  • applicationScope: 用于获取application 范围的属性值。
  • param: 用于获取请求的参数值。
  • paramValues: 用于获取请求的参数值,与param 的区别在于,该对象用于获取属性值为数 组的属性值。
  • header:用于获取请求头的属性值。
  • headerValues: 用于获取请求头的属性值,与header 的区别在于,该对象用于获取属性值 为数组的属性值。
  • initParam:用于获取请求Web 应用的初始化参数。
  • cookie: 用于获取指定的Cookie 值。

可看具体例子:

 <form action="request.jsp" method="post">
     <input type="text" name ="username" }/>
     <input type="submit" value="提交"/>
     form>

    <%
    pageContext.setAttribute("pagecontext","pagecontext");
    session.setAttribute("session", "session");
    request.setAttribute("request1", "request");
    application.setAttribute("application", "application");
    Cookie cookie = new Cookie("name","laoqiang");
    cookie.setMaxAge(12*3600);
    response.addCookie(cookie);
     %>
       ${requestScope.request1}<br>
       ${pageScope.pagecontext}<br>
       ${initParam.user}<br>
       ${param.username}<br>
       ${param["username"]}<br>
       ${header.host}<br>
       ${header["accept"]}<br>
       ${sessionScope.session}<br>
       ${applicationScope.application}<br>
       ${cookie.name.value}

上述的获取中,特别需要注意的有两个一个pageContext和PageScope内置对象,作用效果差不多,但是通过这个内置对象来获取,必须保证在同一个页面赋值和获取,否则无效。另外还有request,必须要保证在同一个请求中在能获取,如果地址栏改变,就默认是两次请求。

另外在获取的时候用方括号和.获取是一样的,可以看作两种方式。

el表达式的自定义函数

加强表达式语言的功能。自定义丽数的开发步骤非常类似于标签的开发步骤,定义方式也几乎一样。区别在于自定义标签直接在页面上生成输出,而自定义函数则需要在表达式语言中使用。

函数功能大大扩充了EL 的功能,EL 本身只是一种数据访问语言,因此它不支持调用方法。如果需要在EL 中进行更复杂的处理,就可以通过函数来完成。函数的本质是:提供一种语法允许在EL 中调用某个类的静态方法。

定义表达式自定义函数步骤
  1. 函数处理类就是普通类,这个普通类中包含若千个静态方法,每个静态方法都可定义成一个函数
  2. 创建tld文件,添加标签。
表达式自定义函数例子
public class MyElFunction {
     public static  String reverse(String text){
         return  new StringBuffer(text).reverse().toString();
     } 

     public static int length(String text){
         return text.length();
     }
}

注意点:函数必须是static修饰,静态的,最好带返回值,因为表达式是直接输出值。

<taglib>
  <tlib-version>1.0tlib-version>
  <jsp-version>1.2jsp-version>
  <short-name>taglibshort-name>
  <uri>/WEB-INF/tag.tlduri>
  <description>
    A simple tab library for the examples
  description>
   <function>
      <name>reversename>//这个name是在使用的时候, ${MyElfunction:**reverse**("dhaskda")}中的黑体字一致
      <function-class>com.example.test.elfunction.MyElFunctionfunction-class>//指定函数的所在类
      <function-signature>java.lang.String reverse(java.lang.String)function-signature> 指定函数的实现方法,包括返回值类型、函数名、参数
   function>

     <function>
      <name>lengthname>
      <function-class>com.example.test.elfunction.MyElFunctionfunction-class>
      <function-signature>int length(java.lang.String)function-signature>
   function>
taglib>

注意:这个需要注意的是如果返回值类型、参数类型是引用类型,例如String必须写成java.lang.String ,否则会出现错误。

在使用的时候必须导入标签库指令:

<%@ taglib prefix="MyElfunction" uri="/WEB-INF/tag.tld" %>  指定它的前缀名,uri正常都写该WEB-INF下的tld文件目录
     ${MyElfunction:reverse("dhaskda")}
     ${MyElfunction:length("hhhh")}

TagFile

TagFile 是自定义标签的简化用法,使用Tag File 可以无须定义标签处理类和标签库文件,但仍然可以在JSP 页面中使用自定义标签。

TagFile 建立一个迭代器标签,其步骤如下。
建立Tag 文件,在JSP 所支持Tag File 规范下,Tag File 代理了标签处理类,它的格式类似于JSP
文件。可以这样理解: 如同JSP 可以代替Servlet 作为表现层一样,Tag File 则可以代替标签处理类。

Tag File 具有以下5 个编译指令。

  • taglib: 作用与JSP 文件中的taglib 指令效果相同,用于导入其他标签库。
  • include:作用与JSP 文件中的include指令效果相同,用于导入其他JSP 或静态页面。
  • tag: 作用类似于JSP 文件中的page 指令,有pageEncoding、body-content 等属性,用于设置页面编码等属性。
  • attribute: 用于设置自定义标签的属性,类似于自定义标签处理类中的标签属性。
  • variable: 用于设置自定义标签的变量,这些变量将传给JSP 页面使用。

下面是一个例子:

<%@tag pageEncoding="GBK" import="java.util.List" %>
<%@ attribute name="bgcolor" %>
<%@ attribute name="title" %> //指定该标签有两个属性
<%
   List<String> list = (List<String>)request.getAttribute("a");
 %>
<table border="1" bgcolor="${bgcolor}" >
<tr>
<td>
${title}
td>
tr>
   <% 
   for(int i = 0;i
   <tr>
   <td>
    <%=list.get(i).toString() %>
   td>
   tr>
   <%
   }
   %>
table>
  <%
   List<String> list = new ArrayList<String>();
   list.add("laoqiang");
   list.add("laohe");
   list.add("qianglao");
   list.add("laohejia");
   request.setAttribute("a", list);
   %>
   <mytagfile:tagfile bgcolor="#99dd99" title="这是tagfile"/>

整个tagfile没有什么难度,需要注意的地方:

<%@taglib prefix="mytagfile" tagdir="/WEB-INF/tags" %>

导入标签库的时候,tagdir放在web-inf文件下,要新建一个tags文件夹,才可以。

TagFile 是自定义标签的简化。事实上,就如同JSP 文件会编译成Servlet一样,Tag File 也会编译成标签处理类,自定义标签的后台依然由标签处理类完成,而这个过程由容器完成。

Servlet 3.0 web模块支持

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

例子

<web-fragment version="3.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">//如果没有这句,xml智能提示就没了,就要手敲啦。

<name>laohename>
<listener>
     <listener-class>com.example.listener.MyListenerOnelistener-class>//这里监听器、servlet、过滤器都可以配置
listener>

<ordering>
<before>
<others/>
before>

ordering>
web-fragment>

将下面的三个文件放到一个文件目录(其中包括web-fragment和所需要类文件以及资源等等),打包成jar:

这里写图片描述

jar在window执行语句:

《轻量级JavaEE-企业应用实战-Spring+Structs2+Hibernate》(二)_第3张图片

将打包好的jar包直拷到项目的lib目录下,就可以直接使用了。

上面我们介绍了使用web-fragment指定加载的顺序,我们同样可以在web.xml指定顺序,它会覆盖web-fragment。

<absolute-ordering>
<name>laohename>//这个name和web-fragment一致
absolute-ordering>

Servlet3.0异步处理

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

由上面可以看出这个异步操作,是针对Servlet。

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

实现例子
    protected void doPost(final HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        final AsyncContext as =  request.startAsync();
        as.setTimeout(10000);
        as.start(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    Thread.sleep(5000);//模拟业务处理
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("我是执行任务返回得到结果");
                request.setAttribute("result","我是异步线程的结果");
                as.dispatch("/index1.jsp");

            }
        });
        System.out.println("我是下面执行的东西");
    }
}
 
"AsyncServlet" method="post"> "submit" value="提交"/>
<% System.out.println("我在主jsp的"); boolean flag = request.isAsyncStarted(); if(flag){ request.getAsyncContext().complete(); System.out.println(flag); System.out.println(request.getAttribute("result")); } %>

上面的代码主要使用了AsyncContext类的complete、dispatch、startAsync,其中使用complete方法必须在startAsync之后,否则会报异常,dispatch是将一部获取的数据结果分发给对应的jsp,complete这个方法尽量在dispatch前面。另外,complete这个方法主要用在客户端中,在客户端发送请求之后,在complete方法之后的代码是会阻塞。

异步监听器

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

实现例子
public class AsyncListener implements javax.servlet.AsyncListener {

    @Override
    public void onComplete(AsyncEvent event) throws IOException {
        // TODO Auto-generated method stub

        System.out.println("这是异步servlet执行完成");

    }

    @Override
    public void onError(AsyncEvent event) throws IOException {
        // TODO Auto-generated method stub
        System.out.println("有错误");

    }

    @Override
    public void onStartAsync(AsyncEvent event) throws IOException {
        // TODO Auto-generated method stub
        System.out.println("这是异步servlet开始");

    }

    @Override
    public void onTimeout(AsyncEvent event) throws IOException {
        // TODO Auto-generated method stub
        System.out.println("这是异步servlet超时");

    }

}

as.addListener(new AsyncListener());//注册监听器

在上述例子中,需要注意的就是监听异步开始的,好像不起作用,网上给的说法是实际在注册的时候就一步开始,这个不必深究。

改进的Serlvet3.0特性

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

Part getPart(String name): 根据名称来获取文件上传域。

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

上传文件的实例
     
"FileServlet" enctype="multipart/form-data" method="post"> type="text" name="filename"/> type="file" name="file"/>//就是浏览文件的 type="submit" value="上传">
@WebServlet(name="com.example.servlet.FileServlet",urlPatterns="/FileServlet")
@MultipartConfig
public class FileServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        PrintWriter pw = resp.getWriter();
        String filename = req.getParameter("filename");
        pw.write("文件名字:"+filename+"\n");
        Part p = req.getPart("file");//通过文件域的name属性来获取part对象
        String filetype =  p.getContentType();
        pw.write("文件类型:"+filetype+"\n");
        Collection headers = p.getHeaderNames();
        for(String headername :headers){
            pw.write("文件头信息:"+headername+"\n");
        }
        pw.write(getServletContext().getRealPath("upload")+"/"+filename);   
    }

}

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

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

ServletContext 则提供了如下方法来动态地注册Servet、Filter。

  1. 多个重载的addServlet: 动态地注册Servlet.
  2. 多个重载的addFilter: 动态地注册Filter.
  3. 多个重载的addListener: 动态地主册Listener.

之前已经具体讲过了,动态注册,就是在使用的时候才注册,灵活性好。

你可能感兴趣的:(《轻量级JavaEE-企业应用实战-Spring+Structs2+Hibernate》(二))