EE规范其实对于Web组件一共定义了三个标准,其中之一就是我们经常使用的Servlet,除此之外还有另外两个组件
简单介绍一下。使用场景并不是特别的多,只有一个ServletContextListener需要记住。
监听器。类比一下现实生活中的监听器
web中的监听器
web中的监听器一共可以分为三大类8种
1.三个域对象创建、销毁的监听器 ServletContextListener
2.三个域对象属性变更的监听器
3.session数据钝化、活化的监听器
其中ServletContextListener监听器,大家需要有一个印象,后面学习Spring框架,它会在该监听器里面进行实例化。
1.编写一个类实现ServletContextListener接口
2.声明该Listener
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("context init");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("context destroy");
}
}
怎么实现的呢?
tomcat的代码是很多年以前已经写好的,它是如何调用后面你写得代码呢?
package test.update;
public class BabyTest {
public static void main(String[] args) {
Baby baby = new Baby();
// baby.addRelationShip(new Dad());
// baby.addRelationShip(new Mom());
baby.addRelationShip(new GrandPa());
baby.addRelationShip(new GrandMa());
baby.cry();
}
}
package test.update;
import java.util.ArrayList;
import java.util.List;
public class Baby {
private List<RelationShip> relations = new ArrayList<>();
public void addRelationShip(RelationShip relationShip){
this.relations.add(relationShip);
}
public void cry(){
for (RelationShip relation : relations) {
relation.action();
}
}
}
ServletContext {
List <ServletContextListener> listeners
add(){
}
init方法会在servletContext被创建之后调用
init(){
listeners.for-------> listener.contextInitialized();
}
destroy(){
listeners.for----->listener.contextDestroyed();
}
}
观察者设计模式。监听器设计模式
其实和Servlet大多数情况下是一致的,所以套着Servlet来就可以了。
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/filter1")
public class FirstFilter implements Filter {
//会在应用启动的时候直接实例化一个对象出来,然后执行init方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("filter init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter doFilter");
}
//应用被卸载、服务器关闭
@Override
public void destroy() {
System.out.println("filter destroy");
}
}
通过url-pattern产生关联。
注意:filter和servlet设置相同的url-pattern并不会导致出错。从功能的角度去理解。EE规范就是这么定义的
功能上去理解:
servlet:开发动态web资源的组件。一个请求只有一个组件来响应
filter:拦截器、过滤器。它不是主要去做出响应的组件,所以设置它可以和servlet的url-pattern。
注意:filter默认情况下执行的是拦截操作,如果希望filter能够将请求进行进一步下发给后面的组件,那么需要设置一个方法,该方法对于程序的放行至关重要。如果没有这行代码,那么就执行的是拦截操作,加上下面这行代码,那么执行的就是放行操作。
chain.doFilter(request,response)
*filter可不可以设置/呢?
可以的。
filter和filter之间可不可以设置相同的url-pattern呢?
可以的。
如果有多个filter,那么filter的执行先后顺序按照什么来呢?
如果是注解的话,那么按照类名首字母的ASCII先后顺序
如果是web.xml的话,那么按照filter-mapping声明的先后顺序来
以访问http://localhost/application/servlet1为例,简述整个请求的访问过程
1.浏览器帮助生成一个HTTP请求报文,传输到服务器
2.被监听着80端口号的Connector接收到,将请求报文解析成为request对象,同时生成一个response对象
3.将这两个对象传给engine,engine进一步挑选合适的host,将对象进行进一步下发
4.host的职责就是来挑选一个合适的Context,将这两个对象进行进一步下发
5.Context里面首先根据请求的资源路径/servlet1,先看有没有对应的filter可以处理该请求,就会把满足条件的filter加入到备选中,按照他们的先后顺序(注解 ASCII、web.xml mapping)形成一个链表,接下来看有没有servlet可以处理该请求,如果有加入进来到链表内,如果没有,则把缺省servlet加入到链表内。
filetr----filter----servlet的链表
context依次去调用该链表内每一个组件的对应方法,filter.doFilter servlet.service,在调用该方法的时候,将这两个对象作为参数传递进去
6.Connector读取response里面的数据,生成HTTP响应报文
Connector{
Engine engine
request
response
action(){
engine.xxx(request,response);
// 读取response里面的数据
}
}
filter doFilter before
second filter doFilter before
servlet doget
second filter doFilter after
filter doFilter after
1.设置编码格式
2.拦截
login.jsp 放行的
info.jsp 而是登录了之后进行放行,没有登录执行拦截
/login 放行