Spring作为当前最优秀的框架之一,可以说和它名字一样,为这个行业带来了春天。它之所以这么强大,很重要的一个原因就是它把对象之间的依赖关系转而用配置文件来管理,也就是依赖注入机制。而实现这种机制的关键,就是Ioc(Inversion of Control)容器。
今天我就用Java代码简单实现一下Ioc容器。先展示一下架构图解:
基本思路:
1.解析xml文件
2.根据XML配置文件生成相应对象
3.将对象存入Ioc容器中
下面介绍一下类的结构:
Bean类就是存放配置文件里类之间关系信息的实体类,其中Property类与配置文件里属性相关联,在Bean类里以一个List形式存在。
XmlConfig就是解读XML配置文件的一个工具类,将XML配置文件里的内容存储到一个Map<String, Bean>的map容器中。
BeanFactory是一个接口,ClassPathXmlApplicationContext实现了它的getBean方法
BeanUtil则是通过反射获取类的setter方法,设置属性值
好,老规矩,上代码:
Property类:
package ioc;
public class Property {
private String name;
//使用value属性直接指定值,也可以使用ref属性来指定依赖的对象
private String value;
private String ref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}
Bean类:
package ioc;
import java.util.ArrayList;
import java.util.List;
public class Bean {
private String id;
private String className;
private List<Property> properties = new ArrayList<Property>();//bean节点下可以有多个property节点
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 List<Property> getProperties() {
return properties;
}
public void setProperties(List<Property> properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Bean [id=" + id + ", className=" + className
+ ", properties=" + properties + "]";
}
}
XmlConfig类:
package ioc;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class XmlConfig {
/**
* 读取配置文件
* @param path 配置文件路径
* @return
*/
public static Map<String, Bean> getConfig(String path){
Map<String, Bean> configMap = new HashMap<String, Bean>();
//使用dom4j和xpath读取xml文件
Document doc = null;
SAXReader reader = new SAXReader();
InputStream in = XmlConfig.class.getResourceAsStream(path);
try {
doc = reader.read(in);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("请检查您的xml配置文件路径是否正确!");
}
//定义xpath,取出所有的bean
String xpath = "//bean";
//对bean进行遍历
List<Element> list = doc.selectNodes(xpath);
if(list!=null){
for (Element beanEle : list) {
Bean bean = new Bean();
//bean节点的id
String id = beanEle.attributeValue("id");
//bean节点的class属性
String className = beanEle.attributeValue("class");
//封装到bean对象中
bean.setId(id);
bean.setClassName(className);
//获取bean节点下所有的property节点
List<Element> proList = beanEle.elements("property");
if(proList != null){
for (Element proEle : proList) {
Property prop = new Property();
String propName = proEle.attributeValue("name");
String propValue = proEle.attributeValue("value");
String propRef = proEle.attributeValue("ref");
//封装到property属性中
prop.setName(propName);
prop.setValue(propValue);
prop.setRef(propRef);
bean.getProperties().add(prop);
}
}
//id是不应重复的
if(configMap.containsKey(id)){
throw new RuntimeException("bean节点ID重复:" + id);
}
//将bean封装到map中
configMap.put(id, bean);
}
}
return configMap;
}
}
BeanFactory接口:
package ioc;
public interface BeanFactory {
Object getBean(String beanName);
}
ClassPathXmlApplicationContext类:
package ioc;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class ClassPathXmlApplicationContext implements BeanFactory{
//定义一个IOC容器
private Map<String, Object> ioc;
private Map<String, Bean> config;
/**
* 构造函数
* 1. 初始化IOC容器
* 2. 加载配置文件,生成bean对象放入IOC容器
* @param path
*/
public ClassPathXmlApplicationContext(String path){
//初始化IOC容器
ioc = new HashMap<String, Object>();
//读取配置文件
config = XmlConfig.getConfig(path);
if(config!=null){
for(Entry<String, Bean> entry : config.entrySet()){
String beanId = entry.getKey();
Bean bean = entry.getValue();
//根据bean生成相应的对象
Object object = createBean(bean);
ioc.put(beanId, object);
}
}
}
/**
* 根据bean生成对象实例
* @param bean
* @return
*/
private Object createBean(Bean bean) {
String beanId = bean.getId();
String className = bean.getClassName();
Class c = null;
Object object = null;
try {
//根据bean的calss属性生成对象
c = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("您配置的class属性不合法:"+className);
}
try {
//该方法调用的是类的无参构造方法
object = c.newInstance();
} catch (Exception e) {
throw new RuntimeException("该类缺少一个无参构造方法:"+className);
}
//将bean的属性封装到对象中
if(bean.getProperties() != null){
for(Property p : bean.getProperties()){
//情况一:配置文件中使用的是value属性注入
if(p.getValue() != null){
//获取属性对应的setter方法
Method setMethod = BeanUtil.getSetterMethod(c, p.getName());
try {
//调用set方法注入
setMethod.invoke(object, p.getValue());
} catch (Exception e) {
throw new RuntimeException("属性名称不合法或者没有相应的setter方法:"+p.getName());
}
}
//情况二:配置文件中使用的是ref属性注入
else if(p.getRef() != null){
//获取属性对应的setter方法
Method setMethod = BeanUtil.getSetterMethod(object, p.getName());
//从容器中找到依赖的对象
Object obj = ioc.get(p.getRef());
if(obj == null){
throw new RuntimeException("没有找到依赖的对象:"+p.getRef());
}else{
//调用set方法注入
try {
setMethod.invoke(object, obj);
} catch (Exception e) {
throw new RuntimeException("属性名称不合法或者没有相应的setter方法:"+p.getName());
}
}
}
}
}
return object;
}
@Override
public Object getBean(String beanName) {
return ioc.get(beanName);
}
}
BeanUtil类:
package ioc;
import java.lang.reflect.Method;
public class BeanUtil {
public static Method getSetterMethod(Object obj, String methodName) {
Class class1=obj.getClass();
return getSetterMethod(class1, methodName);
}
public static Method getSetterMethod(Class class1, String methodName) {
String temp=methodName.substring(0, 1).toUpperCase();
methodName="set"+temp+methodName.substring(1);
// System.out.println(methodName+"@@@@");
Method[] methods=class1.getMethods();
Method method=null;
for(Method m:methods) {
if(methodName.equals(m.getName())) {
method=m;
}
}
if(null==method)
throw new RuntimeException("不存在该方法:"+methodName);
return method;
}
}
ApplicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="address" class="test.Address">
<property name="city" value="fuzhou"></property>
</bean>
<bean id="user" class="test.User">
<property name="userName" value="tom"></property>
<property name="address" ref="address"></property>
</bean>
</beans>
User类:
package test;
public class User {
private String userName;
private Address address;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User [userName=" + userName + ", address=" + address + "]";
}
}
Address类:
package test;
public class Address {
private String city;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address [city=" + city + "]";
}
}
最后写个测试类测试一下
package test;
import java.util.Map;
import java.util.Map.Entry;
import ioc.Bean;
import ioc.BeanFactory;
import ioc.ClassPathXmlApplicationContext;
import ioc.XmlConfig;
public class Test {
public static void main(String[] args) {
testIOC();
//testConfig();
}
/**
* 测试IOC容器
*/
private static void testIOC(){
BeanFactory bf = new ClassPathXmlApplicationContext("/resource/ApplicationContext.xml");
User user = (User) bf.getBean("user");
System.out.println(user);
System.out.println("address hashcode:"+user.getAddress().hashCode());
Address address = (Address) bf.getBean("address");
System.out.println(address);
System.out.println("address hashcode:"+address.hashCode());
}
/**
* 测试读取配置文件
*/
private static void testConfig(){
Map<String,Bean> map = XmlConfig.getConfig("/resource/ApplicationContext.xml");
for (Entry<String, Bean> entry : map.entrySet()) {
System.out.println(entry.getKey()+"==="+entry.getValue());
}
}
}
运行结果:
User [userName=tom, address=Address [city=fuzhou]]
address hashcode:415133057
Address [city=fuzhou]
address hashcode:415133057
通过这次实践,加深了对Spring的Ioc实现的了解,再接再厉。