Web组件
最开始,ee规范中只有servlet一个组件。
后来,又出现了两个组件,listener、filter
listener:监听器()
filter:过滤器(拦截)
Listener
web访问过程中的监听器
被监听者:ServletContext对象
监听者:自己编写了一个监听器
监听事件:对象的创建和销毁
触发行为:调用自己写的监听器对应的方法
作用:主要负责监听ServletContext对象的创建和销毁,一般存放全局性的代码
使用
1.编写一个类实现ServletContextListener接口
2.注册当前监听器(web.xml 注解)
@WebListener public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("init"); ServletContext servletContext = servletContextEvent.getServletContext(); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("destroy"); }
原理
底层维护了一个接口,通过不同的子实现来调用不同的方法
class ServletContext { List<ServletContextListener> listeners; //注册该listener,tomcat会调用 add(ServletContextListener listener){ listeners.add(listener); } init(){ listeners.for{listener.contextInitialized()} } destroy(){ listeners.for{listener.contextDestroyed()} } }
Filter
介绍
过滤器,主要的作业是对请求和响应对象进行检查和修改。—>ServletContext
设置编码格式
可以实现对页面的拦截和放行
与过滤器关联的Servlet
使用
1.编写一个类实现Filter接口
2.注册该Filter
filter如何和servlet关联
最简单的方式就是通过url-pattern进行关联,即把servlet的url-pattern直接赋值给filter即可
filter默认情况下执行的是拦截操作,如果需要放行,必须要加如下代码:
filterChain.doFilter(servletRequest, servletResponse);//底层使用递归调用
filter url-pattern的不同于servlet之处
1.filter可以设置和servlet相同的url-pattern
*2.filter设置/*,并且设置/ 还是很有必要的
3.filter不可以设置/
4.如果有多个filter,设置相同的url-pattern,那么也是允许的
多个filter的执行先后顺序
1.如果是web.xml,按照filter-mapping声明的先后顺序
<filter-mapping> <filter-name>filter2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>filter1</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
2.如果是注解,那么按照类名首字母ASCII先后顺序,即英文字母的顺序
流程
请求的去程,只执行filterChain.doFilter上面的代码
返程只执行下面的代码
请求的完整执行流程
访问http://localhost/app/1.html
以访问http://localhost/app/1.html为例
1.域名解析(浏览器缓存–操作系统缓存–hosts缓存–dns服务器),TCP三次握手建立连接,浏览器生成请求报文经过tcp层拆包打上标签经过ip层打上端口号和目标ip,从链路层发出去在网上中转传输到目标主机
2.到达服务器后被一直监听80端口号的HTTP/1.1 Connector接收到,将其解析为request对象并生成一个response对象
3.Connector将两个对象下发给Engine,Engine进一步下发交给Host
4.Host的职责是挑选一个叫/app的应用,如果找到就将两个对象交给该应用,如果找不到就交给ROOT应用,如果ROOT应用中都没有,那就404
5.此时有效的路径就是/1.html,查找有没有filter可以处理该请求,如果有就将该filter加入组件执行的链表中,如果有多个按照先后顺序来排列,配置在web.xml文件中就按照文件的顺序执行,使用注解就按照首字母的ASCII码大小顺序执行
6.进一步查看有没有servlet可以处理该请求,如果没有就交给缺省的servlet,此时要看servlet有没有重写,如果重写了那就交给自己重写的处理,如果没有重写就交给默认的处理,当做静态资源访问,如果此时没有该资源,就抛出404
7.依次调用链表上的每一个组件,调用Filter的doFilter方法以及servlet的service方法,方法执行时需要传入request和response这两个参数
8.全部执行完毕后,Connector读取response里面的数据生成响应报文,再依次通过tcp,ip,链路层出去发送到客户端,客户端再进行解析
案例
登陆注销案例,加入拦截的功能,如果没有登陆则直接返回登陆页面
package com.fh.filter.login; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/user1/*") public class UserServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String requestURI = request.getRequestURI(); String op = requestURI.replace(request.getContextPath() + "/user1/", ""); //将前面的部分替换为空白,取出具体的详情页面的部分,根据不同的方法进行分发处理 if ("loginxx".equals(op)){ login(request,response); } } private void login(HttpServletRequest request, HttpServletResponse response) throws IOException { //具体的登陆的处理逻辑 String username = request.getParameter("username"); String password = request.getParameter("password"); HttpSession session = request.getSession(); session.setAttribute("username",username); response.getWriter().println("登陆成功,进入个人主页"); response.setHeader("refresh","2;url="+request.getContextPath()+"/user1/infoxx"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //刷新页面是get请求 String requestURI = request.getRequestURI(); String op = requestURI.replace(request.getContextPath() + "/user1/", ""); if ("infoxx".equals(op)){ info(request,response); }else if ("logoutxx".equals(op)){ logout(request,response); } } private void logout(HttpServletRequest request, HttpServletResponse response) throws IOException { //处理注销的逻辑 request.getSession().invalidate(); //清除session实现注销,服务器没有保存此客户端的数据了 response.getWriter().println("注销成功,即将跳回登陆页面"); response.setHeader("refresh","2;url="+request.getContextPath()+"/login5.html"); } private void info(HttpServletRequest request, HttpServletResponse response) throws IOException { //具体的详情页面的逻辑 HttpSession session = request.getSession(); Object username = session.getAttribute("username"); response.getWriter().println("欢迎"+username+"<a href='"+request.getContextPath()+"/user1/logoutxx"+"'>点我注销</a>"); } }
package com.fh.filter.login; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/*") public class AuthFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); if (auth(request)){ //这里的主要逻辑就是如果是需要登陆的页面,并且用户没有登陆,那就进行拦截 //反之如果页面不需要登陆,或者需要登陆且用户登陆了,就不拦截 Object username = request.getSession().getAttribute("username"); //主要看username是否为空,如果当前客户端的session对象中存放了username,那么就说明登陆了,反之则不行 if (username==null){ response.getWriter().println("你还没有登陆!即将跳回登陆页面"); response.setHeader("refresh","2;url="+request.getContextPath()+"/login5.html"); return;//一定要return,否则继续向下执行就放行了 } } filterChain.doFilter(request,response); } private boolean auth(HttpServletRequest request) { //校验是否是需要验证登陆的页面 String s = request.getContextPath() + "/user1/infoxx"; if (s.equals(request.getRequestURI())){ //uri是当前访问到的页面,如果与我们预设好的相等,那就说明需要登陆,返回true return true; } return false; } @Override public void destroy() { } }
equest.getContextPath() + “/user1/infoxx”;
if (s.equals(request.getRequestURI())){
//uri是当前访问到的页面,如果与我们预设好的相等,那就说明需要登陆,返回true
return true;
}
return false;
}
@Override public void destroy() { }
}