杀死Spring - Spring IOC容器的设计与实现
spring使用POJO开发企业应用,提供一致的编程模型,强调对接口编程从而来秉持它的易用性,平台性的思想。
1.依赖反转
哪些方面的控制被反转了?依赖对象的获取被反转了!!因而依赖反转有一个更好的名字:依赖注入!一般地要实现某个功能我们不得不写很多的类,然后类间存在复杂的引用或者说是依赖,通常依赖的获取都是靠自身去实现,这导致了代码的高度耦合并且难以测试。但有了依赖注入后情况后事情出现了转机,开发人员只需配置依赖文件,把对象间的关系交给IOC容器管理,我需要一个对象的时候,只要getBean,spring就把一个依赖已经配置好且实例化的对象给你。这种从具体对象手中交出控制的做法是非常有价值的。
强调两点:
1.spring的IOC和AOP框架只是依赖反转模式的一种具体实现,他可以在对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中。
2.不仅仅是spring有IOC依赖关系管理,在不同语言甚至是在同一语言都有不同的IOC实现。
2.Spring IOC 容器应用场景
IOC它管理着对象之间的关系,这使得它可以作为解耦对象的利器,不仅仅是对象,也可以是组件之间的解耦,毕竟万物互联,万事万物皆对象。我们以EJB编程模式和spring来对比一下,在EJB模式中,需要编写EJB组件,而这种组件需要满足EJB容器的规范才能运行在EJB容器中,从而获得事务管理,生命周期管理这些组件开发的基本服务。从获取到的服务来看,spring提供的服务和EJB容器提供的服务没多大区别,只是两者在获取的方式有很大的不同:在spring中它提供了一个IOC容器,里面放的都是定义好的bean(其实这里还不能这么说,应该是对象的BeanDefinition,spring中bean的实例化是在getBean方法调用后才有的,更具体点就是populate方法后,这个后面说),并通过依赖注入和AOP切面增强了java bean这样的POJO对象赋予事务管理,生命周期管理等基本功能。而对EJB开发来说,一个简单的EJB组件要写远程/本地接口,Home接口以及Bean的实现类,而且EJB运行是不能脱离EJB容器的,查找其他EJB组件也要通过诸如JNDI这样的方式,从而造成了其对EJB容器和规范的依赖。人总是要长大独立的对吧。
同时在应用开发中以应用开发人员的身份设计组件时往往需要引用和调用其他组件的服务,这种依赖关系如果固化在组件设计中,就会造成依赖关系的僵化和维护难度的增加,这个时候如果使用IOC容器把资源获取的方式反转,让IOC容器去管理这些依赖关系,那么当有依赖关系发生改变时开发人员只需修改配置文件即可,这使得这些组件资源之间的依赖关系的适配和管理更加灵活。
另一方面,我们在配置资源/对象/组件之间的关系时,不仅可以通过IOC容器控制依赖反转,还可以工具对这些配置信息可视化的管理和浏览,这样提高了对组件管理的水平,并且如果耦合关系发生改变时并不需要修改和编译源码,因为依赖关系已经从对象本身解放出来了,我们只需要修改配置文件即可,这也符合开闭原则,于此同时结合OSGI的使用特性还可以提高应用的动态部署能力。
3.IOC容器系列的设计:BeanFactory和ApplicationContext
上面的废话太多了,我都打瞌睡了。下面进入今天的正题:IOC容器的设计与实现
先来给大家画个大饼:
图一:BeanFactory系列
解释一下:
1BeanFactory:该接口IOC容器的根,定义了IOC容器所具有的一些基本功能方法。
2ListableBeanfFactory:该接口细化了BeanFactory的许多方法。
3HierarchicalBeanFactory:该接口增加了getParentBeanFactory()方法使其具备了双亲IOC容器的管理功能。
4AutowireCapableBeanFactory:该接口增加了IOC容器能够通过注解的方式注入bean的能力。
5ConfigurableBeanFactory:该接口增加了对IOC容器的一些配置功能,比如setParentBeanFactory()设置双亲IOC容器。且继承了SingletonBeanRegistry接口,同时增加了单例bean的注册功能。
6ConfigurableListableBeanFactory:该接口增加更加细化的IOC容器配置功能。
7AliasRegistry:该接口增加了bean的别名处理功能。
8BeanDefinitionRegistry:该接口是BeanDefinition的注册功能。
9DefaultSingletonBeanRegistry:该类实现单例bean的注册及其别名的处理
10FactoryBeanRegistrySupport:bean是放在容器的,生成bean的工厂也是bean,特别的bean
11AbstractBeanFactory:很重要的一个类,后面说
12AbstractAutowireCapableBeanFactory:很重要的一个类,后面再说
13DefaultListableBeanFactory:该类是Spring IOC容器的祖宗。BeanFactory相当于爱因斯坦的相对论,DefaultListableBeanFactory就相当于现在的核弹。
图二:ApplicationContext系列
解释一下
1ResouceLoader:该接口定义加载bean资源,默认头为classpath:
2ResourcePatternResolver:该接口是定义加载bean资源的拓展,默认头为classpath*:
3DefaultResourceLoader:该类是Spring默认的资源加载类。
4MessageSource:该接口是ApplicationContext具有国际化等功能。
5ApplicationEventPublisher:应用事件发布器。
6ApplicationContext:应用上下文,IOC容器的高级形态。
7Lifecycle:该接口定义了bean的生命周期。
8ConfigurableApplicationContext:该接口定义可配置的应用上下文。
9DisposableBean:该接口表示bean可以自毁。
10InitializingBean:该接口表示bean可以实例化。
11BeanNameAware:该接口表示bean可以知道自己的名字。
12AbstractApplicationContext:很重要的一个类,后面将介绍。
13AbstractRefreshableApplicationContext:很重要的一个类,后面将介绍。
14AbstrackXmlApplicationContext:很重要的一个类,后面将介绍。
15FileSystemXmlApplicationContext:applicationContext系列的一个容器实现。
4.IOC容器系列的实现:BeanFactory和ApplicationContext
由3我们知道了IOC一个基本的接口功能及关系,那么他是怎么具体实现的呢,IOC容器可以理解有BeanFactory和ApplicationContext两个系列。我们分别以他们各自的一个实现来探究他的实现
BeanFactory系列
DefaultListableBeanFactory是BeanFactory的默认IOC容器实现,XmlBeanFactory继承了DefaultListableBeanFactory,是DefaultListableBeanFactory的拓展,我们从分析XmlBeanFactory开始。
XmlBeanFactory
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
//设置父类IOC容器
super(parentBeanFactory);
//新建一个Reader用于读取Resource
this.reader = new XmlBeanDefinitionReader(this);
//加载Resource
this.reader.loadBeanDefinitions(resource);
}
}
分析:
1XmlBeanFactory继承自DefaultListableBeanFactory,这个继承的类就是Spring最简单的IOC容器。
2XmlBeanFactory有一个XmlBeanDefinitionReader,该类继承于AbstractBeanDefinitionReader用于读取Resource,Resource是什么呢?Resource封装了对bean配置文件进行的IO操作(关于Spring的Resource请转至 https://blog.csdn.net/ApatheCrazyFan/article/details/118671039)
3新建完了XmlBeanDefinitionReader后调用她的loadBeanDefinition()方法去加载含有定义信息的xml文件。
4看完XmlBeanFactory的代码后,我们完全可以模拟它的写法来实例一个IOC容器
//包装一下xml定义文件
ClassPathResouce resource = new ClassPathResource("beans.xml");
//整一个IOC容器
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//整一个Reader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//加载xml文件中定义的
reader.loadBeanDefinition(resource);
//从容器获取对象
factory.getBean("apache");
//
5从上面看出来我们开发人员完全就可以自己整一个IOC容器,只要自己整一个Resource的Reader,在spring3.1中XmlBeanFactory被@Deprecated了,原因就在这,因为bean的定义形式不仅仅只有xml文件格式还有其他的等形式,spring干脆deprecated了它,让我们自己选择合适Reader就可以构建一个IOC容器。
ApplicationContext系列
相对于哪些简单拓展BeanFactory的基本IOC容器,ApplicationContext还提供了许多附加功能,如
1支持不同的信息源,这使得它支持国际化
2除了ResourcePatternResolver还有DefaultResourceLoader可以加载各个地方不同形式的bean定义文件
3支持应用事件,在上下文引入了事件机制,这些事件和Bean的生命周期结合为bean的管理提供了便利。
FileSystemXmlApplicationContext
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
//设置父IOC容器
super(parent);
//设置bean定义文件地址
this.setConfigLocations(configLocations);
//启动
if (refresh) {
this.refresh();
}
}
//这个方法很关键
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
FileSystemXmlApplicationContext作为容器的功能在它的父类AbstractXmlApplicationContext中实现了,它作为一个具体的应用上下文要完成和它自身相关的两件事:
1一个功能是:如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用上下文的支持。同时启动IOC容器的refresh()过程。这个refresh()非常重要,它会牵涉到IOC容器启动的一系列复杂操作。并且不同的实现容器对这些操作都是相同,因为他们完成在各个父类。
2另一个功能是:与FileSystemXmlApplicationContext设计相关的功能,这与怎样从文件系统中加载xml文件有关,通过这个过程可以为存在文件系统中读取以xml形式存在的BeanDefinition做准备,因为不同应用上下文对应着不同的读取BeanDefinition方式,我们ClassPathXmlApplicationContext也继承了AbstractXmlApplicationContext,也是一种应用上下文的IOC容器,但是它不是从文件系统读取B而安Definition信息,而是从classpath路径读取。