Spring IoC是Spring Framework的核心。
控制反转和依赖注入:
早在2004年,Martin Fowler就提出了“哪些方面的控制被反转了?”,后来得出的结论是:依赖对象的获得被反转了。基于这个结论,他为控制反转创造了一个更好的名字:依赖注入。
不过我们平时会同时说到控制反转和依赖注入两个概念,控制反转指的就是把对象的获得交给容器,这样就反转了。依赖注入指的是容器把对象之间的依赖注入进去。
控制反转的实现有多种方式,可以在对象生成或初始化的时候,直接将数据注入到对象中。也可以把对象的引用注入进去。
Spring IoC:
Spring IoC容器的设计,最关键的就是两个接口,BeanFactory和ApplicationContext。
BeanFactory接口定义了IoC容器基本的功能规范,ApplicationContext在BeanFactory的基础上,增加了许多面向框架的特性,对应用环境做了很多适配,是容器的高级形态。
在Idea中,通过control+H快捷键,可以看到BeanFactory的继承情况。
网上也可以搜到Spring IoC容器的接口设计图。
在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文件路径。
接下来再看下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:
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技术内幕》 计文柯 著