Spring框架八、Spring IOC重要接口

我们使用Spring的时候,总是定义一个applicationContext.xml的文件,然后里面会定义各种bean,那么Spring框架是怎么对这些外部资源文件访问的呢?我们下面首先来看Resource组件。

一、Resource接口

Spring 为资源访问提供了一个 Resource 接口,该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。
在这里插入图片描述

Resource 接口本身没有提供访问任何底层资源的实现逻辑,针对不同的底层资源,Spring 将会提供不同的 Resource 实现类,不同的实现类负责不同的资源访问逻辑,每个实现类代表一种资源访问策略。
在这里插入图片描述

  • UrlResource:访问网络资源的实现类。
  • ClassPathResource:访问类加载路径里资源的实现类。
  • FileSystemResource:访问文件系统里资源的实现类。
  • ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类。
  • InputStreamResource:访问输入流资源的实现类。
  • ByteArrayResource:访问字节数组资源的实现类。

spring访问资源的方式有了,那么spring是怎么加载资源的呢?下面我们来看ResourceLoader模块。

二、ResourceLoader接口

该组件负责对Spring资源的加载,资源指的是 xml 、 properties 等文件资源,返回一个对应类型的 Resource 对象。

/*
 * 	定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
 */
public interface ResourceLoader {

	Resource getResource(String location);
	
	@Nullable
	ClassLoader getClassLoader();

}

接下来我们来看一下ResourceLoader具体的实现类有哪些:
在这里插入图片描述
从上面的UML图可以看出, ResourceLoader 组件其实跟 Resource 组件差不多,都是一个根接口,对应有不同的子类实现,比如加载来自文件系统的资源,则可以使用 FileSystemResourceLoader ,加载来自 ServletContext 上下文的资源,则可以使用 ServletContextResourceLoader 。 还有最重要的一点,从上图看出, ApplicationContext , AbstractApplication 是实现了 ResourceLoader 的,这说明什么呢?说明我们的应用上下文 ApplicationContext 拥有加载资源的能力,这也说明了为什么可以通过传入一个 String resource path 给 ClassPathXmlApplicationContext(“applicationContext.xml”) 就能获得xml文件资源的原因了!

既然我们拥有了加载器 ResourceLoader ,也拥有了对资源的描述 Resource ,但是我们在xml文件中声明的标签在Spring又是怎么表示的呢?注意这里只是说对 bean 的定义,而不是说如何将转换为 bean 对象。于是就引入一个叫 BeanDefinition的组件。

三、BeanDefinition接口

BeanDefinition是一个接口,内部定义了bean对象的一些基本行为。配置文件中的标签跟我们的 BeanDefinition 是一一对应的, 元素标签拥有 class 、 scope 、 lazy-init 等配置属性, BeanDefinition 则提供了相应的 beanClass 、 scope 、 lazyInit 属性。

BeanDefinition接口部分方法:
在这里插入图片描述
BeanDefinition UML图
在这里插入图片描述
其中RootBeanDefinition 是最常用的实现类,它对应一般性的 元素标签。GenericBeanDefinition 是自 2.5 以后新加入的 bean 文件配置属性定义类,是一站式服务类。在配置文件中可以定义 父和子类 ,父用 RootBeanDefinition 表示,而子用ChildBeanDefiniton 表示,而没有父的话就使用 RootBeanDefinition 表示。AbstractBeanDefinition 对两者共同的类信息进行抽象。

Spring通过 BeanDefinition 将配置文件中的配置信息转换为容器的内部表示,并将这些 BeanDefiniton注册到BeanDefinitonRegistry中。 Spring 容器的 BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,主要是以 map 的形式保存,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。一般情况下, BeanDefinition 只在容器启动时加载并解析,除非容器刷新或重启,这些信息不会发生变化,当然如果用户有特殊的需求,也可以通过编程的方式在运行期调整 BeanDefinition 的定义。

有了加载器 ResourceLoader ,也拥有了对资源的描述 Resource ,也有了对 bean 的定义,我们不禁要问,我们的 Resource 资源是怎么转成我们的 BeanDefinition 的呢?因此就引入了 BeanDefinitionReader。

四、BeanDefinitionReader接口

BeanDefinitionReader作用是把Resource资源转成我们的 BeanDefinition。
在这里插入图片描述
在这里插入图片描述
从上面可以看出,Spring 对reader进行了抽象,具体的功能交给其子类去实现,不同的实现对应不同的类,如 PropertiedBeanDefinitionReader , XmlBeanDefinitionReader 对应从Property和xml的Resource解析成 BeanDefinition 。

我们以XmlBeanDefinitionReader为例:

XmlBeanDefinitionReader.java部分源码

	/**
	* 创建XmlBeanDefinitionReader需要传入一个BeanDefinitionRegistry实例
	*/
	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}

	/**
	 * XmlBeanDefinitionReader加载资源的入口方法
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		// 将读入的XML资源进行特殊编码处理
		return loadBeanDefinitions(new EncodedResource(resource));
	}

	/**
	 * 载入XML形式Bean配置信息方法
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			// 将资源文件转为InputStream的IO流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				// 从InputStream中得到XML的解析源
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				// 具体读取过程
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				// 关闭从Resource中得到的IO流
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

	/**
	 * 从特定XML文件中实际载入Bean配置资源的方法
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			// 将XML文件转换为DOM对象,解析过程由documentLoader方法实现
			Document doc = doLoadDocument(inputSource, resource);
			// 启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (SAXParseException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
		}
		catch (SAXException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"XML document from " + resource + " is invalid", ex);
		}
		catch (ParserConfigurationException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Parser configuration exception parsing XML from " + resource, ex);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"IOException parsing XML document from " + resource, ex);
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Unexpected exception parsing XML document from " + resource, ex);
		}
	}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//得到BeanDefinitionDocumentReader来对XML格式的BeanDefinition进行解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//获取容器中注册的Bean数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//加载及注册bean,具体的解析过程由实现类DefaultBeanDefinitionDocumentReader完成
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//记录本次加载的BeanDefinition个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

有了 BeanDefinition 后,你还必须将它们注册到工厂中去,所以当你使用 getBean() 方法时工厂才知道返回什么给你。BeanDefinitionRegistry出场。

五、BeanDefinitionRegistry接口

BeanDefinitionRegistry接口UML图:
在这里插入图片描述
从图中可以看出, BeanDefinitionRegistry 有三个默认实现,分别是 SimpleBeanDefinitionRegistry , DefaultListableBeanFactory , GenericApplicationContext ,其中 SimpleBeanDefinitionRegistry , DefaultListableBeanFactory 都持有一个Map,也就是说这两个实现类把保存了bean。而 GenericApplicationContext 则持有一个 DefaultListableBeanFactory 对象引用用于获取里边对应的Map。

DefaultListableBeanFactory.java部分源码

/*
 1. 整个bean加载的核心部分,是spring注册及加载bean的默认实现
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
	//存储注册信息的BeanDefinition
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

	//向Spring IOC容器注册解析的BeanDefinition
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		//检验解析的BeanDefinition
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				//注册的过程中需要线程同步,以保证数据的一致性
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		//检查是否已经注册过同名的BeanDefinition
		if (existingDefinition != null || containsSingleton(beanName)) {
			//重置所有已经注册过的BeanDefinition的缓存
			resetBeanDefinition(beanName);
		}
	}
}

六、BeanFactory接口

BeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 来实例化、配置和管理 Bean。
BeanFactory:是IOC容器的核心接口, 它定义了IOC的基本功能,我们看到它主要定义了getBean方法。getBean方法是IOC容器获取bean对象和引发依赖注入的起点。方法的功能是返回特定的名称的Bean。
BeanFactory 是初始化 Bean 和调用它们生命周期方法的“吃苦耐劳者”。注意,BeanFactory 只能管理单例(Singleton)Bean 的生命周期。它不能管理原型(prototype,非单例)Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。
BeanFactory有着庞大的继承、实现体系,有众多的子接口、实现类。来看一下BeanFactory的基本类体系结构(接口为主):
在这里插入图片描述

  1. BeanFactory作为一个主接口不继承任何接口,暂且称为一级接口。
  2. 有3个子接口继承了它,进行功能上的增强。这3个子接口称为二级接口。
  3. ConfigurableBeanFactory可以被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry。
  4. ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,无所不包,称为四级接口。

这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类:

  1. AbstractBeanFactory作为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。
  2. AbstractAutowireCapableBeanFactory同样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory。
  3. DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并非抽象类。
  4. 最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使自己更强大。

BeanFactory.java

package org.springframework.beans.factory;

public interface BeanFactory {

    /**
     * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
     */
    String FACTORY_BEAN_PREFIX = "&";

    /*
     * 四个不同形式的getBean方法,获取实例
     */
    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    boolean containsBean(String name); // 是否存在

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;// 是否为单实例

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;// 是否为原型(多实例)

    boolean isTypeMatch(String name, Class<?> targetType)
            throws NoSuchBeanDefinitionException;// 名称、类型是否匹配

    Class<?> getType(String name) throws NoSuchBeanDefinitionException; // 获取类型

    String[] getAliases(String name);// 根据实例的名字获取实例的别名

BeanFactory最常见的实现类为XmlBeanFactory,可以从classpath或文件系统等获取资源。

通过XmlBeanFactory实现启动Spring IoC容器:

public class MyTest {
    @Test
    public void test01() throws FileNotFoundException {
//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource=resolver.getResource("classpath:application_context.xml");
        BeanFactory beanFactory=new XmlBeanFactory(resource);
        BookService bookService = beanFactory.getBean("bookService", BookService.class);
        System.out.println(bookService);
    }
}
  1. XmlBeanFactory通过Resource装载Spring配置信息冰启动IoC容器,然后就可以通过factory.getBean从IoC容器中获取Bean了。
  2. 通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一个调用时。
  3. 对于单实例(singleton)的Bean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean时直接从IoC容器缓存中获取Bean。

七、ApplicationContext接口

如果说BeanFactory是Spring的心脏,那么ApplicationContext就是完整的躯体了,ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置实现。

BeanFactorty接口提供了配置框架及基本功能,但是无法支持spring的aop功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,相较于BeanFactorty,ApplicationContext还提供了以下的功能:

  • MessageSource, 提供国际化的消息访问
  • 资源访问,如URL和文件
  • 事件传播特性,即支持aop特性
  • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层

ApplicationContext:是IOC容器另一个重要接口, 它继承了BeanFactory的基本功能, 同时也继承了容器的高级功能,如:MessageSource(国际化资源接口)、ResourceLoader(资源加载接口)、ApplicationEventPublisher(应用事件发布接口)等。

如下UML图:

在这里插入图片描述
ApplicationContext的子接口分为两个部分:

  1. ConfigurableApplicationContext:大部分的应用上下文都实现了该接口
  2. WebApplicationContext:在web应用程序中使用

ConfigurableApplicationContext.java

void setId(String id); // 设置应用上下文唯一的id
void setParent(ApplicationContext parent); // 设置应用程序上下文的父级
void setEnvironment(ConfigurableEnvironment environment); // 设置应用上下文的环境
ConfigurableEnvironment getEnvironment();  // 获取应用上下文的环境
// 添加一个新的BeanFactoryPostProcessor
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
// 添加应用程序监听器
void addApplicationListener(ApplicationListener<?> listener);
// 添加协议解析器,可能会覆盖默认的规则
void addProtocolResolver(ProtocolResolver resolver);
// 加载或者刷新配置
void refresh() throws BeansException, IllegalStateException;
// 向JVM runtime注册一个关闭钩子,JVM关闭时关闭这个上下文
void registerShutdownHook();
// 应用程序上问下是否是激活状态
boolean isActive();
// 获取应用上下文内部的bean factory
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

AbstractApplicationContext.java

AbstractApplicationContext是ApplicationContext接口的抽象实现,这个抽象类仅仅是实现了公共的上下文特性。这个抽象类使用了模板方法设计模式,需要具体的实现类去实现这些抽象的方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃透Java

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值