美文网首页
spring(一):BeanFactory

spring(一):BeanFactory

作者: 一个_人鸭 | 来源:发表于2019-05-11 15:59 被阅读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下,这一章节实现它的两个方法

    • getBeanDefinition(String beanId),根据beanId获取bean的定义实体。
    • getBean(String beanId),根据beanId获取bean实体。

           我们来实现这两个方法,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方法只需要保证类已加载即可。


                                                                                                    生活要多点不自量力

    相关文章

      网友评论

          本文标题:spring(一):BeanFactory

          本文链接:https://www.haomeiwen.com/subject/oqixaqtx.html