自己实现简单Spring Ioc

自己实现简单Spring Ioc

      最近又接手一个项目,不出所料,用到的依然是依然是ssm,简单方便。但是这次忍不住多看了看spring的东西,也好好深入学习下,今天抽空随手写了一个spring ioc的实现,下面分享给大家,只是一个简单ioc实现,没有复杂的功能,如有错误还请大家指出,互相交流,互相提高。

      Ioc的概念性的东西不再赘述,相信大家都懂。简单来说ioc容器可以看做由一下四部完成:

      1:创建容器的配置文件

      2:读取配置文件

      3:根据配置文件初始化容器

      4: 根据配置文件创建Bean并放入容器中完成容器初始化。

      用到的技术:dom4j,xpath表达式,java反射,内省

 

通俗点来说就是配置文件中保存了容器中bean的元数据,我们通过读取配置文件获得bean的详细元数据,然后通过反射和内省将对应的类实例化,对应的属性注入(赋值),然后放入容器中。下面是我简单实现的类图:


1BeanFactory定义根据beanname获取bean对象的接口

package com.tgb.main;

public interface BeanFactory {
	//根据bean的name获得bean对象
	Object getBean(String beanName);
}


2ClassPathXmlApplicationContextBeanFactory具体实现

package com.tgb.main;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.tgb.config.Bean;
import com.tgb.config.Property;
import com.tgb.config.parse.ConfigManager;
import com.tgb.utils.BeanUtils;

public class ClassPathXmlApplicationContext implements BeanFactory{
	//配置信息
	private Map<String, Bean> config;
	//用一个map来做spring的容器,放置spring所管理的对象。 
	private Map<String, Object> context=new HashMap<String,Object>();
	
	
	
	
	//在ClassPathXmlApplicationContext一创建就初始化spring容器
	
	
	@Override
	//根据bean名称获得bean实例
	public Object getBean(String beanName) {
		Object bean =context.get(beanName);
		return bean;
	}
	
	public ClassPathXmlApplicationContext(String path) {
		//1读取配置文件获取初始化的bean信息
		config = ConfigManager.getConfig(path);
		
		//2遍历配置,初始化bean
		if (config!=null) {
			for(Entry<String,Bean> en :config.entrySet()){
				//获取配置中的bean信息
				String beanName=en.getKey();
				Bean bean=en.getValue();
				
				Object exsitBean=context.get(beanName);
				//因为createBean方法中也会向context中放置bean,我们在初始化的时候先要查看是否已经存在bean
				//如果不存在再创建bean
				if (exsitBean==null) {
					//根据bean配置创建bean对象
					Object beanObj=creatBean(bean);
					//3将初始化好的bean放入容器
					context.put(beanName, beanObj);
				}

			}
		}
		 
		
		
	}
	//根据bean配置创建bean对象
	private Object creatBean(Bean bean) {
		// TODO Auto-generated method stub
		//1获得要创建的bean的class
		String className = bean.getClassName();
		Class clazz=null;
		try {
			clazz=Class.forName(className);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException("请检查bean的class配置"+className);
		}
		//将class对应的对象创建出来
		Object beanObj=null;
		try {
			 beanObj= clazz.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("bean没有空参构造"+className);
		} 
		
		//2获得bean的属性,将其注入
		
		if (bean.getProperties()!=null) {
			for(Property property:bean.getProperties()){
				//1:简单value注入
				//获取要注入的属性名称
				String name=property.getName();
				//根据属性名称获得注入属性对应的set方法
				Method setMethod = BeanUtils.getWriteMethod(beanObj,name);
				//创建一个需要注入bean中的属性
				Object parm=null;
				if (property.getValue()!=null) {
					//获取要注入的属性值
					String value=property.getValue();
					parm=value;
				
				}
			    //2其他bean的注入
				if (property.getRef()!=null) {
					//先从容器中查找当前要注入的bean是否已经创建并放入容器中
					Object exsitBean = context.get(property.getRef());
					if (exsitBean==null) {
						//如果容器中不存在,则要创建
						 exsitBean = creatBean(config.get(property.getRef()));
						 //将创建好的bean放入容器
						 context.put(property.getRef(), exsitBean);
						
					}
					 parm=exsitBean;
				}
				
				//调用set方法注入
				try {
					setMethod.invoke(beanObj, parm);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					throw new RuntimeException("bean的属性"+parm+"没有对应的set方法,或者参数不正确"+className);
				} 
			}
		}
		 
		return beanObj;
	}

}


3ConfigManager:读取配置文件,获取元数据

package com.tgb.config.parse;

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;

import com.tgb.config.Bean;
import com.tgb.config.Property;

public class ConfigManager {
	//读取配置文件,并返回结果
	public static Map<String, Bean> getConfig( String path){
		
		Map<String, Bean> map =new HashMap<String, Bean>();
		//1创建解析器
		SAXReader saxReader = new SAXReader();
		//2加载配置文件
		InputStream is=ConfigManager.class.getResourceAsStream(path);
		Document document=null;
		try {
			 document = saxReader.read(is);
		} catch (DocumentException e) {
			e.printStackTrace();
			throw new RuntimeException("请检查xml配置");
		}
		//3定义xpath表达式去除所有bean元素
		String xpath="//bean";
		//4对bean元素进行遍历
		List<Element> list =document.selectNodes(xpath);
		if (list!=null) {
			for(Element beanFile:list){
				Bean bean = new Bean();
				//将class,name等属性封装到bean对象中
				String name = beanFile.attributeValue("name");
				String className= beanFile.attributeValue("class");
				bean.setName(name);
				bean.setClassName(className);
				//获得bean元素下的所有property元素,并将其属性封装到property子元素中
				List<Element> children = beanFile.elements("property");
				if (children!=null) {
					for(Element child:children){
						Property property = new Property();
						String pName=child.attributeValue("name");
						String pValue=child.attributeValue("value");
						String pRef=child.attributeValue("ref");
						
						property.setName(pName);
						property.setRef(pRef);
						property.setValue(pValue);
						//将property对象封装到bean中
						bean.getProperties().add(property);
						
						
					}
				}
				//将bean对象封装到map中用于返回。
				
				map.put(name, bean);
			}	
			
		}

		//5返回map
		return map;
		
	}
}


4Bean,Property用于存放从配置文件中读取的元数据。

package com.tgb.config;

import java.util.ArrayList;
import java.util.List;

public class Bean {
	private String name;
	private String className;
	
	private List<Property> properties= new ArrayList<Property>();

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	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 [name=" + name + ", className=" + className + ", properties=" + properties + "]";
	}
	
	
}

package com.tgb.config;

public class Property {
	private String name;
	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;
	}
	
}


5BeanUtils根据对象和属性名获取set方法(内省)

package com.tgb.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public class BeanUtils {
	/**
	 * 
	 * @param beanObj bean对象
	 * @param name 要获得bean对象对应的属性名称
	 * @return
	 */
	public static Method getWriteMethod(Object beanObj, String name) {
		//使用内省实现(基于java反射专门用于操作bean的属性的api)
		Method method=null;
			try {
				//1:分析bean对象-->BeanInfo
				BeanInfo beanInfo = Introspector.getBeanInfo(beanObj.getClass());
				//2:根据BeanInfo获取所有属性的描述器
				PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
				//3:遍历描述器
					if (pds!=null) {
						for(PropertyDescriptor pd:pds){
							//判断当前属性是否是我们要找的属性
							String pName = pd.getName();
							if (pName.equals(name)) {
								method = pd.getWriteMethod();
								
							}
						}
					}
					
				
				//4:返回找到的set方法
					
			} catch (IntrospectionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//如果没有找到--》抛出异常,提示用户检查是否创建属性对应的set方法
			if (method==null) {
				throw new RuntimeException("请检查"+name+"属性的set方法是否创建");
			}
		
		return method;
	}

}


完整代码   点击打开链接,欢迎大家批评指正

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值