springmvc也是在spring framework中的,不是一个单独的项目
MVC就是和Tomcat有关。
01.MVC启动的第一步,启动Tomcat(这个和springboot的run方法启动Tomcat有关)
02.SpringMVC中,最为核心的就是DispatcherServlet,在启动Tomcat的过程中:
- Tomcat会先创建DispatcherServlet对象(这一步在手写springboot框架中已经写到,Tomcat在启动的时候会启动DispatcherServlet)
- 然后调用DispatcherServlet对象的init()
DispatcherServlet对象的init()
01.DispatcherServlet类的init()
public class DispatcherServlet extends FrameworkServlet {//DispatcherServlet中没有init方法,在父类中找
}
HttpServletBean类
@Overridepublic final void init() throws ServletException {// 解析 init-param 并封装只 pvs 中(xml)PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {// 将当前的这个 Servlet 类转化为一个 BeanWrapper,从而能够以 Spring 的方法来对 init-param 的值进行注入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.// 初始化Servlet,创建Spring容器initServletBean();}
DispatcherServlet 继承 httpservletbean
02.FrameworkServlet
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
03.initServletBean
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");}}
04initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {// 获得ContextLoaderListener存的父容器WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// 获得子容器wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {// 如果没有设置父容器 spring doGetBeanif (cwac.getParent() == null) {cwac.setParent(rootContext);}// 配置并且加载子容器configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// 从servlet上下文根据<contextAttribute>名字从域里面获取wac = findWebApplicationContext();}if (wac == null) {// xml会在这里创建wac = createWebApplicationContext(rootContext);}//refreshEventReceived 它会在容器加载完设置为true (通过事件onApplicationEvent)// springboot在这初始化组件if (!this.refreshEventReceived) {synchronized (this.onRefreshMonitor) {onRefresh(wac);}}if (this.publishContext) {// 将当前容器放到servlet域中, 可以再创建子容器String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;}