【Spring5源码解析】

使用xml配置Bean的示例

定义一个类BookServiceImpl

public class BookServiceImpl implements BookService{
    @Override
    public double getBookPrice() {
        return 58.8;
    }
}

定义另一个类PressServiceImpl依赖BookServiceImpl

public class PressServiceImpl implements PressService{

    private BookService bookService;

    public void setBookService(BookService bookService) {
        this.bookService = bookService;
    }

    @Override
    public String say() {
        return "本书的价格是:"+bookService.getBookPrice();
    }
}

定义spring-chapter2.xml文件

<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="bookService" class="com.tool.generate.s0620.BookServiceImpl"></bean>
    <bean id="pressService" class="com.tool.generate.s0620.PressServiceImpl">
        <property name="bookService" ref="bookService"></property>
    </bean>
</beans>
 @Test
    public void testGet(){
        ApplicationContext applicationContext=
                new FileSystemXmlApplicationContext("classpath:spring-chapter2.xml");
        PressService pressService=(PressService)applicationContext.getBean("pressService");

        String price=pressService.say();
        System.out.println(price);
    }

运行结果如下:
本书的价格是:58.8
从上面的示例可以看出,采用xml配置Bean的方式,入口类是FileSystemXmlApplicationContext,我们就先从该类来分析Spring

解析源码1-FileSystemXmlApplicationContext构造函数

执行的是FileSystemXmlApplicationContext构造函数

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
        super(parent);//初始化父容器AbstractApplicationContext
        this.setConfigLocations(configLocations);//设置资源文件的位置
        if (refresh) {
            this.refresh();//使用核心方法refresh(),其实是在超类AbstractApplicationContext中定义的一个模版方法(模版方法模式)
        }
    }

查看FileSystemXmlApplicationContext类图
类图
由类关系图可知,ClassPathXmlApplicationContext 最顶层父类接口有七个:

  1. BeanFactory

BeanFactory 是访问 Spring Bean 容器的根接口;定义根据 Bean name 获取 Bean 的接口;

  1. Aware

1.Spring 的依赖注入最大亮点就是所有的 Bean 对 Spring 容器的存在是没有意识的;
2.但是在实际项目中,我们不可避免的要用到 Spring 容器本身提供的资源,这时候要让 Bean 主动意识到 Spring 容器的存在,才能调用 Spring 所提供的资源,这就是 Spring Aware;

  1. LifeCycle

1.定义启动/停止生命周期控制方法的通用接口;

  1. InitializingBean

1.Spring 提供的拓展性接口,InitializingBean 接口为 Bean 提供了属性初始化后的处理方法,它只有一个 afterPropertiesSet() 方法,凡是继承该接口的类,在 Bean 的属性初始化后都会执行该方法,进行其总体配置和最终初始化验证;

  1. ResourceLoader

1.用于加载资源(例如,类路径或文件系统资源)的策略接口;
1.Spring 中整合了获取资源的工具,就是使用 Resource 接口。此接口是 Spring 为了统一读取诸如本地文件、classpath 项目路径下的文件、url 互联网上的文件等不同类型渠道的资源,封装隐藏如打开流、关闭流、报错处理等大量重复模板代码,而专程设计提供的接口类。
2.而 Spring 框架为了更方便的获取资源,尽量弱化程序员对各个 Resource 接口实现类的感知与分辨,降低学习与使用成本,定义了另一个接口,就是:ResourceLoader接口。

  1. ApplicationEventPublisher

1.封装事件发布功能的接口;
2.函数式接口;

  1. AutoCloseable

1.当一个资源类实现了该接口 close 方法,在使用 try-with-resources 语法创建的资源抛出异常后,JVM 会自动调用 close 方法进行资源释放;当没有抛出异常正常退出 try 代码块时也会自动调用 close 方法。像数据库链接类 Connection,io 类 InputStream 或 OutputStream 都直接或者间接实现了该接口;

以上内容来自:https://blog.csdn.net/weixin_39651041/article/details/129674784

1、 super(parent);解析

super(parent);执行如下
目的:初始化AbstractApplicationContext容器

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
        this();
        this.setParent(parent);
    }
public AbstractApplicationContext() {
        this.logger = LogFactory.getLog(this.getClass());
        this.id = ObjectUtils.identityToString(this);
        this.displayName = ObjectUtils.identityToString(this);
        this.beanFactoryPostProcessors = new ArrayList();
        this.active = new AtomicBoolean();
        this.closed = new AtomicBoolean();
        this.startupShutdownMonitor = new Object();
        this.applicationStartup = ApplicationStartup.DEFAULT;
        this.applicationListeners = new LinkedHashSet();
        this.resourcePatternResolver = this.getResourcePatternResolver();
    }
    public void setParent(@Nullable ApplicationContext parent) {
        this.parent = parent;
        if (parent != null) {
            Environment parentEnvironment = parent.getEnvironment();
            if (parentEnvironment instanceof ConfigurableEnvironment) {
                this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
            }
        }

    }

2、this.setConfigLocations(configLocations);

AbstractRefreshableConfigApplicationContext 设置资源文件的位置

3、refresh()解析

ConfigurableApplicationContext类定义了

void refresh() throws BeansException, IllegalStateException;

抽象类AbstractApplicationContext实现了ConfigurableApplicationContext接口,实现了refresh();

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var10) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }

                this.destroyBeans();
                this.cancelRefresh(var10);
                throw var10;
            } finally {
                this.resetCommonCaches();
                contextRefresh.end();
            }

        }
    }

AbstractApplicationContext.refresh()方法是个模版方法,定义了需要执行的一些步骤。并不是实现了所有的逻辑,只是充当了一个模版,由其子类去实现更多个性化的逻辑。
最核心的两步如下:1、创建BeanFactory:
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
2、实例化Bean:
this.finishBeanFactoryInitialization(beanFactory);

ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();方法有两个步:
1、this.refreshBeanFactory();刷新BeanFactory;
这个方法的定义是在 AbstractApplicationContext中,是一个抽象方法,也是一个模版方法,需要AbstractApplicationContext的子类来实现逻辑。其具体实现是其子类AbstractRefreshableApplicationContext中完成的。refreshBeanFactory()方法实现的部分代码如下:

protected final void refreshBeanFactory() throws BeansException {
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
            this.loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        } catch (IOException var2) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
        }
    }

可以发现,在refreshBeanFactory()方法的实现中,首先检查当前上下文是否已经存在BeanFactory。如果已经存在 BeanFactory,先销毁 Bean 和 BeanFactory,然后创建新的 BeanFactory。DefaultListableBeanFactory beanFactory= createBeanFactory();这行代码只是创建了一个空的BeanFactory,其中没有任何Bean。因此refreshBeanFactory()方法的核心功能是在loadBeanDefinitions(beanFactory);这行代码中实现的。进入 loadBeanDefinitions(beanFactory)方法进行分析。
AbstractRefreshableApplicationContext类的抽象方法

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;

此方法是抽象方法,需要其子类实现。其具体实现是在AbstractXmlApplicationContext类中。其方法实现如下所示:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        this.loadBeanDefinitions(beanDefinitionReader);
    }

loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法中,通过上一步创建的空的BeanFactory 来创建一个XmlBeanDefinitionReader对象。XmlBeanDefinitionReader 是用来解析 XML中定义的 bean 的。下面重点讲解 loadBeanDefinitions(beanDefinitionReader)方法,这是一个重载的方法,这个方法的入参是刚刚生成的XmlBeanDefinitionReader对象。下面将进入重载的loadBeanDefinitions方法进行分析,代码如下:

 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }

    }

这个方法主要功能是解析资源文件的位置,然后调用XmlBeanDefinitionReader 对象的
loadBeanDefinitions 方法解析 Bean 的定义。核心逻辑reader.loadBeanDefinitions(configLocations);XmlBeanDefinitionReader是AbstractBeanDefinitionReader的子类,所以reader.loadBeanDefinitions(configLocations)会调用其父类的方法 loadBeanDefinitions。分析AbstractBeanDefinitionReader类的loadBeanDefinitions(String… locations)

 public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int count = 0;
        String[] var3 = locations;
        int var4 = locations.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String location = var3[var5];
            count += this.loadBeanDefinitions(location);
        }

        return count;
    }

可以发现 loadBeanDefinitions(String… locations)方法会遍历资源数组,最终会调用重载方法loadBeanDefinitions(String location,@Nullable SetactualResources),重载方法的部分实现代码如下:
2、return this.getBeanFactory();获取BeanFactory

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值