代码进口
之前写文章城市啰烦琐嗦一大堆再开始,进入【Spring源码阐明】这个板块就直接切入正题了。
许多伴侣大概想看Spring源码,可是不知道该当如何入手去看,这个可以领略:Java开拓者凡是从事的都是Java Web的事情,对付措施员来说,一个Web项目用到Spring,只是设置一下设置文件罢了,Spring的加载进程相对是不太透明的,不太好去找加载的代码进口。
下面有很简朴的一段代码可以作为Spring代码加载的进口:
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); ac.getBean(XXX.class);
ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring设置文件,软件开发,可以看到,第二行就已经可以获取到Bean的实例了,那么一定第一行就已经完成了对所有Bean实例的加载,因此可以通过ClassPathXmlApplicationContext作为进口。为了后头便于代码阅读,先给出一下ClassPathXmlApplicationContext这个类的担任干系:
大抵的担任干系是如上图所示的,由于版面的干系,没有继承画下去了,左下角的ApplicationContext该当尚有一层担任干系,较量要害的一点是它是BeanFactory的子接口。
最后声明一下,本文利用的Spring版本为3.0.7,较量老,利用这个版本纯粹是因为公司利用罢了。
ClassPathXmlApplicationContext结构函数
看下ClassPathXmlApplicationContext的结构函数:
1 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { 2 this(new String[] {configLocation}, true, null); 3 }
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
从第二段代码看,总共就做了三件事:
1、super(parent)
没什么太大的浸染,配置一下父级ApplicationContext,这里是null
2、setConfigLocations(configLocations)
代码就不贴了,一看就知道,内里做了两件工作:
(1)将指定的Spring设置文件的路径存储到当地
(2)理会Spring设置文件路径中的${PlaceHolder}占位符,替换为系统变量中PlaceHolder对应的Value值,System自己就自带一些系统变量好比class.path、os.name、user.dir等,也可以通过System.setProperty()要领配置本身需要的系统变量
3、refresh()
这个就是整个Spring Bean加载的焦点了,它是ClassPathXmlApplicationContext的父类AbstractApplicationContext的一个要领,顾名思义,用于刷新整个Spring上下文信息,界说了整个Spring上下文加载的流程。
refresh要领
上面已经说了,refresh()要领是整个Spring Bean加载的焦点,因此看一下整个refresh()要领的界说:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
每个子要领的成果之后一点一点再阐明,首先refresh()要领有几点是值得我们进修的:
1、要领是加锁的,这么做的原因是制止多线程同时刷新Spring上下文
2、尽量加锁可以看到是针对整个要领体的,可是没有在要领前加synchronized要害字,而利用了工具锁startUpShutdownMonitor,这样做有两个长处: