zoukankan      html  css  js  c++  java
  • ServletContextInitializer添加 servlet filter listener

    ServletContextInitializer添加 servlet filter listener 

    https://www.cnblogs.com/pomer-huang/p/9639322.html

    关于springboot中添加Filter的方法

    https://www.jianshu.com/p/3d421fbce734

     ———————————————————————————————————————————————————————————————

    Spring Boot Servlet : ServletContextInitializer

    概述

    功能介绍

    Spring Boot提供的在Servlet 3.0+环境中用于程序化配置ServletContext的接口。该接口ServletContextInitializer主要被RegistrationBean实现用于往ServletContext容器中注册Servlet,Filter或者EventListener。这些ServletContextInitializer的设计目的主要是用于这些实例被Spring IoC容器管理。这些ServletContextInitializer实例不会被SpringServletContainerInitializer检测,因此不会被Servlet容器自动启动。

    该接口ServletContextInitializerSpring Web的另外一个接口WebApplicationInitializer看起来几乎一模一样。但二者使用目的不同。Spring Web中,WebApplicationInitializer也是针对Servlet 3.0+环境,设计用于程序化配置ServletContext,跟传统的web.xml相对或者配合使用。WebApplicationInitializer实现类会被SpringServletContainerInitializer自动检测和启动。

    继承关系

    RegistrationBean的继承关系

    使用

    关于ServletContextInitializer的应用可以参考下面的例子。TomcatStarterSpring Boot Web Servlet应用结合Tomcat使用时的一个ServletContainerInitializer实现。从下面代码不难看出,一组ServletContextInitializer会被设置到ServletContainerInitializer TomcatStarter上,而TomcatStarterServlet容器启动过程中调用自己的方法#onStartup,会逐一调用这些ServletContextInitializer的方法#onStartup初始化ServletContext

    package org.springframework.boot.web.embedded.tomcat;
    
    import java.util.Set;
    
    import javax.servlet.ServletContainerInitializer;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.boot.web.servlet.ServletContextInitializer;
    
    
    class TomcatStarter implements ServletContainerInitializer {
    
    	private static final Log logger = LogFactory.getLog(TomcatStarter.class);
    
    	// 使用者会指定一组 ServletContextInitializer 给 TomcatStarter
    	private final ServletContextInitializer[] initializers;
    
    	private volatile Exception startUpException;
    
    	TomcatStarter(ServletContextInitializer[] initializers) {
    		this.initializers = initializers;
    	}
    
    	// Servlet 容器启动时回会用该方法,该方法会逐一调用每个 ServletContextInitializer 的方法
    	// #onStartup 会指定 ServletContext 进行初始化。这些 ServletContextInitializer 的目的
    	// 通常会是 注册 Servlet, Filter 或者 EventListener 。
    	@Override
    	public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
    			throws ServletException {
    		try {
    			for (ServletContextInitializer initializer : this.initializers) {
    				initializer.onStartup(servletContext);
    			}
    		}
    		catch (Exception ex) {
    			this.startUpException = ex;
    			// Prevent Tomcat from logging and re-throwing when we know we can
    			// deal with it in the main thread, but log for information here.
    			if (logger.isErrorEnabled()) {
    				logger.error("Error starting Tomcat context. Exception: "
    						+ ex.getClass().getName() + ". Message: " + ex.getMessage());
    			}
    		}
    	}
    
    	public Exception getStartUpException() {
    		return this.startUpException;
    	}
    
    }
    

    源代码

    源代码版本 : 2.1.3.RELEASE

    package org.springframework.boot.web.servlet;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    import org.springframework.web.SpringServletContainerInitializer;
    import org.springframework.web.WebApplicationInitializer;
    
    
    @FunctionalInterface
    public interface ServletContextInitializer {
    
    	/**
    	 * Configure the given ServletContext with any servlets, filters, listeners
    	 * context-params and attributes necessary for initialization.
    	 * @param servletContext the ServletContext to initialize
    	 * @throws ServletException if any call against the given ServletContext
    	 * throws a ServletException
    	 */
    	void onStartup(ServletContext servletContext) throws ServletException;
    
    }
    
    

    参考文章

    ——————————————————————————————————————————————————————————————————

    springboot 2.1.3.RELEASE添加filter,servlet源码学习

     

    Servlet规范中,通过ServeltContext来注册Filter、Servlet,这里分析Filter,Servlet是相同逻辑

    springboot2.0中,我们通过

    FilterRegistrationBean将指定得filter来实现ServeltContext注册filter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    FilterRegistrationBean的实例化过程
     
    public FilterRegistrationBean(T filter, ServletRegistrationBean... servletRegistrationBeans) {
            super(servletRegistrationBeans);
            Assert.notNull(filter, "Filter must not be null");
            this.filter = filter;
    }
     
    super的实例化
    AbstractFilterRegistrationBean(ServletRegistrationBean... servletRegistrationBeans) {
            Assert.notNull(servletRegistrationBeans, "ServletRegistrationBeans must not be null");
            Collections.addAll(this.servletRegistrationBeans, servletRegistrationBeans);
    }

    可知FilterRegistrationBean得实例化过程就是将Filter保存到servletRegistrationBeans(一个set)中

    再分析FilterRegistrationBean类

    FilterRegistrationBean的父类是一个ServletContextInitializer,他有一个方法onStartup(ServletContext servletContext)
    其结果最终会调用
    servletContext.addFilter(this.getOrDeduceName(filter), filter)
    现在看看ServletContextInitializer.onStartup的调用地方
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    springboot启动
    new SpringApplication(XXApplication.class).run(XXApplication.class,args)
    调用
    refreshContext(context); -> refresh(context); -> ((AbstractApplicationContext) applicationContext).refresh();
    -> ServletWebServerApplicationContext.onRefresh(); -> createWebServer();
     
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }

    再分析getSelfInitializer方法

    1
    2
    3
    4
    prepareWebApplicationContext(servletContext);
            registerApplicationScope(servletContext);
            WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),servletContext);
            for (ServletContextInitializer beans :getServletContextInitializerBeans()) {              
           //调用ServletContextInitializer的onStartup方法
    1
    beans.onStartup(servletContext);
    }
    -> getServletContextInitializerBeans()
    1
    2
    -> new ServletContextInitializerBeans(getBeanFactory())
    -> addAdaptableBeans(beanFactory)

     

    1
    2
    3
    4
    5
    6
    7
    8
    addAdaptableBeans(ListableBeanFactory beanFactory) 代码如下:
    MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
    //将servlet类的bean包装为ServletRegistrationBean
    addAsRegistrationBean(beanFactory, Servlet.class,new ServletRegistrationBeanAdapter(multipartConfig));
    //将Filter的bean包装为FilterRegistrationBean
    addAsRegistrationBean(beanFactory, Filter.class,new FilterRegistrationBeanAdapter());
    for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes())
    {         
    addAsRegistrationBean(beanFactory, EventListener.class,(Class<EventListener>) listenerType,new ServletListenerRegistrationBeanAdapter());
    }

     这里提一下,如果Servlet的bean实例名为dispatcherServlet,且该实例在seen集合中(seen集合为自定义配置的实例,通过ServletContextInitializerBeans.addServletContextInitializerBean入口可以查看源码),则设置默认值为  /

    1
    2
    3
    4
    5
    6
    7
    String url = (totalNumberOfSourceBeans != 1) ? "/" + name + "/" "/";
    if (name.equals(DISPATCHER_SERVLET_NAME)) {
        url = "/"// always map the main dispatcherServlet to "/"
    }
    ServletRegistrationBean<Servlet> bean = new ServletRegistrationBean<>(source,url); 
    bean.setName(name);
    bean.setMultipartConfig(this.multipartConfig);
    return bean;

     

    总结:springboot启动时,会将所有的 FilterRegistrationBean、ServletRegistrationBean以及被spring管理的Servlet和Filter的实例,通过          ServletContext注册Servlet以及Filter

  • 相关阅读:
    [转载] ASP.NET MVC (一)——深入理解ASP.NET MVC
    冒泡排序C#实现,使用委托,包括三种方式:Fun<>,匿名方法,Lambda表达式
    工厂模式怎么用?举例说明
    使用设计模式,到底有什么好处?举例说明
    百度 Echarts 地图表 js 引用路径
    移动Web
    uni-app
    微信公众号分享时,提示invalid signature,签名错误
    windows系统开放外部访问端口
    node报错Request header field Content-Type is not allowed by
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/11494005.html
Copyright © 2011-2022 走看看