spring(一):BeanFactory

2019-05-11  本文已影响0人  一个_人鸭

实现简易BeanFactory(上)

       我们在使用BeanFactory的时候最常用的便是通过它来获取bean。这一章节的目标是实现一个极简版本的BeanFactory。

可以参考gitHub:https://github.com/zmPersevere/litespring/tree/master/litespring_01

首先以使用者的角度来写测试用例:

    @Test
    public void testGetBean(){
        //根据配置文件初始化一个默认工厂
        BeanFactory factory = new DefaultBeanFactory("petstore-v1.xml");
        //通过beanId获取bean的定义
        BeanDefinition bd = factory.getBeanDefinition("petStore");
        //断言bean的ClassName正确
      Assert.assertEquals("org.litespring.service.v1.PetStoreService"
                    ,bd.getBeanClassName());
        //根据bean的id获取Bean
        PetStoreService petStore = (PetStoreService) factory.getBean("petStore");
        //断言获取到bean了。
        Assert.assertNotNull(petStore);
    }

       这里完成了测试用例的编写,每行代码注释标记的已经很明确了,这章节的目的便是让测试用例可以运行通过。

       根据测试用例来看,需要一个BeanFactory接口,DefaultBeanFactory默认的bean工厂实现,存放各种bean定义的BeanDefinition接口。至于PetStoreService则是一个用于测试的Service。

       首先来实现BeanFactory,根据spring的命名规则我们把它放到org.litespring.beans.factory下,这一章节实现它的两个方法

       我们来实现这两个方法,spring命名规则会把具体实现放到support下,我们来实现一个默认的BeanFactory->DefaultBeanFactory。

    private static final String ID_ATTRIBUTE = "id";

    private static final String CLASS_ATTRIBUTE = "class";

    private final Map<String,BeanDefinition> beanDefinitionMap =
            new ConcurrentHashMap<String, BeanDefinition>();

    public DefaultBeanFactory(String configFile) {
        loadBeanDefinition(configFile);
    }

    /**
     * 加载配置文件
     * @param configFile
     */
    private void loadBeanDefinition(String configFile){
        InputStream is = null;
        try {
            //获取默认的ClassLoader
            ClassLoader cl = ClassUtils.getDefaultClassLoader();
            //根据dom4j读取configFile
            is = cl.getResourceAsStream(configFile);
            SAXReader reader = new SAXReader();
            Document doc = reader.read(is);
            Element root = doc.getRootElement();//获取跟节点<beans>
            Iterator<Element> iter = root.elementIterator();//遍历子节点<bean>
            while (iter.hasNext()){
                Element ele = (Element)iter.next();
                String id = ele.attributeValue(ID_ATTRIBUTE);//获取<bean id>
                String beanClassName = ele.attributeValue(CLASS_ATTRIBUTE);//获取<bean class>
                //把id、class都注册到bean的定义中
                BeanDefinition bd = new GenericBeanDefinition(id,beanClassName);
                //把bean的定义放入到并发安全容器beanDefinitionMap中
                this.beanDefinitionMap.put(id,bd);
            }
        }catch (DocumentException e){
            //TODO 抛出异常,不推荐使用e.printStackTrace(),尽量用日志输出异常。
            throw new BeanDefinitionStoreException("IOException parsing XML document" , e);
        }finally {
            if (is != null){
                 try {
                     is.close();//关闭流
                 }catch (IOException e){
                     e.printStackTrace();
                 }
            }
        }
    }

    /**
     * 通过beanId获取bean的定义
     * @param beanId
     * @return
     */
    public BeanDefinition getBeanDefinition(String beanId) {
        return this.beanDefinitionMap.get(beanId);
    }

    /**
     * 通过BeanId获取bean
     * @param beanId
     * @return
     */
    public Object getBean(String beanId) {
        //根据bean的id获取bean的定义
        BeanDefinition bd = this.getBeanDefinition(beanId);
        if (bd == null){
            throw new BeanCreationException("Bean Definition does not exist");
        }
        //注意这里使用的是ClassLoader,ClassLoader只是把class加载进来并没有初始化,在使用时在进行初始化。
        //而Class.forName会把class加载、连接、初始化。
        ClassLoader cl = ClassUtils.getDefaultClassLoader();
        //这里获取的实际上就是<bean class="">中的class内容
        String beanClassName = bd.getBeanClassName();
        try {
            Class<?> clz = cl.loadClass(beanClassName);
            //只能调用无参构造函数且是publish的.
            //newInstance必须保证这个类已经被加载了。
            return clz.newInstance();
        }catch (Exception e){
            throw new BeanCreationException("create bean for " + beanClassName + " failed ", e);
        }
    }

上述的实现注释我已经写的很详尽了,其中在解析xml文件是通过dom4j这个jar包进行解析的,而ClassUtils则是从spring中直接拿过来的,还差一个BeanDefintion没有实现,下面便开始实现BeanDefintion的基本功能。

       首先实现BeanDefintion这个接口,放到了org.litespring.beans包下,这一版本我们赋予其的功能是获取当前bean的className。接下来实现BeanDefintion的实现类GenericBeanDefinition。

private String id;
private String beanClassName;

public GenericBeanDefinition(String id,String beanClassName) {
    this.id = id;
    this.beanClassName = beanClassName;
}

/**
 * 获取bean定义中的className
 * @return className
 */
public String getBeanClassName() {
    return this.beanClassName;
}

这里的实现就很简单了,只是把xml的各种属性转换为实体。
上述实现便可以通过我们最开始的测试用例了。在git上还完善了异常处理,这里的代码最好自己手打一下,而不是仅仅看看。

       这个简易的BeanFactory我碰到的问题是对于class.newInstance()的使用,我上班路上偶然看到了一篇博客,对于newInstance方法的使用必须保证class已加载、连接,而实际使用newInstance方法只需要保证类已加载即可。


                                                                                                生活要多点不自量力

上一篇下一篇

猜你喜欢

热点阅读