从零开始造Spring01---BeanFactory的学习

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 这是学习刘老师的《从零开始造Spring》的第一篇学习笔记。

前言


这是学习刘老师的《从零开始造Spring》的第一篇学习笔记。

主要分为两大块 :

一、解析xml文件,初始化BeanDefinition,

二、生成Bean的实例对象

第一堂课比较简单,我们首先从测试用例出发


测试用例


 

@Test
    public void testGetBean() {
        // 解析xml文件
        reader.loadBeanDefinitions(new ClassPathResource("petstore-v1.xml"));
        // 获得BeanDefinition
        BeanDefinition bd = factory.getBeanDefinition("petStore");
        assertTrue(bd.isSingleton());
        assertFalse(bd.isPrototype());
        assertEquals(BeanDefinition.SCOPE_DEFAULT,bd.getScope());
        assertEquals("org.litespring.service.v1.PetStoreService",bd.getBeanClassName());
        PetStoreService petStore = (PetStoreService)factory.getBean("petStore");
        assertNotNull(petStore);
        PetStoreService petStore1 = (PetStoreService)factory.getBean("petStore");
        assertTrue(petStore.equals(petStore1));
    }


解析xml 文件


我们是通过XmlBeanDefinitionReader 来解析xml 文件的。采用dom4j的方式解析。核心代码如下:


public void loadBeanDefinitions(Resource resource){
        InputStream is = null;
        try{            
            is = resource.getInputStream();
            SAXReader reader = new SAXReader();
            Document doc = reader.read(is);
            Element root = doc.getRootElement(); //<beans>
            Iterator<Element> iter = root.elementIterator();
            while(iter.hasNext()){
                Element ele = (Element)iter.next();
                String id = ele.attributeValue(ID_ATTRIBUTE);
                String beanClassName = ele.attributeValue(CLASS_ATTRIBUTE);
                BeanDefinition bd = new GenericBeanDefinition(id,beanClassName);
                if (ele.attribute(SCOPE_ATTRIBUTE)!=null) {                 
                    bd.setScope(ele.attributeValue(SCOPE_ATTRIBUTE));                   
                }
                this.registry.registerBeanDefinition(id, bd);
            }
        } catch (Exception e) {     
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + resource.getDescription(),e);
        }finally{
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {                   
                    e.printStackTrace();
                }
            }
        }
    }


注意: Resource类主要是用来获取xml 的文件流,它有两个实现类这里写ClassPathResource 以及FileSystemResource ,第一个实现类主要是获取类路径下的文件,也就是说该xml文件在项目中。第二个实现类主要是用于获取指定路径下的文件。该文件可能不在项目中。

public interface Resource {
    public InputStream getInputStream() throws IOException;
    public String getDescription();
}


生成Bean的实例

首先,传入getBean方法传入beanID,在该方法中 首先拿到BeanDefinition,然后,根据beanClassName 字段通过反射的方式生成对应的bean 实例。

核心代码如下:


 

public Object getBean(String beanID) {
        BeanDefinition bd = this.getBeanDefinition(beanID);
        if(bd == null){
            return null;
        }
        //bd是单例
        if(bd.isSingleton()){
            Object bean = this.getSingleton(beanID);
            if(bean == null){
                bean = createBean(bd);
                this.registerSingleton(beanID, bean);
            }
            return bean;
        } 
        return createBean(bd);
    }
    private Object createBean(BeanDefinition bd) {
        ClassLoader cl = this.getBeanClassLoader();
        String beanClassName = bd.getBeanClassName();
        try {
            Class<?> clz = cl.loadClass(beanClassName);
            return clz.newInstance();
        } catch (Exception e) {         
            throw new BeanCreationException("create bean for "+ beanClassName +" failed",e);
        }   
    }


其中,DefaultSingletonBeanRegistry 类中定义了一个ConcurrentHashMap,将生成bean实例放在该Map中,在调用registerSingleton方法时,首先会根据beanName检查是否已存在实例,如果存在在抛出异常。否则,将新生成的实例放入该Map中。


private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
    public void registerSingleton(String beanName, Object singletonObject) {
        Assert.notNull(beanName, "'beanName' must not be null");
        Object oldObject = this.singletonObjects.get(beanName);
        if (oldObject != null) {
            throw new IllegalStateException("Could not register object [" + singletonObject +
                    "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
        }
        this.singletonObjects.put(beanName, singletonObject);
    }
    public Object getSingleton(String beanName) {
        return this.singletonObjects.get(beanName);
    }

源代码:刘老师的源码地址


相关文章
|
2月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
73 9
|
3月前
|
前端开发 Java 数据库
SpringBoot学习
【10月更文挑战第7天】Spring学习
46 9
|
2月前
|
Java Kotlin 索引
学习Spring框架特性及jiar包下载
Spring 5作为最新版本,更新了JDK基线至8,修订了核心框架,增强了反射和接口功能,支持响应式编程及Kotlin语言,引入了函数式Web框架,并提升了测试功能。Spring框架可在其官网下载,包括文档、jar包和XML Schema文档,适用于Java SE和Java EE项目。
36 0
|
3月前
|
XML Java 数据格式
Spring学习
【10月更文挑战第6天】Spring学习
30 1
|
3月前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
120 2
|
3月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
318 1
|
3月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
42 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
3月前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
47 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
3月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
369 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
3月前
|
Java 关系型数据库 MySQL
springboot学习四:springboot链接mysql数据库,使用JdbcTemplate 操作mysql
这篇文章是关于如何使用Spring Boot框架通过JdbcTemplate操作MySQL数据库的教程。
137 0
springboot学习四:springboot链接mysql数据库,使用JdbcTemplate 操作mysql