Spring是Java开发中最常见的框架之一。这里不讲其具体的理论原理之类的,给我印象最深的就是在配置文件ApplicationContext.xml中的<bean>标签下配置好id、class等属性,不需要显式地在代码中new一个对象,通过getBean()方法便能获得我们想要的对象实例。那么,我们不通过new是怎么构造出对象的呢?这其中的原理主要还是应用到“反射”特性。一步步来手动实现这一最基础的spring框架吧。
我们把所需要的类分为四个部分:
1.pojo类,主要用于封装<bean>标签、<property>标签中的属性。
2.registry类,Map作为成员属性,用Map将<bean>标签代表的实体类装起来
3.reader类,registry是reader中的成员属性,reader通过DOM解析.xml的配置文件,并且创建出<bean>标签代表的实体类,放入registry的Map中
4.IoC容器类,reader是容器类的成员属性,通过它的方法获得最终需要的对象实例。这里采用非延迟加载的方式,即创建容器类的时候就已经创建好配置文件中所有的对象实例,并放入容器类的Map中。
所有需要的类如图所示:
1 pojo类
1.1 PropertyValue 封装了<property>标签的属性:name、value、ref
1.2 MutablePropertyValues 一个<bean>标签下有多个<property>标签,在该类中定义成员变量List管理多个PropertyValue类
1.3 BeanDefinition 封装了<bean>标签的属性:id、class,其内部的<property>标签用MutablePropertyValues作为成员变量
2 registry类 包括了一个接口 一个实现类
接口定义了功能:注册BeanDefinition对象到注册表、删除指定BeanDefinition、获取指定BeanDefinition、判断是否存在指定BeanDefinition、获取BeanDefinition对象的个数、获取所有BeanDefinition的名称
实现类实现了如上功能,并且用Map作为成员变量,键是<bean>标签的id属性,值是BeanDefinition对象,代码如下
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();
//注册BeanDefinition对象到注册表
@Override
public void registryBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName,beanDefinition);
}
//删除指定BeanDefinition
@Override
public void deleteBeanDefinition(String beanName) throws Exception {
beanDefinitionMap.remove(beanName);
}
//获取指定BeanDefinition
@Override
public BeanDefinition getBeanDefinition(String beanName) throws Exception {
return beanDefinitionMap.get(beanName);
}
//判断是否存在指定BeanDefinition
@Override
public boolean contains(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
//获取BeanDefinition对象的个数
@Override
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
//获取所有BeanDefinition的名称
@Override
public String[] getBeanDefinitionNames() {
return beanDefinitionMap.keySet().toArray(new String[0]);
}
}
3 reader类 包括了一个接口,一个实现类
接口定义的功能有:获取成员变量注册表registry、加载xml配置文件并且在注册表registry中注册对象,即将BeanDefiniton保存在SimpleBeanDefinitionRegistry类中的Map里。
实现类实现具体功能,将SimpleBeanDefinitionRegistry作为自己的成员变量。主要特点在于利用了dom解析获得了xml配置文件中的<bean>标签,并且进一步按标签层级获得每一个标签的属性值。
@Override
public void loadBeanDefinitions(String configLocation) throws Exception {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation);
SAXReader reader = new SAXReader(); // maven中先配置dom4j
Document document = reader.read(is); // 获得dom对象
Element rootElement = document.getRootElement(); // 获得根标签<beans>
// 解析每一个<bean>
List<Element> elements = rootElement.elements();
for (Element element : elements) { // 一个element代表一个<bean>标签
String id = element.attributeValue("id"); // 获得标签的id属性
String className = element.attributeValue("class"); // 获得标签的class属性
BeanDefinition beanDefinition = new BeanDefinition(); // 封装成BeanDefinition
beanDefinition.setId(id);
beanDefinition.setClassName(className);
List<Element> propertyList = element.elements("property");
MutablePropertyValues propertyValues = new MutablePropertyValues();
for (Element element1 : propertyList) { // 一个element1代表一个<property>标签
String name = element1.attributeValue("name"); // 获得name属性
String ref = element1.attributeValue("ref"); // 获得ref属性
String value = element1.attributeValue("value");// 获得value属性
PropertyValue propertyValue = new PropertyValue(name,value,ref); // 封装为PropertyValue
propertyValues.addPropertyValue(propertyValue);
}
beanDefinition.setPropertyValues(propertyValues);
registry.registryBeanDefinition(id,beanDefinition);
}
}
通过该方法的最后一句代码,将生成的BeanDefinition保存在SimpleBeanDefinitionRegistry类中的Map里。
4 IoC容器类 包括两个接口,一个抽象类,一个具体实现类,它们的继承与实现关系如下
容器的关键在于是否是非延迟加载,抽象类中不仅实现了ApplicationContext接口(该接口继承了BeanFactory接口)。我们采用的非延迟加载的实现就在于在抽象类实现ApplicationContext的方法refresh()。抽象类定义了三个成员变量,修饰符protected(供子类实现)。1.Map<id字符串,Object实例对象>;2.reader具体实现类,加载配置文件,根据注册表中的BeanDefinition进行创建对象。3.配置文件名,是一个字符串。
下面我们着重看一下refresh()方法,这是写在抽象类中的
public abstract class AbstractApplicationContext implements ApplicationContext{
protected Map<String,Object> map = new HashMap<>(); // bean储存容器,是单例
protected BeanDefinitionReader beanDefinitionReader;// 解析XML
protected String configLocation; // XML文件
@Override
public void refresh() throws Exception {
beanDefinitionReader.loadBeanDefinitions(configLocation); // 其中创建了BeanDefinition,PropertyValue,放入registry中
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();// 获得上面的registry
String[] beanNames = registry.getBeanDefinitionNames();
for (String beanName : beanNames) {
getBean(beanName); // 该方法子类实现(模板方法)
}
}
}
在具体实现类ClassPathXmlApplicationContext的构造方法中调用此方法,可以在new ClassPathXmlApplicationContext时执行refresh(),实现非延时加载。在构造方法中还给成员变量reader赋值为具体的reader实现类。
具体实现类最重要的还是通过反射以及依赖注入获得具体的bean实例,代码如下:
@Override
public Object getBean(String name) throws Exception {
Object obj = this.map.get(name);
if(obj != null){
return obj; // map单例中已经存在
}
// 获取BeanDefinition封装的bean标签,通过反射创建bean实例
BeanDefinitionRegistry registry = this.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) {// 每一个propertyValue代表一个property标签
String propertyName = propertyValue.getName();
String value = propertyValue.getValue();
String ref = propertyValue.getRef();
// ref和value只能有一个
if(ref != null && !"".equals(ref)){// 对象
Object beanRef = getBean(ref); // ref属性 通过递归获得ref实例
String methodName = getSetterMethod(propertyName);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(methodName.equals(method.getName())){
method.invoke(beanObj,beanRef); // setRef()
}
}
}
if(value != null && !"".equals(value)){// 基本数据类型/字符串
String methodName = getSetterMethod(propertyName);
Method method = clazz.getMethod(methodName, String.class);
method.invoke(beanObj,value);
}
}
// 返回中放入map,下次不用经过此处
this.map.put(name,beanObj);
return beanObj;
}
在写一个getBean()的重载方法,采用泛型
@Override
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
Object beanObj = getBean(name);
if(beanObj == null){
return null;
}
T beanCast = clazz.cast(beanObj);
return beanCast;
}
5 总结
这只是spring框架实现的冰山一角,但可以帮助我们了解为什么写好配置文件之后变可以获得对象实例。各种类一层套一层,下图可以比较层次清晰地显示出类之间的关系。
简单流程:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
在new实例完成后➡构造方法中的refresh()实现非延迟加载➡调用reader具体实现类的loadBeanDefinitions()方法➡将BeanDefinition放入registry的Map中➡获得registry实例➡获得registry的map中的BeanDefinition➡根据封装的标签,反射创建bean对象➡将bean对象放入IoC的map中➡下次调用getBean()方法直接通过Object obj = this.map.get(name)获得