Spring(二十二)Spring MVC 对Tomcat 的 Servlet,Filter和Listener 组装分析

博主从Spring Boot 去看Spring MVC 启动过程,而Spring Boot 默认集成了内置的tomcat容器,所以分析Spring MVC ,中间还夹着挺多Tomcat逻辑。

Java web中有三大组件:

  1. Servlet
  2. Filter
  3. Listener

记得开始学习Java Web时候,就是通过这几个入门的,定义Servlet用于处理Http请求,定义Filter来对请求进行拦截,而使用ServletContextListener 来监听容器创建和销毁动作。

本文主要基于Tomcat来分析。

在Spring Boot 中,如果要定义这三种组件,可以这样进行:

  1. 使用 @ServletComponentScan("com.anla.springwebmvc.servlet") 开启对自定义Servlet包扫描
  2. 对三种组件,相应使用 @WebServlet@WebFilter@WebListener 进行配置,而后实现相应javax提供接口(HttpServletFilterServletContextListener)。
    例如加上一个自定义Servlet写法:
@WebServlet(urlPatterns = "/myservlelt")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type","text/html;charset=utf-8");
        response.getWriter().println("my springboot servlet……测试在Spring Boot自定义Servlet");
        response.getWriter().flush();
        response.getWriter().close();
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

ServletComponentScan

使用了 ServletComponentScan 注解,然后我们当然会看这个扫描做了什么事。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {

而在 ServletComponentScanRegistrar 中,通过registerBeanDefinitions 注册了一个bean ServletComponentRegisteringPostProcessor

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
		if (registry.containsBeanDefinition(BEAN_NAME)) {
			updatePostProcessor(registry, packagesToScan);
		}
		else {
			addPostProcessor(registry, packagesToScan);
		}
	}

BEAN_NAME 为:servletComponentRegisteringPostProcessor

再看看 ServletComponentRegisteringPostProcessor :

class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware {

它是一个 BeanFactoryPostProcessor,所以应该是Spring 扫描完Configuration 类后,直接就会轮到 它,看看重写的方法:

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		// 只有是内置服务器,才会执行
		if (isRunningInEmbeddedWebServer()) {
		// 构造扫描器,过滤WebServlet,WebFilter,WebListener 三种
			ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider();
			for (String packageToScan : this.packagesToScan) {
				// 具体扫描动作
				scanPackage(componentProvider, packageToScan);
			}
		}
	}

判断扫描规则,而后调用 scanPackage 对注解上传入的报名进行依次扫描:

	private void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan) {
		for (BeanDefinition candidate : componentProvider.findCandidateComponents(packageToScan)) {
			if (candidate instanceof ScannedGenericBeanDefinition) {
				for (ServletComponentHandler handler : HANDLERS) {
				// 依次判断类型
					handler.handle(((ScannedGenericBeanDefinition) candidate),
							(BeanDefinitionRegistry) this.applicationContext);
				}
			}
		}
	}
  1. componentProvider.findCandidateComponents(packageToScan) 扫描到的所有信息进行过滤。
  2. HANDLERS 中存储着三种类型handler WebServletHandlerWebFilterHandlerWebListenerHandler,分别用于解析三种类型组件。
    如果发现是对应类型Bean,则将其作为相应bean注册进容器。
    分别看这三个HandlerdoHandle方法:
    WebFilterHandler
	@Override
	public void doHandle(Map<String, Object> attributes, ScannedGenericBeanDefinition beanDefinition,
			BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterRegistrationBean.class);
		builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));
		builder.addPropertyValue("dispatcherTypes", extractDispatcherTypes(attributes));
		builder.addPropertyValue("filter", beanDefinition);
		builder.addPropertyValue("initParameters", extractInitParameters(attributes));
		String name = determineName(attributes, beanDefinition);
		builder.addPropertyValue("name", name);
		builder.addPropertyValue("servletNames", attributes.get("servletNames"));
		builder.addPropertyValue("urlPatterns", extractUrlPatterns(attributes));
		registry.registerBeanDefinition(name, builder.getBeanDefinition());
	}

WebListenerHandler

	@Override
	protected void doHandle(Map<String, Object> attributes, ScannedGenericBeanDefinition beanDefinition,
			BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletListenerRegistrationBean.class);
		builder.addPropertyValue("listener", beanDefinition);
		registry.registerBeanDefinition(beanDefinition.getBeanClassName(), builder.getBeanDefinition());
	}

WebServletHandler

	@Override
	public void doHandle(Map<String, Object> attributes, ScannedGenericBeanDefinition beanDefinition,
			BeanDefinitionRegistry registry) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletRegistrationBean.class);
		builder.addPropertyValue("asyncSupported", attributes.get("asyncSupported"));
		builder.addPropertyValue("initParameters", extractInitParameters(attributes));
		builder.addPropertyValue("loadOnStartup", attributes.get("loadOnStartup"));
		String name = determineName(attributes, beanDefinition);
		builder.addPropertyValue("name", name);
		builder.addPropertyValue("servlet", beanDefinition);
		builder.addPropertyValue("urlMappings", extractUrlPatterns(attributes));
		builder.addPropertyValue("multipartConfig", determineMultipartConfig(beanDefinition));
		registry.registerBeanDefinition(name, builder.getBeanDefinition());
	}

即通过注解中传入的参数,分别传入到BeanDefinition中,对应三种bean分别为:
ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean
在这里插入图片描述
到目前为止,Spring 对 ServletFilterListener 组装已经结束,就是将他们按照特定类型放入到Spring 容器中。

下篇文章,将看看Spring 何时使用他们,敬请期待。

觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值