使用过Spring框架进行开发的朋友都知道,Spring实例化各种JavaBean,可以通过基于配置文件的方式也可以通过基于注解的方式,让Spring帮我们实例化对应的Bean。
org.springframework.beans.factory.BeanFactory 是Spring IoC容器的实际代表者,IoC容器负责容纳此前所描述的bean,并对bean进行管理。
在Spring中,BeanFactory是IoC容器的核心接口。 它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory实现, XmlBeanFactory就是最常用的一个。该实现将以XML方式描述组成应用的对象 以及对象间的依赖关系。XmlBeanFactory类将获取此XML配 置元数据,并用它来构建一个完全可配置的系统或应用。
从上图可以看到,Spring IoC容器将读取配置元数据; 并通过它对应用中各个对象进行实例化、配置以及组装。通常情况下我们使用简单直观 的XML来作为配置元数据的描述格式。在XML配置元数据中我们可以对那些我们希望通过 Spring IoC容器管理的bean进行定义。
Spring读取配置文件是用反射机制读的jvm的.class文件(采用classLoader调用类似的方法),用这种方法能取消类对象的创建与取出,依靠bean工厂就可以实现。
这里采用dom4j/jdom方式模拟Spring的工作原理。
查看Spring的API可以知道,Spring生产Bean的工厂为BeanFactory,所以我们首先创建一个模拟Spring的Bean工厂的BeanFactory接口:
1.BeanFactory接口
public interface BeanFactory {
public Object getBean(String name);
}
基于读取配置文件来实例化JavaBean,那自然少不了配置文件。这里自定义为beans.xml,没有用默认的applicationContext.xml命名。
2.beans.xml文件
<beans>
<bean id="u" class="com.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.service.UserService">
<property name="UserDao" bean="u"></property>
</bean>
</beans>
property标签可以实现bean的自动装配,依据bean的id将属性注入。property指set方法,即UserService中有个方法setUserDao(),而该方法调用时应该将id名为“u”的bean传进来。
具体的实现方法是:
UserService service = (UserService) factory.getBean(“userService”);
UserDao dao = (UserDao) factory.getBean(“u”);
测试类从工厂里取出
Spring需要知道开发者写的是哪个配置文件,所以需要ClassPathXmlApplicationContext来读取配置文件,这里同样对ClassPathXmlApplicationContext进行模拟。
3.ClassPathXmlApplicationContext 类
//模拟Spring的ClassPathXmlApplicationContext,它实现了BeanFactory接口
public class ClassPathXmlApplicationContext implements BeanFactory{
//模拟Spring装载bean实例的容器
private Map<String,Object> beans = new HashMap<String,Object>();
public ClassPathXmlApplicationContext() throws Exception{
//创建SAXBuilder组建器
SAXBuilder sb = new SAXBuilder();
//加载配置文件
Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml"));
//解析配置文件
Element root = doc.getRootElement();
List list = root.getChildren("bean");
//遍历文件各个bean节点
for(int i=0;i<list.size();i++){
Element element = (Element) list.get(i);
String id = element.getAttributeValue("id");
String clazz = element.getAttributeValue("class");
System.out.println(id+":"+clazz);
//根据类路径clazz创建该类的对象,实例化bean
Object o = Class.forName(clazz).newInstance();
//将实例化好的bean存入容器(map)中
beans.put(id, o);
//遍历每个bean下的property节点
for(Element propertyElement:(List<Element>)element.getChildren("property")){
String name = propertyElement.getAttributeValue("name");
String bean = propertyElement.getAttributeValue("bean");
根据property节点的bean名称,从容器map中取出实例化的bean
Object beanObject = beans.get(bean);
//取得name值对应的set方法
String methodName = "set"+name.substring(0, 1).toUpperCase()+name.substring(1);
System.out.println("method name="+methodName);
/**
* 获取set方法所属的类所实现的接口数组。o为主bean,beanObject为需要装配的bean
* beanObject对象为UserDaoImpl对象,获取其接口数组的第一个值(这里只实现了UserDao接口)
*/
Method m = o.getClass().getMethod(methodName,beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject);
}
}
}
@Override
public Object getBean(String name) {
return beans.get(name);
}
}
4.建立测试类
BeanFactory factory = new ClassPathXmlApplicationContext(“beans.xml”);