springmvc-xml/注解启动流程+servlet2.0/3.0
2024-03-11 21:40:36 1 举报
Spring MVC框架是一个基于Java的Web MVC框架,它利用XML或注解来配置请求处理程序。Spring MVC的启动流程包括: 1. 加载配置文件:Spring MVC首先加载XML或注解配置的文件,确定请求处理器、视图解析器、拦截器等组件。 2. 初始化DispatcherServlet:Spring MVC使用Servlet 2.0或3.0规范创建并初始化DispatcherServlet。在初始化过程中,DispatcherServlet会创建自己的ApplicationContext,并加载配置文件。 3. 处理请求:当收到客户端请求时,DispatcherServlet根据请求URL,通过HandlerMapping找到对应的Handler。如果使用了注解,DispatcherServlet会扫描所有Bean,找到标注了@Controller或@RequestMapping的类,并将其作为Handler。 4. 执行Handler:DispatcherServlet将请求和处理器封装成一个HandlerExecutionChain对象,并调用HandlerAdapter处理Handler。HandlerAdapter是一个接口,有多种实现,如RequestMappingHandlerAdapter(处理标注了@RequestMapping的方法)和HttpRequestHandlerAdapter(处理实现HttpRequestHandler接口的类)。 5. 处理视图:Handler执行完成后,将返回一个ModelAndView对象,其中包含了视图信息和模型数据。DispatcherServlet根据ModelAndView找到相应的视图解析器,如InternalResourceViewResolver,通过视图解析器将视图渲染为HTML,并响应给客户端。 Spring MVC的启动流程利用了Servlet 2.0/3.0规范的特性,如AnnotationConfigWebApplicationContext和RequestDispatcher,使得开发Web应用更加便捷。
作者其他创作
大纲/内容
注意也有父子容器的区分噢
下面来说说配置文件的问题,我们在web.xml的年代,会在web.xml里面配置一个文件路径,这个文件也就是未来springboot淘汰的那个繁重的配置文件,但是我们还得了解一下当年这个文件是怎么玩的。1、如果配置怎么配置2、如果不配置,默认是那个路径下的,spring就喜欢弄这种默认值处理。3、从tomcat的启动,到web.xml的配置,到spring的启动,和这个配置文件的加载,是怎么连贯起来的。
源码如上,看似简单,其实也不复杂,但是有个注意的地方1、首先判断的时候NameSpace参数是否为空,这个参数如果显示配置,肯定不会为空,但是我们一般也不会配置,用默认值,那么这个参数的默认值是什么呢。这就涉及springmvc的父子容器的概念①如果是初始化父容器,这个namespace一般是空的②如果是初始化springmvc也就是子容器,这个默认值就是web.xml里面配置的servlet的名字,加上后缀-servlet源码路径如下,自己可以看看org.springframework.web.servlet.FrameworkServlet#getNamespace从上面的源码可以看到,这是一个servlet里面的方法,所以就是根容器,不会有这个逻辑第3行代码的前缀 /WEB-INF/+servletNameXxx-servlet+.xml2、如果NameSpace是空的,那么就直接走第6行代码,就是获取这个路径下的配置文件/WEB-INF/applicationContext.xml,是不是很陌生,之前旧项目都会有这个/WEB-INF/目录,现在逐渐没有了。
以注解方式来整合Springmvc:继承AbstractAnnotationConfigDispatcherServletInitializer:实现抽象方法getServletConfigClasses,getRootConfigClasses,getServletMappings即可。
上面说的是两个大情况的启动流程1、xml方式2、注解方式这两个方式本质还是一样的那就是都是需要先启动tomcat所以启动tomcat是第一步 xml是先初始化监听器,servlet,过滤器监听器里面先启动父容器servlet里面启动子容器当然xml和注解不同的是,spring父子容器是先启动,然后再和监听器和servlet绑定再初始化监听器,servlet,过滤器那么springboot呢?怎么处理的呢?且听下次分析
到此,这个默认的两个值,都理解了,但是我们一般都会显示配置文件的名称和路径。
protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } }
protected WebApplicationContext createRootApplicationContext() { //这里就是左边哪个案例我们自定义的实现类的方法调用 Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(configClasses); return context; } else { return null; } }
ServletContainerInitializer机制(SCI机制) 采用的就是SPI机制Servlet容器启动会扫描,当前应用里面的每一个jar包的ServletContainerInitializer的实现2.提供ServletContainerInitializer的实现类;必须绑定在:META-INF/services/ javax.servlet.ServletContainerInitializer,文件的内容就是ServletContainerInitializer实现类的全类名
通过spi机制得到一个类
右边的根容器初始化完成 了,还有一个子容器,也就是springmvc,这个初始化,也得和tomcat挂钩,servlet,是什么,相信你知道,然后spring实现一个大大大的servlet,来完成所有请求的处理,既然这样,肯定得依赖spring这个容器,那么就得初始化这个容器,但是我们刚分析了,已经初始化了一个容器了,我们通常称刚刚那个初始化的spring的容器,是根(父)容器,什么是父子容器概念了, 也java的双亲委派机制类似,就是获取一个bean的方式,是先从当前容器开始找,如果没有,就从父容器开始继续找,一直往上找,父容器是无法获取子容器的bean的
得到这个类,然后通过反射实例化这个对象。注意这个只是推断用什么WebApplicationContext,里面还有个配置文件呢
1、先获取这个参数contextConfigLocation2、然后如果这个值配置了,就处理一下,用\
//相当于web.xml//web容器启动的时候创建对象,调用方法来初始化容器以及前端控制器public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //获取根容器的配置类(相当于spring配置文件),然后创建出一个父容器 @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } //获取web容器的配置类(相当于Springmvc的配置文件)子容器 @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{AppConfig.class}; } //获取Dispatcherservlet的映射信息 //拦截所有请求 // \"/\
那就了解一些内置是spring注解容器怎么启动的
这个初始化根容器的方法是这样的
下面是一个案例,感觉是不是和springboot的启动类异曲同工之妙
如果没有配置,就获取默认值getDefaultConfigLocations逐行分析一下这个方法,还很关键
至此,Spring的父(根)上下文已经初始化完毕,并且已经存在ServletContext中。
这个接口有三个抽象子类,最小的抽象子类是AbstractAnnotationConfigDispatcherServletInitializer
到这里,可能配置了,也可能没配置,但是不管怎么,继续下面一个承上启下的操作org.springframework.context.support.AbstractApplicationContext#refresh这里面的13个方法,也是学习spring源码的重点,相信大家都很明白,今天不详解里面的细节,主要看配置文件这一块,因为我们现在还是说的旧时候xml方法开发的项目的流程,所以还是看看这个关键复杂的繁琐的配置文件怎么玩的
总结和思考
进入到org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory里面的org.springframework.context.support.AbstractApplicationContext#refreshBeanFactory方法最终进入到org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)继续跟进里面的内容Resource[] configResources = getConfigResources();String[] configLocations = getConfigLocations();
两个参数其实是一个,这就对上了
先执行AbstractContextLoaderInitializer方法的onStartup方法
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) { return new DispatcherServlet(servletAppContext);//注意这个构造函数的参数,之前说的 }
所以我们要实现这个最小的实现类,并且实现几个必要的方法
回到AbstractDispatcherServletInitializer的onStartup方法继续执行registerDispatcherServlet(servletContext);
web.xml
因为这是内置的逻辑,也是spring推崇的,所以都是注解的方式,当然我们可以自定义WebApplicationInitializer实现类,用xmlweb的方式启动spring容器,没人拦着你
在传统的 Spring MVC 项目中,web.xml 是用于配置 Servlet 容器的部署描述符。<listener> 元素可以用来声明 Servlet 监听器,并通过 <listener-class> 元素指定监听器的类。org.springframework.web.context.ContextLoaderListener 是一个监听器类,它用于在 Servlet 容器启动时加载 Spring 应用上下文,并将其放置在 Servlet 上下文中供整个应用程序使用。这个监听器的作用是为了初始化 Spring 容器和相关的 bean。如果你不配置 <listener> 元素或不指定 org.springframework.web.context.ContextLoaderListener 类,那么在传统的 Spring MVC 项目中,Spring 容器将无法自动加载和初始化,进而导致无法使用 Spring MVC 的功能和特性。因此,在传统的 Spring MVC 项目中,一般是需要配置 <listener> 元素,并将 org.springframework.web.context.ContextLoaderListener 类指定为监听器。web.xml<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>这样,当 Servlet 容器启动时,ContextLoaderListener 会被触发,Spring 容器将被加载并初始化。这对于传统的 Spring MVC 项目的正确运行是必要的。
调用WebApplicationInitializer接口实现类的onStartup方法,
各种推断,用什么对象创建
上面的onStartup方法会判断,前一步获取到的实现类是不是抽象的,因为要实例化,所以spring内置的这些实现类不满足要求
Tomcat启动的时候会依次加载web.xml中配置的Listener、Filter和Servlet。下面这个代码是servlet的源码public interface ServletContextListener extends EventListener { void contextInitialized(ServletContextEvent var1); void contextDestroyed(ServletContextEvent var1);}tomcat会先执行这个里面的第一个方法,在启动的时候,然后spring利用这个方法,然后在里面调用了spring容器的初始化方法,也就是之前学的那个spring容器刷新的那些方法。
先创建容器
protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); //这里就是左边哪个案例我们自定义的实现类的方法调用Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { context.register(configClasses); } return context; }
子容器xml方式
看下getBean获取bean的过程
调用这个类的onStartup方法
如果配置就如下面<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:beans.spring.xml</param-value></context-param>
protected void registerContextLoaderListener(ServletContext servletContext) { WebApplicationContext rootAppContext = createRootApplicationContext();-->右边详解 if (rootAppContext != null) { //这里创建了Servlet中的listener,容器会回调listener的contextInitialized 这个构造方法很重要,里面的参数就是我们的容器,我们之前分析过的噢, 并且注意到了没,这个类就是我i们之前在web.xml必然配置的监听器类 ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(getRootApplicationContextInitializers()); servletContext.addListener(listener); } }上面的说的回调,就是tomcat初始化ContextLoaderListener监听器了,就和我们之前分析的流程对上了,就不展开说了,和之前的区别就是容器类型换成了AnnotationConfigWebApplicationContext
listener初始化时 传入的父容器对象,而web子容器对象是和dispatcher绑在一起的1、父子容器的主要作用应该是划分框架边界。父子容器的主要作用应该是划分框架边界。有点单一职责的味道。在J2EE三层架构中,在service层我们一般使用spring框架来管理, 而在web层则有多种选择,如spring mvc、struts等。因此,通常对于web层我们会使用单独的配置文件。例如在上面的案例中,一开始我们使用spring-servlet.xml来配置web层,使用applicationContext.xml来配置service、dao层。如果现在我们想把web层从spring mvc替换成struts,那么只需要将spring-servlet.xml替换成Struts的配置文件struts.xml即可,而applicationContext.xml不需要改变。2、是否可以把所有类都通过Spring父容器来管理Spring的applicationContext.xml中配置全局扫描,所有的类都通过父容器来管理的配置就是如下:然后在SpringMvc的配置里面不配置扫描包路径。很显然这种方式是行不通的,这样会导致我们请求接口的时候产生404。因为在解析@ReqestMapping注解的过程中initHandlerMethods()函数只是对Spring MVC 容器中的bean进行处理的,并没有去查找父容器的bean, 因此不会对父容器中含有@RequestMapping注解的函数进行处理,更不会生成相应的handler。所以当请求过来时找不到处理的handler,导致404。 3、看上面的font color=\"#e74f4c\
根容器xml方式
tomcat7及以上+servlet3.0+spring全注解+无xml模式--启动流程
启动tomcat
SpringServletContainerInitializer
这个监听器会最终会执行这个方法org.springframework.web.context.ContextLoader#initWebApplicationContext
return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
<!DOCTYPE web-app PUBLIC \
入口
记得看最下面的总结
这个算是子容器
源码分析怎么加载和处理
一、根容器的方法----ContextLoaderListener 这个类的方法触发的public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( \"Cannot initialize context because there is already a root application context present - \" + \"check whether you have multiple ContextLoader* definitions in your web.xml!\
根据这个注解,spring应用会加载这个接口的所有实现类到当前类@HandlesTypes({WebApplicationInitializer.class})具体怎么加载的,我还没懂
这个算是root,父容器
0 条评论
下一页