JavaWeb之Filter讲解和实现页面静态化和更新页面(下)

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,分析完成。开始编码。下面的代码是一个图书分类查找的代码,写的比较简陋。

静态页面生成的代码:

  • 表结构如下:
  • 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集合删掉就行。好像也可以实现功能。。。。








版权声明:本文为qq455276333原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。