- 前言
- 自定义IOC容器的基本架构
- 架构图解
- 基本思路
- IOC容器实现图解
- IOC容器实现
- 创建一个java工程
- 导入 dom4jjar 和 jaxenjar
- 创建测试用的类
- 创建ApplicationContextxml
- XmlConfig
- BeanFactory
- 测试
- demo下载
前言
spring的优点和实现原理不在此详述,想要自己动手写一个简单的IOC容器,要求各位对spring有一定的了解或者使用过。
自定义IOC容器的基本架构
架构图解
基本思路
- 解析xml配置文件
- 根据配置的生成相应的对象
- 将对象存入IOC容器
IOC容器实现图解
IOC容器实现
要求:
1. 我们使用dom4j.jar 和 jaxen.jar 来解析xml文件(自行下载,或在文章末尾下载demo)
2. 需要懂得Java的反射机制
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 + "]";
}
}
4. 创建ApplicationContext.xml
将配置文件ApplicationContext.xml放在src下
<?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>
5. XmlConfig
封装Bean和Property,对应配置文件中的bean节点和property节点
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;
}
}
6. BeanFactory
定义BeanFactory接口
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){
//获取属性对应的getter方法
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){
//获取属性对应的getter方法
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);
}
}
测试
package com.myspring.test;
import java.util.Map;
import java.util.Map.Entry;
import com.myspring.bean.Address;
import com.myspring.bean.User;
import com.myspring.config.Bean;
import com.myspring.config.XmlConfig;
import com.myspring.core.BeanFactory;
import com.myspring.core.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
testIOC();
//testConfig();
}
/**
* 测试IOC容器
*/
private static void testIOC(){
BeanFactory bf = new ClassPathXmlApplicationContext("/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("/ApplicationContext.xml");
for (Entry<String, Bean> entry : map.entrySet()) {
System.out.println(entry.getKey()+"==="+entry.getValue());
}
}
}
demo下载
点击下载