ディレクトリ
Springフレームワークで最も重要なことは、コアSpringフレームワークである春のIoCコンテナ、です。高い解決SPING IoCコンテナの観点から、この記事では、それが設計される方法を参照してください。同じことを理解する最良の方法は、他のものの数が解決される限り、このようなコアの把握など、中核本質から始めています。これは良いスタートです、私たちはそれで始まります...
春のIoCコンテナ
org.springframework.context.ApplicationContext
インターフェイスは、ここで述べたBeanが、アプリケーション内のオブジェクトの組成物を意味する(豆のインスタンス化、設定、組み立てを担当する春のIoCコンテナは、短期では、春のIoCコンテナにBeanを管理することである表し、これらオブジェクトは)春によって管理されています。どのようにインスタンス化するために、どのコンテナオブジェクトを知るために、設定およびそれを組み立てますか?この効果を達成するために、構成ファイルのメタデータを読み込むことで、設定ファイルのメタデータのXML設定は、JavaおよびJavaのコード注釈の構成が示されています。プログラマは唯一の構成メタデータコンテナの春を提供する必要があるとして、これは私たちを可能にする、Springコンテナは、当社のアプリケーションは、これらのオブジェクトの構成や組み立てにインスタンス化することができます。org.springframework.beans
そして、org.springframework.context
パッケージは春のIoCコンテナをベースにしています。春はたくさんの提供Application
インタフェースの実装を。別のアプリケーションでは、作成ClassPathXmlApplicationContext
およびFileSystemXmlApplicationContext
インスタンスは非常に一般的です。例としては、次のとおりです:
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Hello hello = (Hello) ac.getBean("hello");
hello.sayHello();
しかし、ほとんどのシナリオでは、1つ以上のインスタンスのSpring IoCコンテナをインスタンス化する必要があります。たとえば、次のようにシーンのWebアプリケーションでは、コードのわずか7行は、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>
下の写真は、それがどのように動作するか高い斜視春から示しています。そのように、アプリケーションクラスと設定メタデータは、一緒にグループ化されてApplicationContext
作成し、初期化した後、あなたが完全に構成されたシステムを実施してきました。
ApplicationContextの設計解析
容易にするために、ApplicationContext
インターフェイスの階層の一般的な理解を、以下生成するアイデアを使用ApplicationContext
継承グラフ。(...> Diagrams->ショー図表- -右>のApplicationContextインタフェースを選択します)
(ヒント:大きなHDを表示するには画像をクリック)
チャートから、我々はそれをはっきりと見ることができますApplicationContext
継承されたインタフェースは、5つのカテゴリに分類されます。
BeanFactory
:任意のオブジェクトを管理することができる高度な設定機構を提供し、このインタフェースは、より重要なインターフェースのSpringフレームワークです。ListableBeanFactory
:インターフェース、たBeanFactoryの機能を有することに加えてインターフェース、豆リストのすべてのインスタンスでインターフェイスすることもでき、工場の能力の名前を知ることができるだろう。HierarchicalBeanFactory
:たBeanFactory機能を有することに加えて、インタフェースが、またはたBeanFactory自体を見たが、見つからなかった場合、親た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的定义的
或许你已经注意到了,有两个方法含有类型是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)
- ....