Servlet day04
1 Filter(过滤器)
Filter(过滤器)中抽取Servlet中的共性代码,用来解决多个Servlet中代码重复的问题。下面我们来学习下Filter的工作流程和开发步骤。
1.1 Filter的工作流程
Filter的作用和工作流程类似于生活中小区的门卫和警卫。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IqZlpYaJ-1631153116780)(Servlet day04.assets/image-20210529195730899.png)]
门卫警卫的工作流程:
- 访客要拜访程序员,需要经过门卫和警卫才能访问到程序员
- 离开时也要经过门卫和警卫,途径顺序和来访时相反
- 警卫可以拒绝不满足条件的访客的来访请求,改变其流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q47143LH-1631153116783)(Servlet day04.assets/image-20210529201003592.png)]
Filter的工作流程:
- Client发起对Servlet的请求,要先经过Filter后才能到达Servlet
- Servlet中的响应结果也要反向经过Filter
- Filter可以改变Client的请求流向
- Filter中通常抽取Servlet中的共性代码
1.2 Filter的开发步骤
-
编码
class FirstFilter implements Filter{ //过滤请求时执行的方法 public void doFilter(req,resp,chain) throws IOException,ServletException{ //抽取Servlet中共性的代码 //放行请求,没有这行代码,请求不会继续向后传递 chain.doFilter(req,resp); } }
-
配置
//配置Filter的过滤请求的范围 <filter> <filter-name>filter的名字</filter-name> <filter-class>filter的全类名</filter-class> </filter> <filter-mapping> <filter-name>filter的名字</filter-name> <!-- 用来配置filter的过滤请求的范围 /* 过滤所有请求 /前缀/* 过滤以url-pattern以 /前缀/开头的servlet 示例:/student/* 过滤url-pattern以 /student/开头的servlet --> <url-pattern>/*</url-pattern> </filter-mapping>
1.3 Filter的语法细节
-
Filter可以精确配置
<filter> <filter-name>FirstFilter</filter-name> <filter-class>com.baizhi.filter.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name>FirstFilter</filter-name> <!-- 不使用通配符时,是精确配置 --> <url-pattern>/a</url-pattern> </filter-mapping>
-
Filter可以配置多个url-pattern
<filter> <filter-name>FirstFilter</filter-name> <filter-class>com.baizhi.filter.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name>FirstFilter</filter-name> <url-pattern>/a</url-pattern> <url-pattern>/b</url-pattern> </filter-mapping>
-
多个Filter的执行顺序
多个Filter过滤了相同的请求,那么执行顺序和web.xml中配置顺序一致!!! 下面的配置就是先经过First再经过Second <filter> <filter-name>FirstFilter</filter-name> <filter-class>com.baizhi.filter.FirstFilter</filter-class> </filter> <filter-mapping> <filter-name>FirstFilter</filter-name> <url-pattern>/a</url-pattern> <url-pattern>/b</url-pattern> </filter-mapping> <filter> <filter-name>SecondFilter</filter-name> <filter-class>com.baizhi.filter.SecondFilter</filter-class> </filter> <filter-mapping> <filter-name>SecondFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
Filter不但能拦截Servlet的请求,还可以拦截对静态资源的请求
有项目结构如下: +servlet-day04 -login.html -register.html +person -addPerson.html +WEB-INF +classes @WebServlet("/person/showAllPersons") -com.baizhi.controller.ShowAllPersonsController 动态资源查询Controller的URL: http://localhost:8989/servlet-day04/person/showAllPersons 静态资源addPerson.html的URL: http://localhost:8989/servlet-day04/person/addPerson.html 有Filter的url-pattern为/person/*,那么上面的2个资源的请求都会被此Filter过滤。
-
Filter的生命周期
- 创建时期:web应用启动时
- 销毁时期:web应用关闭时
1.4 Filter典型应用场景
1.4.1 编码过滤器
-
编码
public class CharacterEncodingFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //设置编解码集 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); //放行请求 chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } }
-
配置
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.baizhi.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
1.4.2 登录过滤器
-
编码
public class LoginFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub //强制登录判断 //父接口引用强转为子接口引用 HttpServletRequest httpReq = (HttpServletRequest)request; HttpServletResponse httpResp = (HttpServletResponse)response; HttpSession session = httpReq.getSession(); Object o = session.getAttribute("login"); //如果满足条件,放行请求 if(o != null) { chain.doFilter(request, response); }else { //不满足条件,重定向到登录页面 String contextPath = httpReq.getContextPath(); httpResp.sendRedirect(contextPath+"/login.html"); } } @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } }
-
配置
<filter> <filter-name>LoginFilter</filter-name> <filter-class>com.baizhi.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/student/*</url-pattern> </filter-mapping>
2 ServletContext
ServletContext对象表示一个web应用。无论何时何人只要访问的是同1个应用,那么获取到是同1个ServletContext对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uXhcSq4Z-1631153116784)(Servlet day04.assets/image-20210529221050540.png)]
//获取ServletContext作用域
ServletContext servletContext = req.getServletContext();
ServletContext还可以作为作用域对象。
作用域对象的使用:
//保存数据
servletContext.setAttribute("key",数据);
//获取数据
servletContext.getAttribute("key");
//移除数据
servletContext.removeAttribute("key");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yE9bOeIb-1631153116785)(Servlet day04.assets/image-20210529222259746.png)]
作用范围:
- request作用域:一次请求,forward间传递数据
- session作用域:一个浏览器的多次请求
- servletContext作用域:可以在多个浏览器间传递数据
3 Listener
监听器可以监听Web应用一些特定的事件,在事件发生时执行一些代码。
3.1 HttpSessionListener
监听Session的创建和销毁
-
编码
public class MyHttpSessionListener implements HttpSessionListener { @Override //监听到session对象创建时执行 public void sessionCreated(HttpSessionEvent arg0) { // TODO Auto-generated method stub System.out.println("session创建了"); } @Override //监听到session对象销毁时执行 public void sessionDestroyed(HttpSessionEvent arg0) { // TODO Auto-generated method stub System.out.println("session销毁了"); } }
-
配置
<listener> <listener-class>com.baizhi.listener.MyHttpSessionListener</listener-class> </listener>
3.2 ServletContextListener
监听web应用的启动和关闭。
-
编码
public class MyServletContextListener implements ServletContextListener { @Override //web应用关闭时执行 public void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub System.out.println("web应用关闭了,执行一些释放资源的代码"); } @Override //web应用启动时执行 public void contextInitialized(ServletContextEvent arg0) { // TODO Auto-generated method stub System.out.println("web应用启动了,执行一些需要在应用启动早期执行的代码"); } }
-
配置
<listener> <listener-class>com.baizhi.listener.MyServletContextListener</listener-class> </listener>
4 Servlet拾遗
4.1 动态获取当前项目名
在Servlet中如果重定向跳转,跳转路径需要以 /项目名
开头。
resp.sendRedirect("/项目名/url-pattern");
直接硬编码项目名存在维护问题,如果日后项目名发生变化,程序中需要修改很多地方。解决方案:动态获取web项目名
String contextPath = req.getContextPath();// 获取到当前项目名
//重定向代码示例
resp.sendRedirect(contextPath+"/login.html");
4.2 完善更新功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWUONgs7-1631153116786)(Servlet day04.assets/image-20210526141610763.png)]
完善后的更新流程:
- 点击更新先发起查询详情的请求
- 查询详情Controller跳转到View中,动态生成更新表单。表单中的input通过字符串拼接赋初始值
- 在View生成的更新页面中发起更新请求
4.3 Servlet收参:根据一个name获取多个value
Client发送的请求参数通常是一个key对应1个value,但是也有1个key对应多个value的情况,典型场景是复选框发送数据。
<!--
此时选中前3个复选框,请求参数:favorites=1&favorites=2&favorites=3
-->
<form>
爱好:
<input type="checkbox" name="favorites" value="1"> 唱
<input type="checkbox" name="favorites" value="2"> 跳
<input type="checkbox" name="favorites" value="3"> Rap
<input type="checkbox" name="favorites" value="4"> 打篮球 <br>
<input type="submit"/>
</form>
Servlet中就不能通过getParameter获取value,需要通过getParameterValues获取多个value。
String[] values = req.getParameterValues("favorites");
**使用场景:**批量删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EciGWnto-1631153116787)(Servlet day04.assets/image-20210529224620561.png)]
跳
Rap
打篮球
Servlet中就不能通过getParameter获取value,需要通过getParameterValues获取多个value。
String[] values = req.getParameterValues("favorites");
**使用场景:**批量删除
[外链图片转存中…(img-EciGWnto-1631153116787)]