JSP/Servlet

  1. jsp 3个编译指令

    • page:该指令是针对当前页面的指令
    • include:用于指定包含另一个页面
    • taglib:用于定义和访问自定义标签

  2. jsp7个动作指令

    • jsp:forward:执行页面转向,将请求的处理转发到下一个页面。
    • jsp:param:用于传递参数,必须与其他支持参数的标签一起使用。
    • jsp:include:用于动态引入一个jsp页面。
    • jsp:plugin:用于下载JavaBean或Applet到客户端执行。很少会用到。
    • jsp:useBean:创建一个JavaBean实例。
    • jsp:setProperty:设置JavaBean实例的属性。
    • jsp:getProperty:获取JavaBean实例的属性。

  3. 静态导入和动态导入的三点区别

    • 静态导入是将导入页面的代码完全融入,两个页面融合成一个整体servlet;而动态导入则在servlet中使用include方法来引入被导入页面的内容。
    • 静态导入时被导入页面的编译指令会起作用;而动态导入时被导入页面的编译指令则失去作用,只是插入被导入页面的body内容。
    • 动态包含还可以增加额外的参数。

  4. Scope属性用于指定JavaBean实例的作用范围:

    • page:仅在该页面有效。
    • request:在本次请求有效。
    • session:在本次session内有效。
    • application:在本次应用内一直有效。

  5. 重定向

    重定向是response的另外一个用处,与forward不同的是,重定向会丢失所有的请求参数和request范围的属性,因为重定向将生成第二次请求,与前一次请求不在同一个request范围内,所以发送一次请求的请求参数和request范围的属性全部丢失。

    HttpServletResponse提供了一个sendRedirect(String path)方法,该方法用于重定向到path资源,即重新向path资源发送请求。

    forward和redirect对比

    执行redirect后生成第二次请求,而forward依然是上一次请求。
    redirect的目标页面不能访问原请求的请求参数,因为是第二次请求了,所有原请求的请求参数、request范围的属性全部丢失。forward的目标页面可以访问原请求的请求参数,因为依然是同一次请求,所有原请求的请求参数、request范围的属性全部存在。
    地址栏改为重定向的目标URL。相当于在浏览器地址栏里输入新的URL后按回车键。而forward地址栏里请求的URL不会改变

  6. 增加Cookie

    Cookie与session的不同之处在于:session会随浏览器的关闭而失效,但Cookie会一直存放在客户端机器上,除非超出Cookie的生命期限

    增加Cookie也是使用response内置对象完成的,response对象提供了如下方法。
    void addCookie(Cookie cookie):增加Cookie。
    增加Cookie请按如下步骤进行。

    • 创建Cookie实例,Cookie的构造器为Cookie(String name, String value)。
    • 设置Cookie的生命期限,即该Cookie在多长时间内有效。
    • 向客户端写Cookie。
       
              
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
       
              
      <%
      //增加cookie
      String name = request.getParameter( "name");
      //以获取到的请求参数为值,创建一个Cookie对象
      Cookie c = new Cookie( "username" , name);
      //设置Cookie对象的生存期限
      c.setMaxAge( 24 * 3600);
      //向客户端增加Cookie对象
      response.addCookie(c);
      %>
      访问客户端Cookie使用request对象,request对象提供了getCookies()方法,该方法将返回客户端机器上所有Cookie组成的数组,遍历该数组的每个元素,找到希望访问的Cookie即可。
      使用Cookie对象必须设置其生存期限,否则Cookie将会随浏览器的关闭而自动消失。
      默认情况下,Cookie值不允许出现中文字符,如果我们需要值为中文内容的Cookie怎么办呢?同样可以借助于java.net.URLEncoder先对中文字符串进行编码,将编码后的结果设为Cookie值。当程序要读取Cookie时,则应该先读取,然后使用java.net.URLDecoder对其进行解码。

session

session对象也是一个非常常用的对象,这个对象代表一次用户会话。一次用户会话的含义是:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开为止,这个过程就是一次会话

session通常用于跟踪用户的会话信息,如判断用户是否登录系统,或者在购物车应用中,用于跟踪用户购买的商品等。

session对象是HttpSession的实例,HttpSession有如下两个常用的方法。

setAttribute(String attName,Object attValue):设置session范围内attName属性的值为attValue。
getAttribute(String attName):返回session范围内attName属性的值。

关于session还有一点需要指出,session机制通常用于保存客户端的状态信息,这些状态信息需要保存到Web服务器的硬盘上,所以要求session里的属性值必须是可序列化的,否则将会引发不可序列化的异常

session的属性值可以是任何可序列化的Java对象。


JSP/Servlet的生命周期

创建Servlet实例有两个时机。

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

每个Servlet的运行都遵循如下生命周期

  1. 创建Servlet实例。
  2. Web容器调用Servlet的init方法,对Servlet进行初始化。
  3. Servlet初始化后,将一直存在于容器中,用于响应客户端请求。如果客户端发送GET请求,容器调用Servlet的doGet方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost方法处理并响应请求。或者统一使用service()方法处理来响应用户请求。
  4. Web容器决定销毁Servlet时,先调用Servlet的destroy方法,通常在关闭Web应用之时销毁Servlet。

使用Servlet作为控制器

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

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

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

Model通常由JavaBean来充当,所有业务逻辑、数据访问逻辑都在Model中实现。实际上隐藏在Model下的可能还有很多丰富的组件,例如DAO组件、领域对象等。

下面是MVC中各个角色的对应组件。

  • M:Model,即模型,对应JavaBean。
  • V:View,即视图,对应JSP页面。
  • C:Controller,即控制器,对应Servlet。

Filter介绍

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

Filter有如下几个用处。

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

Filter有如下几个种类。

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

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

  • 创建Filter处理类。
  • web.xml文件中配置Filter。

创建Filter类

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

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

配置Filter

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

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

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

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

@WebFilter(filterName=”log”, urlPatterns={“/*”})
@WebFilter修饰一个Filter类,用于对Filter进行配置,它支持如表所示的常用属性

@WebFilter支持的常用属性

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

在web.xml文件中配置Filter与配置Servlet非常相似,需要为Filter指定它所过滤的URL,并且也可以为Filter配置参数。

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
    
<!-- 定义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>

实际上Filter和Servlet极其相似,区别只是Filter的doFilter()方法里多了一个FilterChain的参数,通过该参数可以控制是否放行用户请求。

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

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

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

下载URL Rewrite应下载其src项(urlrewritefilter-3.2.0-src.zip),下载完成后得到一个urlrewritefilter-3.2.0-src.zip文件,将该压缩文件解压缩,得到如下文件结构。

  • api:该路径下存放了URL Rewrite项目的API文档。
  • lib:该路径下存放了URL Rewrite项目的编译和运行所需的第三方类库。
  • manual:该路径下存放了URL Rewrite项目使用手册。
  • src:该路径下存放了URL Rewrite项目的源代码。
  • webapp:该路径是一个URL Rewrite的示例应用。
  • LICENSE.txt等杂项文档。

web.xml

 
    
1
2
3
4
5
6
7
8
9
10
 
    
<!-- 配置Url Rewrite的Filter -->
<filter>
<filter-name>UrlRewriteFilter </filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter </filter-class>
</filter>
<!-- 配置Url Rewrite的Filter拦截所有请求 -->
<filter-mapping>
<filter-name>UrlRewriteFilter </filter-name>
<url-pattern>/* </url-pattern>
</filter-mapping>

上面的配置片段指定使用URL Rewrite Filter拦截所有的用户请求。

在应用的WEB-INF路径下增加urlrewrite.xml文件,该文件定义了伪静态映射规则,这份伪静态规则是基于正则表达式的。

下面是本应用所使用的urlrewrite.xml伪静态规则文件。

 
    
1
2
3
4
5
6
7
8
9
10
 
    
<?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到user.jsp页面,并将(\w)正则表达式所匹配的内容作为username参数值。根据这个伪静态规则,我们应该为该应用提供一个userinf.jsp页面,该页面只是一个模拟了一个显示用户信息的页面

userinf.jsp

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
    
<% @ 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></head>
<body>
<%
//此处应该通过数据库读取该用户对应的信息
//此处只是模拟,因此简单输出:
out.println("现在时间是:" + new java.util.Date() + "<br/>");
out.println("用户名:" + user); %>
</body> </html>

Listener 介绍

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

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

使用Listener只需要两个步骤:

  • 定义Listener实现类。
  • 通过Annotation或在web.xml文件中配置Listerner。

实现Listenner类

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

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

下面先以ServletContextListener为例来介绍Listener的开发和使用,ServletContextListener用于监听Web应用的启动和关闭。该Listener类必须实现ServletContextListener接口,该接口包含如下两个方法。

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

配置Listener

为Web应用配置Listener也有两种方式:

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

在web.xml中使用元素进行配置时只要配置如下子元素即可。
listener-class:指定Listener实现类。

使用ServletContextAttributeListener

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

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

使用ServletRequestListener和ServletRequestAttributeListener

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

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

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

使用HttpSessionListener和HttpSessionAttributeListener

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

  • sessionCreated(HttpSessionEvent se):用户与服务器的会话开始、创建时时触发该方法。
  • sessionDestroyed(HttpSessionEvent se):用户与服务器的会话断开、销毁时触发该方法。

HttpSessionAttributeListener则用于监听HttpSession(session)范围内属性的变化,实现该接口的监听器需要实现attributeAdded、attributeRemoved、attributeReplaced三个方法。

表达式语言

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

表达式语言的语法格式是: ${expression}
如果想输出$符号,则在$前加转义字符\

  1. 表达式语言支持的算术运算符和逻辑运算符

    所有java语言里的算术运算符都支持,甚至java不支持的运算符也支持。
    div(除)、mod(取余)、lt(小于)、gt(大于)、ge(大于等于)、le(小于等于)、eq(等于)、ne(不等于)
    表达式语言不仅可在数字与数字之间比较,还可在字符与字符之间比较,字符串的比较是根据其对应UNICODE值来比较大小的。
    表达式语言把所有数值都当成浮点数处理,所以3/0的实质是3.0/0.0,得到结果应该是Infinity。

  2. 表达式语言的内置对象

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

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

Tag File中只有如下几个内置对象。

  • request:与JSP脚本中的request对象对应。
  • response:与JSP脚本中的response对象对应。
  • session:与JSP脚本中的session对象对应。
  • application:与JSP脚本中的application对象对应。
  • config:与JSP脚本中的config对象对应。
  • out:与JSP脚本中的out对象对应。

Servlet 3.0新特性

Servlet 3.0的Annotation

Servlet 3.0规范在javax.servlet.annotation包下提供了如下Annotation。

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

Servlet 3.0提供的异步处理

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

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

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

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

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

AsyncServlet.java:

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
    
@WebServlet(urlPatterns= "/async",asyncSupported= true)
public class AsyncServlet extends HttpServlet {
@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/>");
out.flush();
//创建AsyncContext,开始异步调用
AsyncContext actx = request.startAsync();
//设置异步调用的超时时长
actx.setTimeout( 30* 1000);
//启动异步调用的线程
actx.start( new Executor(actx));
out.println( "结束Servlet的时间:" + new java.util. Date() + ".<br/>");
out.flush();
}
}

下面是线程执行体的代码。

Executor.java

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
    
public class Executor implements Runnable {
private AsyncContext actx = null;
public Executor(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( "疯狂XML讲义");
request.setAttribute( "books" , books);
actx.dispatch( "/async.jsp");
} catch(Exception e){
e.printStackTrace();
}
}
}

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

被异步请求dispatch的目标页面需要指定session=”false”,表明该页面不会重新创建session。

async.jsp:

 
    
1
2
3
4
5
6
7
8
9
10
 
    
<% @ page contentType="text/html; charset=GBK" language="java" session="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());
//完成异步调用
request.getAsyncContext().complete();
%>

为Servlet开启异步调用有两种方式:

  • 为@WebServlet指定asyncSupported=true。
  • 在web.xml文件的元素中增加子元素。

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

异步监听器需要实现AsyncListener接口,实现该接口的监听器类需要实现如下4个方法。

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

提供了异步监听器之后,还需要通过AsyncContext来注册监听器,调用该对象的addListener()方法即可注册监听器。例如在上面的Servlet中增加如下代码即可注册监听器:

 
    
1
2
3
 
    
AsyncContext actx = request.startAsync();
//为该异步调用注册监听器
actx.addListener( new MyAsyncListener());

虽然上面的MyAsyncListener监听器类可以监听异步调用开始、异步调用完成两个事件,但从实际运行的结果来看,它并不能监听到异步调用开始事件,这可能是因为注册该监听器时异步调用已经开始了的缘故。

在Filter中进行异步调用与在Servlet中进行异步调用的效果完全相似

改进的Servlet API

Servlet 3.0还有一个改变是改进了部分API,这种改进很好地简化了Java Web开发。其中两个较大的改进是:

HttpServletRequest增加了对文件上传的支持。

ServletContext允许通过编程的方式动态注册Servlet、Filter。

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

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

Collection 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属性,提交表单时只会发送文件域的文本框里的字符串,也就是浏览者所选择文件的绝对路径,对服务器获取该文件在客户端上的绝对路径没有任何作用,因为服务器不可能访问客户机的文件系统。

UploadServlet.java:

 
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
    
@WebServlet(name= "upload" , urlPatterns={ "/upload"})
@MultipartConfig
public class UploadServlet extends HttpServlet {
public void service(HttpServletRequest request , HttpServletResponse response) throws IOException , ServletException {
response.setContentType( "text/html;charset=GBK");
PrintWriter out = response.getWriter();
//获取普通请求参数
String fileName = request.getParameter( "name");
//获取文件上传域
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/>");
}
//将上传的文件写入服务器
part.write(getServletContext().getRealPath( "/uploadFiles") + "/" + fileName );
//①
}
}

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

与Servlet 3.0所有Annotation相似的是,Servlet 3.0为@提供了相似的配置元素,我们同样可以通过在元素中添加子元素来达到相同的效果。

上面Servlet上传时保存的文件名直接使用了name请求参数,实际项目中一般不会这么做,因为可能多个用户会填写相同的name参数,这样将导致后面用户上传的文件覆盖前面用户上传的图片。实际项目中可借助于java.util.UUID工具类生成文件名。

http://howiefh.github.io/2015/03/13/jsp-servlet-note/#more


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值