ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

springboot DispatcherServlet

2021-12-29 15:36:10  阅读:187  来源: 互联网

标签:springboot beanFactory List initializer ServletContext DispatcherServlet Servlet


From:https://www.liangzl.com/get-article-detail-133970.html

 

Springboot中我们引入spring-boot-starter-web依赖后,web就自动配置好了,在web.xml的年代,我们需要在web.xml中手动配置DispatcherServlet,但是Springboot中不需要,Springboot是如何替我们做好这一切的呢?

    我们来看下DispatcherServletAutoConfiguration,这个类在Springboot-autoconfig包中,如下List-1

    List-1

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setDispatchOptionsRequest(
            this.webMvcProperties.isDispatchOptionsRequest());
    dispatcherServlet.setDispatchTraceRequest(
            this.webMvcProperties.isDispatchTraceRequest());
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(
            this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    return dispatcherServlet;
}

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
        DispatcherServlet dispatcherServlet) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
            dispatcherServlet, this.serverProperties.getServlet().getPath());
    registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    registration.setLoadOnStartup(
            this.webMvcProperties.getServlet().getLoadOnStartup());
    if (this.multipartConfig != null) {
        registration.setMultipartConfig(this.multipartConfig);
    }
    return registration;
}
  1. 实例化DispatcherServlet,之后注册到Spring容器中。
  2. 实例化DispatcherServletRegistrationBean,并将DispatcherServlet传入到构造方法法中,注册到Spring容器中。

       所以说,在Springboot中,有个DispatcherServlet的bean,我们可以写个单元测试验证从BeanFactory中获取DispatcherServlet这个bean,接下来看DispatcherServletRegistrationBean。

                   

                                                                                          图1

    如图1所示,DispatcherServletRegistrationBean继承了ServletContextInitializer——见List-2,其中onStartUp的参数ServletContext是Servlet里面的。

    RegistrationBean实现了ServletContextInitializer,之后调用register方法,这个是抽象方法,由子类DynamicRegistrationBean实现,DynamicRegistrationBean再将ServletContext用方法addRegistration传递给子类ServletRegistrationBean——见List-3,List-3中用addServlet方法加入的就是DispatcherServletRegistrationBean传递到父类ServletRegistrationBean中的。这样Springboot利用Servlet3.0+的特性,自动注册DispatcherServlet到ServletContext中。

    List-2

@FunctionalInterface
public interface ServletContextInitializer {

	/**
	 * Configure the given {@link ServletContext} with any servlets, filters, listeners
	 * context-params and attributes necessary for initialization.
	 * @param servletContext the {@code ServletContext} to initialize
	 * @throws ServletException if any call against the given {@code ServletContext}
	 * throws a {@code ServletException}
	 */
	void onStartup(ServletContext servletContext) throws ServletException;
}

    List-3

@Override
protected ServletRegistration.Dynamic addRegistration(String description,
        ServletContext servletContext) {
    String name = getServletName();
    logger.info("Servlet " + name + " mapped to " + this.urlMappings);
    return servletContext.addServlet(name, this.servlet);
}

    有个问题,实现了ServletContextInitializer的实例,什么时候会调用onStartup方法呢?来看ServletContextInitializerBeans,这个类在Springboot中,如List-4中:

  1. 实例化的时候会从BeanFactory中获取所有的ServletContextInitializer——在getOrderedBeansOfType方法中,之后用addServletContextInitializerBean方法,将获取到的ServletContextInitializer类型的Bean,添加到属性initializers中。
  2. 这个地方可以看到,实现了ServletContextInitializer的不止是Servlet类型的,还有Listener、Filter类型的,为什么呢,因为他们都需要动态添加到web容器中,即需要ServletContext。

    List-4

public class ServletContextInitializerBeans
		extends AbstractCollection<ServletContextInitializer> {

	private static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";

	private static final Log logger = LogFactory
			.getLog(ServletContextInitializerBeans.class);

	/**
	 * Seen bean instances or bean names.
	 */
	private final Set<Object> seen = new HashSet<>();

	private final MultiValueMap<Class<?>, ServletContextInitializer> initializers;

	private List<ServletContextInitializer> sortedList;

	public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		this.initializers = new LinkedMultiValueMap<>();
		addServletContextInitializerBeans(beanFactory);
		addAdaptableBeans(beanFactory);
		List<ServletContextInitializer> sortedInitializers = this.initializers.values()
				.stream()
				.flatMap((value) -> value.stream()
						.sorted(AnnotationAwareOrderComparator.INSTANCE))
				.collect(Collectors.toList());
		this.sortedList = Collections.unmodifiableList(sortedInitializers);
	}

	private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
				beanFactory, ServletContextInitializer.class)) {
			addServletContextInitializerBean(initializerBean.getKey(),
					initializerBean.getValue(), beanFactory);
		}
	}

	private void addServletContextInitializerBean(String beanName,
			ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
		if (initializer instanceof ServletRegistrationBean) {
			Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
			addServletContextInitializerBean(Servlet.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof FilterRegistrationBean) {
			Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
			addServletContextInitializerBean(Filter.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
			String source = ((DelegatingFilterProxyRegistrationBean) initializer)
					.getTargetBeanName();
			addServletContextInitializerBean(Filter.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof ServletListenerRegistrationBean) {
			EventListener source = ((ServletListenerRegistrationBean<?>) initializer)
					.getListener();
			addServletContextInitializerBean(EventListener.class, beanName, initializer,
					beanFactory, source);
		}
		else {
			addServletContextInitializerBean(ServletContextInitializer.class, beanName,
					initializer, beanFactory, initializer);
		}
	}
...

    接着引出一个问题,ServletContextInitializerBeans这个在哪被调用呢,在ServletWebServerApplicationContext中,在Servlet类型的Sprringboot Web应用中,ApplicationContext是AnnotationConfigServletWebServerApplicationContext,而ServletWebServerApplicationContext正是其父类。

    ServletWebServerApplicationContext中,方法onRefresh()-->createWebServer()-->getSelfInitializer()-->selfInitialize()-->getServletContextInitializerBeans()-->new ServletContextInitializerBeans(getBeanFactory())。

    ServletWebServerApplicationContext的onRefresh方法覆盖了AbstractApplicationContext的onRefresh方法,AbstractApplicationContext中,方法onRefresh被方法refresh调用。SpringApplication的run()-->refreshContext()-->refresh()-->AbstractApplicationContext的refresh()。

    通过上面的分析可以看出,Springboot利用SpringFramework的特性,将DispatcherServlet、Filter或者Listener通过ServletContextInitializer的ServletContext,添加到tomcat之类的web容器中,这些都发生在Springboot启动的过程中。

标签:springboot,beanFactory,List,initializer,ServletContext,DispatcherServlet,Servlet
来源: https://www.cnblogs.com/anenyang/p/15745157.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有