当前位置: 首页 > news >正文

Spring框架自定义实现IOC基础功能/IDEA如何手动实现IOC功能

继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库Java设计模式克隆下载学习使用!

7.4 自定义Spring IOC

创建新模块,结构如图![[Pasted image 20230210173222.png]]

7.4.1 定义bean相关POJO类

7.4.1.1 定义propertyValue类

/**  * @Author:Phil  * @ClassName: PropertyValue  * @Description:  * 用来封装bean标签下的property标签属性  * name属性  * ref属性  * value属性:给基本数据类型及String类型赋值  * @Date 2023/2/8 21:45  * @Version: 1.0  **/public class propertyValue {  private String name;  private String ref;  private String value;  public propertyValue() {  }  public propertyValue(String name, String ref, String value) {  this.name = name;  this.ref = ref;  this.value = value;  }  public String getName() {  return name;  }  public void setName(String name) {  this.name = name;  }  public String getRef() {  return ref;  }  public void setRef(String ref) {  this.ref = ref;  }  public String getValue() {  return value;  }  public void setValue(String value) {  this.value = value;  }  
}

7.4.1.2 定义MultiplePropertyValue类

一个bean 标签可以有多个property子标签,故用multiplePropertyValue类来存储PropertyValue对象

public class MultiplePropertyValues implements Iterable<PropertyValue>{  
//    定义list集合对象,用来存储PropertyValue对象  private final List<PropertyValue> propertyValueList;  public MultiplePropertyValues() {  this.propertyValueList = new ArrayList<PropertyValue> ();  }  public MultiplePropertyValues(List<PropertyValue> propertyValueList) {  if(propertyValueList == null)  this.propertyValueList = new ArrayList<PropertyValue>();  else            this.propertyValueList = propertyValueList;  }  
//    获取所有propertyValue对象,以数组形式返回  public PropertyValue[] getPropertyValues(){  return propertyValueList.toArray(new PropertyValue[0]);  }  
//    根据name属性值返回对应PropertyValue对象  public PropertyValue getPropertyValues(String propertyName){  
//        遍历集合返回  for (PropertyValue propertyValue : propertyValueList) {  if(propertyValue.getName().equals(propertyName))  return propertyValue;  }  return null;  }  
//    判断集合是否为空  public boolean isEmpty(){  return propertyValueList.isEmpty();  }  
//    添加PropertyValue对象  public MultiplePropertyValues addPropertyValue(PropertyValue pv){  
//        若有则进行覆盖  for (int i = 0; i < propertyValueList.size(); i++) {  if(propertyValueList.get(i).getName().equals(pv.getName())){  propertyValueList.set(i,pv);  return this;//目的是链式编程  }  }  
//            添加新的  this.propertyValueList.add(pv);  return this;    }  
//    判断是否有指定name的PropertyValue对象  public boolean contains(String propertyName){  return getPropertyValues(propertyName) != null;  }  
//    获取迭代器对象  @Override  public Iterator<PropertyValue> iterator() {  return propertyValueList.iterator();  }  
}

7.1.4.3 BeanDefinition类

BeanDefinition类用来封装bean信息,主要包含id(bean 名称),class(bean全类名)及子标签property对象数据

public class BeanDefinition {  private String id;  private String className;  private MultiplePropertyValues multiplePropertyValues;  public BeanDefinition() {  multiplePropertyValues = new MultiplePropertyValues();  }  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;  }  public MultiplePropertyValues getMultiplePropertyValues() {  return multiplePropertyValues;  }  public void setMultiplePropertyValues(MultiplePropertyValues multiplePropertyValues) {  this.multiplePropertyValues = multiplePropertyValues;  }  
}

7.4.2 定义注册表类

7.4.2.1 定义BeanDefinitionRegistry接口

BeanDefinitionRegistry接口定义了注册表相关操作,定义如下功能:

  • 注册BeanDefinition对象到注册表中
  • 根据名称从注册表中获取后去BeanDefinition对象
  • 从注册表中删除指定名称的BeanDefinition对象
  • 判断注册表中是否包含指定名称的BeanDefinition对象
  • 获取注册表中BeanDefinition对象个数
  • 获取注册表中所有的Bean
public interface BeanDefinitionRegistry {  //往注册表中注册bean  void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) ;  //从注册表删掉指定名称bean  void removeBeanDefinition(String beanName) throws Exception;  //获取指定名称bean  BeanDefinition getBeanDefinition(String beanName) throws Exception;  //判断是否包含指定名称bean  boolean containsBeanDefinition(String beanName);  //获取所有bean  String[] getBeanDefinitionNames();  int getBeanDefinitionCount();  boolean isBeanNameInUse(String var1);  
}

7.4.2.2 SimpleBeanDefinitionRegistry类

public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry{  
//    创建容器,用于存储  Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>();  @Override  public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {  beanDefinitionMap.put(beanName,beanDefinition);  }  @Override  public void removeBeanDefinition(String beanName) throws Exception {  beanDefinitionMap.remove(beanName);  }  @Override  public BeanDefinition getBeanDefinition(String beanName) throws Exception {  return beanDefinitionMap.get(beanName);  }  @Override  public boolean containsBeanDefinition(String beanName) {  return beanDefinitionMap.containsKey(beanName);  }  @Override  public String[] getBeanDefinitionNames() {  return beanDefinitionMap.keySet().toArray(new String[0]);  }  @Override  public int getBeanDefinitionCount() {  return beanDefinitionMap.size();  }  @Override  public boolean isBeanNameInUse(String var1) {  return beanDefinitionMap.containsKey(var1);  }  
}

7.4.3 定义解析器类

7.4.3.1 BeanDefinitionReader接口

BeanDefinitionReader用来解析配置文件并在注册表中注册bean的信息,定义了两规范:

  • 获取注册表功能,让外界可通过该对象获取注册表对象
  • 加载配置文件,并注册bean数据
public interface BeanDefinitionReader{//获取注册表对象BeanDefinitionRegistry getRegistry();//加载配置文件斌在注册表中进行注册void loadBeanDefinitions(String configuration);
}

7.4.3.2 XmlBeanDefinitionReader类

XmlBeanDefinitionReader类是专门来解析xml配置文件,实现了BeanDefinitionReader接口的两个功能。

public class XmlBeanDefinitionReader implements BeanDefinitionReader {  
//    声明注册表对象  private BeanDefinitionRegistry registry;  public XmlBeanDefinitionReader() {  this.registry = new SimpleBeanDefinitionRegistry();  }  @Override  public BeanDefinitionRegistry getRegistry() {  return registry;  }  @Override  public void loadBeanDefinitions(String configuration) throws Exception{  
//        使用dom4j进行xml配置文件的解析  SAXReader saxReader = new SAXReader();  
//        后去类路径下的配置文件  InputStream resourceAsStream = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configuration);  Document document = saxReader.read(resourceAsStream);  
//        根据Document对象获取根标签对象(beans)  Element rootElement = document.getRootElement();  
//       获取根标签下所有的bean标签对象  List<Element> elements = rootElement.elements("bean");  
//        遍历集合  for (Element element : elements) {  
//            获取id属性  String id = element.attributeValue("id");  
//            获取className  String className = element.attributeValue("class");  
//          将id和className封装到BeanDefinition对象中  
//          创建BeanDefinition对象  BeanDefinition beanDefinition = new BeanDefinition();  beanDefinition.setId(id);  beanDefinition.setClassName(className);  
//            创建MultiplePropertyValue对象  MultiplePropertyValues multiplePropertyValues = new MultiplePropertyValues();  
//          获取bean标签下的所有property标签对象  List<Element> propertyElements = element.elements("property");  for (Element propertyElement : propertyElements) {  String name = propertyElement.attributeValue("name");  String ref = propertyElement.attributeValue("ref");  String value = propertyElement.attributeValue("value");  PropertyValue propertyValue = new PropertyValue(name, ref, value);  multiplePropertyValues.addPropertyValue(propertyValue);  }  
//            将multiplePropertyValues封装到BeanDefinition中  beanDefinition.setMultiplePropertyValues(multiplePropertyValues);  
//            将BeanDefinition注册到注册表中  registry.registerBeanDefinition(id,beanDefinition);  }  }  
}

7.4.4 容器相关类

7.4.4.1 BeanFactory接口

该接口定义IOC容器的统一规范即获取bean对象

public interface BeanFactory{//根据bean对象的名称获取bean对象Object getBean(String name) throws Exception;//根据bean对象的名称获取bean对象,并进行类型转换<T> T getBean(String name,Class<? extends T> clazz) throws Exception;
}

7.4.4.2 ApplicationContext接口

该接口的子实现类对bean 对象的创建都是非延时的,所以该接口定义refresh方法,主要有两功能:

  • 加载配置文件
  • 根据注册表中BeanDefinition对象封装的数据进行bean对象的创建
public interface ApplicationContext extends BeanFactory{  void refresh()throws Exception;  
}

7.4.4.3 AbstractApplicationContext接口

  • 作为ApplicationContext接口的子类,故该类是非延时加载,故需要在该类中定义Map集合,作为bean对象存储容器
  • 声明BeanDefinition类型变量,用来进行xml配置文件解析,符合单一职责原则
  • BeanDefinition类型对象创建交由子类实现,子类明确创建BeanDefinitionReader
public abstract class AbstractApplicationContext implements ApplicationContext {  
//    声明解析器对象  protected BeanDefinitionReader beanDefinitionReader;  
//   存储bean容器,key存储的bean的id,value是bean对象  protected Map<String,Object> singletonObject = new HashMap<String,Object>();;  
//    存储配置文件路径  String configLocation;  public void refresh() throws Exception{  
//   加载BeanDefinition  beanDefinitionReader.loadBeanDefinitions(configLocation);  
//       初始化bean  finishBeanInitialization();  }  public void finishBeanInitialization() throws Exception{  
//       获取注册表对象  BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();  
//        获取BeanDefinition对象  String [] beanNames = registry.getBeanDefinitionNames();  
//        初始化bean  for (String beanName : beanNames) {  getBean(beanName);  }  }  
}

7.4.4.4 ClassPathXmlApplicationContext接口

该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要有以下功能:

  • 在构造方法中,创建BeanDefinitionReader对象
  • 在构造方法中,调用refresh方法,用于进行配置文件加载,创建bean对象并存储到容器中
  • 重写父类中的getBean方法,并实现依赖注入
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{  public ClassPathXmlApplicationContext(String configLocation){  this.configLocation = configLocation;  
//        构建解析器对象  beanDefinitionReader = new XmlBeanDefinitionReader();  try {  this.refresh();  }catch (Exception exception){  exception.printStackTrace();  }  }  
//    根据bean对象的名称获取bean对象  @Override  public Object getBean(String name) throws Exception {  
//        判断对象容器中是否包含指定bean对象,若包含则返回,否则创建  Object object = singletonObject.get(name);  if(object != null)  return object;  
//        获取BeanDefinition对象  BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();  BeanDefinition beanDefinition = registry.getBeanDefinition(name);  
//       获取bean信息中的className  String className = beanDefinition.getClassName();  
//        通过反射获取对象  Class<?> clazz = Class.forName(className);  Object instance = clazz.newInstance();  
//        进行依赖注入操作  MultiplePropertyValues multiplePropertyValues = beanDefinition.getMultiplePropertyValues();  for (PropertyValue propertyValue : multiplePropertyValues) {  
//            获取name属性值  String propertyValueName = propertyValue.getName();  
//            获取value值  String value = propertyValue.getValue();  
//            获取ref值  String ref = propertyValue.getRef();  if(ref != null && !"".equals(ref)){  
//                获取依赖的对象  Object bean = getBean(ref);  
//                拼接方法名  String setterMethodByField = StringUtils.getSetterMethodByField(propertyValueName);  
//                获取所有方法  Method[] methods = clazz.getMethods();  for (Method method : methods) {  if(method.getName().equals(setterMethodByField))  
//                        执行setter方法  method.invoke(instance,bean);  }  }  if(value != null && !"".equals(value)){  
//                拼接方法名  String methodName = StringUtils.getSetterMethodByField(propertyValueName);  
//                获取method对象  Method method = clazz.getMethod(methodName, String.class);  method.invoke(instance, value);  }  }  
//        在返回instance对象之前,将该对象存储到map容器中  singletonObject.put(name,instance);  return instance;  }  @Override  public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {  Object bean = getBean(name);  if(bean == null)  return null;  return clazz.cast(bean);  }  
}

7.4.4.5 测试

将前文回顾Spring框架项目中的pom文件的spring-context依赖换为上述新建项目依赖,如图
![[Pasted image 20230210173346.png]]
运行后如图
![[Pasted image 20230210173447.png]]

7.4.5 总结

7.4.5.1 使用到的设计模式

  • 工厂模式:工厂模式+ 配置文件
  • 单例模式。Spring IOC管理的bean都是单例的,此处单例不是通过构造器进行单例构建,且框架对每个bean只创建一个对象。
  • 模板方法模式。AbstractApplicationContext类中的finishInitialization方法调用getBean方法,因为getBean实现和环境有关。
  • 迭代器模式。其中MultiplePropertyValyes类使用了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器。
  • 还使用了很多设计模式,如AOP使用到了代理模式,选择JDK代理或CGLIB代理使用了策略模式,还有适配器模式,装饰者模式,观察者模式等。

7.4.5.2 符合大部分设计原则

7.4.5.3 整个设计和Spring设计还有一定出入

Spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性,自定义Spring IOC容器有两目的:

  • 了解Spring底层对对象的大体管理机制
  • 了解设计模式在具体开发中的使用
  • 以后学习Spring源码,通过该案例实现,可以降低Spring学习入门成本
http://www.lryc.cn/news/4278.html

相关文章:

  • pip离线安装windows版torch
  • Redis核心知识点
  • 14. 最长公共前缀
  • SignalR注册成Windows后台服务,并实现web前端断线重连
  • 【前端笔试题二】从一个指定数组中,每次随机取一个数,且不能与上次取数相同,即避免相邻取数重复
  • 专栏关注学习
  • 【手写 Vuex 源码】第八篇 - Vuex 的 State 状态安装
  • Mac下拉式终端的安装与配置 (iTerm2)
  • 使用 Spring 框架结合阿里云 OSS 实现文件上传的代码示例
  • 神经网络基础知识
  • SpringBoot开发规范部分通用模板+idea配置【项目通用-1】
  • 程序的机器级表示part3——算术和逻辑操作
  • 基于YOLOV5的钢材缺陷检测
  • Session与Cookie的区别(三)
  • 七大设计原则之接口隔离原则应用
  • 【Shell1】shell语法,ssh/build/scp/upgrade,环境变量,自动升级bmc
  • JavaScript HTML DOM - 改变CSS
  • mycat连接mysql 简单配置
  • Spring常用注解
  • I.MX6ULL内核开发9:kobject-驱动的基石
  • Docker-harbor私有仓库
  • Java之动态规划之子序列问题
  • java ArrayList
  • 前端——周总结系列四
  • Linux重定向符、管道符讲解
  • 【C++】多态
  • 分布式项目-品牌管理(5、6)
  • 自定义ESLint规则开发与使用
  • 【JavaScript】35_包装类与垃圾回收机制
  • 【CS224W】(task3)NetworkX工具包实践