会在一组资源(jsp、servlet、css、html等)的前面执行 有拦截请求的能力 编写:
写一个类实现Filter接口在web.xml中进行配置Filter接口: void init(FilterConfig)创建之后马上执行,初始化;Filter会在服务器启动时创建 void destroy()销毁前执行;在服务器关闭时销毁 void doFilter(ServletRequest,ServletResponse,FilterChain)每次过滤都会执行
public class AFilter implements Filter { public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { System.out.println("拦截中..."); } public void destroy() { System.out.println("过滤器即将销毁"); } public void init(FilterConfig filterConfig) throws ServletException { System.out.println("过滤器初始化"); } }web.xml
<filter> <filter-name>AFilter</filter-name> <filter-class>filter.AFilter</filter-class> </filter> <filter-mapping> <filter-name>AFilter</filter-name> <url-pattern>/AServlet</url-pattern> 这里也可以不用url-pattern,直接: <servlet-name>AServlet</servlet-name> </filter-mapping>FilterConfig:与ServletConfig相似 getInitParameter():获取初始化参数 getFilterName():获取过滤器名称 getServletContext():获取application FilterChain doFilter(ServletRequest,ServletResponse):放行,相当于执行了service方法
Filter和FilterChain的doFilte方法的区别
Filter的doFilter(ServletRequest,ServletResponse,FilterChain)有三个参数;而FilterChain的doFilter(ServletRequest,ServletResponse)有两个参数Filter中在每次过滤时调用,而FilterChain中的方法用于放行如果此时有两个过滤器:
<filter> <filter-name>AFilter</filter-name> <filter-class>filter.AFilter</filter-class> </filter> <filter-mapping> <filter-name>AFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>BFilter</filter-name> <filter-class>filter.BFilter</filter-class> </filter> <filter-mapping> <filter-name>BFilter</filter-name> <url-pattern>/AServlet</url-pattern> </filter-mapping>AFilter:
public class AFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("AFilter Start"); FilterChain(request,response); System.out.println("AFilter End") } public void destroy() { } public void init(FilterConfig filterConfig) throws ServletException { } }BFilter:
```java public class BFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("BFilter Start"); FilterChain(request,response); System.out.println("BFilter End") } public void destroy() { } public void init(FilterConfig filterConfig) throws ServletException { } }AServlet:
public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("----AServlet!----"); } }访问AServlet,控制台打印: AFilter Start BFilter Start ----AServlet!---- BFilter End AFilter End
FilterChain 中doFilter(ServletRequest,ServletResponse): 在没有其他过滤器时,起放行作用; 在有其他过滤器时,执行其他过滤器多个过滤器的执行顺序按web.xml中filter-mapping出现的顺序请求 REQUEST(不配置则默认为请求) 转发 FORWARD 包含 INCLUDE 错误 ERROR 在filter-mapping中进行配置,标签为DISPATCHER:
<filter-mapping> <filter-name>AFilter</filter-name> <url-pattern>/AServlet</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>过滤器的应用场景:
执行目标资源前的预处理工作通过条件判断是否放行目标资源执行后的后续处理a.jsp:用于访问
<body> <h1>啦啦啦啦啦啦啦哒哒哒哒哒我是快乐的白鹦鹉</h1> </body>AListener:在服务器启动时创建一个Map用于装载统计数据,并保存到ServletContext中
public class AListener implements ServletContextListener { public void contextInitialized(ServletContextEvent sce) { //创建Map Map<String,Integer> map=new LinkedHashMap<String,Integer>(); //得到ServletContext ServletContext application=sce.getServletContext(); application.setAttribute("map", map); } public void contextDestroyed(ServletContextEvent sce) { } }AFilter:得到Map,保存数据后放行
@WebFilter("/*") public class AFilter implements Filter { private FilterConfig config; public void init(FilterConfig fConfig) throws ServletException { this.config=fConfig; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //得到application中的Map Map<String,Integer> map=(Map<String, Integer>) config.getServletContext().getAttribute("map"); //从request中获取ip String ip=request.getRemoteAddr(); //查看Map中是否存在这个ip对应访问次数,并做相应保存 if(map.containsKey(ip)) map.put(ip, map.get(ip)+1); else map.put(ip, 1); chain.doFilter(request, response); } public void destroy() {} }用户身份不同,能访问的页面也不同 如: 游客只能访问游客页面 用户可以访问用户页面、游客页面 管理员可以访问全部页面
先创建三种页面: index.jsp
<body> <h1>游客,欢迎</h1> <a href="/day21_example2/index.jsp">游客入口</a><br/> <a href="/day21_example2/users/u.jsp">会员入口</a><br/> <a href="/day21_example2/admin/a.jsp">管理员入口</a><br/> </body>users/u.jsp
<body> <h1>用户,您好</h1> <a href="/day21_example2/index.jsp">游客入口</a><br/> <a href="/day21_example2/users/u.jsp">会员入口</a><br/> <a href="/day21_example2/admin/a.jsp">管理员入口</a><br/> </body>admin/a.jsp
<body> <h1>管理员,您好</h1> <a href="/day21_example2/index.jsp">游客入口</a><br/> <a href="/day21_example2/users/u.jsp">会员入口</a><br/> <a href="/day21_example2/admin/a.jsp">管理员入口</a><br/> </body>然后是登录页面: login.jsp
<body> <h1>登录</h1> ${msg } <form action="/day21_example2/LoginServlet" method="post"> 用户名:<input type="text" name="username"/> <input type="submit" value="登录"/> </form> </body>LoginServlet
/* * session中存在"username"键,则为非游客 * "username"的值含有admin字样的为管理员 */ public class LoginServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username=request.getParameter("username"); if(username.contains("admin")) { request.getSession().setAttribute("admin",username);//管理员 }else { request.getSession().setAttribute("username", username);//用户 } request.getRequestDispatcher("index.jsp").forward(request, response); } }下面是两个过滤器,分别对用户页面和管理员页面进行检查: UserFilter
public class UserFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req=(HttpServletRequest)request; String name=(String)req.getSession().getAttribute("admin"); if(name!=null) { chain.doFilter(request, response); return; } name=(String)req.getSession().getAttribute("username"); if(name!=null) { chain.doFilter(request, response); }else { req.setAttribute("msg","游客不能访问此页面"); req.getRequestDispatcher("/login.jsp").forward(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }AdminFilter
public class AdminFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req=(HttpServletRequest)request; String name=(String)req.getSession().getAttribute("admin"); if(name!=null) { chain.doFilter(request, response); return; }else { req.setAttribute("msg","只有管理员能访问此页面"); req.getRequestDispatcher("/login.jsp").forward(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }servlet中编码的解决: POST:request.setCharacterEncoding(“utf-8”); GET: 1. 得到参数 String username=request.getParameter(“username”); 2. 解编重编 username=new String(username.getBytes(“ISO-8859-1”,“utf-8”));
然而在页面中,无法得知每一个参数名称,并为每一个参数编码。 先写index.jsp页面,用于产生get和post请求: (点击提交后,照理说两个请求都没编码,应该会乱码,然而我的浏览器GET请求没有乱码…应该是智能的浏览器的原因…不过还是先假装都是乱码)
<body> <a href="<c:url value='/AServlet?username=张三'/>">点击这里</a> <form action="<c:url value='/AServlet'/>" method="post"> 用户名:<input type="text" name="username" value="李四"/> <input type="submit" value="提交"/> </form> </body>AServlet:不进行请求编码处理
public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); String username=request.getParameter("username"); response.getWriter().println(username); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); String username=request.getParameter("username"); response.getWriter().println(username); } }EncodingFilter过滤器:将原有request变成我们自己写的类,它在getParameter()方法中加入了解码和再编码的过程,产生用utf-8编码的键值 在放行时掉包request,AServlet将使用掉包后的request
public class EncodingFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //处理post请求的编码,只需: request.setCharacterEncoding("utf-8"); HttpServletRequest req=(HttpServletRequest)request; /* * 处理get请求的编码: * 写一个request的装饰类,在放行时,使用我们自己的request */ if(req.getMethod().equals("GET")) { EncodingRequest er=new EncodingRequest(req); chain.doFilter(er, response); }else if(req.getMethod().equals("POST")) { chain.doFilter(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } }EncodingRequest:用于掉包request的类
//这个wrapper是HttpServletRequest的实现类 public class EncodingRequest extends HttpServletRequestWrapper{ private HttpServletRequest req; public EncodingRequest(HttpServletRequest request) { super(request); this.req=request; } public String getParameter(String name) {//处理编码问题 String value=req.getParameter(name); try { value=new String(value.getBytes("utf-8"),"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return value; } }先写一个小项目:
写一个图书管理链接页面BookServlet,其中有findAll()、findByCategory()方法BookServiceBookDaolink.jsp:链接页面,选择查询方法
<body> <h1>链接页面</h1> <a href="<c:url value='/BookServlet?method=findAll'/>">查询所有</a><br/> <a href="<c:url value='/BookServlet?method=findByCategory&category=1'/>">查询SE</a><br/> <a href="<c:url value='/BookServlet?method=findByCategory&category=2'/>">查询EE</a><br/> <a href="<c:url value='/BookServlet?method=findByCategory&category=3'/>">查询Framework</a><br/> </body>BookServlet:调用dao的方法,并返回路径给BaseServlet完成重定向
public class BookServlet extends BaseServlet { private BookDao bookDao=new BookDao(); public String findAll(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ request.setAttribute("bookList", bookDao.findAll()); return "/show.jsp"; } public String findByCategory(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ String value=request.getParameter("category"); int category=Integer.parseInt(value); request.setAttribute("bookList", bookDao.findByCategory(category)); return "/show.jsp"; } }BookDao:使用数据库连接池完成查询,将结果返回给servlet
public class BookDao { private QueryRunner qr=new TxQueryRunner(); public List<Book> findAll(){ String sql="select * from t_book"; try { return qr.query(sql, new BeanListHandler<Book>(Book.class)); } catch (SQLException e) { throw new RuntimeException(e); } } public List<Book> findByCategory(int category){ String sql="select * from t_book where category=?"; try { return qr.query(sql,new BeanListHandler<Book>(Book.class),category); } catch (SQLException e) { throw new RuntimeException(e); } } }Book
public class Book { private String bid; private String bname; private double price; private int category; public String getBid() { return bid; } public void setBid(String bid) { this.bid = bid; } public String getBname() { return bname; } public void setBname(String bname) { this.bname = bname; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getCategory() { return category; } public void setCategory(int category) { this.category = category; } }show.jsp:显示页面
<body> <h1 align="center">查询结果</h1> <table border="1" align="center" width="50%"> <tr> <th>书名</th> <th>单价</th> <th>类别</th> </tr> <c:forEach items="${bookList }" var="book"> <tr> <td>${book.bname}</td> <td>${book.price}</td> <c:choose> <c:when test="${book.category eq 1 }"><td style="color:red">JavaSE</td></c:when> <c:when test="${book.category eq 2 }"><td style="color:blue">JavaEE</td></c:when> <c:when test="${book.category eq 3 }"><td style="color:green">JavaFramework</td></c:when> </c:choose> <td>${book.category}</td> </tr> </c:forEach> </table> </body>下一步完成页面静态化: 使用一个过滤器,把servlet请求的资源输出保存到html中,再重定向到html页面。二次访问时直接重定向,不用再访问servlet。
StaticFilter:第一次访问时html不存在,掉包request使输出转移到新建的html文件中,然后重定向到该页面;二次访问时,html已存在,直接重定向到html页面
@WebFilter("BookServlet") public class StaticFilter implements Filter { private FilterConfig config; public void destroy() {} public void init(FilterConfig fConfig) throws ServletException { this.config=fConfig; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req=(HttpServletRequest) request; HttpServletResponse res=(HttpServletResponse)response; /* * 查询对应html页面是否存在,若存在重定向到html */ String category=request.getParameter("category"); String htmlPage=category+".html";//得到文件名称 String htmlPath=config.getServletContext().getRealPath("/htmls");//得到文件存放目录 File destFile=new File(htmlPath,htmlPage); if(destFile.exists()) {//如果文件存在,重定向 res.sendRedirect(req.getContextPath()+"/htmls/"+htmlPage); return; } /* * html不存在:掉包request,让它的getWriter()与一个html文件绑定, * 那么show.jsp的输出就到该html文件中 */ StaticResponse sr=new StaticResponse(res,destFile.getAbsolutePath()); chain.doFilter(request, sr);//放行 res.sendRedirect(req.getContextPath()+"/htmls/"+htmlPage);//重定向 } }StaticResponse:在构造器中获取需要创建的html文件名和路径,将一个流对象与之绑定,在重写的getWriter()方法返回该流对象。
public class StaticResponse extends HttpServletResponseWrapper{ private HttpServletResponse response; private PrintWriter pw; public StaticResponse(HttpServletResponse response,String path) throws FileNotFoundException, UnsupportedEncodingException { super(response); this.response=response; //创建一个与html文件路径绑定的流对象 pw=new PrintWriter(path,"utf-8"); } public PrintWriter getWriter() { //返回一个与html绑定在一起的printWriter对象 return pw; } }在点击链接后,可在tomcat(注意项目中是看不到生成文件的)该项目下找到生成的html文件: