Spring
最近做IT测试时,每次Spring 加载配置等都很慢,抽空把Spring 容器的启动整理一下。
容器
Spring中听的最多的就是依赖注入、控制反转。一般情况下,A对象使用B对象,都是A先创建B,然后调用B的方法。这样两个对象间有依赖的。控制反转简单来说就是对象-对象的依赖关系变成对象-容器-对象。这样每次A只需要调用传给它的B即可。所以容器的管理对象的作用就显而易见了。联系它的功能,容器其实就是创建、维护与管理对象的工厂类,只不过这个类的附加功能比较强大而已。
BeanFactory
BeanFactory 是容器的最基本的接口,所有的factory和context都继承或实现它。提供getBean() 供获取容器中受管理的Bean,这里是通过Bean的名字获取的。实际上,在Spring 把 DefaultListableBeanFactory 作为默认的容器来使用的,然后容器加载配置来创建Bean。
一般情况下,从Spring配置文件beans.xml 中加载Bean,这个主要是加载BeanDefinitions的过程。
看个代码(我用的版本是5.0.4.RELEASE):
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader { // ...省略其他 protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; if (this.registry instanceof ResourceLoader) { this.resourceLoader = (ResourceLoader)this.registry; } else { this.resourceLoader = new PathMatchingResourcePatternResolver(); } if (this.registry instanceof EnvironmentCapable) { this.environment = ((EnvironmentCapable)this.registry).getEnvironment(); } else { this.environment = new StandardEnvironment(); } } // ...省略其他 public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; Resource[] var3 = resources; int var4 = resources.length; for(int var5 = 0; var5 < var4; ++var5) { Resource resource = var3[var5]; counter += this.loadBeanDefinitions((Resource)resource); } } // ...省略其他 }
简单来说:
1. 创建容器配置文件的抽象资源,它包含BeanDefinition 的定义信息。
2. 创建一个BeanFactory,Spring默认使用 DefaultListableBeanFactory。
3. 创建一个BeanDefinition 的读取器。
4. 读取配置信息(loadBeanDefinitions函数),解析并注册。
然后容器就可以使用了。
ApplicationContext
看到名字就知道它是上下文。实际上ApplicationContext 是一个高级点的容器。另外,因为继承了六个interface,因此它还有语言国际化、访问资源、发布应用事件等功能。下面看下类图:
从图中看,ApplicationContext 其实是BeanFactory的子接口。另外还继承了其他功能接口。简单来说,它主要提供资源的定位和加载。
容器初始化
在上边图中,从ApplicationContext 接口往下看是 ConfigurableApplicationContext。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { // ... 省略 void refresh() throws BeansException, IllegalStateException; // ... 省略 }
其实,容器初始化就是由refresh() 函数来启动的,这个方法标志着容器的正式启动。简单来说,启动分为三个基本过程:BeanDefinition的Resource定位、载入和注册。
现在配置一个beans.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="springHelloWorld" class="com.oocl.spring.SpringHello"></bean> </beans>
新建一个bean,并给一个方法:
public class SpringHello { public void prints() { System.out.println("hello spring"); } }
有了bean的配置,一般测试时,我们会采用手动的方式初始化:
public class TestSpring { public static void main(String[] args) { TestSpring ts = new TestSpring(); ts.startSpring(); } private void startSpring() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SpringHello bean = (SpringHello) context.getBean("springHelloWorld"); bean.prints(); } }
运行上边代码,你会看到如下输出:
Mar 29, 2018 10:31:48 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@442d9b6e: startup date [Thu Mar 29 22:31:48 CST 2018]; root of context hierarchy Mar 29, 2018 10:31:49 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [beans.xml] hello spring
从输出看,先是执行了 ClassPathXmlApplicationContext 的 prepareRefresh() 方法。一路看代码发现该方法定义在AbstractApplicationContext.java 中,整个方法设计上使用模板方法。
@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();// 若已存在BeanFactory,则销毁,并新建。调用子类的refreshBeanFactory()。 // 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);//反射调用BeanFactory的后处理器(Bean定义的) // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory);// 注册Bean的后处理器,在Bean创建过程中调用 // Initialize message source for this context. initMessageSource();// 初始化上下文中的消息源 // Initialize event multicaster for this context. initApplicationEventMulticaster();// 初始化上下文中的事件机制 // Initialize other special beans in specific context subclasses. onRefresh();// 如有需要,初始化其他特殊Bean, // Check for listener beans and register them. registerListeners();// 检查监听Bean并且将这些Bean向容器注册 // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);// 实例化所有 non-lazy-init 的bean // Last step: publish corresponding event. finishRefresh();// 发布容器事件,结束refresh 过程 } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans();// f防止Bean资源占用,遇到异常,销毁已经创建的 singletons 的bean // Reset 'active' flag. cancelRefresh(ex);// 重置 'active' 标志 // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches();// 重置 spring core 中内置缓存 } } }
在上边方法中重置BeanFactory时,
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory();// 在子类中实现 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
子类AbstractRefreshableApplicationContext中:
@Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) {// 如果已经有 BeanFactory,则销毁、关闭 destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory();// 新建一个默认容器 beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory);// 重头戏,启动对BeanDefinition的载入。具体由子类实现。又是模板方法! synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }由于是模板方法,在上边的类图中继续向下找,即 AbstractXmlApplicationContext.java
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. // 创建XmlBeanDefinitionReader,并通过回调方式设置到Bean中去 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this);// 为reader 配置 ResourceLoader(因为从图上看该类本身继承了DefaultResourceLoader类) 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); } // ... protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 以Resource 的方式获取配置文件的资源位置 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 以String 的形式获取配置文件的位置。 String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }此时就完成了第二个过程:载入。
综合上述代码,其实就new ClassPathXmlApplicationContext("beans.xml") 等效于将代码拆分如下:
public class TestSpring { public static void main(String[] args) { TestSpring ts = new TestSpring(); ts.startSpringByStep(); } private void startSpringByStep() { ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res); SpringHello bean = (SpringHello) factory.getBean("springHelloWorld"); bean.prints(); } }
注册
Spring中对接口BeanFactory的继承结构,分两部分:ApplicationContext 和 *Factory. 下边看下第一个继承结构。
从上边的类图看有三个接口设计路线:
红色:AbstractBeanFactory -> ConfigurableBeanFactory -> HierarchicalBeanFactory -> BeanFactory。提供一个具有别名管理、单例Bean创建与注册、工厂方法等功能的容器。
绿色:AbstractAutowireCapableBeanFactory -> AutowireCapableBeanFactory -> BeanFactory。提供一个具有自动装配功能的容器。
蓝色:DefaultListableBeanFactory -> ConfigurableListableBeanFactory。提供一个集所有功能于一体的功能强大的容器。
同时,DefaultListableBeanFactory 依赖SimpleAutowireCandidateResolver.java 这样就能对一些配置有autowiring属性的Bean自动完成依赖注入。
待续