一、Spring的工厂体系
我们先来说一下spring的工厂体系(也称之为容器),得益于大佬们对于单一职责模式的坚决贯彻,在十几年以来spring的发展路上,扩展出来大量的工厂类,每一个工厂类都承担着自己的功能(其实就是有对应的方法实现)。对此我想说,我谢谢你们,我谢谢你们全家。
我们以前玩spring入门的时候大家都见过ApplicationContext这个工厂类,这个类类似于一个门面,类似我们以前玩Mybatis的时候的的SqlSession这个东西。就是一个门面,给程序员使用的。他的功能实际上非常多,因为继承了N多底层类,拥有很多其他类的功能,他的底层下面其实还有为他办事的其他类,直接研究他容易乱,那么我们就来看看下面的这个比较最基层的工厂BeanFactory。
1、BeanFactory及其扩展工厂
我们一路跟到了BeanFactory这个类里面,看一下他的注释。
我们看到注释表达的意思就是这个类就是容器类的根类,也就是最底层的那个类。那我们就基于这个类看一下他的类结构图,你鼠标放在这个类名上面,键盘敲Ctrl+H,IDEA就能显示出BeanFactory的类结构图。我们可以看到这个东西。
你也可以在右边全选(Ctrl +A),然后右键显示Diagram。而且这个Diagram还可以右键继续导出成图片格式。成为这样。
然后我们就来看看这个类结构的展开格式。解释一下其中几个类的作用。也就是这几个类。
下面先简单一一解释一下几个工厂类的作用也就是其功能。
BeanFactory (org.springframework.beans.factory):Spring体系的最顶层工厂,这里面定义了一些关于工厂的基本操作。就是基于类工厂的一些功能,你既然是类工厂,那你肯定能创建类,创建完了还能获取到你创建的类。这些基本功能都在这里被规范好了基础定义。基于他是个方法,那么其实他定义的功能后面肯定有接口为其实现。他规定的是最早的一些工厂能力。
HierarchicalBeanFactory (org.springframework.beans.factory):这个接口的能力是父子容器的管理(springmvc里面有这个概念),这个类里面的方法,实现了关于父子容器管理的能力。
ConfigurableBeanFactory (org.springframework.beans.factory.config):这个工厂提供了一些配置能力,比如单实例,多实例的配置,类型的转换,后置处理bean的方法能力。都在这个接口里面被定义,他实现了这些方法拥有了这些功能。
ApplicationContext (org.springframework.context):这个是集工厂类能力大成的一个工厂类,他基本拥有了所有能力,所以我们编程的时候,一般使用的也就是这个类。你也能看到他其实处于一个相对高层的继承了,他拥有了很多功能。
SimpleJndiBeanFactory (org.springframework.jndi.support)
AutowireCapableBeanFactory (org.springframework.beans.factory.config):提供了自动注入的能力(Autowaire),我们说自动注入其实就是这个工厂实现的他的功能,并且在生命周期中的intinitalBean(初始化bean的时候调)和DisposeBean(销毁的时候调)这两个个bean也是在这里提供的能力,你的bean实现这两个接口就可以在初始化和销毁的时候回调对应的函数,做一些自己的定制化。而销毁和初始化的能力就是这个工厂提供的。
ConfigurableListableBeanFactory (org.springframework.beans.factory.config)
AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
ListableBeanFactory (org.springframework.beans.factory):获取相关的配置信息,constrainsBeanDefinition和getBeanNameForType
StaticListableBeanFactory (org.springframework.beans.factory.support)
ApplicationContext (org.springframework.context)
ConfigurableListableBeanFactory (org.springframework.beans.factory.config)
DefaultListableBeanFactory (org.springframework.beans.factory.support):这个也是一个大成类,基本拥有所有的主要能力。
XmlBeanFactory (org.springframework.beans.factory.xml):可以读取xml配置文件,创建相应的对象。
DefaultListableBeanFactory这个提供了基本所有主要能力的工厂,给了我们很多作用,然后现在基本的spring都是基于xml配置的,所以他又提供了关于读取xml的能力,也就是他还有的一个子类XmlBeanFactory,这个类看名字也知道什么路子,所以XmlBeanFactory其实更牛逼,他还能基于xml配置的时候进行处理,XMLBeanFactory能处理xml的原因是他内部有一个属性工具类XmlBeanDefinitionReader。
ApplicationContext也是一个集大成的类,他拥有很多功能,所以我们编程一般会使用这个。
spring的学习会有一个词经常被提及,就是那个工厂类有哪些能力,我们说在编程世界中,能力指的就是他提供了这个方法去做这件事。而怎么做这件事,就是定义的方法,可能有的方法为了内聚会封装在一些类中,作为属性初始化的时候获取,后面使用这个能力。
1.1、关于xml的读取简述
我们开始使用spring的时候,很多都是直接通过解析xml来配置bean。我们先以这种最开始的模式来看一下操作。
其实我们进入spring的一个入口就是读取这个xml文件。我们一般的写法是:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) ctx.getBean("u");
我们在这个程序中,只需要指明了配置文件的路径(代码中是classpath)就可以了。可见真的很顶层,实现全部封装了。但是我们这里是开始学习spring的底层的,所以我们就玩点底层的类。也就是BeanFactory类。
// 1. 读取XML配置文件,XML配置文件 封装 Java对象 ---> 创建工厂生产的对象
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
// 2. 从工厂容器中获得对象
User user = (User) beanFactory.getBean("u");
我们使用了更底层的BeanFactory来承接,我们看到这个就需要做更多工作了,传进去了ClassPathResource这个参数进去了,而ApplicationContext则封装了这一步。你看到他的意思其实就是从类路径加载类资源。我们跟进去ClassPathResource这个类看看。继续点击CTRL+H我们看看这个类的继承关系。因为是接口,所以选红框那个。
我们看到最后这个类实现自InputStreamSource这个接口,这个接口里面提供了一个方法:
InputStream getInputStream() throws IOException;
获取输入流,这里其实我们就可以知道了,他是在这里把xml文件拿到最后得到文件的一个文件流,这里其实很像Mybatis,都是获取配置文件的文件流。其实也就是这样的。
而Resource接口实现了InputStreamSource,InputStreamSource这个接口的getInputStream()方法提供了花式读取文件获取输入流的各种实现,差不多有几十种,我简单列几种。
Resource接口
目的 读取相关资源文件的内容 获得输入流
文件 ---xml .properties .txt
网络中的资源
FileSystemResource
ClassPathResource
ByteArrayResource
ServletContextResource
...
至此我们知道了主体是啥样的。再进一步说我们以前玩Mybatis的时候知道,文件读取这种操作设计IO,是重资源,肯定是要一次读取到JVM解析完了然后封装在java对象里面,后面用的时候再拿(万物皆对象)。
那么:
spring配置文件的内容以什么形式体现。
mybatis-config.xml解析之后封装到了Configuration对象中。
mapper文件解析之后封装到了MappedStatement对象中。
其实在Spring中解析了配置文件之后把流文件解析封装到了BeanDefinition这个类中,只不过Mybatis用的是Xpath解析的,Spring用的是SAX解析的(年代久的都是这个), 至此,我们知道了她的来龙去脉,所以我们到这里就触及到了BeanDefinition。下面我们就来研究这个过程。
1.2、总结一下
我们使用xml来读取工厂,代码如下:
// 1. 读取XML配置文件,XML配置文件 封装 Java对象 ---> 创建工厂生产的对象
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
// 2. 从工厂容器中获得对象
User user = (User) beanFactory.getBean("u");
# 总结起来就是:
# 1、XmlBeanFactory工厂拥有通过解析xml来封装bean的能力。这个能力来自于他的一个属性,XmlBeanDefinitionReader,他解析xml需要传入的参数是 ClassPathResource。
# 2、ClassPathResource最终实现了Resource接口,Resource接口定义了getInputStream读取输入流的方法,Resource接口有N种实现类,其中包括ClassPathResource,实现了不同种类的getInputStream,包括获取文件输入流,网络文件输入流等等。而ClassPathResource实现的就是通过指定文件路径来获取文件输入流。
# 3、最后就等于XmlBeanFactory拥有XmlBeanDefinitionReader,XmlBeanDefinitionReader传入了ClassPathResource,ClassPathResource通过getInputStream()方法获取到了xml的文件输入流,此时XmlBeanDefinitionReader就获取到了文件输入流,此时在程序中就能解析这个文件了,表现在代码使用的那两句就是XmlBeanFactory解析了xml文件。
OK,至此我们就知道了关于我们使用代码获取工厂和bean的一个门面操作,后续的操作我们会逐步展开,分析其中的流程和逻辑。