主页 > 游戏开发  > 

SpringMvc请求原理流程

SpringMvc请求原理流程

springmvc是用户和服务沟通的桥梁,官网提供了springmvc的全面使用和解释:DispatcherServlet :: Spring Framework

流程

1.Tomcat启动

2.解析web.xml文件,根据servlet-class找到DispatcherServlet,根据init-param来获取spring的配置文件,spring的配置文件配置的主要内容就是参数的扫描路径(扫描Bean)和自定义Bean

<servlet> <servlet-name>mvc_shine_aa</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:mvc.xml</param-value> </init-param> <!-- Servlet默认懒加载,改成饿汉式加载(可选) --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc_shine_aa</servlet-name> <!--<url-pattern>*.action</url-pattern>--> <url-pattern>/</url-pattern> </servlet-mapping>

mvc.xml文件

<!-- 注解扫描 --> <context:component-scan base-package="com.qf.web"/> <bean class="com.yuyu.AService"> <property name="id" value="2"></property> </bean>

3.创建DispatcherServlet实例,创建实例后会执行父类(FrameworkServlet)的父类(HttpServletBean)的init方法,HttpServletBean 在执行init()方法最后会执行initServletBean()方法,该方法在HttpServletBean是一个空方法,目的是让继承他的子类自己去完成接下来的初始化操作。

@Override public final void init() throws ServletException { // Set bean properties from init parameters. // 进行一些初始化操作 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. // 核心代码,将剩余的初始化操作交给子类自己去实现 initServletBean(); }

接下来程序就走到了FrameworkServlet的initServletBean()方法,

@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { // 核心代码 this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } }

该方法的核心就是

this.webApplicationContext = initWebApplicationContext();

这一步就是创建一个spring容器,该方法首先会尝试获取父容器,然后判断之前是否有创建过webApplicationContext容器。

protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // 创建本地实例 // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }

如果之前没有创建容器,程序会走到

        if (wac == null) {             // 核心代码,创建本地容器实例             // No context instance is defined for this servlet -> create a local one             wac = createWebApplicationContext(rootContext);         }

通过createWebApplicationContext(rootContext),之后就会成功创建并初始化一个spring容器了

protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } // 创建容器实例,里面的属性都是空的 等待填充 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); // 设置容器的父容器 wac.setParent(parent); String configLocation = getContextConfigLocation(); if (configLocation != null) { wac.setConfigLocation(configLocation); } // 对容器进行初始化等操作 填充容器 configureAndRefreshWebApplicationContext(wac); return wac; }

之后spring容器创建完成,就可以给外部提供访问了。

springmvc的父子容器

在前文中发现,DispatcherServlet在生成容器之前会去找一个rootContext父容器,那么这个父容器是什么呢?为什么要找父容器呢?

现在如果在配置文件中声明了两个servlet,并且对应的spring配置文件配置了不同的bean,但是扫描的bean路径都相同的话,就会出现两个DispatcherServlet容器里会有一部分重复的bean

<!-- servlet1 --> <servlet> <servlet-name>app1</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation1</param-name> <param-value>spring1.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app1</servlet-name> <url-pattern>/app1/*</url-pattern> </servlet-mapping> <!-- servlet2 --> <servlet> <servlet-name>app2</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation2</param-name> <param-value>spring2.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app2</servlet-name> <url-pattern>/app2/*</url-pattern> </servlet-mapping>

springmvc为了解决这个问题就创造了一个父容器的概念,在springmvc官方提供的配置文件中就有一个<listener/>属性 该属性就是定义的父容器,tomcat在读取web.xml文件时,首先读取的就是<listener/>和<context-param>来创建父容器。之后再创建子容器,创建完子容器后就会将父容器放入子容器中,这样就可以避免Bean的重复创建。

<web-app> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app-context.xml</param-value> </context-param> <servlet> <servlet-name>app</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value></param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> </web-app> springMvc的零配置

spring官方不止提供了通过配置文件来配置springMvc,还提供了配置类的形式来配置,这样就可以省略掉xml配置文件了。

public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { // Load Spring web application configuration AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); // AppConfig对应之前的spring.xml配置文件配置包扫描路径 context.register(AppConfig.class); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(context); ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } } @ComponentScan("com.yuyu") public class AppConfig { }

使用代码的方式来实现父子容器也非常简单

public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { // Load Spring web application configuration AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(AppConfig.class); // 创建子容器,将context 父容器注入子容器 AnnotationConfigWebApplicationContext context2 = new AnnotationConfigWebApplicationContext(); context2.register(AppConfig.class); context2.setParent(context); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(context); ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }

标签:

SpringMvc请求原理流程由讯客互联游戏开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“SpringMvc请求原理流程

上一篇
list.toArray

下一篇
vue3实现pdf预览