Spring源码阅读入口web.xml

Spring 源码阅读起始web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!---->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      <!--加载spring配置-->
      classpath:config/spring/applicationContext-core.xml
    </param-value>
</context-param>
  <context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>ServicePlatform.root</param-value>
  </context-param>

  <context-param>
    <param-name>globalInitializerClasses</param-name>
    <param-value>com.zhuguang.jack.applicationContextInitializer.MyApplicationContextInitializer</param-value>
  </context-param>
  <!--<context-param>
      <param-name>log4jRefreshInterval</param-name>
      <param-value>600000</param-value>
  </context-param>

  <context-param>
      <param-name>log4jConfigLocation</param-name>
      <param-value>classpath:config/log/log4j.properties</param-value>
  </context-param>
  <listener>
      <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>-->
  <context-param>
    <param-name>allowBeanDefinitionOverriding</param-name>
    <param-value>true</param-value>
  </context-param>

  <listener>
  <!-- 当加载servlet 的时候此监听器执行-->
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--
  <filter>
    <filter-name>sessionFilter</filter-name>
    <filter-class>com.zhuguang.jack.filter.SessionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>sessionFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>-->

  <servlet>
    <servlet-name>spring-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--springmvc的配置文件-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:config/spring/spring-dispatcher.xml</param-value>
    </init-param>
    <init-param>
      <param-name>detectAllHandlerMappings</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>contextInitializerClasses</param-name>
      <param-value>com.zhuguang.jack.applicationContextInitializer.MyApplicationContextInitializer</param-value>
    </init-param>
    <init-param>
      <param-name>allowBeanDefinitionOverriding</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

spring 与springmvc 的区别

spring

有时候不是web项目但是想用spring的 aop 和ioc 怎么办
new ClassPathXmlApplicationContext(“application.xml”);

springmvc

包括spring aop ioc的功能
mvc 表示 model view controller

可能有些读者就要问了,web.xml 里面即加载了spring的ioc 和aop ,springmvc 里面又加载了aop 和ioc 那么不重复了么?

重复也不重复。何解? spring 容器将先加载,然后springmvc 的容器将拷贝一份放入自己的容器中,初始化容器的方法不在同一个类中。所以是没有冲突的

tomcat会先调用spring的Servlet 于是配置的监听器(ContextLoaderListener)将会执行

/**
	 * Initialize the root web application context.
	 * 这是spring 自己的注释 很明显的会调用这个方法初始化
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
	 //参数是 servelt容器
		initWebApplicationContext(event.getServletContext());
	}

进入initWebApplicationContext方法
在这里插入图片描述
刚开始debug 发现context 一定为空所以会走createWebApplicationContext 方法

创建WebApplicationContext

/**
	 * Return the WebApplicationContext implementation class to use, either the
	 * default XmlWebApplicationContext or a custom context class if specified.
	 * @param servletContext current servlet context
	 * @return the WebApplicationContext implementation class to use
	 * @see #CONTEXT_CLASS_PARAM
	 * @see org.springframework.web.context.support.XmlWebApplicationContext
	 */
//注释上写着返回WebApplicationContext 类型为默认的xnlWebApplicationContext 或者配置的类
	protected Class<?> determineContextClass(ServletContext servletContext) {
	//CONTEXT_CLASS_PARAM很明显如果配置则取的这个值, 我进去看了一下
	//public static final String CONTEXT_CLASS_PARAM = "contextClass";
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
		//很明显不配置为空走这里 ,contextClassName 和defaultStrategies有关
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
			//反射出类然后返回, 完美
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

// 寻找defaultStrategies 发现了这个静态代码块 defaultStrategies 是Properties类型
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
		//那么真相只有一个 这边 和DEFAULT_STRATEGIES_PATH 有关,所以去ClassPath下寻找ContextLoader.properties
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
		}
	}

在这里插入图片描述
找到了 在这呢:
在这里插入图片描述
内容如上图,之所以默认是XmlWebApplicationContext 的原因也找到了
回到createWebApplicationContext()方法

if (this.context == null) {
//这边其实按照我的理解 就是xml 里面配置了就用xml 里面的 ,没有的话就用默认的
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					//如果没有parent属性就去servlet 里面找找看
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					//这句代码里面做了很多事情
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			//servlet 也放了一份spring的
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);



String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
				wac.setId(idParam);
			}
			//contextid没有设置所以设为默认的
			else {
				// Generate default id...
				//参数如上 类名加冒号+项目名称
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}
		//将servlet容器放入了spring容器中
		wac.setServletContext(sc);
		//查看web.xml里面有没有配置contextConfigLocation,有就放进来,很明显这个是spring 写死的名称
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
		//实际上调到AbstractRefreshableWebApplicationContext类的方法里面了
			wac.setConfigLocation(configLocationParam);
		}

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}
		//当我们需要对spring的容器做点事情的时候 有时会用到这里
		customizeContext(sc, wac);
		wac.refresh();
	}

如何在spring容器初始化之前做点事情呢?
首先调用了wac.refresh就是对容器初始化了

//传入sc 和spring容器

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
//重点在这个方法里面
		List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
				determineContextInitializerClasses(sc);
		if (initializerClasses.isEmpty()) {
			// no ApplicationContextInitializers have been declared -> nothing to do
			return;
		}

		ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
				new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();

		for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
			Class<?> initializerContextClass =
					GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
			if (initializerContextClass != null) {
				Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
						"Could not add context initializer [%s] since its generic parameter [%s] " +
						"is not assignable from the type of application context used by this " +
						"context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
						wac.getClass().getName()));
			}
			initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
		}
		//对所用继承ApplicationContextInitializer的类排序
		AnnotationAwareOrderComparator.sort(initializerInstances);
		for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
		//循环调用initialize方法
			initializer.initialize(wac);
		}
	}

determineContextInitializerClasses()方法

public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
//只要在web.xml配置了globalInitializerClasses,contextInitializerClasses就会加载进来
	protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
			determineContextInitializerClasses(ServletContext servletContext) {

		List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
				new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();

		String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
		if (globalClassNames != null) {
			for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
				classes.add(loadInitializerClass(className));
			}
		}

		String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
		if (localClassNames != null) {
			for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
				classes.add(loadInitializerClass(className));
			}
		}

		return classes;
	}

至于如何排序则需要继承Ordered类

public class MyApplicationContextInitializer1 implements ApplicationContextInitializer,PriorityOrdered {
    //在spring容器初始化之前对spring的上下文做一些修改
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("===========MyApplicationContextInitializer");
    }

    @Override
    //返回值越大越先执行上面的方法
    public int getOrder() {
        return 1;
    }
}

我们再来看看springmvc 是如何做的,看springmvc 的话,根据web.xml 中的指示我们应该去看DispatcherServlet这个类

//DispatcherServlet 这个类继承了FrameworkServlet 
public class DispatcherServlet extends FrameworkServlet 
//FrameworkServlet 继承了HttpServletBean 和实现了ApplicationContextAware(ApplicationContextAware可以获取spring上下文)
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
//HttpServletBean 继承了HttpServlet 所以 DispatcherServlet 这东西就是一个servlet
public abstract class HttpServletBean extends HttpServlet
		implements EnvironmentCapable, EnvironmentAware

HttpServletBean 无脑看init 方法

	@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		// Set bean properties from init parameters.
		try {
			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
			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) {
			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
			throw ex;
		}

		// Let subclasses do whatever initialization they like.
		//初始化bean
		initServletBean();

		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

进入initServletBean()方法 又看见如下代码

	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
		//这边也是初始化webApplicationContext 方法
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值