标题自定义IOC容器的基本架构:
基本思路
解析xml配置文件
根据配置的生成相应的对象
将对象存入IOC容器
IOC容器实现图解
IOC容器实现
0.将配置文件ApplicationContext.xml放在根目录下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="address" class="com.example.xmlsax_reader.entity.Address">
<property name="city" value="fuzhou"></property>
</bean>
<bean id="user" class="com.example.xmlsax_reader.entity.User">
<property name="userName" value="tom"></property>
<property name="address" ref="address"></property>
</bean>
</beans>
1.建立实体类
@Data
public class User {
private String userName;
private Address address;
}
@Data
public class Address {
private String city;
}
2.封装Bean和Property,对应配置文件中的bean节点和property节点
@Data
public class Bean {
private String id;
private String className;
private List<Property> properties = new ArrayList<Property>();
}
@Data
public class Property {
private String name;
private String value;
private String ref;
}
3.用于解析配置文件.xml的类
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;
}
}
4.初始化IOC容器,生成对象放入容器中
public interface BeanFactory {
Object getBean(String beanName);
}
public class ClassPathXmlApplicationContext implements BeanFactory {
//定义一个IOC容器
//所谓的容器,在代码中的表现形式其实就是个集合,我们使用HashMap来作为容器
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(Map.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);
}
}
BeanUtil工具类
public class BeanUtil {
/**
* 获取obj类的name属性的setter方法
* @param obj
* @param name
* @return
*/
public static Method getSetterMethod(Object obj, String name){
Method method = null;
//setter方法名称(驼峰)
name = "set"+name.substring(0,1).toUpperCase()+name.substring(1);
try {
Method[] methods = obj.getClass().getMethods();
//遍历该类的所有方法
for(int i=0;i<methods.length;i++){
Method m = methods[i];
if(m.getName().equals(name)){
method = obj.getClass().getMethod(name,m.getParameterTypes());
break;
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return method;
}
}
测试类
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 (Map.Entry<String, Bean> entry : map.entrySet()) {
System.out.println(entry.getKey()+"==="+entry.getValue());
}
}
}