Spring源码阅读-IoC容器解析
目录
在spring框架中最重要的是spring ioc容器,它是spring框架的核心。本文将从更高的角度来解析sping ioc容器,了解其是如何设计的。了解一样东西最好的办法是从其核心本质出发,只要把握住了这样一个核心,其他的一些东西也迎刃而解了。这是一个很好的开端,我们一起开始吧...
spring ioc容器
org.springframework.context.applicationcontext
接口代表spring ioc容器,主要负责bean的实例化、配置、装配,简而言之,spring ioc容器是管理这些bean的(这里所说的bean指的是组成你的应用程序中的对象,并且这些对象被spring所管理)。容器如何知道哪些对象要进行实例化、配置和装配的呢?是通过读取配置文件元数据来达到这个效果的,配置文件元数据是用xml配置、java注解和java代码配置来表示的。这使得作为程序员的我们,只需要向spring容器提供配置元数据,spring容器就能在我们的应用中实例化、配置和装配这些对象。org.springframework.beans
和org.springframework.context
包是spring ioc容器的基础。spring提供了很多application
接口的实现。在单独的应用中,创建classpathxmlapplicationcontext
和filesystemxmlapplicationcontext
的实例是非常常用的做法。示例如下:
applicationcontext ac = new classpathxmlapplicationcontext("applicationcontext.xml"); hello hello = (hello) ac.getbean("hello"); hello.sayhello();
然而在大部分的应用场景中,不需要实例化一个或者多个spring ioc容器的实例。例如在web应用的场景下,只需要在web.xml中创建七行样板配置的代码如下:
<context-param> <param-name>contextconfiglocation</param-name> <param-value>/web-inf/applicationcontext.xml</paramvalue> </context-param> <listener> <listener-class>org.springframework.web.context.contextloaderlistener</listener-class> </listener>
下面这张图从更高的视角展示了spring是怎样工作的。你的应用程序中的类是和配置元数据组合在一起,以便在applicationcontext
创建和初始化之后,你拥有了一个完全配置的、可执行的系统。
applicationcontext设计解析
为了方便对applicationcontext
接口的层次结构有一个大概的认识,下面使用idea来生成applicationcontext
的继承关系图。(选中applicationcontext接口->右键->diagrams->show diagrams...)
(温馨提示:点击图片可以查看高清大图)
从上图就能很清楚的看出applicationcontext
继承的接口分为五类:
-
beanfactory
:提供了能够管理任何对象的高级配置机制,这个接口是spring框架中比较重要的一个接口。-
listablebeanfactory
:从该接口的名字就能知道,该接口除了拥有beanfactory的功能外,该接口还有能列出factory中所有bean的实例的能力。 -
hierarchicalbeanfactory
:该接口除了拥有beanfactory的功能外,还提供了beanfactory分层的机制,查找bean的时候,除了在自身beanfactory查找外,如果没有查找到,还会在父级beanfactory进行查找。
-
-
messagesource
:消息资源的处理,用于国际化。 -
applicationeventpublisher
:用于处理事件发布机制。 -
environmentcapable
:提供了environment
的访问能力。 -
resourceloader
:用于加载资源的策略接口(例如类路径下的资源、系统文件下的资源等等)。-
resourcepatternresolver
:用于将位置模式(例如ant风格的路径模式)解析成资源对象的策略接口。classpath*:
前缀能匹配所以类路径下的资源。
-
先看一下在applicationcontext
中定义的方法:
string getid(); // 获取applicationcontext的唯一id string getapplicationname(); // 该上下文所属的已经部署了的应用的名字,默认为"" string getdisplayname(); // 友好的展示名字 long getstartupdate(); // 该上下文第一次加载的时间 applicationcontext getparent(); // 父级applicationcontext autowirecapablebeanfactory getautowirecapablebeanfactory() throws illegalstateexception;
前四个方法用于获取该applicationcontext
的一些基本信息,getautowirecapablebeanfactory()
用于暴露autowirecapablebeanfactory
的功能,这通常不是提供给用于代码使用的,除非你想要在应用上下文的外面初始化bean的实例,关于autowirecapablebeanfactory
后面会有更加详细的解析。
beanfactory
beanfactory
是spring框架中比较重要的一个接口,下面列出了这个接口中的方法的定义:
// 获取bean object getbean(string name) throws beansexception; <t> t getbean(string name, class<t> requiredtype) throws beansexception; object getbean(string name, object... args) throws beansexception; <t> t getbean(class<t> requiredtype) throws beansexception; <t> t getbean(class<t> requiredtype, object... args) throws beansexception; // 获取bean的提供者(对象工厂) <t> objectprovider<t> getbeanprovider(class<t> requiredtype); <t> objectprovider<t> getbeanprovider(resolvabletype requiredtype); boolean containsbean(string name); // 是否包含指定名字的bean boolean issingleton(string name) throws nosuchbeandefinitionexception; // 是否为单例 boolean isprototype(string name) throws nosuchbeandefinitionexception; // 是否为原型 // 指定名字的bean是否和指定的类型匹配 boolean istypematch(string name, resolvabletype typetomatch); boolean istypematch(string name, class<?> typetomatch) throws nosuchbeandefinitionexception; class<?> gettype(string name) throws nosuchbeandefinitionexception; // 获取指定名字的bean的类型 string[] getaliases(string name); // 获取指定名字的bean的所有别名
这些方法大致可以分为三类:
getbean()
方法用于获取匹配的bean的实例对象(有可能是singleton或者prototype的),如果没有找到匹配的bean则抛出beansexception
子类的异常。如果在当前的工厂实例中没有找到匹配的bean,会在父级的工厂中进行查找。带有args
参数的getbean()
方法,允许显式的去指定构造器或者工厂方法的参数,会覆盖了在bean的定义中定义的参数,这仅仅在创建一个新的实例的时候才起作用,而在获取一个已经存在的实例是不起作用的。-
getbeanprovider()
方法用于获取指定bean的提供者,可以看到它返回的是一个objectprovider
,其父级接口是objectfactory
。首先来看一下objectfactory
,它是一个对象的实例工厂,只有一个方法:t getobject() throws beansexception;
调用这个方法返回的是一个对象的实例。此接口通常用于封装一个泛型工厂,在每次调用的时候返回一些目标对象新的实例。
objectfactory
和factorybean
是类似的,只不过factorybean
通常被定义为beanfactory
中的服务提供者(spi)实例,而objectfactory
通常是以api的形式提供给其他的bean。简单的来说,objectfactory
一般是提供给开发者使用的,factorybean
一般是提供给beanfactory
使用的。objectprovider
继承objectfactory
,特为注入点而设计,允许可选择性的编程和宽泛的非唯一性的处理。在spring 5.1的时候,该接口从iterable
扩展,提供了对stream
的支持。该接口的方法如下:// 获取对象的实例,允许根据显式的指定构造器的参数去构造对象 t getobject(object... args) throws beansexception; // 获取对象的实例,如果不可用,则返回null t getifavailable() throws beansexception; t getifavailable(supplier<t> defaultsupplier) throws beansexception; void ifavailable(consumer<t> dependencyconsumer) throws beansexception; // 获取对象的实例,如果不是唯一的或者没有首先的bean,则返回null t getifunique() throws beansexception; t getifunique(supplier<t> defaultsupplier) throws beansexception; void ifunique(consumer<t> dependencyconsumer) throws beansexception; // 获取多个对象的实例 iterator<t> iterator(); stream<t> stream(); stream<t> orderedstream()
这些接口是分为两类,
- 一类是获取单个对象,
getifavailable()
方法用于获取可用的bean(没有则返回null),getifunique()
方法用于获取唯一的bean(如果bean不是唯一的或者没有首选的bean返回null)。getifavailable(supplier<t> defaultsupplier)
和getifunique(supplier<t> defaultsupplier)
,如果没有获取到bean,则返回defaultsupplier提供的默认值,ifavailable(consumer<t> dependencyconsumer)
和ifunique(consumer<t> dependencyconsumer)
提供了以函数式编程的方式去消费获取到的bean。 - 另一类是获取多个对象,
stream()
方法返回连续的stream
,不保证bean的顺序(通常是bean的注册顺序)。orderedstream()
方法返回连续的stream
,预先会根据工厂的公共排序比较器进行排序,一般是根据org.springframework.core.ordered
的约定进行排序。
- 一类是获取单个对象,
-
其他的是一些工具性的方法:
- 通过名字判断是否包含指定bean的定义的
containsbean(string name)
方法 - 判断是单例和原型的
issingleton(string name)
和isprototype(string name)
方法 - 判断给定bean的名字是否和类型匹配的
istypematch
方法 - 根据bean的名字来获取其类型的
gettype(string name)
方法 - 根据bean的名字来获取其别名的
getaliases(string name)
方法
- 通过名字判断是否包含指定bean的定义的
或许你已经注意到了,有两个方法含有类型是
简单的来说, 上面的这些方法都不考虑祖先工厂中的bean,只会考虑在当前工厂中定义的bean。 这两个方法都比较直接明了, 用于判断本地的工厂是否包含指定的bean,忽略在祖先工厂中定义的bean。 以上的三个方法都是用于获取消息的,第一个方法提供了默认消息,第二个接口如果没有获取到指定的消息会抛出异常。第三个接口中的 第一个方法用于发布特定于应用程序事件。第二个方法能发布任意的事件,如果事件不是 先来看一下 该接口只有简单明了的两个方法,一个是用来获取指定位置的资源,一个用于获取资源加载器所使用的类加载器。 接下来在来看一下 假如让你设计ioc容器,你该如何去做呢?首先你应该要明确你设计的容器的功能和特性,然后根据这些功能和特性设计出合理的接口。下面只是粗略的分析一下:resolvabletype
的参数,那么resolvabletype
是什么呢?假如说你要获取泛型类型的bean:mybean<thetype>
,根据classresolvabletype
就能满足此需求,代码如下:resolvabletype type = resolvabletype.forclasswithgenerics(mytype.class, thetype.class);
objectprovider<mytype<thetype>> op = applicationcontext.getbeanprovider(type);
mytype<thetype> bean = op.getifavailable()
resolvabletype
是对java java.lang.reflect.type
的封装,并且提供了一些访问该类型的其他信息的方法(例如父类, 泛型参数,该类)。从成员变量、方法参数、方法返回类型、类来构建resolvabletype
的实例。listablebeanfactory
listablebeanfactory
接口有能列出工厂中所有的bean的能力,下面给出该接口中的所有方法:boolean containsbeandefinition(string beanname); // 是否包含给定名字的bean的定义
int getbeandefinitioncount(); // 工厂中bean的定义的数量
string[] getbeandefinitionnames(); // 工厂中所有定义了的bean的名字
// 获取指定类型的bean的名字
string[] getbeannamesfortype(resolvabletype type);
string[] getbeannamesfortype(class<?> type);
string[] getbeannamesfortype(class<?> type, boolean includenonsingletons, boolean alloweagerinit);
// 获取所有使用提供的注解进行标注的bean的名字
string[] getbeannamesforannotation(class<? extends annotation> annotationtype);
// 查找指定bean中的所有指定的注解(会考虑接口和父类中的注解)
<a extends annotation> a findannotationonbean(string beanname, class<a> annotationtype)
throws nosuchbeandefinitionexception;
// 根据指定的类型来获取所有的bean
<t> map<string, t> getbeansoftype(class<t> type) throws beansexception;
<t> map<string, t> getbeansoftype(class<t> type, boolean includenonsingletons, boolean alloweagerinit)
throws beansexception;
// 获取所有使用提供的注解进行标注了的bean
map<string, object> getbeanswithannotation(class<? extends annotation> annotationtype) throws beansexception;
factorybean
创建的bean,这也意味着factorybean
会被初始化。为什么这里的三个方法不返回list?map不光包含这些bean的实例,而且还包含bean的名字,而list只包含bean的实例。也就是说map比list更加的通用。hierarchicalbeanfactory
hierarchicalbeanfactory
接口定义了beanfactory
之间的分层结构,configurablebeanfactory
中的setparentbeanfactory
方法能设置父级的beanfactory
,下面列出了hierarchicalbeanfactory
中定义的方法:beanfactory getparentbeanfactory(); // 获取父级的beanfactory
// 本地的工厂是否包含指定名字的bean
boolean containslocalbean(string name);
getparentbeanfactory
方法用于获取父级beanfactory
。containslocalbean
messagesource
messagesource
主要用于消息的国际化,下面是该接口中的方法定义:// 获取消息
string getmessage(string code, object[] args, string defaultmessage, locale locale);
string getmessage(string code, object[] args, locale locale) throws nosuchmessageexception;
string getmessage(messagesourceresolvable resolvable, locale locale) throws nosuchmessageexception;
messagesourceresolvable
参数是对代码、参数值、默认值的一个封装。applicationeventpublisher
applicationeventpublisher
接口封装了事件发布功能,提供spring中事件的机制。接口中的方法定义如下:// 发布事件
void publishevent(applicationevent event);
void publishevent(object event);
applicationevent
,那么会被包裹成payloadapplicationevent
事件。environmentcapable
environmentcapable
提供了访问environment
的能力,该接口只有一个方法:environment getenvironment();
environment
表示当前正在运行的应用的环境变量,它分为两个部分:profiles和properties。它的父级接口propertyresolver
提供了property的访问能力。resourceloader和resourcepatternresolver
resourceloader
,该接口是用来加载资源(例如类路径或者文件系统中的资源)的策略接口。该接口中的方法如下:resource getresource(string location); // 根据指定的位置获取资源
classloader getclassloader(); // 获取该资源加载器所使用的类加载器
resource
是从实际类型的底层资源(例如文件、类路径资源)进行抽象的资源描述符。先看下resource
中的方法:
boolean exists(); // 资源实际上是否存在
boolean isreadable(); // 资源是否可读
boolean isopen(); // 检查资源是否为打开的流
boolean isfile(); // 资源是否为文件系统上的一个文件
url geturl() throws ioexception; // 获取url
uri geturi() throws ioexception; // 获取uri
file getfile() throws ioexception; // 获取文件
readablebytechannel readablechannel() throws ioexception; // 获取readablebytechannel
long contentlength() throws ioexception; // 资源的内容的长度
long lastmodified() throws ioexception; // 资源的最后修改时间
// 相对于当前的资源创建一个新的资源
resource createrelative(string relativepath) throws ioexception;
string getfilename(); // 获取资源的文件名
string getdescription(); // 获取资源的描述信息
resource
的父级接口inputstreamsource
,可以简单的理解为inputstream
的来源,只有一个方法,如下:inputstream getinputstream() throws ioexception; // 获取输入流
resourcepatternresolver
,该接口用于解析一个位置模式(例如ant风格的路径模式),该接口只有一个方法,如下:// 将给定的位置模式解析成资源对象
resource[] getresources(string locationpattern) throws ioexception;
spring ioc容器设计复盘
本文思维导图
上一篇: LeetCode链表简单题