上海魔力网站建设公司门户网站啥意思
继续整理记录这段时间来的收获,详细代码可在我的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]]](https://img-blog.csdnimg.cn/d9e3da1a2d324f51b74cb3d59c3c9773.png)
 运行后如图
 ![![[Pasted image 20230210173447.png]]](https://img-blog.csdnimg.cn/e7f6f555a68848d6b18187b454c2de5e.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学习入门成本
 
