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() {
}
}