spring源码之初始化
1.spring的入口
spring的核心是ioc容器。spring如何在tomcat启动的时候,实现IOC容器初始化?这里用到了监听器。
【代码清单】:在web.xml中配置监听器listener
<!--【代码清单】:在web.xml中配置监听器listener--> <!-- 配置spring初始化监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
在tomcat启动的时候,会自动运行配置的监听器ContextLoaderListener的contextInitialized()方法。这是因为ContextLoaderListener继承了ServletContextListener的接口。
2.spring初始化主要工作
//【代码清单】:contextInitialized()方法 publicvoid contextInitialized(ServletContextEvent event) { //创建一个ContextLoader this.contextLoader =createContextLoader(); //初始化ioc容器 this.contextLoader.initWebApplicationContext(event.getServletContext()); }
2.1.创建ContextLoader
①为什么需要ContextLoader?
spring的ioc容器有很多,不同的应用层面有不同的ioc服务,如接口的有BeanFactoy和ApplicationContext,继承这两个接口类又有很多,如继承applicationContext的有ConfigurableApplicationContext和WebApplicationContext,而实现这些接口的类也有很多,如实现webApplicationContext的有xmlWebApplictationContext,实现ConfigurableApplicationContext的有FileSystemApplicationContext。这些都是针对不用应用层面提供的ioc服务。FileSystemApplicationContext是在文件目录下解析xml 文件,而xmlWebApplictationContext是在xml配置中获取xml路径并解析。
针对这么多的ioc容器,tomcat启动的时候,到底初始化哪个ioc容器?这就需要一个类来对这些IOC容器进行管理,并决定在tomcat启动的时候,决定加载哪个ioc容器,这个类就是ContextLoader。
实际web项目开发是在web.xml中配置applicationContext.xml文件的路径。因此本文以webapplicationContext作主要解析。
<!--代码清单:web.xml中配置 applicationContext.xml路径--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param>
classpath:表示从类路劲加载配置文件,详细讲解,请看spring的策略管理。
②什么是ContextLoader?
//【代码清单】:ContextLoader publicclass ContextLoader { //初始化IOC容器 WebApplicationContext initWebApplicationContext(); //获得当前ioc容器 WebApplicationContext getCurrentWebApplicationContext(); //关闭ioc容器 void closeWebApplicationContext }
ContextLoader主要提供了初始化ioc容器,获取当前ioc容器,和关闭当前ioc容器的几个方法。我们可以创建自己的ContextLoader来加载自己想要的ioc容器。
③怎么创建ContextLoader?
//【代码清单】:createContextLoader() protected ContextLoadercreateContextLoader() { //实例化一个ContextLoader对象 returnnew ContextLoader(); }
在createContextLoader()方法中主要是new了一个ContextLoader实例。
2.2.初始化IOC容器
初始化IOC容器主要是调用了ContextLoader. initWebApplicationContext()方法。
【代码清单】:initWebApplicationContext() public WebApplicationContextinitWebApplicationContext (ServletContext servletContext) throwsIllegalStateException, BeansException { // 因为webApplicationContext最后要保存到request的*.ROOT常量中,所以这里先判断这个这个变量是否为null,为null则抛出异常。此处代码省略。 try { // 1.获取根上下文, ApplicationContextparent = loadParentContext(servletContext); //2. 创建webApplicationContext,完成初始化后添加到根上下文中 this.context =createWebApplicationContext(servletContext, parent); //3保存到servletContext.ROOT中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); // 4.添加到当前线程中 currentContextPerThread.put(Thread.currentThread().getContextClassLoader(), this.context); // 此处异常信息,代码省略。 returnthis.context; }
(1)获取根上下文
LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector"; LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; 【代码清单】:loadParentContext() protected ApplicationContext loadParentContext(ServletContextservletContext)throws BeansException { // 1.从ServletContext获取上面那2个常量值 servletContext.getInitParameter(); // 2.如果parentContextKey的值为空,则返回一个空的applicationContext,否则 if (parentContextKey!= null) { //3.先判断locatorFactorySelector是否为null,如果是null,则用默认值classpath*:beanRefContext.xml,如果不为null,则根据locatorFactorySelector实例化一个BeanFactoryLocator BeanFactoryLocatorlocator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector); // 4.然后用BeanFactoryLocator. useBeanFactory()方法实例化一个parentContextRef .ApplicationContext是parentContextRef的一个field. parentContextRef.applicationContext= new ClassPathXmlApplicationContext(parentContextKey) this.parentContextRef = locator.useBeanFactory(parentContextKey); //5. return parentContextRef.applicationContext parentContext = (ApplicationContext) this.parentContextRef.getFactory(); } return parentContext; }
(2)创建webApplicationContext
【代码清单】:createWebApplicationContext() protectedWebApplicationContext createWebApplicationContext( ServletContextservletContext, ApplicationContext parent) throws BeansException { //1.判断载入的applicationContext类型,如果没有则默认使用继承WebApplicationContext接口的类。 ClasscontextClass = determineContextClass(servletContext); //判断contextClass是否是ConfigurableWebApplicationContext.class类型,不是则抛出异常。此处代码省略。ConfigurableWebApplicationContext是一个继承了webApplicationContext和ConfiguteApplicationContext接口的接口。、 //2.根据contextClass实例化一个ConfigurableWebApplicationContext ConfigurableWebApplicationContextwac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); //3.添加根上下文 wac.setParent(parent); //4.添加servletContext wac.setServletContext(servletContext); //5.获取web.xml中配置的contextConfigLocation参数 wac.setConfigLocation(servletContext.getInitParameter(CONFIG_LOCATION_PARAM)); //6.定制Context,这是一个什么也不做的方法,用于拓展,可以忽略 customizeContext(servletContext,wac); //7.解析applicationContext.xml,完成初始化 wac.refresh(); return wac; }
①判断载入的根applicationContext类型
【代码清单】:determineContextClass() protected Class determineContextClass(ServletContextservletContext) throws ApplicationContextException { //1.获取我们在web.xml中配置的contextClass参数,这个参数可以可以决定我们需要载入的applicationContext类型 StringcontextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName!= null) { try { return ClassUtils.forName(contextClassName); } } else { //2.如果没配置有,则使用继承webApplicationContext接口的Class类 contextClassName= defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName,ContextLoader.class.getClassLoader()); } } }
②实例化一个实现ConfigurableWebApplicationContext的类
【代码清单】:instantiateClass(contextClassName) publicstatic Object instantiateClass(Classclazz) throws BeanInstantiationException { try { //重载instantiateClass方法 return instantiateClass(clazz.getDeclaredConstructor((Class[])null), null); } }
在instantiateClass(contextClassName)方法中重载了instantiateClass方法
【代码清单】:instantiateClass(constructor,args) publicstatic Object instantiateClass(Constructorctor, Object[] args) throws BeanInstantiationException { try { //setAccessible(true) ReflectionUtils.makeAccessible(ctor); //实例化 returnctor.newInstance(args); } }
③添加根上下文
【代码清单】:setParent() publicvoid setParent(ApplicationContextparent) { this.parent = parent; }
④设置servletContext
【代码清单】:setServletContext() publicvoid setServletContext(ServletContext servletContext) { this.servletContext = servletContext; }
⑤获取配置文件信息
CONFIG_LOCATION_PARAM = "contextConfigLocation"; servletContext.getInitParameter(CONFIG_LOCATION_PARAM)
获的web.xml配置contextConfigLocation的参数
【代码清单】:web.xml的contextConfigLocation配置参数
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param>
【代码清单】:setConfigLocation()
publicvoid setConfigLocation(String location) { //String CONFIG_LOCATION_DELIMITERS = ",; \t\n"; //“制表符(‘\t’)”、“换行符(‘\n’)”、“回车符(‘\r’)” setConfigLocations(StringUtils.tokenizeToStringArray(location,CONFIG_LOCATION_DELIMITERS)); }
tokenizeToStringArray()方法是处理“classpath:applicationContext*.xml”这个字符串成一个数组,然后调用setConfigLocations(String[]locations)方法。
【代码清单】:tokenizeToStringArray() publicstatic String[]tokenizeToStringArray( Stringstr, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { StringTokenizerst = new StringTokenizer(str, delimiters); Listtokens = new ArrayList(); while(st.hasMoreTokens()) { //分割 Stringtoken = st.nextToken(); //前后去掉空格。此处代码省略 if (!ignoreEmptyTokens || token.length() > 0) { //添加到数组 tokens.add(token); } } //转字符数组 returntoStringArray(tokens); }
【代码清单】:setConfigLocations(String[] locations)
//configLocations只是一个字符数组 private String[] configLocations; publicvoid setConfigLocations(String[] locations) { if (locations != null) { this.configLocations = newString[locations.length]; for (int i = 0; i <locations.length; i++) { //循环遍历,赋给configLocations this.configLocations[i] =resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
⑥解析applicationContext.xml,完成初始化
AbstractRefreshableWebApplicationContext是实现了
ConfigurableWebApplicationContext接口的类,但是
AbstractRefreshableWebApplicationContext中 并没有refresh()方法。当我们一层一层找它的父类的时候,在父类AbstractApplicationContext中找到了refresh()这个方法。
代码清单:AbstractApplicationContext.refresh() publicvoid refresh() throws BeansException,IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1.容器启动的预先准备,记录容器启动的时间和标记. prepareRefresh(); //2.创建BeanFactory,如果已有就销毁,没有就创建.此类实现了对BeanDefinition的装载 ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory(); //3.配置BeanFactory标准上下文特性,如类装载器、postProcesser等 prepareBeanFactory(beanFactory); try { // 4.在bean被装载后,提供一个修改BeanFactory的入口 postProcessBeanFactory(beanFactory); // 5.调用postProceessBeanFactory invokeBeanFactoryPostProcessors(beanFactory); //6.注册用于拦截bean创建过程中的BeanPostProcessors registerBeanPostProcessors(beanFactory); // 7.Initialize message source for this context. initMessageSource(); // 8.Initialize event multicaster for this context. initApplicationEventMulticaster(); //9.Initialize other special beans in specific contextsubclasses. onRefresh(); //10. 注册监听器 registerListeners(); //11.完成容器的初始化,里面的preInstantiateSingletons完成单例对象的创建 finishBeanFactoryInitialization(beanFactory); // 12.Last step: publish corresponding event. finishRefresh(); } } }