JavaWeb之Filter讲解和实现页面静态化和更新页面(下)
静态化页面的生成
- 生成静态化页面还是使用的过滤器和装饰者模式,来实现。
- 分析:
- 这个时候,就要想到response的getWriter()方法,得到PrintWriter对象,调用out方法来进行输出。Jsp页面是这样向浏览器输出信息的。这里也可以查看翻译后的Jsp。在tomcat的worke文件夹中。输出页面的是:JspWriter。这个类和PrintWriter是什么关系呢。其实内部使用的是PrintWriter。如下图:
- 如果知道这些的话就好办了,只要将response中的getPrintWriter()方法重写,将返回的PrintWriter对象写入一个HTML文件就行。
- 这个时候就要想怎么存储,请求和静态文件的一对一关系。你肯定想到map集合,没错就是使用map集合存储。那什么作为key什么,作为value。value好说静态文件的文件名就可以作为value。key可以从请求的参数入手。那存储在哪呢,肯定是application域中。每个用户都要能访问。Ok,分析完成。开始编码。下面的代码是一个图书分类查找的代码,写的比较简陋。
- 这个时候,就要想到response的getWriter()方法,得到PrintWriter对象,调用out方法来进行输出。Jsp页面是这样向浏览器输出信息的。这里也可以查看翻译后的Jsp。在tomcat的worke文件夹中。输出页面的是:JspWriter。这个类和PrintWriter是什么关系呢。其实内部使用的是PrintWriter。如下图:
静态页面生成的代码:
- 表结构如下:
- bid是主键,category是分类。后面的查询就是根据这个字段查询的。
- bean的代码就不贴了。dao的代码如下(没使用框架,不会):
public class BookDao { private QueryRunner runner = new QueryRunner(MyJdbcUtils.getDataSource()); public List<Book> findAll(){ String sql = "select * from t_book"; try { return runner.query(sql, new BeanListHandler<Book>(Book.class)); } catch (SQLException e) { throw new RuntimeException("findAll:"+e); } } public List<Book> findBookByCategory(int category){ String sql = "select * from t_book where category=?"; try { return runner.query(sql, new BeanListHandler<Book>(Book.class),category); } catch (SQLException e) { throw new RuntimeException("findAll:"+e); } } }
- 这上面好像使用了我写的一个内,源码很简单如下:
private static DataSource dataSource = new ComboPooledDataSource(); public static DataSource getDataSource() { return dataSource; }
- service和servlet也就不贴了,太简单了。
下面是重点Filter
- Filter的代码如下,如果能看懂解决get和post请求乱码的问题的写法。这个也没问题。只不过是重写response中的方法而已。
- 先看过滤器类(Filter)的写法(里面包含了更新,动态页面的方法,最后的时候会介绍)
public class BookFilter implements Filter { private ServletContext context; private ScheduledExecutorService service; private String filePath; public void destroy() { //服务器停止的时候,结束线程 service.shutdown(); } /** * 思路如下: 使用map集合存储 * * 我是用分类的作为文件命名的一部分 */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; //得到ServletContext中的"page_map"属性的值,也就是上面说的map集合 Map<String, String> map = (Map<String, String>) context .getAttribute("page_map"); if (map == null) { map = new HashMap<String, String>(); context.setAttribute("page_map", map); } //这就是键值 String key = "category_" + req.getParameter("category"); //只是value String htmlPath = map.get(key); //得到项目路径 String contextPath = context.getContextPath(); //为空说明,没有静态页面,不为空直接重定向到该页面 if (htmlPath != null) { res.sendRedirect(contextPath + "/static/" + htmlPath); return; } //组成HTML页面的名称 htmlPath = key + ".html"; //得到文件的绝对路径 String filePath = context.getRealPath("/static/" + htmlPath); //自己写的response,转递参数是文件的绝对路径。和HttpServletRequest对象 StaticResponse staticResponse = new StaticResponse(filePath, res); //将键和值存入集合中 map.put(key, htmlPath); //放行 chain.doFilter(request, staticResponse); //重定向到生产的页面 res.sendRedirect(contextPath + "/static/" + htmlPath); //关闭流 staticResponse.close(); } public void init(FilterConfig config) throws ServletException { //初始化的时候得到servletContext对象 context = config.getServletContext(); /** * 使用Jdk5的新线程类,创建和执行方法。实现静态页面更新的操作。 */ service = Executors.newSingleThreadScheduledExecutor(); filePath= context.getRealPath("/static"); //每1分钟执行一次下面的代码,如果在run方法调用了调用方法,则该方法不能有返回值。好像是这样,我不是了解的深入 service.scheduleWithFixedDelay(new Runnable() { @Override public void run() { showAllDir(filePath); context.removeAttribute("page_map"); } }, 60, 60, TimeUnit.SECONDS); } /** * 使用递归遍历,并删除文件 * @param dir */ public void showAllDir(String dir) { File f = new File(dir); //查看取到的文件路径 System.out.println(f); File[] files = f.listFiles(); if (files != null && files.length>0) { for (File file : files) { if (!file.isHidden()) { //判断是否是文件 if (file.getAbsoluteFile().isDirectory()) { showAllDir(file.getAbsolutePath()); } else { //判断是否删除成功 System.out.println(new File(dir+"/"+file.getName()).delete()); } } } } } }
- 接下来是使用装饰者模式实现的response类的写法:
public class StaticResponse extends HttpServletResponseWrapper { private HttpServletResponse response; private PrintWriter out; public StaticResponse(String filePath,HttpServletResponse response) { super(response); this.response = response; try { //新建一个PrintWriter对象,让它指向一个文件,并设置编码 out = new PrintWriter(filePath,"utf-8"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @Override public PrintWriter getWriter() throws IOException { //返回给jsp页面,新的out对象 return out; } public void close(){ out.close(); } }
- 上面包含生产静态页面的方法和更新静态页面的方法。生成的方法就不介绍了。
更新静态页面的方法:
- 更新页面的方法是我自己想的,不知道可不可以这样实现。但功能能实现。
- 分析:
- 我的想法是,在服务器启动的时候,就新建一个线程。该线程专门负责,更新静态页面。还可以指定多长时间更新一次。这里我为了测试是一分钟更新一次,当然也可以指定天,消失,分钟,秒和毫秒。具体可以看TimeUnit这个类的API。
- 页面的代码如下:
<a href="<c:url value='/servlet/BookServletmethodName=findAll'/>">查询全部</a><br/><br/>
<a href="<c:url value='/servlet/BookServlet?methodName=queryBy&category=1'/>">JavaSE分类</a><br/><br/>
<a href="<c:url value='/servlet/BookServlet?methodName=queryBy&category=2'/>">JavaEE分类</a><br/><br/>
<a href="<c:url value='/servlet/BookServlet?methodName=queryBy&category=3'/>">framework分类</a><br/><br/>
<style type="text/css">
.category1{color:red;}
.category2{color:green;}
.category3{color:blue;}
</style>
</head>
<body>
<table align="center" border="1" width="60%">
<tr>
<th>书名</th>
<th>价格</th>
<th>分类</th>
</tr>
<c:forEach items="${requestScope.list}" var="b">
<tr class="category${b.category }">
<td>${b.bname }</td>
<td>${b.price }</td>
<c:choose>
<c:when test="${b.category eq 1}">
<td>JavaSE分类</td>
</c:when>
<c:when test="${b.category eq 2}">
<td>JavaEE分类</td>
</c:when>
<c:when test="${b.category eq 3}">
<td>Framework分类</td>
</c:when>
</c:choose>
</tr>
</c:forEach>
</table>
</body>
第一个Jsp代码我使用的是我自己写的一个servlet类,这样方便,不需要一个请求一个servlet。如果看不懂可以忽略。想象成如下即可:
<a href="<c:url value='/servlet/BookServlet'/>">查询全部</a><br/><br/>
<a href="<c:url value='/servlet/BookServlet1?category=1'/>">JavaSE分类</a><br/><br/>
<a href="<c:url value='/servlet/BookServlet2?category=2'/>">JavaEE分类</a><br/><br/>
<a href="<c:url value='/servlet/BookServlet3?category=3'/>">framework分类</a><br/><br/>
可能无须删文件,只要将放到servletContext中的,map集合删掉就行。好像也可以实现功能。。。。