依赖注入

依赖注入是框架最核心的能力,框架提供的AOP,等其它功能都是以依赖注入容器作为基础构建的,依赖注入容器类似于一个用于组装对象的框架内核,任何应用或者基于的框架都能从中受益

核心概念

理解一个项目,我习惯从它的领域模型开始,领域模型即项目中的核心概念,的依赖注入容器中有哪些核心概念?

· bean是中的核心概念之一,是操作的实体,对于bean,中有接口与之对应,接口表示bean的定义,它有多个实现类,比如n、等,每个不同的实现类针对特定的配置方式

· 是的服务域,用于管理bean,也有多个实现类和抽象类,用于实现依赖注入容器的不同的使用方式

· 是用于对做增强,提供了的生命周期管理,状态变更广播,注解配置等功能

ftl中引入jstl_ftl语法_ftlinclude

核心类之间的关系

在介绍的框架设计原则之前,我们先通过一个类图来看一下依赖注入容器的核心类之间的关系:

ftl语法_ftlinclude_ftl中引入jstl

这里是 的继承结构中的几个有代表性的类,由于相差类较多,这里省略了继承关系中间的抽象类,有兴趣的读者可以自己翻阅的源代码,看看其实现细节。的继承体系使用了模板方法模式,中不同的抽象类为不同类型的提供了基本的支持,比如是通过xml配置使用的的抽象实现。抽象类中实现了应该具备的共同特性和逻辑相同的方法,具体类中再实现具体的不同点的逻辑,当然,子类中可以覆盖那些抽象类中的方法,以便对做扩展。

我们先简单的看一下类,很多初学者最初就是从类开始接触的对象的,这类是通过加载下的xml配置来初始化的依赖注入容器,并完成bean的装载和依赖对象的注入过程,此类的使用步骤如下:

1. 添加的配置文件,比如/META-INF/.xml

2. 在.xml中配置需要的bean

3. 通过.xml创建对象:

ftl中引入jstl_ftl语法_ftlinclude

前面讲述了常用的,现在这里再看一下一个不常用,却更能说明的结构的类ntext,从上面的继承关系图中可以看出此类实现了ry接口,ry接口有什么作用呢?我们在使用的依赖注入容器前,都需要通过XML或者注解的方式向中配置bean,在解析xml或者注解的同时,会将每一个配置解析成一个对象注册到容器中,ry接口就是用来做bean的注册的,可以先看一下ry接口的定义:

ftl中引入jstl_ftl语法_ftlinclude

可以看到,ry接口提供了Bean的注册相关的方法,可以用此接口向中注册对象,移除对象,获取已注册的对象等,ntext类没有配置加载功能,无法通过xml配置完成初始化,如果想要使用ntext类,则需要以编码的方式向中注册Bean,例如:

ftl语法_ftl中引入jstl_ftlinclude

我们再回到前文讲到的ClassPathXmlApplicationContext,ClassPathXmlApplicationContext是GenericApplicationContext的子类,只不过ClassPathXmlApplicationContext通过解析xml的方式来完成对BeanDefinition的注册前面讲到的示例中,在xml中配的配置就类似于以繁琐的编码的方式使用GenericApplicationContext时通过代码创建的BeanDefinition对象,如果我们打开ClassPathXmlApplicationContext及其父类AbstractXmlApplicationContext类,会发现它们有如下结构 (为了简单起见,这里同样省略了中间类,XmlBeanDefinitionReader实际上在AbstractXmlApplicationContext中):

ftl中引入jstl_ftl语法_ftlinclude

使用后,注册Bean的过程由ntext的手动编码变成了的通过xml配置,注册Bean的过程由der负责,配置只是编码方式的一种简化,它并不是框架的核心der的执行过程后文会有详细讲解

框架的初始化

上一节讲过的ntext的使用中,调用了一次方法完成了初始化,同样,也需要通过方法完成初始化,只不过默认是自动调用了方法,如果打开的源码,我们可以看到多个构造器,其中有一个构造器可以指定是否自动刷新依赖注入容器完成依赖注入窗口的初始化,如果想要手动初始化,我们可以换使用如下方式创建对象:

ftlinclude_ftl语法_ftl中引入jstl

通过IDE的帮助,我们很容易能找到方法的声明,它声明在的一个子接口中,我们暂且先不解析此接口的作用,我们可以先看一看方法的实现。

打开类的方法,这是的初始化入口,这里先介绍方法的结构:

· 首先被调用的是方法,此方法是的模板方法模式的扩展方法,子类可覆盖此方法,用于在初始化前做前置准备

· 其次再是获取对象,并不是从头开始做依赖注入容器,它是建立在之上,是实现依赖注入的最核心的接口

· 第三个步骤是调用,此方法用于向中注册默认的单例bean以及默认的等

· 第四步是调用ry,此方法用于执行准备好了的环境后的后置逻辑,子类可覆盖它实现特定逻辑

· 第五步是,从方法名上可以看出,这一步是在调用容器提供的ssor接口,此接口是提供的扩展接口之一,我们后文再详细讨论

· 第六步调用,此方法用于注册,用于对bean的创建做拦截处理,这一步会创建的对象,接口也是提供的一个扩展接口,后面再详细讨论

· 第七步是,用于初始化

· 第八步是,用于初始化事件广播器,在初始化的前后可以广播,用户可自定义来响应这些事件

· 第九步调用,此方法也是用于扩展,子类中可用它实现其它特殊bean的装配和定制

· 第十步调用,这一步是识别出中配置的对象

· 第十一步调用,创建所有非lazy的单例对象

· 第十二步ftl语法,完成初始化,此方法中会触发接口,以及广播事件,触发

初始化的整个过程非常清晰,整个流程见如下方法的代码:

ftl中引入jstl_ftl语法_ftlinclude

从整个初始化的大方法中,不难发现,提供了以下没有的能力:

· ssor,对做拦截

· ,可用于实现消息的转换,国际化等功能

· 事件广播机制,可对依赖注入民容器的状态做监听

· 更加可扩展,可定制化

看完了初始化的大方法后,我们再来看看ry方法,此方法用于获取对象,其实现如下:

ftl语法_ftlinclude_ftl中引入jstl

这个方法实现非常简洁,其中重要的一步便是方法的调用,此方法是抽象方法,子类可实现以各种不同的方式创建,比如中通过读取xml配置来创建,而ntext实现类中中则只是创建,没有注册对象,需要手动编码注册。

方法的实现之一中的实现是对xml的加载相关的特定逻辑,如下代码:

方法在中是一个抽象方法,用于从xml中加载Bean,中不关注如何加载,其子类的实现类中处理如何加载bean,中实现了方法,其实现中通过der加载,而类中则实现从类路径下加载xml配置同时类则实现了从文件系统中的文件路径下加载xml配置。中的方法中使用了der对象,使用方式如下 :

ftlinclude_ftl语法_ftl中引入jstl

从上图中可以看到,der中使用到了接口,此接口定义如下:

ftl语法_ftl中引入jstl_ftlinclude

接口用于加载资源,的实现了此接口,用于加载配置。中的方法子类也可覆盖,比如其中一个子类则覆盖了此方法,用于加载文件系统中的xml文件。

模板方法模式的扩展

说了这么多中使用的模板方法模式,这里举个例子,通过处理的xml配置,即在的xml配置中可使用语法,这里通过扩展中的常用类类,并覆盖其方法,在覆盖的方法中对加载的xml配置做的处理,先看看代码如何实现:

ftl语法_ftlinclude_ftl中引入jstl

实现代码后,再来看看如何使用,我们先准备好一个属性文件,用于创建的对象并供做渲染ftl语法,我这里的路径为META-INF//app. 我这里只是一个示例,创建一个简单的属性文件即可:

ftl语法_ftlinclude_ftl中引入jstl

这里只有一个key为的属性,下面再来写一个使用了语法的xml配置文件,作为示例,只写一个简单的xml配置文件,其中只有一个bean并且只使用的if语法,其中使用了前面创建的属性文件中的属性,判断如果的值为test,则创建bean,否则什么都不做。因为前面的属性文件中的的值确实为test,所以if判断会满足,我们可以从中获取到配置的bean。我这里的xml路径为META-INF/.xml.ftl,其内容如下 :

配置好了bean之后,最后再来创建对象,过程如下图所示:

ftl中引入jstl_ftlinclude_ftl语法

整个示例到此结束

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注