Spring源码解读(一)
前期准备
首先搭建一个简单的Spring Demo工程
项目目录结构如下图所示:
applicationContect.xml (可以取其他文件名,只要在加载配置文件时指定文件路径)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="chinese" class="com.bean.ChineseImpl"> <property name="name"> <value>小明</value> </property> <property name="age"> <value>10</value> </property> </bean> <bean id="american" class="com.bean.AmericanImpl"> <property name="name"> <value>Tom</value> </property> <property name="age"> <value>15</value> </property> </bean> </beans>
Person.java
package com.bean; public interface Person{ public void Speak(); }
ChineseImpl.java
package com.bean; public class ChineseImpl implements Person{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void Speak() { // TODO Auto-generated method stub System.out.println("I'm Chinese,My name is "+this.name+",I'm "+this.age+" years old!"); } }
AmericanImpl.java
package com.bean; public class AmericanImpl implements Person{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void Speak() { // TODO Auto-generated method stub System.out.println("I'm American,My name is "+this.name+",I'm "+this.age+" years old!"); } }
Test.java
package com.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.bean.Person; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = (Person) context.getBean("chinese"); person.Speak(); person = (Person) context.getBean("american"); person.Speak(); } }
下面将按照Spring初始化的过程:
构造函数
ClassPathXmlApplicationContext构造函数(ClassPathXmlApplicationContext[只能读放在web-info/classes目录下的配置文件],FileSystemXmlApplicationContext读具体路径)
public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); }
根据传入参数的不同,调用不同的构造函数,最终调用以下构造函数
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
设置配置文件路径
即AbstractRefreshableConfigApplicationContext.setConfigLocations
public void setConfigLocations(String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
resolvePath:
protected String resolvePath(String path) { return getEnvironment().resolveRequiredPlaceholders(path); }
此方法的目的在于将占位符(placeholder)解析成实际的地址。比如可以这么写: new ClassPathXmlApplicationContext("classpath:config.xml");
那么classpath:就是需要被解析的
refesh()
Spring bean解析就在此方法,所以单独提出来。
AbstractApplicationContext.refresh:
@Override 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) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
prepareRefresh
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); this.active.set(true); if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Initialize any placeholder property sources in the context environment initPropertySources(); // Validate that all properties marked as required are resolvable // see ConfigurablePropertyResolver#setRequiredProperties getEnvironment().validateRequiredProperties(); }
属性校验
AbstractEnvironment.validateRequiredProperties:
@Override public void validateRequiredProperties() throws MissingRequiredPropertiesException { this.propertyResolver.validateRequiredProperties(); }
AbstractPropertyResolver.validateRequiredProperties:
@Override public void validateRequiredProperties() { MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException(); for (String key : this.requiredProperties) { if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); } } if (!ex.getMissingRequiredProperties().isEmpty()) { throw ex; } }
requiredProperties是通过setRequiredProperties方法设置的,保存在一个list里面,默认是空的,也就是不需要校验任何属性
BeanFactory创建
由 refesh() 中的 obtainFreshBeanFactory 调用 AbstractRefreshableApplicationContext.refreshBeanFactory:
@Override protected final void refreshBeanFactory() throws BeansException {
//如果存在就销毁 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
BeanFactory定制
AbstractRefreshableApplicationContext.customizeBeanFactory方法用于给子类提供一个自由配置的机会,默认实现:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }
Bean加载
AbstractXmlApplicationContext.loadBeanDefinitions,这个便是核心的bean加载了:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }