实现BeanFactory 首先,IOC是个容器,可以帮我们管理对象的整个声明周期。
要实现IOC(控制反转),其实就是要实现DI(注入依赖)
DI其实就是对spring管理的对象进行赋值
那么首先要能够管理,其次要能够赋值
想要获取IOC管理的对象,首先要获得IOC。
BeanFactory ,这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用 。
ApplicationContext 这是BeanFactory 的子接口 ,,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用ApplicationContext 而不是底层的 BeanFactory。注意,ApplicationContext 是BeanFactory 的子接口 ,在接口中只有声明,没有实现。
而ClassPathXmlApplicationContex t是ApplicationContext 的实现类,通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象 。
因此我们要先实现BeanFactory
和ClassPathXmlApplicationContext
实现简单版本的bean 在spring中,bean的配置和使用过程为 :
先配置bean
1 <bean id ="helloworld" class ="com.atguigu.spring.bean.HelloWorld" > </bean >
再获取和使用bean
1 2 3 4 5 6 7 8 9 ApplicationContext ac = new ClassPathXmlApplicationContext ("applicationContext.xml" );HelloWorld helloworld = (HelloWorld) ac.getBean("helloworld" );HelloWorld helloworld = ac.getBean(HelloWorld.class);HelloWorld helloworld = ac.getBean("helloworld" , HelloWorld.class);
要做到这种效果,在我们的minispring中,先要实现一个BeanDefinition类和一个 ClassPathXmlApplicationContext类
1 2 3 4 5 6 7 8 9 10 11 public class BeanDefinition { private String id; private String className; public BeanDefinition (String id,String className) { this .id = id; this .className = className; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.minis;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class ClassPathXmlApplicationContext { private List<BeanDefinition> beanDefinitions = new ArrayList <>(); private Map<String, Object> singletons = new HashMap <>(); public ClassPathXmlApplicationContext (String fileName) { this .redXml(fileName) ; this .InstanceBeans() ; } private void redXml (String fileName) { SAXReader saxReader = new SAXReader (); try { URL xmlPath = this .getClass().getClassLoader().getResource(fileName); Document document = saxReader.read(xmlPath); Element rootElement = document.getRootElement(); for (Element element : (List<Element>) rootElement.elements()) { String beanID = element.attributeValue("id" ); String beanClassName = element.attributeValue("class" ); BeanDefinition beanDefinition = new BeanDefinition (beanID,beanClassName); beanDefinitions.add(beanDefinition); } } } private void InstanceBeans () { for (BeanDefinition beanDefinition : beanDefinitions) { try { singletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance()); } } } public Object getBean (String beanName) { return singletons.get(beanName); } }
以上,在填充完毕 redXml
和 InstanceBeans
两个方法后,就初步实现了IOC
注意,SAXReader需要导入以下jar包
1 2 3 4 5 <dependency > <groupId > dom4j</groupId > <artifactId > dom4j</artifactId > <version > 1.6.1</version > </dependency >
目前的 ClassPathXmlApplicationContext
兼具了 BeanFactory
的功能,它通过 singletons
和 beanDefinitions
初步实现了 Bean 的管理,其实这也是 Spring 本身的做法。
对上述代码解耦 上面的 ClassPathXmlApplicationContext
承担了太多责任,需要解耦。
将这个类解耦,其分解为两个部分 :
一是提出一个最基础的核心容器
把 XML 这些外部配置信息的访问单独剥离出去
主要是一个类只做一件事的思想。
现在我们只有 XML 这一种方式,但是之后还有可能配置到 Web 或数据库文件里,拆解出去之后也便于扩展。
以spring目录结构为范本,定义项目的代码结构 :
1 2 3 4 com.minis.beans; com.minis.context; com.minis.core; com.minis.test;
解耦后的代码目录:
定义BeansException BeansException
是一个异常处理类 :
1 2 3 4 5 6 public class BeansException extends Exception { public BeansException (String msg) { super (msg); } }
定义BeanFactory BeanFactory 这个接口是拆解出的一个基础容器,,先让这个接口拥有两个特性:一是获取一个 Bean(getBean),二是注册一个 BeanDefinition(registerBeanDefinition)。后面在XmlBeanDefinitionReader中实现这个接口
1 2 3 4 public interface BeanFactory { Object getBean (String beanName) throws BeansException; void registerBeanDefinition (BeanDefinition beanDefinition) ; }
定义 Resource 把外部的配置信息都当成 Resource(资源)来进行抽象
1 2 public interface Resource extends Iterator <Object> {}
定义 ClassPathXmlResource 操作 XML 文件格式都是 dom4j 帮我们做的,这里主要就是解析XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class ClassPathXmlResource implements Resource { Document document; Element rootElement; Iterator<Element> elementIterator; public ClassPathXmlResource (String fileName) { SAXReader saxReader = new SAXReader (); URL xmlPath = this .getClass().getClassLoader().getResource(fileName); try { this .document = saxReader.read(xmlPath); this .rootElement = document.getRootElement(); this .elementIterator = this .rootElement.elementIterator(); } } public boolean hasNext () { return this .elementIterator.hasNext(); } public Object next () { return this .elementIterator.next(); } }
XmlBeanDefinitionReader 这里就是 ClassPathXmlApplicationContext 中有关 BeanDefinition 实例化以及加载 到内存中的相关内容提取出来了。
即将解析好的XML转换成我们需要的BeanDefinition,加载到BeanFactory
这里会先尝试直接拿bean实例,拿不到再根据bean的定义注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class SimpleBeanFactory implements BeanFactory { private List<BeanDefinition> beanDefinitions = new ArrayList <>(); private List<String> beanNames = new ArrayList <>(); private Map<String, Object> singletons = new HashMap <>(); public SimpleBeanFactory () { } public Object getBean (String beanName) throws BeansException { Object singleton = singletons.get(beanName); if (singleton == null ) { int i = beanNames.indexOf(beanName); if (i == -1 ) { throw new BeansException (); } else { BeanDefinition beanDefinition = beanDefinitions.get(i); try { singleton = Class.forName(beanDefinition.getClassName()).newInstance(); } singletons.put(beanDefinition.getId(), singleton); } } return singleton; } public void registerBeanDefinition (BeanDefinition beanDefinition) { this .beanDefinitions.add(beanDefinition); this .beanNames.add(beanDefinition.getId()); } }
ClassPathXmlApplicationContext 那么现在的ClassPathXmlApplicationContext一部分交给了 BeanFactory,一部分又交给了 Resource 和 Reader。
现在只需要做三件事 :
解析 XML 文件中的内容。
加载解析的内容,构建 BeanDefinition。
读取 BeanDefinition 的配置信息,实例化 Bean,然后把它注入到 BeanFactory 容器中。
接下来看看它的实现吧 !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.minis.context;import com.minis.beans.*;import com.minis.core.ClassPathXmlResource;import com.minis.core.Resource;public class ClassPathXmlApplicationContext implements BeanFactory { BeanFactory beanFactory; public ClassPathXmlApplicationContext (String fileName) { Resource resource = new ClassPathXmlResource (fileName); BeanFactory beanFactory = new SimpleBeanFactory (); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (beanFactory); reader.loadBeanDefinitions(resource); this .beanFactory = beanFactory; } public Object getBean (String beanName) throws BeansException { return this .beanFactory.getBean(beanName); } public void registerBeanDefinition (BeanDefinition beanDefinition) { this .beanFactory.registerBeanDefinition(beanDefinition); } }
小结 整体流程图 :