Spring容器初始化整理

Spring

最近做IT测试时,每次Spring 加载配置等都很慢,抽空把Spring 容器的启动整理一下。

容器

Spring中听的最多的就是依赖注入、控制反转。一般情况下,A对象使用B对象,都是A先创建B,然后调用B的方法。这样两个对象间有依赖的。控制反转简单来说就是对象-对象的依赖关系变成对象-容器-对象。这样每次A只需要调用传给它的B即可。所以容器的管理对象的作用就显而易见了。联系它的功能,容器其实就是创建、维护与管理对象的工厂类,只不过这个类的附加功能比较强大而已。

在这里先说下两个概念: BeanFactoryApplicationContext,简单来说它俩都是Spring 提供的容器。不过稍有区别,

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,因此它还有语言国际化、访问资源、发布应用事件等功能。下面看下类图:



扫描二维码关注公众号,回复: 1436261 查看本文章

从图中看,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自动完成依赖注入。


待续







猜你喜欢

转载自blog.csdn.net/hustzw07/article/details/79749421
今日推荐