感谢作者:https://blog.csdn.net/aa1358075776/article/details/80188952
spring 原理,好处,以后在详细述说,这里主要是想要手动写一个简单的IOC容器。借此可以更好的了解spring .
spring 的架
基本思路
1. 解析配置文件
2. 根据配置文件申城相应的对象
3. 将文件放入IOC容器中
IOC 容器实现图解
1. 创建一个java 工程,
2. 导入dom4j.jar,jaxen.jar
3. 创建测试用的类
package com.myspring.bean; 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 + "]"; }
package com.myspring.bean; 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 + "]"; }
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="address" class="com.myspring.bean.Address"> <property name="city" value="fuzhou"></property> </bean> <bean id="user" class="com.myspring.bean.User"> <property name="userName" value="tom"></property> <property name="address" ref="address"></property> </bean> </beans>
package com.myspring.config; import java.util.ArrayList; import java.util.List; /** * 封装配置文件中的bean节点 * @author 周君 */ 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 + "]"; }
package com.myspring.config; /** * 封装配置文件中的property节点 * * @author 周君 */ 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; }
package com.myspring.config; 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; /** * 读取xml配置文件的类 * @author 周君 */ 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; }
package com.myspring.core; public interface BeanFactory { Object getBean(String beanName); }
实现类,作用是初始化IOC容器,生成对象放入容器中,
所谓的容器,在代码的表现中其实是个集合,我们用hashmap 来作为容器。
package com.myspring.core; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.myspring.config.Bean; import com.myspring.config.Property; import com.myspring.config.XmlConfig; import com.myspring.utils.BeanUtil; 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 getMethod = BeanUtil.getSetterMethod(object,p.getName()); try { //调用set方法注入 getMethod.invoke(object, p.getValue()); } catch (Exception e) { throw new RuntimeException("属性名称不合法或者没有相应的getter方法:"+p.getName()); } } //情况二:配置文件中使用的是ref属性注入 if(p.getRef() != null){ //获取属性对应的setter方法 Method getMethod = BeanUtil.getSetterMethod(object,p.getName()); //从容器中找到依赖的对象 Object obj = ioc.get(p.getRef()); if(obj == null){ throw new RuntimeException("没有找到依赖的对象:"+p.getRef()); }else{ //调用set方法注入 try { getMethod.invoke(object, obj); } catch (Exception e) { throw new RuntimeException("属性名称不合法或者没有相应的getter方法:"+p.getName()); } } } } } return object; } @Override public Object getBean(String beanName) { return ioc.get(beanName); } }