这一次搞懂Spring Web零xml配置原理以及父子容器关系

在使用SpringSpringMVC的老版本进行开发时,我们需要配置很多的xml文件,非常的繁琐,总是让用户自行选择配置也是非常不好的。基于约定大于配置的规定,Spring提供了很多注解帮助我们简化了大量的xml配置;但是在使用SpringMVC时,我们还会使用到WEB-INF/web.xml,但实际上我们是完全可以使用Java类来取代xml配置的,这也是后来SpringBoott的实现原理。本篇就来看看Spring是如何实现完全的零XML配置。

正文

先来看一下原始的web.xml配置:

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value> <!--加载spring配置--> classpath:spring.xml </param-value> </context-param> <context-param> <param-name>webAppRootKey</param-name> <param-value>ServicePlatform.root</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> <!--<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>--> </listener> <servlet> <servlet-name>spring-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!--springmvc的配置文件--> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-dispatcher.xml</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>

这里各个配置的作用简单说下,context-param是加载我们主的sping.xml配置,比如一些bean的配置和开启注解扫描等;listener是配置监听器,Tomcat启动会触发监听器调用;servlet则是配置我们自定义的Servlet实现,比如DispatcherServlet。还有其它很多配置就不一一说明了,在这里主要看到记住context-paramservlet配置,这是SpringIOC父子容器的体现。在之前的I文章中讲过IOC容器是以父子关系组织的,但估计大部分人都不能理解,除了看到复杂的继承体系,并没有看到父容器作用的体现,稍后来分析。
了解了配置,我们就需要思考如何替换掉这些繁琐的配置。实际上Tomcat提供了一个规范,有一个ServletContainerInitializer接口:

public interface ServletContainerInitializer { void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException; }

Tomcat启动时会调用该接口实现类的onStartup方法,这个方法有两个参数,第二个不用说,主要是第一个参数什么?从哪里来?另外我们自定义的实现类又怎么让Tomcat调用呢?
首先解答最后一个问题,这里也是利用SPI来实现的,因此我们实现了该接口后,还需要在META-INF.services下配置。其次,这里传入的第一个参数也是我们自定义的扩展接口的实现类,我们可以通过我们自定义的接口实现很多需要在启动时做的事,比如加载Servlet,但是Tomcat又是怎么知道我们自定义的接口是哪个呢?这就需要用到@HandlesTypes注解,该注解就是标注在ServletContainerInitializer的实现类上,其值就是我们扩展的接口,这样Tomcat就知道需要传入哪个接口实现类到这个onStartup方法了。来看一个简单的实现:

@HandlesTypes(LoadServlet.class) public class MyServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException { Iterator var4; if (set != null) { var4 = set.iterator(); while (var4.hasNext()) { Class<?> clazz = (Class<?>) var4.next(); if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) && LoadServlet.class.isAssignableFrom(clazz)) { try { ((LoadServlet) clazz.newInstance()).loadOnstarp(servletContext); } catch (Exception e) { e.printStackTrace(); } } } } } } public interface LoadServlet { void loadOnstarp(ServletContext servletContext); } public class LoadServletImpl implements LoadServlet { @Override public void loadOnstarp(ServletContext servletContext) { ServletRegistration.Dynamic initServlet = servletContext.addServlet("initServlet", "org.springframework.web.servlet.DispatcherServlet"); initServlet.setLoadOnStartup(1); initServlet.addMapping("/init"); } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjpjj.html