专栏首页java干货Small Spring系列一:BeanFactory(一)

Small Spring系列一:BeanFactory(一)

人生如逆旅,我亦是行人。

前言

Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。

准备

  • bean-v1.xml配置bean的信息
  • BeanDefinition用于存放bean的定义
  • BeanFactory获取bean的信息,实例化bean`
  • BeanFactoryTest测试BeanFactory是否可用

bean-v1.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <bean id = "nioCoder"
        class = "com.niocoder.service.v1.NioCoderService">
    </bean>

    <bean id ="invalidBean"
        class="xxx.xxx">
    </bean>
</beans>

BeanDefinition

bean-v1.xml中定义了每个bean,但这些信息我们该如何存储呢? spring是通过BeanDefinition接口来描述bean的定义

BeanDefinition

package com.niocoder.beans;

/**
 * bean.xml bean的定义
 * @author zhenglongfei
 */
public interface BeanDefinition {

    /**
     * 获取bean.xml中 bean的全名 如 "com.niocoder.service.v1.NioCoderService"
     * @return
     */
    String getBeanClassName();
}

GenericBeanDefinition

GenericBeanDefinition实现了BeanDefinition接口

package com.niocoder.beans.factory.support;


import com.niocoder.beans.BeanDefinition;

/**
 * BeanDefinition 实现类
 *
 * @author zhenglongfei
 */
public class GenericBeanDefinition implements BeanDefinition {

    private String id;
    private String beanClassName;

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

    public String getBeanClassName() {
        return this.beanClassName;
    }
}

BeanFactory

我们已经使用BeanDefinition来描述bean-v1.xmlbean的定义,下面我们使用BeanFactory来获取bean的实例

BeanFactory

package com.niocoder.beans.factory;

import com.niocoder.beans.BeanDefinition;

/**
 * 创建bean的实例
 * @author zhenglongfei
 */
public interface BeanFactory {

    /**
     * 获取bean的定义
     * @param beanId
     * @return
     */
    BeanDefinition getBeanDefinition(String beanId);

    /**
     * 获取bean的实例
     * @param beanId
     * @return
     */
    Object getBean(String beanId);
}

DefaultBeanFactory

DefaultBeanFactory实现了BeanFactory接口

package com.niocoder.beans.factory.support;

import com.niocoder.beans.BeanDefinition;
import com.niocoder.beans.factory.BeanCreationException;
import com.niocoder.beans.factory.BeanDefinitionStoreException;
import com.niocoder.beans.factory.BeanFactory;
import com.niocoder.util.ClassUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * BeanFactory的默认实现类
 *
 * @author zhenglongfei
 */
public class DefaultBeanFactory implements BeanFactory {

    private static final String ID_ATTRIBUTE = "id";

    private static final String CLASS_ATTRIBUTE = "class";
    /**
     * 存放BeanDefinition
     */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /**
     * 根据文件名称加载,解析bean.xml
     *
     * @param configFile
     */
    public DefaultBeanFactory(String configFile) {
        loadBeanDefinition(configFile);
    }

    /**
     * 具体解析bean.xml的方法 使用dom4j
     *
     * @param configFile
     */
    private void loadBeanDefinition(String configFile) {
        ClassLoader cl = ClassUtils.getDefaultClassLoader();
        try (InputStream is = cl.getResourceAsStream(configFile)) {
            SAXReader reader = new SAXReader();
            Document doc = reader.read(is);

            Element root = doc.getRootElement();
            Iterator<Element> elementIterator = root.elementIterator();
            while (elementIterator.hasNext()) {
                Element ele = elementIterator.next();
                String id = ele.attributeValue(ID_ATTRIBUTE);
                String beanClassName = ele.attributeValue(CLASS_ATTRIBUTE);
                BeanDefinition bd = new GenericBeanDefinition(id, beanClassName);
                this.beanDefinitionMap.put(id, bd);
            }
        } catch (Exception e) {
            throw new BeanDefinitionStoreException("IOException parsing XML document", e);
        }
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanId) {
        return this.beanDefinitionMap.get(beanId);
    }

    @Override
    public Object getBean(String beanId) {
        BeanDefinition bd = this.getBeanDefinition(beanId);
        if (bd == null) {
            throw new BeanCreationException("BeanDefinition does not exist");
        }
        ClassLoader cl = ClassUtils.getDefaultClassLoader();

        String beanClassName = bd.getBeanClassName();
        try {
            // 使用反射创建bean的实例,需要对象存在默认的无参构造方法
            Class<?> clz = cl.loadClass(beanClassName);
            return clz.newInstance();
        } catch (Exception e) {
            throw new BeanCreationException("Bean Definition does not exist");
        }
    }
}

BeanFactoryTest

以上,我们已经创建了bean.xmlBeanDefinition来描述bean的定义,并且使用BeanFactory来获取bean的实例。下面我们来测试一下BeanFactory是否可用。

package com.niocoder.test.v1;

import com.niocoder.beans.BeanDefinition;
import com.niocoder.beans.factory.BeanCreationException;
import com.niocoder.beans.factory.BeanDefinitionStoreException;
import com.niocoder.beans.factory.BeanFactory;
import com.niocoder.beans.factory.support.DefaultBeanFactory;
import com.niocoder.service.v1.NioCoderService;
import org.junit.Assert;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

/**
 * BeanFactory 测试类
 */
public class BeanFactoryTest {

    /**
     * 测试获取bean
     */
    @Test
    public void testGetBean() {
        BeanFactory factory = new DefaultBeanFactory("bean-v1.xml");
        BeanDefinition bd = factory.getBeanDefinition("nioCoder");

        assertEquals("com.niocoder.service.v1.NioCoderService", bd.getBeanClassName());

        NioCoderService nioCoderService = (NioCoderService) factory.getBean("nioCoder");

        assertNotNull(nioCoderService);
    }

    /**
     * 测试无效的bean
     */
    @Test
    public void testInvalidBean() {
        BeanFactory factory = new DefaultBeanFactory("bean-v1.xml");
        try {
            factory.getBean("invalidBean");
        } catch (BeanCreationException e) {
            return;
        }

        Assert.fail("expect BeanCreationException ");
    }

    /**
     * 测试无效的xml
     */
    @Test
    public void testInvalidXML() {
        try {
            new DefaultBeanFactory("xxx.xml");
        } catch (BeanDefinitionStoreException e) {
            return;
        }

        Assert.fail("expect BeanDefinitionStoreException ");
    }
}

代码下载

类图

参考资料


从零开始造Spring

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Small Spring系列二:BeanFactory(二)

    在Small Spring系列一:BeanFactory(一)中,我们用DefaultBeanFactory读取bean.xlm中的bean信息,并且也实现了B...

    java干货
  • Small Spring系列八:aop (一)

    我们终于不辱使命完成了Spring的注解注入,接下来我们要实现更为关键aop部分,在这开始之前你需要了解什么事aop以及aop的常用术语,参考链接

    java干货
  • Small Spring系列十一:aop (四)

    在前四篇,我们已经实现了使用Cglib实现了aop动态代理。但是在spring中如果代理对象实现了接口,则默认使用jdk动态代理,也可以通过配置强制使用cgli...

    java干货
  • Small Spring系列九:aop (二)

    在Small Spring系列八:aop (一)中,我们实现了Pointcut和MethodLocatingFactory,Pointcut根据给定一个类的方法...

    java干货
  • 《Spring 手撸专栏》第 2 章:小试牛刀,实现一个简单的Bean容器!

    你总会在小傅哥的文章前言里,发现一些关于成长、学习、感悟以及对当篇内容的一个介绍,其实之所以写这样的铺垫性内容,主要是为了让大家对接下来的内容学习有一个较轻松的...

    小傅哥
  • 《Spring 手撸专栏》第 2 章:小试牛刀(让新手能懂),实现一个简单的Bean容器

    你总会在小傅哥的文章前言里,发现一些关于成长、学习、感悟以及对当篇内容的一个介绍,其实之所以写这样的铺垫性内容,主要是为了让大家对接下来的内容学习有一个较轻松的...

    小傅哥
  • 《Spring 手撸专栏》第 9 章:虎行有雨,定义标记类型Aware接口,实现感知容器对象

    大佬的代码,就像“赖蛤蟆泡青蛙,长的丑玩的花”:一个类实现了多个接口、继承的类又继承了其他类、接口还可以和接口继承、实现接口的抽象类再由类实现抽象类方法、类A继...

    喜欢天文的pony站长
  • 《Spring 手撸专栏》第 7 章:所向披靡,实现应用上下文,自动识别、资源加载、扩展机制

    依照项目落地经验来看,我们在承接紧急的产品需求时候,通常会选择在原有同类项目中进行扩展,如果没有相关类型项目的储备,也可能会选择临时搭建出一个工程来实现产品的需...

    小傅哥
  • 手写Spring,定义标记类型Aware接口,实现感知容器对象

    大佬的代码,就像 “赖蛤蟆泡青蛙,张的丑玩的花” :一个类实现了多个接口、继承的类又继承了其他类、接口还可以和接口继承、实现接口的抽象类再由类实现抽象类方法、类...

    小傅哥
  • 我想挑战下我的软肋,动手实现个Spring应用上下文!

    依照项目落地经验来看,我们在承接紧急的产品需求时候,通常会选择在原有同类项目中进行扩展,如果没有相关类型项目的储备,也可能会选择临时搭建出一个工程来实现产品的需...

    小傅哥
  • 《Spring 手撸专栏》第 9 章:虎行有雨,定义标记类型Aware接口,实现感知容器对象

    大佬的代码,就像“赖蛤蟆泡青蛙,长的丑玩的花”:一个类实现了多个接口、继承的类又继承了其他类、接口还可以和接口继承、实现接口的抽象类再由类实现抽象类方法、类A继...

    小傅哥
  • 《Spring 手撸专栏》第 15 章:万人之敌,通过注解给属性注入配置和Bean对象

    你听过扰动函数吗?你写过斐波那契(Fibonacci)散列吗?你实现过梅森旋转算法吗?怎么 没听过这些写不了代码吗!不会的,即使没听过你一样可以写的了代码,比如...

    小傅哥
  • Spring的底层源码分析

    Spring 启动时读取应用程序提供的Bean 配置信息,并在Spring 容器中生成一份相应的Bean 配置注册表,然后根据这张注册表实例化Bean,装配好B...

    时间静止不是简史
  • Spring之Aware接口介绍

      在Bean对象的生命周期的方法中有好几个接口是Aware接口的子接口,所以弄清楚Aware接口对于理解Spring框架还是很有帮助的。

    用户4919348
  • 《Spring 手撸专栏》第 4 章:崭露头角,基于Cglib实现含构造函数的类实例化策略

    你觉得自己的技术什么时候得到了快速的提高,是CRUD写的多了以后吗?想都不要想,绝对不可能!CRUD写的再多也只是能满足你作为一个搬砖工具人,敲击少逻辑流水代码...

    小傅哥
  • 三条路线告诉你如何掌握Spring IoC容器的核心原理

    前三篇已经从历史的角度和大家一起探讨了为什么会有Spring,Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国。本节的主...

    Java后端技术
  • 《Spring 手撸专栏》第 3 章:初显身手,运用设计模式,实现 Bean 的定义、注册、获取

    讲道理,无论产品功能是否复杂,都有很大一部分程序员会写出一堆 if...else 来完成开发并顺利上线。这主要是原因没法预见当前的需求,发展是否长远、流量是否庞...

    小傅哥
  • 《Spring 手撸专栏》第 11 章:更上层楼,基于观察者实现,容器事件和事件监听器

    摔杯为号、看我眼色行事、见南面火起,这是在嘎哈么?这其实是在通过事物传播进行解耦引线和炸弹,仅仅是这样的一个解耦,它放到了多少村夫莽汉,劫了法场,篡了兵权!

    喜欢天文的pony站长
  • Spring 的核心组件详解

    Bean组件是 Spring核心中的重点,Spring 就是面向Bean编程的(Bean Oriented Programming:BOP)就像Object 对...

    Java架构师必看

扫码关注云+社区

领取腾讯云代金券