SpringMVC初始化源码解析(基于XML配置)

18 篇文章 0 订阅

熟悉SpringMVC的同学都知道SpringMVC的核心是DispatcherServlet(以下简称ds), 由于继承了HttpServlet, 所以它本质就是servlet, 当我们使用tomcat或其他Web容器启动项目时, 会先扫描项目的web.xml文件, 我们会在该文件中指定ds的具体路径, 并设置启动load-on-startup即加载此ds, 然后容器会自动调用ds父类中的init方法
在这里插入图片描述

<servlet>
	<servlet-name>spring</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:/spring/spring-admin.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>spring</servlet-name>
	<url-pattern>*.html</url-pattern>
</servlet-mapping>
public final void init() throws ServletException {
    ...
	// 此处开始初始化
    this.initServletBean();
    ...
}

protected final void initServletBean() throws ServletException {
	...
	// 开始初始化web容器
    this.webApplicationContext = this.initWebApplicationContext();
    // 空方法
    this.initFrameworkServlet(); 
    ...
}

protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    WebApplicationContext wac = null;
    if(this.webApplicationContext != null) {
        wac = this.webApplicationContext;
        if(wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
            if(!attrName.isActive()) {
                if(attrName.getParent() == null) {
                    attrName.setParent(rootContext);
                }

                this.configureAndRefreshWebApplicationContext(attrName);
            }
        }
    }
	
    if(wac == null) {
    	// 先去查找有没有已经创建的ac
        wac = this.findWebApplicationContext();
    }

    if(wac == null) {
    	// 本地没有, 则创建ac, 重点
        wac = this.createWebApplicationContext(rootContext);
    }

    if(!this.refreshEventReceived) {
    	// 如果没有执行refresh则调用ds的onRefresh方法, 正常情况下是有回调的, 为spring自动发起的
        this.onRefresh(wac);
    }
	...
    return wac;
}

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    Class contextClass = this.getContextClass();
    ...
    // 利用反射, 调用无参数的构造函数创建对象
    ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
    // 设置环境
    wac.setEnvironment(this.getEnvironment());
    wac.setParent(parent);
    // 设置配置文件路径
    wac.setConfigLocation(this.getContextConfigLocation());
    // 执行刷新
    this.configureAndRefreshWebApplicationContext(wac);
    return wac;
}

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    ...
    wac.setServletContext(this.getServletContext());
    wac.setServletConfig(this.getServletConfig());
    wac.setNamespace(this.getNamespace());
    // 向容器中注册一个监听器, 后边会用到
    wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null)));
    ConfigurableEnvironment env = wac.getEnvironment();
    if(env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
    }

    this.postProcessWebApplicationContext(wac);
    this.applyInitializers(wac);
    // 执行spring容器的初始化, 此处不做太多分析, 等解析Spring源码时再讲解
    wac.refresh();
}

Spring默认会帮我们扫描到一个RequestMappingHandlerMapping类, 此类实现了ApplicationContextAware和InitializingBean接口及对应的方法, 在其被spring容器实例化,初始化并属性赋值后, 会回调对应方法afterPropertiesSet(), 然后调用父类的initHandlerMethods()方法

protected void initHandlerMethods() {
    if(this.logger.isDebugEnabled()) {
        this.logger.debug("Looking for request mappings in application context: " + this.getApplicationContext());
    }
	// 根据类型获取所有的beanName
    String[] beanNames = this.detectHandlerMethodsInAncestorContexts?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class):this.getApplicationContext().getBeanNamesForType(Object.class);
    String[] var2 = beanNames;
    int var3 = beanNames.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String beanName = var2[var4];
        if(!beanName.startsWith("scopedTarget.")) {
            Class beanType = null;

            try {
            	// 从容器中获取对应的bean
                beanType = this.getApplicationContext().getType(beanName);
            } catch (Throwable var8) {
                if(this.logger.isDebugEnabled()) {
                    this.logger.debug("Could not resolve target class for bean with name \'" + beanName + "\'", var8);
                }
            }
			// 判断是否有Controller或RequestMapping注解
            if(beanType != null && this.isHandler(beanType)) {
          		// 查找对应的method
                this.detectHandlerMethods(beanName);
            }
        }
    }
    // 空方法
    this.handlerMethodsInitialized(this.getHandlerMethods());
}

protected void detectHandlerMethods(Object handler) {
	// 从容器中获取对应name的beanClass
    Class handlerType = handler instanceof String?this.getApplicationContext().getType((String)handler):handler.getClass();
    final Class userType = ClassUtils.getUserClass(handlerType);
    // 将所有的带有rm注解的方法保存到map中
    Map methods = MethodIntrospector.selectMethods(userType, new MetadataLookup() {
        public T inspect(Method method) {
            try {
            	// 判断方法是否有rm注解, 如果有返回, 没有返回null
                return AbstractHandlerMethodMapping.this.getMappingForMethod(method, userType);
            } catch (Throwable var3) {
                throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var3);
            }
        }
    });
    if(this.logger.isDebugEnabled()) {
        this.logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
    }

    Iterator var5 = methods.entrySet().iterator();
    while(var5.hasNext()) {
        Entry entry = (Entry)var5.next();
        Method invocableMethod = AopUtils.selectInvocableMethod((Method)entry.getKey(), userType);
        Object mapping = entry.getValue();
        // 将方法注册到map中
        this.registerHandlerMethod(handler, invocableMethod, mapping);
    }
}

由于在spring中添加了一个监听器, 所以在容器初始化完成后会主动调用ds父类中的onApplicationEvent()方法

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    private ContextRefreshListener() {
    }

    public void onApplicationEvent(ContextRefreshedEvent event) {
    	// 调用ds类中的onRefresh方法
        FrameworkServlet.this.onApplicationEvent(event);
    }
}

protected void onRefresh(ApplicationContext context) {
   this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
	// 各种初始化, 从Spring容器中获取对应bean并填充到当前对象中
    this.initMultipartResolver(context);
    this.initLocaleResolver(context);
    this.initThemeResolver(context);
    this.initHandlerMappings(context);
    this.initHandlerAdapters(context);
    this.initHandlerExceptionResolvers(context);
    this.initRequestToViewNameTranslator(context);
    this.initViewResolvers(context);
    this.initFlashMapManager(context);
}

至此, SpringMVC的web容器已初始化完成

由于初次阅读SpringMVC源码, 可能会有很多不足, 如有问题, 欢迎评论指正, 感激不尽!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值