栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java学习 day56

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java学习 day56

Web组件 Listener

监听器。

在EE规范早期,其实只有servlet一个组件,后面又引入了listener、filter等另外两个组件。

JavaEE中有三大组件:Servlet(开发动态web资源)、Listener(监听器)、Filter(过滤器)


web中的监听器:

被监听者:比如ServletContext对象

监听者:编写的监听器

监听事件:ServletContext对象的创建和销毁

触发事件:调用监听器里面对应的方法


使用(掌握)

1.编写一个类实现ServletContextListener接口

2.配置该Listener(web.xml 注解)


    com.cskaoyan.listener.MyServletContextListener

或者注解

package com.cskaoyan.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");
    }
}

不需要通过访问任何地址就可以执行。

有什么用呢?

其实我们可以将之前写在servlet的init方法里面,然后设置load-on-startup=1的这些代码逻辑放到listener里面来写。更为恰当一些。

如果之前想设置一段代码,该代码的运行和当前servlet是否被访问无关,那么可以设置一个init ,随着应用加载而运行。将这块代码逻辑写在init里面。

这段代码逻辑其实和当前servlet其实关系并不是特别的紧密。比如购物车案例,实例化商品。可以写在indexServlet中,也可以写在GoodsServlet中,只要设置init load-on-startup=1即可。

这段代码其实可以认为是一个全局性的代码。全局性的代码最好不要写在某个serlvet中。因为如果你写在某个serlvet中,别人很容易认为该段代码逻辑是和当前servlet是密切相关的。

而应该写在listener里面。

你可以认为最开始时,只有servlet,只能将这些全局性代码写在sevlet init中, 后面版本中出现了listener,建议就将这些代码逻辑写到listenner中。


原理(不做统一要求)

listener是如何实现的?

ServletCotontext对象被创建的这段代码应该是早就已经写好了的,我们编写的监听器是后来写的,如何做到十几年前的老代码调用你写的代码的呢?

宝宝哭,哄宝宝

爸爸干什么

妈妈干什么

package com.cskaoyan.listener.test;

public class Baby {
    private Dad dad;

    private Mom mom;

    public Baby(Dad dad, Mom mom) {
        this.dad = dad;
        this.mom = mom;
    }

    public void cry(){
        dad.boil();
        mom.feed();
    }
}
package com.cskaoyan.listener.test;

public class Dad {

    public void boil(){
        System.out.println("爸爸热奶");
    }
}
package com.cskaoyan.listener.test;

public class Mom {

    public void feed(){
        System.out.println("妈妈喂奶");
    }
}
package com.cskaoyan.listener.test;

public class MainTest {

    public static void main(String[] args) {
        Baby baby = new Baby(new Dad(),new Mom());
        baby.cry();
    }
}

但是爸爸妈妈都是独生子,也要工作,工作日就无人照看孩子,爷爷奶奶来照看

但是Baby里面维护的引用是爸爸和妈妈,怎么办?

周末还需要将爷爷奶奶变回来

爷爷和爸爸出去干活,奶奶和妈妈在家

package com.cskaoyan.listener.test.update;

public interface Family {

    void action();
}
package com.cskaoyan.listener.test.update;

public class Dad implements Family {
    @Override
    public void action() {
        System.out.println("爸爸热奶");
    }
}
package com.cskaoyan.listener.test.update;

public class Mom implements Family {
    @Override
    public void action() {
        System.out.println("妈妈喂奶");
    }
}
package com.cskaoyan.listener.test.update;

public class Grandpa implements Family {
    @Override
    public void action() {
        System.out.println("爷爷哄宝宝");
    }
}
package com.cskaoyan.listener.test.update;

public class Grandma implements Family {
    @Override
    public void action() {
        System.out.println("奶奶抱宝宝");
    }
}
package com.cskaoyan.listener.test.update;

import java.util.ArrayList;
import java.util.List;

public class Baby {

    private List members = new ArrayList<>();

    public void add(Family family){
        this.members.add(family);
    }

    public void cry(){
        for (Family member : members) {
            member.action();
        }
    }
}
package com.cskaoyan.listener.test.update;

public class MainTest {

    public static void main(String[] args) {
        Baby baby = new Baby();
//        baby.add(new Dad());
//        baby.add(new Mom());
        baby.add(new Grandma());
        baby.add(new Grandpa());
        baby.cry();
    }
}

ServletContextListener接口就是Family

MyServletContextListener

就是一个实现类

Baby其实就是tomcat里面当前应用的的ServletContext

ServletContext{
	List listeners;
	
	add()----{listeners.add(listener)}
	
	
	init(){
		listeners.for{listener.contextInitialized()}
	}
	
	destroy(){
		listners.for{listener.contextDestroyed()}
	}


}

包装设计模式

监听器设计模式

代理设计模式

本质来说都是多态


Filter 概念

过滤器、拦截器。

打游戏的时候 发消息 我***

比如古城市,城门,进出城都需要经过城门。城门就类似于filter

filter位于servlet的前面,在请求到达servlet之前,会先经过filter

响应返回给客户端之前,要再次经过filter出去

过滤器关联的servlet


开发使用

1.编写一个类,实现Filter接口,实现方法

2.声明该Filter

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/filter1")
public class FirstFilter implements Filter {

    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init");
    }

    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter");
    }

    
    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

通过filter的介绍,我们清楚请求到达servlet之前会先经过filter,我们创建了一个servlet,访问该servlet

但是发现请求并没有经过filter。

说明了fitler此时并没有和servlet产生关联。

filter如何和servlet产生关联呢?

最简单的方式就是通过url-pattern,最简单的方式就是将servlet的url-pattern给filter。


Filter的注意事项

1.filter可以设置和servlet相同的url-pattern,这是完全允许的。

默认情况下,filter执行的是拦截操作,如果希望filter能够放行请求,需要设置一行代码

package com.cskaoyan.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/servlet1")
public class FirstFilter implements Filter {

    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init");
    }

    
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter");
        //这行代码对于请求的进一步放行非常重要,如果没有这行代码,那么filter执行的就是拦截操作
        //如果有这行代码,那么filter执行的就是放行操作
        //比如未登录的情况下访问个人主页
        //这行代码的逻辑的比较复杂:其实是采用了递归的形式去调用下一个组件
        // 管道设计模式、责任链设计模式(不做要求)
        filterChain.doFilter(servletRequest, servletResponse);
    }

    
    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

2.filter和filter之间可不可以设置相同的url-pattern呢?

完全可以。

*3.filter可不可以设置/

完全可以。

其实filter设置/*还是很有必要的,假设设置编码格式的代码写在filter中,filter如果设置了/ *,那其实只需要写一处该代码即可,后面所有的servlet均无需重新设置。

4.既然filter可以设置相同的url-pattern,并且也都会加入到特定请求的处理中,那么执行的先后顺序是如何呢?

比如一个filter设置了/* 一个filter设置了/servlet1

那么当我访问/servlet1时,两个filter均进入到了该请求的执行中来,那么执行的先后顺序应该是什么样的呢?

如果多个filter加入到了一个请求中,那么执行的先后顺序按照如下顺序

1.如果是web.xml,按照filter-mapping声明的先后顺序


    filter2
    /servlet1



    filter1
    /*

2.如果是注解,按照类名首字母的ASCII先后顺序来

注意:和url-pattern优先级,覆盖范围没有任何关系。

有什么实际使用意义呢?

比如有一个后台管理系统(管理员使用的 管理员去维护数据的地方),有一个前台系统(用户使用的系统 jd.com)。

分别设置两个filter,这两个filter分别对应的是后台管理系统的filter和前台用户系统的filter

除此之外还可以设置一个全局性编码格式的filter

CharacterEncodingFilter /*

AdminFilter /admin/*

MallFilter /mall/*

CharacterEncodingFilter 、AdminFilter

假设AdminFilter里面需要有一个读取请求参数的逻辑,如果AdminFilter在前,CharacterEncodingFilter 在后,那么CharacterEncodingFilter 里面设置编码格式的代码逻辑还有效果吗?

CharacterEncodingFilter、 MallFilter


引入Filter之后的请求处理流程

以访问http://localhost/app/servlet1为例,阐述请求的执行流程

1.输入对应的地址,域名解析,建立TCP连接,发送HTTP请求报文,发送到目标机器之后

2.被监听80端口号的Connector程序接收到,将其解析成为reqeust对象,同时提供一个response对象

3.进一步交给engine、host,host去选择一个叫做/app的应用,如果找到,则将这两个对象交该该应用来处理

4.应用拿到的有效路径为/servlet1,首先先在当前应用下寻找是否有合适的filter可以处理该请求(/ /servlet1),发现有两个filter可以同时处理该请求,那么会将这两个filter按照filter先后顺序加入到一个链表中;filter寻找完毕,接下来去寻找有没有合适的servlet(/servlet1)可以处理该请求,将可以处理该请求的这个servlet加入到链表的后面(最终只会找到一个servlet,只会找到一个优先级最高的,如果没有找到servlet,会交给缺省Servlet,最终肯定有一个servlet)*

5.依次去调用链表上面的每一个组件,依次去执行filter的doFilter方法以及servlet的service方法,方法需要一个request、response对象,恰好有这两个对象,将这两个对象作为参数传递给对应的方法去执行

6.当这些组件的所有方法执行完毕,整个请求处理结束,最终Connector读取response里面的数据生成响应报文。

其实整个请求处理流程就是不同组件依次去操作request、response的过程。

案例
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/285319.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号