自定义SpringIOC
现在要对下面的配置文件进行解析,并自定义Spring框架的IOC对涉及到的对象进行管理。
1 2 3 4 5 6 7
| <?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>
|
1、定义bean相关的pojo类
【1】PropertyValue类
用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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; } getter、setter }
|
【2】MutablePropertyValues类
一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存储并管理多个PropertyValue对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class MutablePropertyValues implements Iterable<PropertyValue> { private final List<PropertyValue> propertyValueList; public MutablePropertyValues() { this.propertyValueList = new ArrayList<PropertyValue>(); } public MutablePropertyValues(List<PropertyValue> propertyValueList) { this.propertyValueList = propertyValueList==null ? new ArrayList<PropertyValue>() : propertyValueList; } public Iterator<PropertyValue> iterator() { returen propertyValueList.iterator(); } public PropertyValue[] getPropertyValues() { return propertyValueLIst.toArray(new PropertyValue[0]); } public PropertyValue getPropertyValue(String propertyName) { for(PropertyValue propertyValue : propertyValueList) { if(null == propertyValue || null == propertyValue.getName()) { continue; } if(propertyValue.getName().equals(propertyName)) { return propertyValue; } } return null; } public boolean isEmpty() { return propertyValueList.isEmpty(); } public MutablePropertyValues addPropertyValue(PropertyValue pv) { for(int i = 0; i < propertyValueList.size(); i++) { if(null == propertyValue || null == propertyValue.getName()) { continue; } if(propertyValue.getName().equals(pv.getName())) { propertyValueList.set(i, pv); return this; } } propertyValueList.add(pv); return this; } public boolean contains(String propertyName) { return getPropertyValue(propertyName) != null } }
|
【3】BeanDefinition类
BeanDefinition类用来赋值bean信息的,主要包含id(即bean对象的名称)、class(需要交由spring管理的类的全类名)及子标签property数据。
1 2 3 4 5 6 7 8 9 10 11
| public class BeanDefinition { private String id; private String className; private MutablePropertyValues propertyValues; public BeanDefinition() { propertyValues = new MutablePropertyValues(); } getter、setter }
|
【4】定义注册表相关类
BeanDefinitionRegistry接口定义了注册表的相关操作,定义如下功能:
- 注册BeanDefinition对象到注册表中。
- 从注册表中删除指定名称的BeanDefinition对象。
- 根据名称从注册表中获取BeanDefinition对象。
- 判断注册表中是否包含指定名称的BeanDefinition对象。
- 获取注册表中BeanDefinition对象的个数。
- 获取注册表中所有的BeanDefinition的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public interface BeanDefinitionRegistry { void registerBeanDefinition(String beanName, BeanDefinition beanDefinition); void removeBeanDefinition(String beanName) throws Exception; BeanDefinition getBeanDefinition(String beanName) throws Exception; boolean containsBeanDefinition(String beanName); int getBeanDefinitionCount(); String[] getBeanDefinitionNames(); }
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry { private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { beanDefinitionMap.put(beanName, beanDefinition); } public void removeBeanDefinition(String beanName) throws Exception { beanDefinitionMap.remove(beanName); } public BeanDefinition getBeanDefinition(String beanName) throws Exception { return beanDefinitionMap.get(beanName); } public boolean containsBeanDefinition(String beanName) { return beanDefinitionMap.containsKey(beanName); } public int getBeanDefinitionCount() { return beanDefinitionMap.size(); } public String[] getBeanDefinitionNames() { return beanDefinitionMap.keySet().toArray(new String[0]); } }
|
【5】定义解析器相关类
BeanDefinitionReader是用来解析配置文件并在注册表中注册bean的信息。定义了两个规范:
- 获取注册表的功能,让外界可以通过该对象获取注册表对象。
- 加载配置文件,并注册bean数据。
1 2 3 4 5 6
| public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry(); void loadBeanDefinitions(String configLocation) throws Exception; }
|
XmlBeanDefinitionReader类是专门用来解析xml配置文件的。该类实现BeanDefinitionReader接口并实现接口中的两个功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| 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 {
SAXReader reader = new SAXReader(); InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation); Document document = reader.read(is); Element rootElement = document.getRootElement(); List<Element> beanElements = rootElement.elements("bean"); for(Elements beanElement : beanElements) { String id = beanElements.attributeValue("id"); String className = beanElement.attributeValue("class"); BeanDefinition beanDefinition = new BeanDifinition(); beanDefinition.setId(id); beanDefinition.setClassName(className); MutablePropertyValues mutablePropertyValues = new MutablePropertyValues(); 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); } beanDefinition.setPropertyValues(mutablePropertyValues); registry.registerBeanDefinition(id, beanDefinition); } } }
|
【6】IOC容器相关类
BeanFactory接口,在该接口中定义IOC容器的统一规范,即获取bean对象。
1 2 3 4 5 6 7
| public interface BeanFactory { Object getBean(String name) throws Exception; <T> T getBean(String name, Class<? extends T> clazz) throws Exception; }
|
ApplicationContext接口,该接口的所有子实现类对bean对象的创建都是非延时的,所以在该接口中定义refresh()方法,该方法主要完成以下两个功能:
- 加载配置文件
- 根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
1 2 3 4
| public interface ApplicationContext extends BeanFactory { void refresh() throws Exception; }
|
AbstractApplicationContext类,作为ApplicationContext接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个Map集合,作为bean对象存储的容器。
声明BeanDefinitionReader类型的变量,用来进行xml配置文件的解析,符合单一职责原则。BeanDefinitionReader类型的对象创建交由子类实现,因为只有子类明确到底创建BeanDefinitionReader哪个子实现类对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public abstract class AbstractApplicationContext implements ApplicationContext { protected BeanDefinitionReader beanDefinitionReader; protected Map<String, Object> singletonObjects = new HashMap(); protected String configLocation; public void refresh() throws Exception { beanDefinitionReader.loadBeanDefinitions(configLocation); finishBeanInitialization(); } private void finishBeanInitialization() throws Exception { BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); String[] beanNames = registry.getBeanDefinitionNames(); for(String beanName : beanNames) { getBean(beanName); } } }
|
ClassPathXmlApplicationContext类,该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要完成以下功能。
- 在构造方法中,创建BeanDefinitionReader对象。
- 在构造方法中,调用refresh()方法,用于进行配置文件加载、创建bean对象并存储到容器中。
- 重写父接口中的getBean()方法,并实现依赖注入操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| public class ClassPathXmlApplicationContext extends AbstractApplicationContext { public ClassPathXmlApplicationContext(String configLocation) { this.configLocation = configLocation; beanDefinitionReader = new XmlBeanDefinitionReader(); try { this.refresh(); } catch(Exception e) { } } public Object getBean(String name) throws Exception { Object obj = singletonObjects.get(name); if (null != obj) { return obj; } BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); BeanDefinition beanDefinition = registry.getBeanDefinition(name); String className = beanDefinition.getClassName(); Class<?> clazz = Class.forName(className); Object beanObj = clazz.newInstance(); MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); for(PropertyValue propertyValue : propertyValues) { String propertyName = propertyValue.getName(); String value = propertyValue.getValue(); String ref = propertyValue.getRef(); if(ref != null && !"".equals(ref)) { Object bean = getBean(ref); String methodName = StringUtils.getSetterMethodByFiledName(propertyName); Method[] methods = clazz.getMethods(); for(Method method : methods) { if(methodName.equals(method.getName())) { method.invoke(beanObj, bean); break; } } } if(null != value && !"".equals(value)) { String methodName = StringUtils.getSetterMehodByFieldName(propertyName); Method method = clazz.getMethod(methodName, String.class); method.invoke(beanObj, value); } } 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); } }
public class StringUtils { private StringUtils() { } public static String getSetterMethodByFieldName() { String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); return methodName; } }
|
【7】总结自定义Spring IOC容器
1)使用到的设计模式
- 工厂模式。这个使用工厂模式 + 配置文件的方式。
- 单例模式。Spring IOC管理的bean对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是spring框架对每一个bean只创建了一个对象。
- 模板方法模式。AbstractApplicationContext类中的finishBeanInitialization()方法调用了子类的getBean()方法,因为getBean()的实现和环境息息相关。
- 迭代器模式。对于MutablePropertyValue类定义使用到了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器,所以给该容器提供一个遍历方式。
spring框架其实使用到了很多设计模式,如AOP使用到了代理模式,选择JDK代理或者CGLIB代理使用到了策略模式,还有适配器模式,装饰者模式,观察者模式等。
2)符合大部分设计原则
我们的整个设计和Spring的设计还是有一定的出入。spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性。而我们自定义SpringIOC有以下几个目的:
- 了解Spring底层对对象的大体管理机制。
- 了解设计模式在具体的开发中的使用。
- 以后学习spring源码,通过该案例的实现,可以降低spring学习的入门成本。