ApplicationContext实现的简单容器

x33g5p2x  于2021-03-14 发布在 Spring  
字(9.2k)|赞(0)|评价(0)|浏览(134)

ApplicationContext实现的简单容器

package com.wcj.simplebeanfactory;

import com.wcj.pojo.Student;
import org.springframework.context.support.FileSystemXmlApplicationContext;

/**
 * @author wcj
 *
 */

public class SimpleBeanFactory2 {
	public static void main(String[] args) {
		FileSystemXmlApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:applicationContext.xml");
		Student student = applicationContext.getBean("student",Student.class);
		student.setAge(18);
		student.setName("wcj");
		student.printAge();
	}
}

ApplicationContext.java

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;

/**
 * ApplicationContext是spring中较高级的容器。和BeanFactory类似,它可以加载配置文件中定义的bean,将所有的bean集中在一起,当有请求的
 * 时候分配bean。 另外,它增加了企业所需要的功能,比如,从属性文件从解析文本信息和将事件传递给所指定的监听器。这个容器在org.springframework.
 * context.ApplicationContext接口中定义。ApplicationContext包含BeanFactory所有的功能,一般情况下,相对于BeanFactory,ApplicationContext
 * 会被推荐使用。但BeanFactory仍然可以在轻量级应用中使用,比如移动设备或者基于applet的应用程序。
 * 
 * ApplicationContext接口关系
 *  1.支持不同的信息源。扩展了MessageSource接口,这个接口为ApplicationContext提供了很多信息源的扩展功能,比如:国际化的实现为多语言版本的应用提供服务。
 *  2.访问资源。这一特性主要体现在ResourcePatternResolver接口上,对Resource和ResourceLoader的支持,这样我们可以从不同地方得到Bean定义资源。
 *  这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的IO途径得到Bean定义信息。这在接口上看不出来,不过一般来说,具体ApplicationContext都是
 *  继承了DefaultResourceLoader的子类。因为DefaultResourceLoader是AbstractApplicationContext的基类,关于Resource后面会有更详细的介绍。
 *  3.支持应用事件。继承了接口ApplicationEventPublisher,为应用环境引入了事件机制,这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
 *  4.附件服务。EnvironmentCapable里的服务让基本的Ioc功能更加丰富。
 *  5.ListableBeanFactory和HierarchicalBeanFactory是继承的主要容器。
 * 
 * 最常被使用的ApplicationContext接口实现类:
 *  1,FileSystemXmlApplicationContext:该容器从XML文件中加载已被定义的bean。在这里,你需要提供给构造器XML文件的完整路径。
 *  2,ClassPathXmlApplicationContext:该容器从XML文件中加载已被定义的bean。在这里,你不需要提供XML文件的完整路径,只需正确配置CLASSPATH 
 *  环境变量即可,因为,容器会从CLASSPATH中搜索bean配置文件。
 *  3,WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在XML文件中
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * 得到这个应用环境的独一无二ID
	 * @return 得到这个应用独一无二的ID,或者返回一个空值
	 */
	String getId();

	/**
	 * 得到这个应用的名称
	 * @return 得到这个应用的名称,或者返回一个空值
	 */
	String getApplicationName();

	/**
	 * 返回这个应用环境的显示名
	 * @return 这个应用环境的显示名,或者返回一个空值
	 */
	String getDisplayName();
	
	/**
	 * 当这个应用第一次载入时,返回一个时间差(ms)
	 * @return 返回一个时间差(ms)
	 */
	long getStartupDate();

	/**
	 * 得到一个父类的环境,或者空值
	 * @return 返回一个父类的环境,或者空值
	 */
	ApplicationContext getParent();

	/**
	 * 你可以使用它来自动装配依赖对象,但这是一个非常专业的使用情况下,你在99.99%的时间是不需要他的。
	 * @return 这个应用环境的AutowireCapableBeanFactory接口
	 * @throws IllegalStateException 如果这个应用并不支持AutowireCapableBeanFactory接口,抛出异常
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

ApplicationContext的层次结构

FileSystemXmlApplicationContext.java

package org.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

/**
 * 在FileSystemXmlApplicationContext的设计中,我们看到ApplicationContext的主要功能其实已经在AbstractXmlApplicationContext
 * 中完成了,而在FileSystemXmlApplicationContext只需要完成它自身的两个功能。
 * 
 * 一个就是启动Ioc容器的refresh()过程。这个会在下一章进行重点论述。
 * 另一个就是加载XML的Bean定义资源,主要是getResourceByPath方法来完成。
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
	
	/**
	 * 创建FileSystemXmlApplicationContext类
	 */
	public FileSystemXmlApplicationContext() {
	}

	/**
	 * 根据父类,创建FileSystemXmlApplicationContext类
	 * @param parent 父类的接口
	 */
	public FileSystemXmlApplicationContext(ApplicationContext parent) {
		super(parent);
	}

	/**
	 * 根据XML文件名,创建FileSystemXmlApplicationContext类
	 * @param configLocation BeanDefinition所在的文件路径
	 * @throws BeansException 如果创建失败就抛出异常
	 */ 
	public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

	/**
	 * 根据XML文件数组名,创建FileSystemXmlApplicationContext类
	 * @param configLocations XML文件名,可以指定多个BeanDefinition资源路径
	 * @throws BeansException 如果创建失败就抛出异常
	 */
	public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
		this(configLocations, true, null);
	}

	/**
	 * 根据载入的父类接口,以及XML文件名自动刷新环境以及创建FileSystemXmlApplicationContext类
	 * @param configLocations XML文件名
	 * @param parent 父类接口,同时指定自己的双亲容器
	 * @throws BeansException 如果创建失败就抛出异常
	 */
	public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
		this(configLocations, true, parent);
	}

	/**
	 * 根据XML文件名自动刷新环境以及创建FileSystemXmlApplicationContext类
	 * @param configLocations XML文件名
	 * @param refresh 是否自动刷新环境
	 * @throws BeansException 如果创建失败就抛出异常
	 */
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
		this(configLocations, refresh, null);
	}

	/**
	 * 根据XML文件名、父类接口、自动刷新环境以及创建FileSystemXmlApplicationContext类
	 * 调用父类AbstractRefreshableConfigApplicationContext的方法,设置BeanDefinition定义的资源文件,完成IoC容器Bean定义资源的定位
	 * FileSystemXmlApplicationContext中最重要的实现方法,其他构建大部分都是基于它,类似的设计也在其他实现类中得以运用
	 * 
	 * @param configLocations XML文件名
	 * @param refresh 是否自动刷新环境
	 * @param parent 父类接口,同时指定自己的双亲容器
	 * @throws BeansException 如果创建失败就抛出异常
	 */
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();  	//Ioc容器的refresh()过程,是个非常复杂的过程,但不同的容器实现这里都是相似的,因此基类中就将他们封装好了
		}
	}

	/**
	 * 加载XML Bean的给定资源
	 * 在文件应用中读取XML中的BeanDefinition以应对不同的BeanDefinition读取方式。
	 * 
	 * @param string 具体路径
	 * @return Resource 返回一个具体资源
	 */
	@Override
	protected Resource getResourceByPath(String path) {
		if (path != null && path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}
}

ApplicationContext容器的入口:refresh()方法,refresh定义在AbstractApplicationContext类中,它详细的描述了整个ApplicationContext的初始化过程,比如:BeanFactory的更新、MessageSource和PostProcessor的注册等。

AbstractApplicationContext.java

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
                // 准备刷新容器, 获取容器的当时时间, 同时给容器设置同步标识  
                prepareRefresh();

                // 启动子类的refreshBeanFactory方法.  
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

                // 为BeanFactory配置容器特性,例如类加载器、事件处理器等.
                prepareBeanFactory(beanFactory);
                try {
                        // 设置BeanFactory的后置处理.  
                        postProcessBeanFactory(beanFactory);

                        // 调用BeanFactory的后处理器, 这些后处理器是在Bean定义中向容器注册的.  
                        invokeBeanFactoryPostProcessors(beanFactory);

                        // 注册Bean的后处理器, 在Bean创建过程中调用.  
                        registerBeanPostProcessors(beanFactory);

                        // 初始化上下文中的消息源.  
                        initMessageSource();

                        // 初始化上下文中的事件机制.  
                        initApplicationEventMulticaster();

                        // 初始化其它特殊的Bean. 
                        onRefresh();

                        // 检查并向容器注册监听器Bean.  
                        registerListeners();

                        // 实例化所有剩余的(non-lazy-init) 单例Bean.
                        finishBeanFactoryInitialization(beanFactory);

                        // 发布容器事件, 结束refresh过程.  
                        finishRefresh();
                }
                catch (BeansException ex) {
                        if (logger.isWarnEnabled()) {
                                logger.warn("Exception encountered during context initialization - " +
                                                        "cancelling refresh attempt: " + ex);
                        }
                        // 销毁已经创建的单例Bean, 以避免资源占用. 
                        destroyBeans();
                        // 取消refresh操作, 重置 'active' 标志.  
                        cancelRefresh(ex);
                        throw ex;
                }
                finally {
                        //重置Spring的核心缓存
                        resetCommonCaches();
                }
        }
}

FileSystemXmlApplicationContext运行时序图

AbstractXmlApplicationContext.java

public AbstractXmlApplicationContext(ApplicationContext parent) {
        super(parent);
}

在AbstractXmlApplicationContext中其实,也已经完成仅仅只是继承一下。

AbstractRefreshableConfigApplicationContext.java

private String[] configLocations;

public AbstractRefreshableConfigApplicationContext(ApplicationContext parent) {
        super(parent);
}

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;
        }
}

而在AbstractRefreshableConfigApplicationContext中,除了继承父类方法,还有就是FileSystemXmlApplicationContext中setConfigLocations方法的实现,setConfigLocations方法主要用于载入XML。

AbstractRefreshableApplicationContext.java

private DefaultListableBeanFactory beanFactory;

public AbstractRefreshableApplicationContext(ApplicationContext parent) {
        super(parent);
}

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
                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);
        }
}

在AbstractRefreshableApplicationContext类里,核心部分都在实现refreshBeanFactory方法中得到了实现。但是,却不是在这里进行的调用。

AbstractApplicationContext.java

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
}

protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

最后在AbstractApplicationContext类中,进行了调用。通过同在AbstractApplicationContext中的refresh调用obtainFreshBeanFactory,进而调用子类中的refreshBeanFactory。至此,容器核心部分也就初始化完成。

相关文章