阳城做网站,企业信用信息公示系统(全国)官网,python可以用来干什么,公司网站一般多少钱Springmvc 是基于servlet 规范来完成的一个请求响应模块#xff0c;也是spring 中比较大的一个 模块#xff0c;现在基本上都是零xml 配置了#xff0c;采用的是约定大于配置的方式#xff0c;所以我们的springmvc 也是采用这种零xml 配置的方式。 要完成这种过程#xff… Springmvc 是基于servlet 规范来完成的一个请求响应模块也是spring 中比较大的一个 模块现在基本上都是零xml 配置了采用的是约定大于配置的方式所以我们的springmvc 也是采用这种零xml 配置的方式。 要完成这种过程要解决两个问题1、取代web.xml 配置 2、取代springmvc.xml 配置。 取代web.xml 配置在servlet 中有一个规范就是当servlet 容器启动的时候会根据SPI 规范加载 META-INF/services 文件夹下面的javax.servlet.ServletContainerInitializer 文件该文件下面的 类会实现javax.servlet.ServletContainerInitializer 接口。 SPI 也被称为服务接口扩展(Service Provider Interface) 直译服务提供商接口 不要被这个名字唬到了 其实很好理解的一个东西 就是根据Servlet厂商服务提供商提供要求的一个接口 在固定的目录 META-INF/services放上以接口全类名 为命名的文件 文件中放入接口的实现的 全类名该类由我们自己实现按照这种约定的方式即SPI规范服务提供商会 调用文件中实现类的方法 从而完成扩展。 该类SpringServletContainerInitializer在启动的时候会被servlet 容器例如tomcat实例化然后调用onStartup 方法并且servlet 容器会收集实现了HandlesTypes 注解里面的接口的类WebApplicationInitializer并且做为入参传入到onStartup 方法中我们拿到set 容器中的类就可以反射调用接口里面的方法了这是servlet 规范该规范就能保证servlet 容器在启动的时候就会完成这些操作。Springmvc 就借助这一点完成了取代web.xml 的工作。 我们定义了一个类MyWebAppInitializer继承自AbstractAnnotationConfigDispatcherServletInitializer最上层的接口其实就是WebApplicationInitializer这个接口有两个重要实现getRootConfigClasses将RootConfig引入了进来这个RootConfig就是父容器的配置类相当于之前的spring.xml另一个方法getServletConfigClasses引入进来了WebAppConfig这个是是子容器的配置类相当于之前的spring-mvc.xml。 父容器会对com.dsk下的包进行扫描但是排除了类上注解为RestController和Controller的类。因为controller要交给springmvc自身的子容器去管理父容器会管理一些Service相关的类。 子容器会对com.dsk下的包进行扫描但是只扫描了类上注解为RestContrller和Controller的类 相关的类介绍完之后看下核心的onStartup方法我们定义的MyWebAppInitializer与其父类的继承关系分别是MyWebAppInitializer-》AbstractAnnotationConfigDispatcherServletInitializer-》AbstractDispatcherServletInitializer-》AbstractContextLoaderInitializer-》WebApplicationInitializer
第一步继续会调用父类的onStartUp方法
第二步注册dispatcherServlet
我们再往下一层AbstractContextLoaderInitializer父类的onStartup方法可以看到这一步就是注册监听器ContextLoaderListener注册进servletContext这个监听器的作用其实就是当servlet容器例如tomcat启动成功后会调用监听器的contextInitialized方法。 这一步相当于在web.xml中配置了ContextLoaderListener如下 registerContextLoaderListener方法中不只是注册了listener还创建了父容器rootAppContext 创建父容器的方法createRootApplicationContext中其实就是熟悉的AnnotationConfigWebApplicationContext他将父容器也就是RootConfig注册进了BeanDefinitionRegistry中 总结registerContextLoaderListener方法做了两件事情
1、创建父容器也就是spring的AnnotationConfigWebApplicationContext
2、将ContextLoaderListener加入到servletContet中注意此时ContextLoaderListener持有父容器对象rootContext方法目的是当servlet容器启动成功后会通知到监听器此时监听器会调用父容器的refresh方法将类注入到spring容器中。
那么父类AbstractContextLoaderInitializer的onStartUp方法就看完了回到AbstractDispatcherServletInitializer类 这一步就是将dispatcherServlet注册进servletContext中并把上下文对象设置到了 dispatcherServlet 对象中相当于web.xml的如下配置 这个方法registerDispacherServlet不止是将dispatcherServlet加入到了servletContext同时也创建了子容器可以看到这个子容器也是AnnotationConfigWebApplicationContext只不过此时注册进来的类是WebAppConfig子类会将带有Controller的类扫描进来注入到子容器中。 那么onStartUp方法就执行完了做个总结
一、registerContextLoaderListener(servletContext);
做了两件事情1、创建父容器且listener中持有父容器对象2、将ContextLoaderListener加入到ServletContext
二、registerDispatcherServlet(servletContext);
做了两件事情1、创建子容器且dispatcherServlet中持有子容器对象2、将DispatcherServlet加入到ServletContext
ContextLoaderListener启动
执行完onStartUp方法后此时父容器和子容器还没有启动因为并没有执行到他们的refresh方法我们知道当servlet容器tomcat启动成功后会调用监听器ContextLoaderListener.contextInitialized方法这里会启动spring容器把spring上下文对象放入到了servletContext中 DispatcherServlet 的启动
了解DispatcherServlet的生命周期的会知道DispatcherServlet启动过程中会执行init()方法在这里会进行springmvc子容器的启动 这里会从servletContext中获取父容器对象并将子容器webApplicationContext的parent赋值为rootContext然后执行子容器的refresh()方法。 在调用子容器的刷新方法前这里会注册一个监听器该监听会初始化springmvc所需信息 ContextRefreshedEvent可以看到该监听器监听的是容器refreshed事件 会在子容器的refresh方法中的finishRefresh中发布这是spring的监听器模式。 子容器的refresh()方法最后一步会发布事件触发监听器的执行 这里面的每一个方法不用太细看 就是给SpringMVC准备初始化的数据 为后续SpringMVC处理请求做准备基本都是从容器中拿到已经配置的BeanRequestMappingHandlerMapping、 RequestMappingHandlerAdapter、HandlerExceptionResolver 放到dispatcherServlet中做准备。但是这些Bean又是从哪来的呢 回到我们的WebAppConfig使用的EnableWebMvc也就是这个注解取代了springmvc.xml 1. 导入了DelegatingWebMvcConfigurationImport(DelegatingWebMvcConfiguration.class) 2. DelegatingWebMvcConfiguration的父类就配置了这些Bean 3. SpringBoot也是用的这种方式
总结 1. Tomcat在启动时会通过SPI注册 ContextLoaderListener和DispatcherServlet对象 a. 同时创建父子容器 i. 分别创建在ContextLoaderListener初始化时创建父容器设置配置类 ii. 在DispatcherServlet初始化时创建子容器设置配置类 2. Tomcat在启动时执行ContextLoaderListener和DispatcherServlet对象的初始化方 法 执行容器refresh进行加载 3. 在子容器加载时 创建SpringMVC所需的Bean和预准备的数据(通过配置类 EnableWebMvc配置DelegatingWebMvcConfiguration——可实现WebMvcConfigurer进行定制扩展 a. RequestMappingHandlerMapping它会处理RequestMapping 注解 b. RequestMappingHandlerAdapter则是处理请求的适配器确定调用哪个类的哪个方法并且构造方法参数返回值。 c. HandlerExceptionResolver 错误视图解析器 d. addDefaultHttpMessageConverters 添加默认的消息转换器解析json、解析xml等 4. 子容器需要注入父容器的Bean时比如Controller中需要Autowired例如Service的Bean; 会先从子容器中找没找到会去父容器中找。 一、Spring和SpringMVC为什么需要父子容器不要不行吗就实现层面来说不用子父容器也可以完成所需功能参考SpringBoot就没用子父容器 1. 所以父子容器的主要作用应该是早期Spring为了划分框架边界。有点单一职责的味道service、dao层我们一般使用spring框架来管理、controller层交给springmvc管理 2. 规范整体架构 使 父容器service无法访问子容器controller、子容器controller可以访问父容器 service 3. 方便子容器的切换。如果现在我们想把web层从spring mvc替换成struts那么只需要将springmvc.xml替换成Struts的配置文件struts.xml即可而spring.xml不需要改变。 4. 为了节省重复bean创建 二、是否可以把所有Bean都通过Spring容器来管理Spring的applicationContext.xml中配置全局扫描) 不可以这样会导致我们请求接口的时候产生404。 如果所有的Bean都交给父容器SpringMVC在初始化HandlerMethods的时候initHandlerMethods无法根据Controller的handler方法注册HandlerMethod并没有去查找父容器的bean也就无法根据请求URI 获取到 HandlerMethod来进行匹配. 三、是否可以把我们所需的Bean都放入Spring-mvc子容器里面来管理springmvc的springservlet.xml中配置全局扫描? 可以 因为父容器的体现无非是为了获取子容器不包含的bean, 如果全部包含在子容器完全用 不到父容器了 所以是可以全部放在springmvc子容器来管理的。虽然可以这么做不过一般应该是不推荐这么去做的一般人也不会这么干的。如果你的项目里有用到事物、或者aop记得也需要把这部分配置需要放到Spring-mvc子容器的配置文件来不然一部分内容在子容器和一部分内容在父容器,可能就会导致你的事务或者AOP不生效。 所以如果aop或事务如果不生效也有可能是通过父容器中的类(spring)去增强子容器的类(Springmvc)也就无法增强。
下个章节我们解析springmvc的请求源码流程。