JSP&Servlet学习笔记(二)
来源:互联网 发布:淘宝优惠券链接转化 编辑:程序博客网 时间:2024/06/05 18:30
一、Servlet
JSP的本质就是Servlet,而Web容器会将JSP编译成对应的Servlet。自MVC规范出现后,Servlet的责任开始明确下来,仅仅作为控制器使用,不再需要生成页面标签,也不再作为视图层角色使用。
1.1 Servlet的开发
Servlet是运行在服务器端的程序,用于处理及响应客户端的请求。Servlet是个特殊的Java类必须继承HttpServlet,每个Servlet可以响应客户端的请求。Servlet提供不同的方法用于响应客户端请求:
- doGet:用于响应客户端的GET请求
- doPost:用于响应客户端的POST请求
- doPut&doDelete:基本不用
- init(ServletConfig config):创建Servlet实例时,调用该方法的初始化Servlet资源
- destroy():销毁Servlet实例时,自动调用该方法的回收资源。
注意:如果重写init()方法,则应该在重写该方法的第一行调用super.init(config)。
Servlet和JSP的区别在于:
- Servlet中没有内置对象,原来JSP的内置对象都必须有程序显示创建
- 对于静态的HTML标签,Servlet都必须使用页面输出流逐行输出
1.2 Servlet的配置
- 在Servlet类中使用@WebServlet注解进行配置
- 通过在web.xml文件中进行配置
- name:指定该Servlet的名称
- urlPatterns / value:这两个属性的作用完全相同,都是指定该Servlet处理的URL
- asyncSupported:指定该Servlet是否支持异步操作模式
- display:指定该Servlet的显示名
- initParams:用于为该Servlet配置参数
- loadOnStartup:用于将该Servlet配置成load-on-startup的Servlet
- 不要在web.xml文件的根元素(<web-app../>)中指定metadata-complete=“true”
- 不要在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的声明周期
- 客户端第一次请求某个Servlet时,系统创建该Servlet的实例:大部分的Servlet都是这种Servlet
- Web应用启动时立即创建Servlet实例,即load-on-startup Servlet
1.4 load-on-startup Servlet
- 在web.xml文件中通过<servlet../>元素的<load-on-startup../>子元素进行配置
- 通过@WebServlet注解的loadOnStartup属性指定
<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的配置参数
- 通过@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对象获取配置参数:dirverString driver = config.getInitParameter("driver");// 通过ServletConfig对象获取配置参数:urlString url = config.getInitParameter("url");// 通过ServletConfig对象获取配置参数:userString user = config.getInitParameter("user");// 通过ServletConfig对象获取配置参数:passString 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作为控制器
- 开发效率低,所有的HTML标签都需使用页面输出流完成
- 不利于团队协作开发,美工人员无法参与Servlet界面的开发
- 程序可维护性差,即使修改一个按钮的标题,都必须重新编辑Java代码,并重新编译
- 负责收集用户请求参数
- 将应用的处理结果、状态数据呈现给用户
二、JSP2的自定义标签
JSP2规范简化了标签的开发,在JSP2中开发标签库只需要如下几个步骤:- 开发自定义标签处理类
- 建立一个*.tld文件,每个*.tld文件对应一个标签库,每个标签库可包含多个标签
- 在JSP文件中使用自定义标签
2.1 开发自定义标签类
- 如果标签类包含属性,每个属性都有对应的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文件复制到Web应用的WEB-INF路径下(可以是任意子路径下)。
<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">
<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属性来定位标签库的
- name:该标签的名称,这个子元素很重要,JSP页面中就是根据该名称来使用此标签的
- tag-class:指定标签的处理类
- body-content:这个子元素也很重要,它指定标签体内容。其值可以为:
- tagdependent:指定标签处理类自己负责处理标签体
- empty:指定该标签只能作用空标签使用
- scriptless:指定该标签的标签体可以是静态HTML元素、表达式语言,但不允许出现JSP脚步
- JSP:指定该标签的标签体可以使用JSP脚本
2.3 使用标签库
- 标签库URI:确定使用哪个标签库
- 标签名:确定使用哪个标签
- 导入标签库:使用taglib编译指令导入标签库,就是将标签库和指定前缀关联起来
- 使用标签:在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属性所定义的值。
<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语句查询数据库,并将查询结果显示在当前页面中。
- name:设置属性名,子元素的值是字符串内容
- required:设置该属性是否为必须属性,该子元素的值时true或false
- fragment:设置该属性是否支持JSP脚本、表达式等动态内容,子元素的值true或false
<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:querydriver="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/javaee"user="root"pass="32147"sql="select * from news_inf"/>
2.5 带标签体的标签(重点)
public class IteratorTag extends SimpleTagSupport {// 标签属性,用于指定需要被迭代的集合private String collection;// 标签属性,指定迭代集合元素,为集合元素指定的名称private String item;// 省略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()方法首先从page范围内获取了指定名称的Collection对象,然后遍历Collection对象的元素,每次遍历都调用getJspBody()方法,该方法返回该标签所包含的标签体:JSPFragment对象,执行该对象的invoke()方法,即可输出标签体内容。该标签的作用是:遍历指定集合,每遍历一个集合元素,技术处标签体一次。
<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标签库提供了一套功能强大的标签,如果有现成的轮子就没必要再造轮子了。
2.6 以页面片段作为属性的标签(暂不理解)
- 标签处理类中定义类型为JSPFragment的属性,该属性代表了“页面片段”
- 使用标签库,通过<jsp:attribute.../>动作指令为标签的属性指定值
public class FragmentTag extends SimpleTagSupport {private JspFragment fragment;//省略fragment的setter和getter方法@Overridepublic 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 动态属性的标签(终点)
- 标签处理类还需要实现DynamicAttributes接口
- 配置标签时通过<dynamic-attributes.../>子元素指定该标签支持动态属性
public class DynaAttributesTag extends SimpleTagSupport implements DynamicAttributes {// 保存每个属性名的集合private ArrayList<String> keys = new ArrayList<String>();// 保存每个属性值的集合private ArrayList<Object> values = new ArrayList<Object>();@Overridepublic 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>");}@Overridepublic 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再对服务器响应进行后处理。- 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletResponse
- 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据
- 在HttpServletResponse到达客户端之前,拦截HttpServletResponse
- 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据
- 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求
- 日志Filter:详细记录某些特殊的用户请求
- 负责解码的Filter:包括对非标准编码的请求解码
- 能改变XML内容的XSLT Filter等
- Filter可负责拦截多个请求或相应:一个请求或相应也可被多个Filter拦截
- 创建Filter处理类
- web.xml文件中配置Filter
3.1 创建Filter类
- void init(FilterConfig config):用于完成Filter的初始化
- void destroy():用于Filter销毁前,完成某些资源的回收
- void doFilter(ServletRequest request , ServletResponse response , FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理
@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名
- 配置Filter拦截URL模式
- 在Filter类中通过注解进行配置
- 在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 使用示例
@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实现网站伪静态
4.1 利用URL Rewrite实现网站伪静态
<1> 下载URL Rewrite jar包
<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文件,该文件定义了伪静态映射规则,这份伪静态规则是基于正则表达式。
<?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介绍
- 定义Listener实现类
- 通过注解或web.xml文件中配置Listener
5.1 实现Listener类
- ServletContextListener:用于监听Web应用的启动和关闭
- ServletContextAttributeListener:用于监听ServletContext范围(application)内属性的改变
- ServletRequestListener:用于监听用户请求
- ServletRequestAttributeListener:用于监听ServletRequest范围(request)内属性的改变
- HttpSessionListener:用于监听用户session的开始和结束
- HttpSessionAttributeListener:用于监听HttpSession范围(session)内属性的改变
5.2 配置Listener
- 使用@WebListener修饰Listener实现类即可
- 在web.xml文档中使用<listener../>元素进行配置
- listener-class:指定Listener实现类
5.3 使用ServletContextListener
- contextInitialized(ServletContextEvent sce):启动Web应用时,系统调用Listener的该方法
- contextDestroyed(ServletContextEvent sce):关闭Web应用时,系统调用Listener的该方法
@WebListenerpublic class GetConnListener implements ServletContextListener {// 应该启动时,该方法被调用。public void contextInitialized(ServletContextEvent sce) {try {// 取得该应用的ServletContext实例ServletContext application = sce.getServletContext();// 从配置参数中获取驱动String driver = application.getInitParameter("driver");// 从配置参数中获取数据库urlString 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范围内的属性时将触发该方法
@WebListenerpublic 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):用户请求结束、被销毁时触发该方法
@WebListenerpublic 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)
@WebListenerpublic class OnlineListener implements HttpSessionListener {// 当用户与服务器之间开始session时触发该方法public void sessionCreated(HttpSessionEvent se) {HttpSession session = se.getSession();ServletContext application = session.getServletContext();// 获取session IDString 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文件语法
<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属性
- 是否允许使用表达式语言:使用<el-ignored/>元素确定,默认值为false,即允许使用表达式语言
- 是否允许使用JSP脚本:使用<scripting-invalid/>元素确定,默认值为false,即允许使用JSP脚本
- 声明JSP页面的编码:使用<page-encoding/>元素确定,配置该元素后,可以代替每个页面里page指令contentType属性的charset部分
- 使用隐士包含:使用<include-prelude/>和<include-coda/>元素确定,可以代替在每个页面里使用include编译指令来包含其他页面
6.2 表达式语言&Tag File
6.3 Servlet3.0新特性
6.3.1 Servlet3.0的注解
- @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模块支持
- <name.../>:用于指定该模块的名称
- <ordering.../>:用于指定加载该Web模块的相对顺序
<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提供的异步处理
- AsyncContext startAsync()
- AsyncContext startAsync(ServletRequest,ServletResponse)
@WebServlet(urlPatterns = "/async", asyncSupported = true)public class AsyncServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overridepublic 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页面。
<%@ 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):当异步调用超时时触发该方法
6.3.4 改进的Servlet API
- HttpServletRequest增加了对文件上传的支持
- ServletContext允许通过编程的方式动态注册Servlet、Filter
- Part getPart(String name):根据名称来获取文件上传域
- Collection<Part> getParts():获取所有的文件上传域
- application/x-www-form-urlencoded:这是默认的编码,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容封装到请求参数里
- text/plain:这种编码方式当表单的action属性为mailto:URL的形式时比较方便,这种方式主要适用于直接通过表单发送邮件的形式
<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" })@MultipartConfigpublic 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 NameCollection<String> headerNames = part.getHeaderNames();// 遍历文件上传域的Header Name、Valuefor (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上传时保存的文件名直接使用了上传文件的原始文件名,在实际项目中一般不会这么做,因为可能多个用户可能上传同名的文件,这样将导致后面用户上传的文件覆盖前面用户上传的文件。在实际项目中可借助于java.util.UUID工具类生成文件名。
- 多个重载的addServlet()方法:动态地注册Servlet
- 多个重载的addFilter()方法:动态地注册Filter
- 多个重载的addListener():动态地注册Listener
- setInitParameter(String name,String value)方法:为Web应用设置初始化参数
6.4 Servlet 3.1 新增的非阻塞式IO
- ServletInputStream:Servlet用于读取数据的输入流
- ServletOutputStream:Servlet用于输出数据的输出流
从Servlet 3.1 开始,ServletInputStream新增了一个setReadListener(ReadListener readListener)方法,该方法允许以非阻塞IO读取数据,实现ReadListener监听器需要实现如下三个方法:
- onAllDataRead():当所有数据读取完成时激发该方法
- onDataAvailable():当有数据可用时激发该方法
- onError(Throwable t):读取数据出现错误时激发该方法
- 调用ServletRequest的startAsync()方法开启异步模式
- 通过ServletRequest获取ServletInputStream,并为ServletInputStream设置监听器(ReadListener实现类)
- 实现ReadListener接口来实现监听器,在该监听器的方法中以非阻塞方式读取数据。
@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;}@Overridepublic 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();}}@Overridepublic void onAllDataRead() {System.out.println("数据读取完成");}@Overridepublic void onError(Throwable t) {t.printStackTrace();}}下面创建页面:
<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支持(暂未完成)
********************************************************************************结束语********************************************************************************************
我在写这篇博客的时候也是一名初学者,有任何疑问或问题请留言,或发邮件也可以,邮箱为:fanxiaobin.fxb@qq.com,我会尽早的进行更正及更改。
在我写过的博客中有两篇博客是对资源的整理,可能对大家都有帮助,大家有兴趣的话可以看看!!
下载资料整理——目录:http://blog.csdn.net/fanxiaobin577328725/article/details/51894331
这篇博客里面是我关于我见到的感觉不错的好资源的整理,里面包含了书籍及源代码以及个人搜索的一些资源,如果有兴趣的可以看看,我会一直对其进行更新和添加。
优秀的文章&优秀的学习网站之收集手册:http://blog.csdn.net/fanxiaobin577328725/article/details/52753638
这篇博客里面是我对于我读过的,并且感觉有意义的文章的收集整理,纯粹的个人爱好,大家感觉有兴趣的可以阅读一下,我也会时常的对其进行更新。
********************************************************************************感谢********************************************************************************************
- JSP&Servlet学习笔记(二)
- JSP/Servlet 学习笔记(二)
- Servlet与JSP学习笔记(二) Servlet核心
- JSP&&SERVLET学习笔记(二):Web.xml
- JSP/Servlet学习笔记
- Jsp+servlet学习笔记
- jsp&servlet学习笔记
- JSP Servlet学习笔记
- JSP/Servlet学习笔记
- Jsp&servlet学习笔记
- JSP&Servlet学习笔记
- jsp&servlet学习笔记
- jsp+servlet学习笔记
- JSP&Servlet学习笔记
- Servlet+JSP学习笔记
- servlet+jsp学习笔记
- servlet学习笔记(二)
- servlet学习笔记(二)
- 性能调优-shuffle调优
- 【计算机视觉】【视频开发】智能视频监控中的遗留物或搬移物检测
- 为新员工分配部门(初次使用哈希码)
- C/C++学习之路之常用关键字
- Apriori算法解析
- JSP&Servlet学习笔记(二)
- 基于JavaScript 声明全局变量的三种方式详解
- Spring AOP基本原理及AOP两种配置方式初体验
- 计算组合数
- 移动构造函数和移动赋值运算符
- 页面分页实现
- POJ 2387 (Dijkstra)
- 条件变量:pthread_cond_wait使用while循环判断的理解
- oss存储