自己实现简单Spring Ioc
最近又接手一个项目,不出所料,用到的依然是依然是ssm,简单方便。但是这次忍不住多看了看spring的东西,也好好深入学习下,今天抽空随手写了一个spring ioc的实现,下面分享给大家,只是一个简单ioc实现,没有复杂的功能,如有错误还请大家指出,互相交流,互相提高。
Ioc的概念性的东西不再赘述,相信大家都懂。简单来说ioc容器可以看做由一下四部完成:
1:创建容器的配置文件
2:读取配置文件
3:根据配置文件初始化容器
4: 根据配置文件创建Bean并放入容器中完成容器初始化。
用到的技术:dom4j,xpath表达式,java反射,内省
通俗点来说就是配置文件中保存了容器中bean的元数据,我们通过读取配置文件获得bean的详细元数据,然后通过反射和内省将对应的类实例化,对应的属性注入(赋值),然后放入容器中。下面是我简单实现的类图:
1:BeanFactory定义根据bean的name获取bean对象的接口
package com.tgb.main;
public interface BeanFactory {
//根据bean的name获得bean对象
Object getBean(String beanName);
}
2:ClassPathXmlApplicationContext是BeanFactory具体实现
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;
}
}
3:ConfigManager:读取配置文件,获取元数据
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;
}
}
4:Bean,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;
}
}
5:BeanUtils根据对象和属性名获取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;
}
}