springMVC零配置之上下文配置

记得刚参加工作的时候,搭建springMVC框架还是需要手动配置web.xml文件,现在已经被渐渐淘汰了,在项目中几乎看不到有这种配置了,取而代之的是springMVC的零配置,原理就是tomcat的SPI的技术。

通过spi机制,tomcat会扫描所有jar下的
/META-INF/services/javax.servlet.ServletContainerInitializer文件。
在这里插入图片描述

在这里插入图片描述

在SpringServletContainerInitializer中通过@HandlesTypes注解,将实现了所有WebApplicationInitializer接口的类作为参数传入onstartup方法中。
在这里插入图片描述
在这里插入图片描述

然后遍历所有实现了WebApplicationInitializer接口的类,调用他们的onstartup方法。

接下来定义一个WebApplicationInitializer接口的类:

代码片段一:

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //父容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{SpringContainer.class};
    }

    //SpringMVC配置子容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{MvcContainer.class};
    }

    //获取DispatcherServlet的映射信息
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

代码片段二:

//不扫描有@Controller注解的类
@ComponentScan(value = "com.dongsq",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class SpringContainer {
}

代码片段三:

//扫描有@Controller注解的类
@ComponentScan(value = "com.dongsq",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class MvcContainer {
}

定义的类继承了AbstractAnnotationConfigDispatcherServletInitializer,而被继承类 又实现了WebApplicationInitializer接口。

所以由继承关系可知:会调用到AbstractDispatcherServletInitializer的onStartup方法。
在这里插入图片描述
看super.onStartup方法:
在这里插入图片描述
super.onStartup方法做了两件事:
一、创建spring上下文,createRootApplicationContext方法会调用复写的getRootConfigClasses方法,会将自定义的SpringConfainer类加入spring容器。
在这里插入图片描述
二、servlet容器注册监听。

AbstractDispatcherServletInitializer的onStartup方法中的super.onStartup方法调用完成,接下来会调用registerDispatcherServlet方法,创建mvc上下文,注册DispatcherServlet。

代码片段四:

protected void registerDispatcherServlet(ServletContext servletContext) {
	String servletName = getServletName();
	Assert.hasLength(servletName, "getServletName() must not return null or empty");

	//创建mvc的上下文,这里会调代码片段一中的getServletConfigClasses
	WebApplicationContext servletAppContext = createServletApplicationContext();
	Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

	//创建DispatcherServlet对象,把springmvc上下文设置到DispatcherServlet中
	FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
	Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
	dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

	//把DispatcherServlet放到servlet上下文中
	ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
	if (registration == null) {
		throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
				"Check if there is another servlet registered under the same name.");
	}


	registration.setLoadOnStartup(1);
	//钩子方法 会调用代码片段一中的getServletMappings方法
	registration.addMapping(getServletMappings());
	registration.setAsyncSupported(isAsyncSupported());

	//定义拦截器
	Filter[] filters = getServletFilters();
	if (!ObjectUtils.isEmpty(filters)) {
		for (Filter filter : filters) {
			registerServletFilter(servletContext, filter);
		}
	}

	customizeRegistration(registration);
}

接下来在servlet容器启动时,会调到监听类中的contextInitialized方法。
在这里插入图片描述

看initWebApplicationContext方法:

代码片段五:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	.....

	try {
		.....
		if (this.context instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
			if (!cwac.isActive()) {
				if (cwac.getParent() == null) {
					ApplicationContext parent = loadParentContext(servletContext);
					cwac.setParent(parent);
				}
				//这里会调用refresh方法,启动spring上下文
				configureAndRefreshWebApplicationContext(cwac, servletContext);
			}
		}
	//将spring上下文设置到servlet容器中
	servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
		.....
	}
	....
}

接下来servlet容器在启动的时候,会调到DispatcherServlet中的init方法,最终会调到initWebApplicationContext方法。在这里插入图片描述
initWebApplicationContext方法代码:

protected WebApplicationContext initWebApplicationContext() {
	//从servlet下午文中获取spring的上下文对象
	WebApplicationContext rootContext =
			WebApplicationContextUtils.getWebApplicationContext(getServletContext());
	WebApplicationContext wac = null;

	if (this.webApplicationContext != null) {
		wac = this.webApplicationContext;
		if (wac instanceof ConfigurableWebApplicationContext) {
			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
			if (!cwac.isActive()) {
				if (cwac.getParent() == null) {
					//设置spring上下文为springMVC的父上下文
					cwac.setParent(rootContext);
				}
				//在这启动springMVC上下文
				configureAndRefreshWebApplicationContext(cwac);
			}
		}
	}
	if (wac == null) {
		wac = findWebApplicationContext();
	}
	if (wac == null) {
		wac = createWebApplicationContext(rootContext);
	}

	if (!this.refreshEventReceived) {
		onRefresh(wac);
	}

	if (this.publishContext) {
		String attrName = getServletContextAttributeName();
		getServletContext().setAttribute(attrName, wac);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
					"' as ServletContext attribute with name [" + attrName + "]");
		}
	}

	return wac;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值