JavaWeb三大组件
1. 都需要在web.xml中进行配置
Servlet
Listener(2个感知监听器不需要配置)
Filter
过滤器
它会在一组资源(jsp、servlet、.css、.html等等)的前面执行!
它可以让请求得到目标资源,也可以不让请求达到!
* 过滤器有拦截请求的能力!
登录:
允许它访问AServlet、BServlet、CServlet
-------------------------------
过滤器如何编写
1. 写一个类实现Filter接口
2. 在web.xml中进行配置
Filter接口
void init(FilterConfig)
* 创建之后,马上执行;Filter会在服务器启动时就创建!
void destory()
* 销毁之前执行!在服务器关闭时销毁
voiddoFilter(ServletRequest,ServletResponse,FilterChain)
* 每次过滤时都会执行
Filter是单例的!
web.xml部署
<filter>
<filter-name>xxx</filter-name>
<filter-class>cn.itcast.web.filter.AFitler</fitler-class>
</filter>
<fitler-mapping>
<filter-name>xxx</filter-name>
<url-pattern>/*</url-pattern>
<!--除了url-pattern servlet也可以 -->
</filter-mapping>
-------------------------------
FilterConfig-->与ServletConfig相似
* 获取初始化参数:getInitParameter()
* 获取过滤器名称:getFilterName()
* 获取appliction:getServletContext()
FilterChain
*doFilter(ServletRequest, ServletResponse):放行!
放行,就相当于调用了目标Servlet的service()方法!
-------------------------------
-------------------------------
多过滤器
FilterChain#doFilter()方法:
执行目标资源,或是执行下一个过滤器!如果没有下一个过滤器那么执行的是目标资源,如果有,那么就执行下一个过滤器!
-------------------------------
过滤器的四种拦截方式
<dispatcher>REQUEST</dispatcher>默认的!
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
在<filter-mapping>中进行配置!
-------------------------------
多个过滤器的执行顺序
<filter-mapping>的配置顺序决定了过滤器的执行顺序!
Filter应用案例
1.分IP统计网站访问次数
代码
lintener
public class AListener implements ServletContextListener { /** * 在服务器启动时创建Map,保存到ServletContext */ public void contextInitialized(ServletContextEvent sce) { // 创建Map Map<String,Integer> map = new LinkedHashMap<String,Integer>(); // 得到ServletContext ServletContext application = sce.getServletContext(); // 把map保存到application中 application.setAttribute("map", map); } public void contextDestroyed(ServletContextEvent sce) { } }
filter
/** * 从application中获取Map * 从request中得到当前客户端的IP * 进行统计工作,结果保存到Map中 * @author cxf * */ public class AFilter implements Filter { private FilterConfig config; public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 1. 得到application中的map * 2. 从request中获取当前客户端的ip地址 * 3. 查看map中是否存在这个ip对应访问次数,如果存在,把次数+1再保存回去 * 4. 如果不存在这个ip,那么说明是第一次访问本站,设置访问次数为1 */ /* * 1. 得到appliction */ ServletContext app = config.getServletContext(); Map<String,Integer> map = (Map<String, Integer>) app.getAttribute("map"); /* * 2. 获取客户端的ip地址 */ String ip = request.getRemoteAddr(); /* * 3. 进行判断 */ if(map.containsKey(ip)) {//这个ip在map中存在,说明不是第一次访问 int cnt = map.get(ip); map.put(ip, cnt+1); } else {//这个ip在map中不存在,说明是第一次访问 map.put(ip, 1); } app.setAttribute("map", map);//把map再放回到app中 chain.doFilter(request, response);//肯定放行 } /** * 在服务器启动时就会执行本方法,而且本方法只执行一次! */ public void init(FilterConfig fConfig) throws ServletException { this.config = fConfig; } }
2.粗粒度权限控制
public class AdminFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 1. 得到session * 2. 判断session域中是否存在admin,如果存在,放行 * 3. 判断session域中是否存在username,如果存在,放行,否则打回到login.jsp,并告诉它不要瞎留达 */ HttpServletRequest req = (HttpServletRequest) request; String name = (String)req.getSession().getAttribute("admin"); 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 { } } ------------------ public class UserFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /* * 1. 得到session * 2. 判断session域中是否存在admin,如果存在,放行 * 3. 判断session域中是否存在username,如果存在,放行,否则打回到login.jsp,并告诉它不要瞎留达 */ 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 { } }
3.解决post/get中文编码问题
原理:利用装饰模式及HttpServletRequestWrapper包装类
filter
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请求的编码问题 */ // String username = request.getParameter("username"); // username = new String(username.getBytes("ISO-8859-1"), "UTF-8"); /* * 调包request * 1. 写一个request的装饰类 * 2. 在放行时,使用我们自己的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 { } }
装饰包装类
/** * 装饰reqeust * @author cxf * */ 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("iso-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return value; } }
4.页面静态化
1. 第一步
写一个简单的单表查询(略)
2. 第二步:什么是页面静态化!
首次访问去数据库获取数据,然后把数据保存到一个html页面中(服务器)
二次访问,就不再去数据库获取了,而是直接显示html
----------------------------------------------------------
1. 目标!
给出一个过滤器,把servlet请求的资源所做输出保存到html中,重定向到html页面。
二次访问时,这个html已经存在,那么直接重定向,不用再去访问servlet!
代码
filter
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; /* * 1. 第一次访问时,查找请求对应的html页面是否存在,如果存在重定向到html * 2. 如果不存在,放行!把servlet访问数据库后,输出给客户端的数据保存到一个html文件中 * 再重定向到html */ /* * 一、获取category参数! * category有四种可能: * * null --> null.html * * 1 --> 1.html * * 2 --> 2.html * * 3 --> 3.html * * html页面的保存路径, htmls目录下 * * 判断对应的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文件不存在,我们要生成html * 1. 放行,show.jsp会做出很多的输出,我们要让它别再输出给客户端,而是输出到我们指定的一个html文件中 * 完成: * * 调包response,让它的getWriter()与一个html文件绑定,那么show.jsp的输出就到了html文件中 */ StaticResponse sr = new StaticResponse(res, destFile.getAbsolutePath()); chain.doFilter(request, sr);//放行,即生成了html文件 // 这时页面已经存在,重定向到html文件 res.sendRedirect(req.getContextPath() + "/htmls/" + htmlPage); } }
包装响应类
public class StaticResponse extends HttpServletResponseWrapper { private PrintWriter pw; /** * String path:html文件路径! * @param response * @param path * @throws UnsupportedEncodingException * @throws FileNotFoundException */ public StaticResponse(HttpServletResponse response, String path) throws FileNotFoundException, UnsupportedEncodingException { super(response); // 创建一个与html文件路径在一起的流对象 pw = new PrintWriter(path, "utf-8"); } public PrintWriter getWriter() { // 返回一个与html绑定在一起的printWriter对象 // jsp会使用它进行输出,这样数据都输出到html文件了。 return pw; } }