联盟网站制作,杭州做网站外包公司哪家好,公众号注册流程,佛山公众平台网站推广多少钱一、前言
相信大家在看本篇文章的时候#xff0c;对IoC应该有一个比较清晰的理解#xff0c;我在这里再重新描述下#xff1a;它的作用就是实现一个容器将一个个的Bean#xff08;这里的Bean可以是一个Java的业务对象#xff0c;也可以是一个配置对象#xff09;统一管理…一、前言
相信大家在看本篇文章的时候对IoC应该有一个比较清晰的理解我在这里再重新描述下它的作用就是实现一个容器将一个个的Bean这里的Bean可以是一个Java的业务对象也可以是一个配置对象统一管理起来。在Java中我们创建一个对象最简单的方法是使用new关键字。Spring框架的IoC容器则是将创建Bean的动作与使用Bean解耦将应用层程序员无需关注底层对象的构建以及其生命周期以便更好的专注于业务开发。
本节我们则开始进入手写Spring框架的第一步实现一个最简易的IoC容器。
二、一个最简易的IoC容器
1、简易流程 我们在面向Spring框架开发时想要使用一个Bean时通常会将bean的一些元信息配置在xml文件中也可以通过注解Spring IoC容器会加载指定路径的xml文件将其进一步解析成BeanDefinition并存储到IoC容器中当我们应用层去获取Bean实例通过getBean时如果该Bean没有被初始化则会触发Bean实例创建的动作创建实例由反射实现。
2、简易功能下UML图 3、相关代码
BeanDefinition
package com.tiny.spring.beans.factory.config;/*** author: markus* date: 2023/10/7 8:14 PM* Description: Bean配置元信息* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public class BeanDefinition {private String id;private String className;public BeanDefinition() {}public BeanDefinition(String id, String className) {this.id id;this.className className;}public String getId() {return id;}public void setId(String id) {this.id id;}public String getClassName() {return className;}public void setClassName(String className) {this.className className;}
}ClassPathXmlApplicationContext
package com.tiny.spring.context.support;import com.tiny.spring.beans.factory.config.BeanDefinition;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** author: markus* date: 2023/10/7 8:16 PM* Description: 基于xml的Spring应用上下文* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public class ClassPathXmlApplicationContext {private ListBeanDefinition beanDefinitions new ArrayList();private MapString, Object singletons new HashMap();public ClassPathXmlApplicationContext(String pathname) {this.readXml(pathname);this.instanceBeans();}private void readXml(String pathname) {SAXReader saxReader new SAXReader();try {URL xmlPath this.getClass().getClassLoader().getResource(pathname);Document document saxReader.read(xmlPath);Element rootElement document.getRootElement();// 对配置文件的每一个bean标签进行处理for (Element element : rootElement.elements()) {// 获取Bean的基本信息String beanId element.attributeValue(id);String beanClassName element.attributeValue(class);BeanDefinition beanDefinition new BeanDefinition(beanId, beanClassName);// 将Bean的定义存放到BeanDefinitionbeanDefinitions.add(beanDefinition);}} catch (DocumentException e) {e.printStackTrace();}}/*** 利用反射创建Bean实例并存储在singletons中*/private void instanceBeans() {for (BeanDefinition beanDefinition : beanDefinitions) {try {singletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {e.printStackTrace();}}}/*** 对外提供的方法让外部程序获取Bean实例* param beanName* return*/public Object getBean(String beanName) {return singletons.get(beanName);}
}测试类
package com.tiny.spring.test;import com.tiny.spring.beans.BeansException;
import com.tiny.spring.context.support.ClassPathXmlApplicationContext;
import com.tiny.spring.test.service.AService;/*** author: markus* date: 2023/10/7 8:37 PM* Description: 最原始的IoC容器功能测试* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public class OriginalIoCContainerTest {public static void main(String[] args) throws BeansException {ClassPathXmlApplicationContext classPathXmlApplicationContext new ClassPathXmlApplicationContext(beans.xml);AService aService (AService) classPathXmlApplicationContext.getBean(aService);aService.sayHello();}
}三、单一职责原则
我们可以看到上面的ClassPathXmlApplicationContext承担了太多的功能这不符合对象单一职责原则。
原本ClassPathXmlApplicationContext既承担了对外提供Bean实例访问对内进行配置文件的加载并解析成BeanDefinition存储起来以及进行Bean实例的创建操作。
我们需要对此进行优化将IoC容器的核心功能Bean实例访问BeanDefinition注册和外部信息的访问剥离出来。
1、优化后的UML类图 2、相关代码
Resource
package com.tiny.spring.core.io;import java.util.Iterator;/*** author: markus* date: 2023/10/7 8:45 PM* Description: 外部的配置信息抽象* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public interface Resource extends IteratorObject {
}ClassPathXmlResource
package com.tiny.spring.core.io;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.net.URL;
import java.util.Iterator;/*** author: markus* date: 2023/10/7 8:47 PM* Description:* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public class ClassPathXmlResource implements Resource {Document document;Element rootElement;IteratorElement elementIterator;public ClassPathXmlResource(String pathname) {SAXReader saxReader new SAXReader();URL xmlPath this.getClass().getClassLoader().getResource(pathname);try {this.document saxReader.read(xmlPath);this.rootElement document.getRootElement();this.elementIterator this.rootElement.elementIterator();} catch (DocumentException e) {e.printStackTrace();}}Overridepublic boolean hasNext() {return this.elementIterator.hasNext();}Overridepublic Object next() {return this.elementIterator.next();}
}XmlBeanDefinitionReader
package com.tiny.spring.beans.factory.xml;import com.tiny.spring.beans.factory.BeanFactory;
import com.tiny.spring.beans.factory.config.BeanDefinition;
import com.tiny.spring.core.io.Resource;
import org.dom4j.Element;/*** author: markus* date: 2023/10/7 8:50 PM* Description:* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public class XmlBeanDefinitionReader {BeanFactory beanFactory;public XmlBeanDefinitionReader(BeanFactory beanFactory) {this.beanFactory beanFactory;}public void loadBeanDefinitions(Resource resource) {while (resource.hasNext()) {Element element (Element) resource.next();String beanId element.attributeValue(id);String className element.attributeValue(class);BeanDefinition beanDefinition new BeanDefinition(beanId, className);this.beanFactory.registerBeanDefinition(beanDefinition);}}
}BeanFactory
package com.tiny.spring.beans.factory;import com.tiny.spring.beans.BeansException;
import com.tiny.spring.beans.factory.config.BeanDefinition;/*** author: markus* date: 2023/10/7 8:43 PM* Description: IoC底层容器的根类* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public interface BeanFactory {/*** 根据beanName获取Bean实例* param beanName* return* throws BeansException*/Object getBean(String beanName) throws BeansException;/*** 注册Bean配置元信息* param beanDefinition*/void registerBeanDefinition(BeanDefinition beanDefinition);
}ClassPathXmlApplicationContext
package com.tiny.spring.context.support;import com.tiny.spring.beans.BeansException;
import com.tiny.spring.beans.factory.BeanFactory;
import com.tiny.spring.beans.factory.config.BeanDefinition;
import com.tiny.spring.beans.factory.support.SimpleBeanFactory;
import com.tiny.spring.beans.factory.xml.XmlBeanDefinitionReader;
import com.tiny.spring.core.io.ClassPathXmlResource;
import com.tiny.spring.core.io.Resource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** author: markus* date: 2023/10/7 8:16 PM* Description: 基于xml的Spring应用上下文* Blog: https://markuszhang.com* Its my honor to share what Ive learned with you!*/
public class ClassPathXmlApplicationContext implements BeanFactory {BeanFactory beanFactory;public ClassPathXmlApplicationContext(String pathname) {Resource resource new ClassPathXmlResource(pathname);BeanFactory beanFactory new SimpleBeanFactory();XmlBeanDefinitionReader reader new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions(resource);this.beanFactory beanFactory;}/*** 对外提供的方法让外部程序获取Bean实例** param beanName* return*/public Object getBean(String beanName) throws BeansException {return this.beanFactory.getBean(beanName);}Overridepublic void registerBeanDefinition(BeanDefinition beanDefinition) {this.beanFactory.registerBeanDefinition(beanDefinition);}
}功能验证 四、本文总结
可以看到经过上述构建我们在使用一个对象时不需要再去手动new一个了只需要进行一些简单的配置将其交给框架容器去管理就可以获取我们所需的对象。通过功能解耦我们定义出以下几个核心类
BeanFactory : 底层根容器SimpleBeanFactory : 容器的实现类ClassPathXmlApplicationContext : 应用层容器交付给上层程序调用Resource : 外部资源对象抽象ClassPathXmlResource : 外部Xml资源对象XmlBeanDefinitionLoader : xml文件加载并解析为BeanDefinition
通过功能解耦我们后续对容器进行扩展时就会更方便适配更多的场景。
就这样一个简易的IoC容器就实现了接下来我们就一步一步的将其他功能添加上去让一颗小树苗发育成一个参天大树。
完整代码参见https://github.com/markuszcl99/tiny-spring