实现IOC功能的简单Spring框架
Posted litos
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现IOC功能的简单Spring框架相关的知识,希望对你有一定的参考价值。
需求分析
设计一个含有IOC的简单Spring,要求含有对象注册、对象管理以及暴露给外部的获取对象功能。
项目设计
- 对于注册的对象用一个类BeanInfo来描述其信息,包括对象标识、全类名以及属性名与值的Map。
- 对于IOC容器设定一个顶层接口BeanFactory,定义通过对象标识获取对象示例的方法getBean(String id)。AbstractBeanFactory实现该接口,在该类中实现解析生成目标对象,以及获取对象方法,并在该类中添加注册器接口,以便能从注册器中读取注册的对象。
- 对于注册器,提供一个顶层接口SourceReader,并在其中添加加载用户注册的对象的方法loadBeans(String filePath)。本项目中使用读取XML的方式,从XML中读取出注册的对象,并把它们封装成BeanInfo放入Map中。
- 设定一个上下文XMLContext,继承AbstractBeanFactory,负责选择使用哪种注册方式,并决定何时加载注册的对象。
代码实现
BeanInfo类
package ex1;
import java.util.HashMap;
import java.util.Map;
/**
* 该类用于描述注册在容器中的对象
*/
public class BeanInfo {
private String id; //对象ID,名字
private String type; //全类名
private Map<String, Object> properties = new HashMap<>(); //属性名与值的映射集合
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Map<String, Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
public void addProperty(String key,Object value) {
properties.put(key,value);
}
}
BeanFactory接口
package ex1;
public interface BeanFactory {
/**
* 根据对象的名称标识来获取对象实例
* @param id 对象名称,即对象描述信息中的对象标识
* @return 指定名称的对象实例
*/
Object getBean(String id);
}
AbstractBeanFactory类
package ex1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 最顶层的IOC实现
* 该类负责从注册器中取出注册对象
* 实现从对象描述信息转换为对象实例的过程
* 实现根据名称获取对象的方法
*/
public abstract class AbstractBeanFactory implements BeanFactory {
private String filePath; //注册文件路径
private Map<String, BeanInfo> container; //注册对象信息Map(IOC容器)
protected SourceReader reader; //对象注册读取器
public AbstractBeanFactory(String filePath) {
this.filePath = filePath;
}
/**
* 抽象方法,需由子类实现,用于指定使用什么样的注册读取器
* @param reader 指定的注册读取器
*/
protected abstract void setReader(SourceReader reader);
//从注册读取器中读取注册对象的信息Map
public void registerBeans() {
this.container = this.reader.loadBeans(this.filePath);
}
//实现BeanFactory定义的根据名称获取指定对象的方法
@Override
public Object getBean(String id) {
BeanInfo beanInfo = this.container.get(id); //根据对象名称获取该对象的描述信息
if (beanInfo == null) {
return null;
} else {
//根据对象信息,解析并生存指定对象实例,返回给用户
return this.parseBean(beanInfo);
}
}
/**
* 解析并生成对象实例
* 该方法主要通过反射完成,步骤如下:
* 1.根据类名,加载指定类,并获取该类的Class对象clazz
* 2.使用clazz实例化该类,获取一个对象,注意,这里采用无参构造方法
* 3.逐个设置对象子段的值,这里采用setter Method方式,而不是直接使用Field对象
* 4.返回对象实例
* @param beanInfo 指定对象的描述信息
* @return
*/
protected Object parseBean(BeanInfo beanInfo) {
Class clazz;
Object bean = null;
try {
clazz = Class.forName(beanInfo.getType()); //根据对象的全类名,指定类
bean = clazz.newInstance(); //使用注册对象的无参构造函数,实例化对象
Method[] methods = clazz.getMethods(); //获取所有公共方法(其实Spring获取的是所有方法,包括非公有是)
for (String property : beanInfo.getProperties().keySet()) {
//首字母大写
String name = property.toUpperCase().charAt(0) + property.toLowerCase().substring(1);
//获取属性的setter方法名称(命名规范)
String setter = "set" + name;
for (Method method : methods) {
if (method.getName().equals(setter)) {
Object value = beanInfo.getProperties().get(property);
method.invoke(bean, value); //通过反射对属性赋值
break;
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return bean;
}
}
SourceReader接口
package ex1;
import java.util.Map;
/**
* 注册读取器接口
* 复制读取用户注册的对象
* 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等
*/
public interface SourceReader {
/**
* 读取用户注册的对象信息
* @param filePath 注册路径
* @return 注册对象信息Map
*/
Map<String, BeanInfo> loadBeans(String filePath);
}
XMLSourceReader类
这里实现从xml配置文件中读取。需要用到dom4j包,用来解析XML文件。本项目中XML文件放置在根目录下,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="Person" class="ex1.Person">
<property name="name" value="fang"/>
</bean>
</beans>
实现代码
package ex1;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* XML注册读取器
* 该类继承了注册读取器接口,并模拟实现了读取注册对象信息的方法
*/
public class XMLSourceReader implements SourceReader {
/**
* 实现读取注册对象信息方法
* 自己编写通过配置文件读取的实现
*/
@Override
public Map<String, BeanInfo> loadBeans(String filePath) {
BeanInfo info = new BeanInfo(); //注册对象的info
InputStream is = XMLContext.class.getClassLoader().getResourceAsStream(filePath);//获取xml文件
SAXReader reader = new SAXReader();
Map<String,BeanInfo> beanMap = new HashMap<>();
try {
Document document = reader.read(is);
Element root = document.getRootElement(); //获取根标签,这里是beans
//遍历所有bean
for(Iterator iterator = root.elementIterator("bean");iterator.hasNext();){
Element element = (Element)iterator.next();
//获取id和class
Attribute id = element.attribute("id");
Attribute clazzName = element.attribute("class");
info.setId(id.getText());
info.setType(clazzName.getText());
//遍历该bean的property
for(Iterator it=element.elementIterator("property");it.hasNext();){
Element tmp = (Element)it.next();
//获取name和value
Attribute name = tmp.attribute("name");
Attribute value = tmp.attribute("value");
info.addProperty(name.getText(),value.getText());
}
beanMap.put(id.getText(),info);
}
} catch (DocumentException e) {
e.printStackTrace();
}
return beanMap;
}
}
XMLContext类
package ex1;
public class XMLContext extends AbstractBeanFactory {
/**
* 上下文的构造方法
* 该方法中指明注册读取器
* 并在构造该方法时一次性加载注册的对象
* @param filePath
*/
public XMLContext(String filePath) {
super(filePath);
this.setReader(new XMLSourceReader());//添加注册读取器
this.registerBeans(); //加载注册的对象信息
}
//设置注册读取器
@Override
protected void setReader(SourceReader reader) {
this.reader = reader;
}
}
测试类
Speakable接口
package ex1;
public interface Speakable {
void speak(String message);
}
Person类
package ex1;
public class Person implements Speakable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void speak(String message) {
System.out.println(this.name + " say: " + message);
}
}
Bootstrap类
package ex1;
public class Bootstrap {
public static void main(String[] args) {
BeanFactory beanFactory = new XMLContext("bean.xml");
Speakable speakable = (Speakable) beanFactory.getBean("Person");
speakable.speak("Experience One!");
}
}
运行结果
fang say: Experience One!
以上是关于实现IOC功能的简单Spring框架的主要内容,如果未能解决你的问题,请参考以下文章
Spring框架介绍和IoC容器中Bean配置(Spring框架)
Spring我抄袭了Spring,手写一套MySpring框架。。。