文章目录
-
-
- 1、Spring IOC容器实现源码
-
- 1、配置文件的封装:Resource接口
- 2、XMLBeanFactory的初始化
- 3、加载(读取)配置文件:XmlBeanDefinitionReader
- 4、解析及注册BeanDefinitions
- 2、手写一个Spring IOC容器
-
1、Spring IOC容器实现源码
1、配置文件的封装:Resource接口
Resource资源抽象接口:来封装底层资源
1、抽象了所有Spring内部使用到的底层资源:File、URL、ClassPath等
2、不同来源的资源文件有相应的Resource实现:FileSystemResource、UrlResource、ClassPathResource等
3、Spring配置文件的读取是通过ClassPathResource进行封装的
//封装配置文件:将配置文件封装为Resource类型的实例方法
Resource res = new ClassPathResource("beans.xml");
2、XMLBeanFactory的初始化
XMLBeanFactory的初始化有若干方法,Spring中提供了很多构造函数:
1、XmlBeanFactory类源码:里面提供了两个构造方法,可实现XMLBeanFactory初始化与加载配置文件资源
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
//构造方法的重载,内部调用了下面的构造方法
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
//加载资源的真正实现,但是在加载之前还有一个调用父类构造函数初始化的过程
this.reader.loadBeanDefinitions(resource);
}
}
2、跟踪到父类DefaultListableBeanFactory中:一个构造方法,只是实例化BeanFactory,但是并没有加载资源
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
//和子类相同,这个父类仍然需要调用父类构造函数初始化
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
}
3、跟踪父类到父类:AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
/**
* Create a new AbstractAutowireCapableBeanFactory.
*/
public AbstractAutowireCapableBeanFactory() {
super();
/**
* 忽略给定接口的自动装配功能,这样做的目的是什么?
* 当A中有属性B时,那么Spring在获取A的Bean的时候,如果属性B还没有初始化,那么Spring会自动初始化B
* 但是在某些特定场景下,B不会被初始化,其中的一种情况就是B实现了BeanNameAware接口。
* Spring这样介绍的:自动装配是忽略给定的依赖接口,典型应用就是通过其他方式解析Application上下文依赖
* 类似于BeanFactoryAware进行注入或者ApplicationContext通过ApplicationContextAware注入
*/
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
}
3、加载(读取)配置文件:XmlBeanDefinitionReader
/**
1、XmlBeanDefinitionReader中loadBeanDefinitions的方法:
1、封装资源文件,当进入XmlBeanDefinitionReader后首先对参数Resource类使用EncodedResource进行封装
2、获取输入流,从Resource中获取对应的InputStream并构造InputSource
3、通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
/**
2、进入doLoadBeanDefinitions(inputSource, encodedResource.getResource());
1、加载XML文件,并得到对应的Document
2、根据返回的Document注册Bean信息,解析及注册BeanDefinitions
这两个步骤就是Spring容器部分的实现基础,就是我们手写的Spring IOC容器的思想
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource){
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
/**
3、加载配置文件,获取Document
同样XmlBeanDefinitionReader对于文档的读取并没有亲力亲为,而是委托了DocumentLoader
但是DocumentLoader是个接口,真正调用loadDocument()方法的是实现类DefaultDocumentLoader
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource){
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());
}
/**
4、defaultDocumentLoader:
1、创建DocumentBuilderFactory
2、通过DocumentBuilderFactory创建DocumentBuilder
3、解析inputSource来返回Document对象
*/
public class DefaultDocumentLoader implements DocumentLoader {
@Override
public Document loadDocument(..){
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(..) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
return factory;
}
protected DocumentBuilder createDocumentBuilder(..){
DocumentBuilder docBuilder = factory.newDocumentBuilder();
return docBuilder;
}
}
4、解析及注册BeanDefinitions
上面我们已经把文件转成了Document,接下来就是提取及注册bean就是我们的重头戏了。
当程序已经拥有XML文档文件的Document实例对象时,就会调用下面这个方法了:
/**
1、registerBeanDefinitions:
在这个方法中使用了单一职责,将逻辑处理委托给单一的类进行处理,这个类就是BeanDefinitionDocumentReader
BeanDefinitionDocumentReader是接口,实例化的工作在createBeanDefinitionDocumentReader()中完成。
通过这个方法BeanDefinitionDocumentReader真正的类型是DefaultcreateBeanDefinitionDocumentReader
DefaultcreateBeanDefinitionDocumentReader是其实现类
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录加载bean前BeanDefinition的个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载及注册Bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
/**
2、进入DefaultcreateBeanDefinitionDocumentReader后:
发现registerBeanDefinitions方法时为了提取root作为参数继续注册Bean
doRegisterBeanDefinitions(root);这个方法时真正的xml解析阶段,前面都是准备阶段
*/
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
/**
3、doRegisterBeanDefinitions(Element root)解析及注册bean
*/
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//处理profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
....
}
//解析前留给子类实现,空方法,模板方法模式
preProcessXml(root);
//专门处理解析
parseBeanDefinitions(root, this.delegate);
//解析后留给子类实现,空方法,模板方法模式
postProcessXml(root);
this.delegate = parent;
}
/**
4、parseBeanDefinitions(root, this.delegate);
因为Spring的xml配置有两大Bean声明:
一个是默认的:<bean id="" class=""/>
另一个是自定义的:<tx:annotation-driven/>
如果采用Spring默认的Spring当然知道怎么做,但是如果是自定义的,就需要实现一些接口及配置了
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//处理自定义的
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
//处理默认的
delegate.parseCustomElement(root);
}
}
2、手写一个Spring IOC容器
Spring IOC的底层原理:
1、容器中存放的两个组件:
@Data
public class Car {
private String name;
private String length;
private String width;
private String height;
private Wheel wheel;
}
@Data
public class Wheel {
private String brand;
private String specification ;
}
2、需要被Spring容器解析的XML文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="wheel" class="com.hh.bean.Wheel">
<property name="brand" value="Michelin" />
<property name="specification" value="265/60 R18" />
</bean>
<bean id="car" class="com.hh.bean.Car">
<property name="name" value="Mercedes Benz G 500"/>
<property name="length" value="4717mm"/>
<property name="width" value="1855mm"/>
<property name="height" value="1949mm"/>
<property name="wheel" ref="wheel"/>
</bean>
</beans>
3、自定义的IOC容器:
- 加载 xml 配置文件,遍历其中的标签
- 获取标签中的 id 和 class 属性,加载 class 属性对应的类,并创建 bean
- 遍历标签中的标签,获取属性值,并将属性值填充到 bean 中
- 将 bean 注册到 bean 容器中
public class SpringIOC {
//使用一个Map来做Spring的容器,作用就是放置Spring管理的对象
private Map<String, Object> beanMap = new HashMap<>();
public SpringIOC(String location) throws Exception {
loadBeans(location);
}
public Object getBean(String name) {
Object bean = beanMap.get(name);
if (bean == null) {
throw new IllegalArgumentException("there is no com.hh.bean with name " + name);
}
return bean;
}
private void loadBeans(String location) throws Exception {
/**
* 1、读取配置文件
* javax.xml.parsers 包中的DocumentBuilderFactory用于创建DOM模式的解析器对象 ,
* DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,
* 但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,
* 自动创建一个工厂的对象并返回。
* 1、调用 DocumentBuilderFactory.newInstance() 方法得到创建 DOM 解析器的工厂
* 2、调用工厂对象的 newDocumentBuilder方法得到 DOM 解析器对象。
* 3、把要解析的 XML 文档转化为输入流,以便 DOM 解析器解析它
* 4、调用 DOM 解析器对象的 parse() 方法解析 XML 文档,得到代表整个文档的 Document 对象,
* 利用DOM特性对整个XML文档进行操作。
* 5、得到 XML 文档的根节点:doc.getDocumentElement();---<beans></beans>
* 6、得到节点的子节点:root.getChildNodes();---<com.hh.bean></com.hh.bean>
*/
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
InputStream inputStream = new FileInputStream(location);
Document doc = docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodes = root.getChildNodes();
/**
* 2、遍历Bean标签,创建Bean,并将其放入map容器中
* 1、<com.hh.bean id="wheel" class="com.hh.com.hh.bean.Wheel">
* 1、获取Bean的id
* 2、获取Bean的className:这是个全类名
*
* 2、根据反射创建Bean对象:beanClass = Class.forName(className);
* 如果bean的全类名不正确,抛出异常
*
* 3、获得beanClass后,将其对应的对象创建出来:bean = beanClass.newInstance();
* 1、设计的容器默认调用无参构造函数创建对象,如果没有会抛出异常
* 2、暂时没有使用带参构造函数逻辑
*/
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");
Class beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("请检查Bean的class配置是否正确"+className);
}
// 创建bean对象
Object bean = null;
try {
bean = beanClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("你的Bean没有空参数构造函数");
}
/**
* 遍历 <property> 标签:将Bean的属性注入
* 注入分为两种情况:
* 1、简单--->value属性注入:if(value != null)
* 2、麻烦--->其他Bean注入:else if(ref!=null)
*
* 1、<property name="brand" value="Michelin"/>
* 1、获取属性name ,2、获取属性value
* 2、<property name="wheel" ref="wheel"/>
* 1、获取属性name ,2、获取属性ref
*/
NodeList propertyNodes = ele.getElementsByTagName("property");
for (int j = 0; j < propertyNodes.getLength(); j++) {
Node propertyNode = propertyNodes.item(j);
if (propertyNode instanceof Element) {
Element propertyElement = (Element) propertyNode;
String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
// 利用反射将 com.hh.bean 相关字段访问权限设为可访问
Field declaredField = bean.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
if (value != null && value.length()>0) {
// 将属性值填充到相关字段中
declaredField.set(bean, value);
} else {
String ref = propertyElement.getAttribute("ref");
if (ref == null || ref.length()==0) {
throw new IllegalArgumentException("ref config error");
}
// 将引用填充到相关字段中
declaredField.set(bean, getBean(ref));
}
/**
* 3、将 com.hh.bean 注册到 com.hh.bean 容器中
*/
registerBean(id, bean);
}
}
}
}
}
private void registerBean(String id, Object bean) {
beanMap.put(id, bean);
}
}
4、测试SpringIOCTest:
public class SpringIOCTest {
@Test
public void getBean() throws Exception {
String location = SpringIOC.class.getClassLoader().getResource("beans.xml").getFile();
SpringIOC bf = new SpringIOC(location);
Wheel wheel = (Wheel) bf.getBean("wheel");
System.out.println(wheel);
Car car = (Car) bf.getBean("car");
System.out.println(car);
}
}