spring及其子项目,在服务端中的使用非常广泛,由于服务端的特点以及spring大部分都是单例bean的特点,通常是尽量在启动时完成初始化。请求时完成少量初始化或者直接使用已经初始化的数据,从而保证性能。
spring-mvc的启动看似简单,但是实际上考虑到各种场景,其中包含了跟多区别。
spring-boot上手即用的特性使得spring的初始化细节被隐藏。
首先总结:Spring有三种使用方式:1.单独Spring-mvc使用 2.结合Spring使用 3.spring-boot
1.单独Spring-mvc方式
Spring-mvc可以单独使用,本质上作为一个Servlet实例,被Servlet容器实例化,实例化之后会init()方法。
Spring-mvc需要一个容器,利用这个容器做IOC和AOP,这种情况下,init()方法就是创建容器的时机。具体细节时首先从ServletContext中通过WebApplicationContext.class.getName() +".ROOT"这个key获取ApplicationContext,此时肯定获取不到,后面执行createWebApplicationContext方法进行创建,此时需要提供contextConfigLocation配置文件的地址。随后会执行org.springframework.context.support.AbstractApplicationContext#refresh()方法。这个方法是Spring的启动时的重要方法。会解析配置得到BeanDefinitions,其中的一步关键步骤是preInstantiateSingletons(),创建bean对象。此时,你配置的其他Spring-mvc配置就能生效,并且在容器中缓存,preInstantiateSingletons阶段有一些重要的初始化:
解析RequestMappingHandlerAdapter
解析RequestMappingHandlerMapping
添加SimpleUrlHandlerMapping
解析ExceptionHandlerExceptionResolver
添加SimpleUrlHandlerMapping规则
当这些提前解析好,并放在容器,后续才能从容器中直接获取装配使用.
但是,以上的初始化并不完全,运行时Spring仍然要完成其他项。refresh()完成之后,代码将执行到org.springframework.web.servlet.DispatcherServlet#onRefresh,这个方法将会执行initStrategies(ApplicationContext)方法,传入WebApplicationContext,这个方法会执行下面几个方法。用途附上。
//从容器中获取MultipartResolver的配置,并将配置设给DispatcherServlet的multipartResolver属性。没有会忽略。
initMultipartResolver(context);
//从容器中获取LocaleResolver的配置,并将配置设给DispatcherServlet的LocaleResolver属性。没有配置会从DispatcherServlet.properties文件中获取默认实现类。这个文件里面还有很多的其他默认配置。
initLocaleResolver(context);
initThemeResolver(context);
//重要组件,用来匹配请求
initHandlerMappings(context);
//重要组件,负责请求后的参数注入
initHandlerAdapters(context);
//重要组件,负责处理全集异常处理
initHandlerExceptionResolvers(context);
//重要,用来处理后缀
initRequestToViewNameTranslator(context);
//重要组件,用来进行视图匹配.
initViewResolvers(context);
//用于redirecting传递数据.
initFlashMapManager(context);
执行完上面的步骤,Spring-mvc就能处理请求了.
这种方式很多配置需要自己配,但是了解的话就能根据自己实际情况配置合适的配置,不像spring-boot有很多配置已经默默配置完成,出现问题也很难排查.
2.Spring+Mvc
这种情况下跟上面情况类似,区别在于已经有Spring的容器了.如果Spring的容器本身也是WebApplicationContext,则会用同一个容器,如果不是,就会专门创建一个Web容器给Mvc,跟第一种情况类似,只不过会将Spring容器作为父容器.参见org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.web.context.WebApplicationContext),这种情况下有一个区别,由于Spring容器有事件,很可能不需要等DispatcherServlet init()方法被执行,由Spring事件监听从而执行org.springframework.web.servlet.DispatcherServlet#onRefresh
还有区别是通过Spring能做到org.springframework.web.servlet.config.annotation.EnableWebMvc这个注解导入很多配置.从而不需要mvc.xml文件了.但是跟具体配置方式相关.
3.Spring-boot
Spring-boot是在第二个情况下的完善.做到了无配置使用.
这种情况下,Spring+Mvc共用一个容器.整体思路跟上面几乎一样.但是因为默认导入了EnableWebMvc注解,会启用很多配置.而且本身Spring-boot 附带了autoconfigure模块,里面能根据classpath下的某个类是否存在来决定是否启用某个@Bean配置.最终做到了添加spring-boot的web依赖就能直接可用.但是spring-boot易用难精.整体配置脉络很难掌握.导致很容易因为依赖问题出错.但是其核心还是通过Java annotation方式将initStrategies方法中的方法所需要的依赖提前在容器中添加好,以便于DispatcherServlet能够取出,从而在请求阶段能够正确响应.
网友评论