Spring系列-Spring IoC容器概述

Spring IoC是Spring Framework的核心。

控制反转和依赖注入:
早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”,后来得出的结论是:依赖对象的获得被反转了。基于这个结论,他为控制反转创造了一个更好的名字:依赖注入。

不过我们平时会同时说到控制反转和依赖注入两个概念,控制反转指的就是把对象的获得交给容器,这样就反转了。依赖注入指的是容器把对象之间的依赖注入进去。

控制反转的实现有多种方式,可以在对象生成或初始化的时候,直接将数据注入到对象中。也可以把对象的引用注入进去。

Spring IoC:
Spring IoC容器的设计,最关键的就是两个接口,BeanFactory和ApplicationContext。
BeanFactory接口定义了IoC容器基本的功能规范,ApplicationContext在BeanFactory的基础上,增加了许多面向框架的特性,对应用环境做了很多适配,是容器的高级形态。
Spring系列-Spring IoC容器概述_第1张图片
在Idea中,通过control+H快捷键,可以看到BeanFactory的继承情况。
网上也可以搜到Spring IoC容器的接口设计图。
Spring系列-Spring IoC容器概述_第2张图片

在Spring IoC容器中,有两条线路,一条是BeanFactory,一条是ApplicationContext。BeanFactory这条线是简单的IoC容器,ApplicationContext继承了ListableBeanFactory,具备了简单IoC容器的功能,同时又继承了MessageSource、ResourcePatternResolver、ApplicationEventPublisher等接口,拥有了更高级的IoC容器特性。

下面先看下BeanFactory这条线:
BeanFactory算是IoC容器中最顶级的一个接口了,它定义了IoC容器最基本的形式,提供了最基本的功能。比如,接口中定义了getBean方法,用来获取IoC容器中的一个Bean。

下面是BeanFactory的源码:

public interface BeanFactory {

	/**
	 * Used to dereference a {@link FactoryBean} instance and distinguish it from
	 * beans created by the FactoryBean. For example, if the bean named
	 * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
	 * will return the factory, not the instance returned by the factory.
	 */
	String FACTORY_BEAN_PREFIX = "&";


	/**
	 * Return an instance, which may be shared or independent, of the specified bean.
	 * 

This method allows a Spring BeanFactory to be used as a replacement for the * Singleton or Prototype design pattern. Callers may retain references to * returned objects in the case of Singleton beans. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no bean definition * with the specified name * @throws BeansException if the bean could not be obtained */ Object getBean(String name) throws BeansException; /** * Return an instance, which may be shared or independent, of the specified bean. *

Behaves the same as {@link #getBean(String)}, but provides a measure of type * safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the * required type. This means that ClassCastException can't be thrown on casting * the result correctly, as can happen with {@link #getBean(String)}. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @param requiredType type the bean must match. Can be an interface or superclass * of the actual class, or {@code null} for any match. For example, if the value * is {@code Object.class}, this method will succeed whatever the class of the * returned instance. * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanNotOfRequiredTypeException if the bean is not of the required type * @throws BeansException if the bean could not be created */ T getBean(String name, Class requiredType) throws BeansException; /** * Return the bean instance that uniquely matches the given object type, if any. * @param requiredType type the bean must match; can be an interface or superclass. * {@code null} is disallowed. *

This method goes into {@link ListableBeanFactory} by-type lookup territory * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. * @return an instance of the single bean matching the required type * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found * @throws BeansException if the bean could not be created * @since 3.0 * @see ListableBeanFactory */ T getBean(Class requiredType) throws BeansException; /** * Return an instance, which may be shared or independent, of the specified bean. *

Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. * @param name the name of the bean to retrieve * @param args arguments to use when creating a bean instance using explicit arguments * (only applied when creating a new instance as opposed to retrieving an existing one) * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but * the affected bean isn't a prototype * @throws BeansException if the bean could not be created * @since 2.5 */ Object getBean(String name, Object... args) throws BeansException; /** * Return an instance, which may be shared or independent, of the specified bean. *

Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. * @param requiredType type the bean must match; can be an interface or superclass. * {@code null} is disallowed. *

This method goes into {@link ListableBeanFactory} by-type lookup territory * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. * @param args arguments to use when creating a bean instance using explicit arguments * (only applied when creating a new instance as opposed to retrieving an existing one) * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but * the affected bean isn't a prototype * @throws BeansException if the bean could not be created * @since 4.1 */ T getBean(Class requiredType, Object... args) throws BeansException; /** * Does this bean factory contain a bean definition or externally registered singleton * instance with the given name? *

If the given name is an alias, it will be translated back to the corresponding * canonical bean name. *

If this factory is hierarchical, will ask any parent factory if the bean cannot * be found in this factory instance. *

If a bean definition or singleton instance matching the given name is found, * this method will return {@code true} whether the named bean definition is concrete * or abstract, lazy or eager, in scope or not. Therefore, note that a {@code true} * return value from this method does not necessarily indicate that {@link #getBean} * will be able to obtain an instance for the same name. * @param name the name of the bean to query * @return whether a bean with the given name is present */ boolean containsBean(String name); /** * Is this bean a shared singleton? That is, will {@link #getBean} always * return the same instance? *

Note: This method returning {@code false} does not clearly indicate * independent instances. It indicates non-singleton instances, which may correspond * to a scoped bean as well. Use the {@link #isPrototype} operation to explicitly * check for independent instances. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether this bean corresponds to a singleton instance * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @see #getBean * @see #isPrototype */ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /** * Is this bean a prototype? That is, will {@link #getBean} always return * independent instances? *

Note: This method returning {@code false} does not clearly indicate * a singleton object. It indicates non-independent instances, which may correspond * to a scoped bean as well. Use the {@link #isSingleton} operation to explicitly * check for a shared singleton instance. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether this bean will always deliver independent instances * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 2.0.3 * @see #getBean * @see #isSingleton */ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /** * Check whether the bean with the given name matches the specified type. * More specifically, check whether a {@link #getBean} call for the given name * would return an object that is assignable to the specified target type. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param typeToMatch the type to match against (as a {@code ResolvableType}) * @return {@code true} if the bean type matches, * {@code false} if it doesn't match or cannot be determined yet * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 4.2 * @see #getBean * @see #getType */ boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; /** * Check whether the bean with the given name matches the specified type. * More specifically, check whether a {@link #getBean} call for the given name * would return an object that is assignable to the specified target type. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param typeToMatch the type to match against (as a {@code Class}) * @return {@code true} if the bean type matches, * {@code false} if it doesn't match or cannot be determined yet * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 2.0.1 * @see #getBean * @see #getType */ boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException; /** * Determine the type of the bean with the given name. More specifically, * determine the type of object that {@link #getBean} would return for the given name. *

For a {@link FactoryBean}, return the type of object that the FactoryBean creates, * as exposed by {@link FactoryBean#getObjectType()}. *

Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return the type of the bean, or {@code null} if not determinable * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 1.1.2 * @see #getBean * @see #isTypeMatch */ Class getType(String name) throws NoSuchBeanDefinitionException; /** * Return the aliases for the given bean name, if any. * All of those aliases point to the same bean when used in a {@link #getBean} call. *

If the given name is an alias, the corresponding original bean name * and other aliases (if any) will be returned, with the original bean name * being the first element in the array. *

Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the bean name to check for aliases * @return the aliases, or an empty array if none * @see #getBean */ String[] getAliases(String name); }

实现了BeanFactory接口的类,我们最常见的是XmlBeanFactory:

@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {

	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


	/**
	 * Create a new XmlBeanFactory with the given resource,
	 * which must be parsable using DOM.
	 * @param resource XML resource to load bean definitions from
	 * @throws BeansException in case of loading or parsing errors
	 */
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}

	/**
	 * Create a new XmlBeanFactory with the given input stream,
	 * which must be parsable using DOM.
	 * @param resource XML resource to load bean definitions from
	 * @param parentBeanFactory parent bean factory
	 * @throws BeansException in case of loading or parsing errors
	 */
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

}

从代码中可以看到,XmlBeanFactory类继承了DefaultListableBeanFactory ,而DefaultListableBeanFactory类可以看成一个默认的功能完整的IoC容器。XmlBeanFactory在继承了DefaultListableBeanFactory的基础上,定义了一个XmlBeanDefinitionReader,然后通过reader.loadBeanDefinitions方法,来加载xml文件中定义的Bean。可以看到XmlBeanFactory的构造函数需要传入一个Resource类型的参数,这个参数就是指定我们xml文件的来源的。Resource是一个接口,我们可以传入具体的某个实现类来指定xml文件路径。
Spring系列-Spring IoC容器概述_第3张图片

接下来再看下ApplicationContext:
ApplicationContext是一个高级形态的IoC容器,和BeanFactory相比,由于ApplicationContext继承了MessageSource、ResourcePatternResolver、ApplicationEventPublisher等接口,使得ApplicationContext可以拥有BeanFactory不具备的特性,比如支持不同的信息源,访问资源,支持应用事件,提供附加服务,所以在开发应用中,推荐使用ApplicationContext作为IoC容器的基本形式。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * Return the unique id of this application context.
	 * @return the unique id of the context, or {@code null} if none
	 */
	String getId();

	/**
	 * Return a name for the deployed application that this context belongs to.
	 * @return a name for the deployed application, or the empty String by default
	 */
	String getApplicationName();

	/**
	 * Return a friendly name for this context.
	 * @return a display name for this context (never {@code null})
	 */
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

	/**
	 * Return the parent context, or {@code null} if there is no parent
	 * and this is the root of the context hierarchy.
	 * @return the parent context, or {@code null} if there is no parent
	 */
	ApplicationContext getParent();

	/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * 

This is not typically used by application code, except for the purpose of * initializing bean instances that live outside of the application context, * applying the Spring bean lifecycle (fully or partly) to them. *

Alternatively, the internal BeanFactory exposed by the * {@link ConfigurableApplicationContext} interface offers access to the * {@link AutowireCapableBeanFactory} interface too. The present method mainly * serves as a convenient, specific facility on the ApplicationContext interface. *

NOTE: As of 4.2, this method will consistently throw IllegalStateException * after the application context has been closed. In current Spring Framework * versions, only refreshable application contexts behave that way; as of 4.2, * all application context implementations will be required to comply. * @return the AutowireCapableBeanFactory for this context * @throws IllegalStateException if the context does not support the * {@link AutowireCapableBeanFactory} interface, or does not hold an * autowire-capable bean factory yet (e.g. if {@code refresh()} has * never been called), or if the context has been closed already * @see ConfigurableApplicationContext#refresh() * @see ConfigurableApplicationContext#getBeanFactory() */ AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; }

实现了ApplicationContext的类中,比较常见的是FileSystemXmlApplicationContext:
Spring系列-Spring IoC容器概述_第4张图片

FileSystemXmlApplicationContext继承了AbstractXmlApplicationContext,主要的功能都是在AbstractXmlApplicationContext中实现的,可以看到在FileSystemXmlApplicationContext的构造方法中,有这么一个方法:

	/**
	 * Create a new FileSystemXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of file paths
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

如果有人想直接使用FileSystemXmlApplicationContext的话,这个构造方法就会调用refresh(),来启动IoC容器。

IoC容器的初始化过程分为三个阶段:
1.Resource定位
2.BeanDefinition载入
3.在IoC容器中注册BeanDefinition

Resource的定位指的是我们定义的那些Bean的配置文件,比如定义在classpath下的一些xml。IoC容器首先得能够定位到这些文件,才能从中读取配置。

BeanDefinition的载入,就是把我们定义的Bean,转换成IoC容器内部的数据结构(因为作为一个容器来说,肯定不会和我们业务自己定义的那些Bean耦合,容器会自己定义一种类型,可以用来表示所有的Bean),也就是BeanDefinition的类型。通过这个BeanDefinition,IoC容器就可以方便的进行管理。

IoC容器注册BeanDefinition:把Bean解析成BeanDefinition之后,还要向IoC容器注册,这样IoC收到了这个注册信息,就会把这个BeanDefinition维护起来(通过HashMap方式),这样在使用的时候,就可以直接找到这个BeanDefinition了。

这里先对IoC容器进行了大体的介绍,介绍了BeanFactory和ApplicationContext这两条线路,并且说明了IoC容器初始化的过程,接下来会对初始化过程的这三个阶段详细的分析。

参考资料:
1.《Spring技术内幕》 计文柯 著

你可能感兴趣的:(Spring)