目录
Spring IoC 容器
ApplicationContext 设计解析
- BeanFactory
- ListableBeanFactory
- HierarchicalBeanFactory
- MessageSource
- ApplicationEventPublisher
- EnvironmentCapable
ResourceLoader 和 ResourcePatternResolver
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)
方法
或许你已经注意到了, 有两个方法含有类型是 ResolvableType 的参数, 那么 ResolvableType 是什么呢? 假如说你要获取泛型类型的 bean:MyBean<TheType>, 根据 Class 来获取, 肯定是满足不了要求的, 泛型在编译时会被擦除. 使用 ResolvableType 就能满足此需求, 代码如下:
- 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;
上面的这些方法都不考虑祖先工厂中的 bean, 只会考虑在当前工厂中定义的 bean.
前八个方法用于获取 bean 的一些信息
最后的三个方法用于获取所有满足条件的 bean, 返回结果 Map 中的键为 bean 的名字, 值为 bean 的实例. 这些方法都会考虑通过 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
用于判断本地的工厂是否包含指定的 bean, 忽略在祖先工厂中定义的 bean.
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 容器设计复盘
假如让你设计 IoC 容器, 你该如何去做呢? 首先你应该要明确你设计的容器的功能和特性, 然后根据这些功能和特性设计出合理的接口. 下面只是粗略的分析一下:
IoC 容器对 bean 的配置和管理, 那么是不是需要设计一个接口来完成这些功能呢?(BeanFactory)
既然需要这些元数据的配置, 那么是不是需要设计一个接口来完成对一些配置文件的读取.(ResourceLoader 和 Resource)
在 IoC 容器初始化, 摧毁的时候, 是不是可能要执行一些操作呢? 那么是不是需要使用事件机制来完成呢?(ApplicationEventPublisher)
....
本文思维导图
来源: https://www.cnblogs.com/zhangfengxian/p/11086695.html