章
目
录
现要对下面的配置文件进行解析,并自定义Spring框架的IOC对涉及到的对象进行管理。
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean> </beans>
定义bean相关的pojo类
PropertyValue类
用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。bean标签下的property标签的属性:
- name属性
- ref属性
- value属性 : 给基本数据类型及String类型数据赋的值
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; } }
MutablePropertyValues类
一个bean标签可以有多个property
子标签,所以再定义一个MutablePropertyValues
类,用来存储并管理多个PropertyValue
对象
public class MutablePropertyValues implements Iterable<PropertyValue> { //定义list集合对象,用来存储PropertyValue对象 private final List<PropertyValue> propertyValueList; public MutablePropertyValues() { this.propertyValueList = new ArrayList<PropertyValue>(); } public MutablePropertyValues(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 getPropertyValue(String propertyName) { //遍历集合对象 for (PropertyValue propertyValue : propertyValueList) { if (propertyValue.getName().equals(propertyName)) { return propertyValue; } } return null; } //判断集合是否为空 public boolean isEmpty() { return propertyValueList.isEmpty(); } //添加PropertyValue对象 public MutablePropertyValues addPropertyValue(PropertyValue pv) { //判断集合中存储的PropertyValue对象是否和传递进行的重复了,如果重复了,进行覆盖 for (int i = 0; i < propertyValueList.size(); i++) { //获取集合中每一个PropertyValue对象 PropertyValue currentPv = propertyValueList.get(i); if(currentPv.getName().equals(pv.getName())) { propertyValueList.set(i,pv); return this; //目的就是实现链式编程 } } this.propertyValueList.add(pv); return this;//目的就是实现链式编程 } //判断是否有指定name属性值的对象 public boolean contains(String propertyName) { return getPropertyValue(propertyName) != null; } //获取迭代器对象 public Iterator<PropertyValue> iterator() { return propertyValueList.iterator(); } }
BeanDefinition类
BeanDefinition
类用来封装bean信息的,主要包含id(即bean对象的名称)、class(需要交由spring管理的类的全类名)及子标签property数据。
public class BeanDefinition { private String id; private String className; private MutablePropertyValues propertyValues; public BeanDefinition() { propertyValues = new MutablePropertyValues(); } 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 MutablePropertyValues getPropertyValues() { return propertyValues; } public void setPropertyValues(MutablePropertyValues propertyValues) { this.propertyValues = propertyValues; } }
定义注册表相关类
BeanDefinitionRegistry接口
BeanDefinitionRegistry
接口定义了注册表的相关操作,定义如下功能:
- 注册BeanDefinition对象到注册表中
- 从注册表中删除指定名称的BeanDefinition对象
- 根据名称从注册表中获取BeanDefinition对象
- 判断注册表中是否包含指定名称的BeanDefinition对象
- 获取注册表中BeanDefinition对象的个数
- 获取注册表中所有的BeanDefinition的名称
public interface BeanDefinitionRegistry { //注册BeanDefinition对象到注册表中 void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); //从注册表中删除指定名称的BeanDefinition对象 void removeBeanDefinition(String beanName) throws Exception; //根据名称从注册表中获取BeanDefinition对象 BeanDefinition getBeanDefinition(String beanName) throws Exception; boolean containsBeanDefinition(String beanName); int getBeanDefinitionCount(); String[] getBeanDefinitionNames(); }
SimpleBeanDefinitionRegistry类
该类实现了BeanDefinitionRegistry
接口,定义了Map集合作为注册表容器。
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry { //定义一个容器,用来存储BeanDefinition对象 private 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 int getBeanDefinitionCount() { return beanDefinitionMap.size(); } @Override public String[] getBeanDefinitionNames() { return beanDefinitionMap.keySet().toArray(new String[1]); } }
定义解析器相关类
BeanDefinitionReader接口
BeanDefinitionReader
是用来解析配置文件并在注册表中注册bean的信息。定义了两个规范:
- 获取注册表的功能,让外界可以通过该对象获取注册表对象。
- 加载配置文件,并注册bean数据。
public interface BeanDefinitionReader { //获取注册表对象 BeanDefinitionRegistry getRegistry(); //加载配置文件并在注册表中进行注册 void loadBeanDefinitions(String configLocation) throws Exception; }
XmlBeanDefinitionReader类
XmlBeanDefinitionReader
类是专门用来解析xml配置文件的。该类实现BeanDefinitionReader接口并实现接口中的两个功能。
public class XmlBeanDefinitionReader implements BeanDefinitionReader { //声明注册表对象 private BeanDefinitionRegistry registry; public XmlBeanDefinitionReader() { registry = new SimpleBeanDefinitionRegistry(); } public BeanDefinitionRegistry getRegistry() { return registry; } public void loadBeanDefinitions(String configLocation) throws Exception { //使用dom4j进行xml配置文件的解析 SAXReader reader = new SAXReader(); //获取类路径下的配置文件 InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation); Document document = reader.read(is); //根据Document对象获取根标签对象 (beans) Element rootElement = document.getRootElement(); //获取根标签下所有的bean标签对象 List<Element> beanElements = rootElement.elements("bean"); //遍历集合 for (Element beanElement : beanElements) { //获取id属性 String id = beanElement.attributeValue("id"); //获取class属性 String className = beanElement.attributeValue("class"); //将id属性和class属性封装到BeanDefinition对象中 //1,创建BeanDefinition BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setId(id); beanDefinition.setClassName(className); //创建MutablePropertyValues对象 MutablePropertyValues mutablePropertyValues = new MutablePropertyValues(); //获取bean标签下所有的property标签对象 List<Element> propertyElements = beanElement.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); mutablePropertyValues.addPropertyValue(propertyValue); } //将mutablePropertyValues对象封装到BeanDefinition对象中 beanDefinition.setPropertyValues(mutablePropertyValues); //将beanDefinition对象注册到注册表中 registry.registerBeanDefinition(id,beanDefinition); } } }
IOC容器相关类
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; }
ApplicationContext接口
该接口的所以的子实现类对bean对象的创建都是非延时的,所以在该接口中定义 refresh()
方法,该方法主要完成以下两个功能:
- 加载配置文件。
- 根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
public interface ApplicationContext extends BeanFactory { //进行配置文件加载并进行对象创建 void refresh() throws IllegalStateException, Exception; }
AbstractApplicationContext类
作为ApplicationContext
接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个Map集合,作为bean对象存储的容器。
声明BeanDefinitionReader
类型的变量,用来进行xml
配置文件的解析,符合单一职责原则。
BeanDefinitionReader
类型的对象创建交由子类实现,因为只有子类明确到底创建BeanDefinitionReader
哪儿个子实现类对象。
public abstract class AbstractApplicationContext implements ApplicationContext { //声明解析器变量 protected BeanDefinitionReader beanDefinitionReader; //定义用于存储bean对象的map容器 protected Map<String, Object> singletonObjects = new HashMap<String, Object>(); //声明配置文件路径的变量 protected String configLocation; public void refresh() throws Exception { //加载BeanDefinition对象 beanDefinitionReader.loadBeanDefinitions(configLocation); //初始化bean finishBeanInitialization(); } //bean的初始化 private void finishBeanInitialization() throws Exception { //获取注册表对象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); //获取BeanDefinition对象 String[] beanNames = registry.getBeanDefinitionNames(); for (String beanName : beanNames) { //进行bean的初始化 getBean(beanName); } } }
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 e) { } } //根据bean对象的名称获取bean对象 public Object getBean(String name) throws Exception { //判断对象容器中是否包含指定名称的bean对象,如果包含,直接返回即可,如果不包含,需要自行创建 Object obj = singletonObjects.get(name); if (obj != null) { return obj; } //获取BeanDefinition对象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); BeanDefinition beanDefinition = registry.getBeanDefinition(name); //获取bean信息中的className String className = beanDefinition.getClassName(); //通过反射创建对象 Class<?> clazz = Class.forName(className); Object beanObj = clazz.newInstance(); //进行依赖注入操作 MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); for (PropertyValue propertyValue : propertyValues) { //获取name属性值 String propertyName = propertyValue.getName(); //获取value属性 String value = propertyValue.getValue(); //获取ref属性 String ref = propertyValue.getRef(); if(ref != null && !"".equals(ref)) { //获取依赖的bean对象 Object bean = getBean(ref); //拼接方法名 String methodName = StringUtils.getSetterMethodByFieldName(propertyName); //获取所有的方法对象 Method[] methods = clazz.getMethods(); for (Method method : methods) { if (methodName.equals(method.getName())) { //执行该setter方法 method.invoke(beanObj,bean); } } } if(value != null && !"".equals(value)) { //拼接方法名 String methodName = StringUtils.getSetterMethodByFieldName(propertyName); //获取method对象 Method method = clazz.getMethod(methodName, String.class); method.invoke(beanObj,value); } } //在返回beanObj对象之前,将该对象存储到map容器中 singletonObjects.put(name,beanObj); return beanObj; } 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); } }
打成jar包测试
最后将该项目打成jar包,引入到Spring框架回顾与源码解析(29)案例中替换掉原先的spring核心包,再去测试,发现效果是一样的。
自定义Spring IOC总结
1)使用到的设计模式
- 工厂模式。这个使用工厂模式 + 配置文件的方式。
- 单例模式。Spring IOC管理的bean对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是spring框架对每一个bean只创建了一个对象。
- 模板方法模式。AbstractApplicationContext类中的finishBeanInitialization()方法调用了子类的getBean()方法,因为getBean()的实现和环境息息相关。
- 迭代器模式。对于MutablePropertyValues类定义使用到了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器,所以给该容器提供一个遍历方式。
spring框架其实使用到了很多设计模式,如AOP使用到了代理模式,选择JDK代理或者CGLIB代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等。
2)符合大部分设计原则
3)整个设计和Spring的设计还是有一定的出入
spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性。而我们自定义SpringIOC有以下几个目的:
- 了解Spring底层对对象的大体管理机制。
- 了解设计模式在具体的开发中的使用。
- 以后学习spring源码,通过该案例的实现,可以降低spring学习的入门成本。
文章目录 前言 第一章:设计模式相关内容介绍 第二章:创建者模式(5种) 第三章:结构型模式(7种) 第四章: […]