Spring IoC-原始Ioc
# 一、IoC容器概念
# 1、基本概念
使用Bean容器去管理一个个的Bean,通过BeanFactory
将创建对象与使用对象的业务代码解耦,把Bean的管理交给容器,业务开发人员无须去关心Bean的构建和生命周期管理。
# 2、组成结构
一个部件去对应Bean内存的映射
XML reader从外部XML文件获取Bean的声明
反射部件加载Bean Class并创建实例
一个Map保存Bean的实例
对外提供
getBean()
方法供外部使用
# 二、简易IoC容器实现
# 1、XML解析工具
xml文件中会去配置Bean的id
和class
id
:别名class
:实际加载的bean类
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id = "xxxid" class = "me.xu.xxxclass"></bean>
</beans>
我们需要引入dom4j依赖,帮助我们快速处理XML文件:
<!--xml解析工具-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
# 2、Bean定义
XML中我们只有两个最简单的属性id
和Class
,所以我们这个Bean定义类很简单:
public class BeanDefinition {
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;
}
/**
* Bean id
*/
private String id;
/**
* Bean类名
*/
private String className;
public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
}
# 3、解析XML构建上下文
XML所对应Bean构建好之后,我们就需要去解析XML,并把解析后的内容用于构建上下文:
public class ClassPathXmlApplicationContext {
/**
* BeanDefinition集合
*/
private List<BeanDefinition> beanDefinitions = new ArrayList<>();
/**
* Bean的单例
*/
private Map<String, Object> singletons = new HashMap<>();
public ClassPathXmlApplicationContext(String fileName) {
this.readXml(fileName);
this.instanceBeans();
}
/**
* 解析XML,获取Bean实例,放入Bean集合中
*/
private void readXml(String fileName) {
// 创建读取对象
SAXReader saxReader = new SAXReader();
try {
// XML路径获取
URL xmlPath = this.getClass().getClassLoader().getResource(fileName);
Document document = saxReader.read(xmlPath);
Element rootElement = document.getRootElement();
// 对配置文件中每一个Bean进行处理
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);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void instanceBeans() {
for (BeanDefinition beanDefinition : beanDefinitions) {
try {
// 反射创建Bean实例放入单例中
singletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public Object getBean(String beanName) {
return singletons.get(beanName);
}
}
类中两个变量:
beanDefinitions
:用于存储BeanDefinition变量singletons
:Bean单例集合
类中两个方法:
readXml
:按照一定格式读取XML文件,并获取XML文件中每个Bean的id和class,实例化对象beanDefinition,并放入集合中。instanceBeans
:由于beanDefinitions
中存储的只是Class名,并非Bean实例,通过遍历这个集合,反射加载Bean对象,放入单例集合中。
# 4、功能验证
在SpringBoot中如果我们要注入一个Bean,通常是采用注解的方式,现在使用这个最简单的IoC容器,我们在XML文件中进行正确配置,然后进行功能验证。
服务类
public interface DemoService {
void test();
}
public class DemoServiceImpl implements DemoService{
@Override
public void test() {
System.out.println("hello mini-spring");
}
}
XML配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id = "DemoService" class = "me.xu.spring.test.demo1.DemoServiceImpl"></bean>
</beans>
测试
public class Demo {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("demo.xml");
DemoService demoService = (DemoService)classPathXmlApplicationContext.getBean("DemoService");
demoService.test();
}
}
# 三、优化Ioc容器
在上例的案例中,我们的ClassPathXmlApplicationContext
主要负责了加载XML文件和实例化Bean对象,我们需要将这个类进行分解:
- 最基本的核心容器
- XML外部访问抽离出来,便于后续对其他文件比如数据库、WEB等需求进行扩展
# 1、自定义Bean异常
public class BeansException extends Exception{
public BeansException(String msg) { super(msg); }
}
# 2、资源加载
资源定义
public interface Resource extends Iterator<Object>{
}
目前由于是从XML进行资源加载的,后续可能会从其他地方进行加载,通过这个接口方便后续的扩展。
XML资源加载
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();
} catch (DocumentException e) {
e.printStackTrace();
}
}
public boolean hasNext() {
return this.elementIterator.hasNext();
}
public Object next() {
return this.elementIterator.next();
}
}
# 3、Bean工厂
Bean工厂接口
public interface BeanFactory {
/**
* 根据Bean名称,获取Bean对象
*
* @param beanName Bean名称
* @return Bean对象
* @throws BeansException Bean自定义异常
*/
Object getBean(String beanName) throws BeansException;
/**
* 注册BeanDefinition
*
* @param beanDefinition Bean定义对象
*/
void registerBeanDefinition(BeanDefinition beanDefinition);
}
Bean工厂具体实现
public class SimpleBeanFactory implements BeanFactory {
/**
* Bean定义集合
*/
private List<BeanDefinition> beanDefinitions = new ArrayList<>();
/**
* Bean的id集合
*/
private List<String> beanNames = new ArrayList<>();
/**
* Bean单例集合
*/
private Map<String, Object> singletons = new HashMap<>();
public SimpleBeanFactory() {
}
@Override
public Object getBean(String beanName) throws BeansException {
// 先尝试从单例集合中获取
Object singleton = singletons.get(beanName);
// 如果没有,开始实例化Bean
if (singleton == null) {
// 序号
int i = beanNames.indexOf(beanName);
if (i == -1) {
throw new BeansException("BeanName错误");
} else {
// 获取Bean定义对象
BeanDefinition beanDefinition = beanDefinitions.get(i);
try {
// 反射创建Bean对象
singleton = Class.forName(beanDefinition.getClassName()).newInstance();
// 注册Bean实例
singletons.put(beanDefinition.getId(), singleton);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return singleton;
}
@Override
public void registerBeanDefinition(BeanDefinition beanDefinition) {
beanDefinitions.add(beanDefinition);
beanNames.add(beanDefinition.getId());
}
}
# 4、Bean注册
public class XmlBeanDefinitionReader {
BeanFactory beanFactory;
/**
* 构造函数
* @param beanFactory bean工厂
*/
public XmlBeanDefinitionReader(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* 遍历资源,将资源中的bean对象进行注册
* @param resource 外部资源
*/
public void loadBeanDefinitions(Resource resource) {
while (resource.hasNext()) {
Element element = (Element) resource.next();
String beanID = element.attributeValue("id");
String beanClassName = element.attributeValue("class");
BeanDefinition beanDefinition = new BeanDefinition(beanID, beanClassName);
beanFactory.registerBeanDefinition(beanDefinition);
}
}
}
# 参考
上次更新: 2024/06/29, 15:13:44