手写Spring-第六章-让我访问!实现前置后置处理扩展功能

前言

上一次我们实现了bean定义的自动化注册,这让我们的demo看起来已经很有Spring的那个味道了。但是扩展性还是差了一些。我们都知道,要写出高内聚,低耦合,富有扩展性的代码。那么如何增加扩展性呢?想想Spring中的AOP吧。在我们使用切面的时候,是不是可以在方法执行的前后进行拦截,加入我们自己的逻辑?这就是一种扩展性。它可以让我们在不修改主体逻辑的前提下,完成一些功能的扩展。那么我们这次要做的,虽然不是AOP,但思想和这个是类似的。我们要在Bean定义创建和bean实例化的前后,增加前置后置处理器,以便进行扩展。

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              ├─beans
│  │  │              │  ├─exception
│  │  │              │  │      BeanException.java
│  │  │              │  │  
│  │  │              │  └─factory
│  │  │              │      │  BeanFactory.java
│  │  │              │      │  ConfigurableListableBeanFactory.java
│  │  │              │      │  HierarchicalBeanFactory.java
│  │  │              │      │  ListableBeanFactory.java
│  │  │              │      │  
│  │  │              │      ├─config
│  │  │              │      │      AutowireCapableBeanFactory.java
│  │  │              │      │      BeanDefinition.java
│  │  │              │      │      BeanFactoryPostProcessor.java
│  │  │              │      │      BeanPostProcessor.java
│  │  │              │      │      BeanReference.java
│  │  │              │      │      ConfigurableBeanFactory.java
│  │  │              │      │      DefaultSingletonBeanRegistry.java
│  │  │              │      │      PropertyValue.java
│  │  │              │      │      PropertyValues.java
│  │  │              │      │      SingletonBeanRegistry.java
│  │  │              │      │  
│  │  │              │      ├─support
│  │  │              │      │      AbstractAutowireCapableBeanFactory.java
│  │  │              │      │      AbstractBeanDefinitionReader.java
│  │  │              │      │      AbstractBeanFactory.java
│  │  │              │      │      BeanDefinitionReader.java
│  │  │              │      │      BeanDefinitionRegistry.java
│  │  │              │      │      CglibSubclassingInstantiationStrategy.java
│  │  │              │      │      DefaultListableBeanFactory.java
│  │  │              │      │      InstantiationStrategy.java
│  │  │              │      │      SimpleInstantiationStrategy.java
│  │  │              │      │  
│  │  │              │      └─xml
│  │  │              │              XmlBeanDefinitionReader.java
│  │  │              │  
│  │  │              ├─context
│  │  │              │  │  ApplicationContext.java
│  │  │              │  │  ConfigurableApplicationContext.java
│  │  │              │  │  
│  │  │              │  └─support
│  │  │              │          AbstractApplicationContext.java
│  │  │              │          AbstractRefreshableApplicationContext.java
│  │  │              │          AbstractXmlApplicationContext.java
│  │  │              │          ClasspathXmlApplicationContext.java
│  │  │              │  
│  │  │              ├─core
│  │  │              │  └─io
│  │  │              │          ClasspathResource.java
│  │  │              │          DefaultResourceLoader.java
│  │  │              │          FileSystemResource.java
│  │  │              │          Resource.java
│  │  │              │          ResourceLoader.java
│  │  │              │          UrlResource.java
│  │  │              │  
│  │  │              └─util
│  │  │                      ClassUtils.java
│  │  │        
│  │  └─resources
│  └─test
│      ├─java
│      │  └─com
│      │      └─akitsuki
│      │          └─springframework
│      │              └─test
│      │                  │  ApiTest.java
│      │                  │  
│      │                  ├─bean
│      │                  │      UserDao.java
│      │                  │      UserService.java
│      │                  │  
│      │                  └─common
│      │                          MyBeanFactoryPostProcessor.java
│      │                          MyBeanPostProcessor.java
│      │            
│      └─resources
│              config.yml
│              spring.xml

这次又多出来不少类,又是一块难啃的骨头。

Processor?那是什么?

Processor是什么?如果去查词典,可能会得到这么一个译名:处理器。它是用来处理一些内容的。既然我们要扩展,那么就要留出口子。怎么留?用接口!

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;

/**
 * BeanFactory的后置处理器
 * 用于在BeanDefinition加载完成后,但尚未实例化Bean对象之前,提供修改BeanDefinition属性的功能
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:10
 */
public interface BeanFactoryPostProcessor {

    /**
     * 在所有的BeanDefinition加载完成后,实例化Bean对象前,提供修改BeanDefinition的机制
     *
     * @param beanFactory
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.exception.BeanException;

/**
 * 在bean对象初始化的前后,提供插入点
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:59
 */
public interface BeanPostProcessor {

    /**
     * 在 Bean 对象执行初始化方法之前,执行此方法
     *
     * @param bean     准备初始化的bean
     * @param beanName bean的名称
     * @return 修改后的bean
     * @throws BeanException e
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException;

    /**
     * 在 Bean 对象执行初始化方法之后,执行此方法
     *
     * @param bean     初始化后的bean
     * @param beanName bean的名称
     * @return 修改后的bean
     * @throws BeanException e
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException;

}

这里写了两个接口,分别是在BeanDefinition加载完成后、Bean对象初始化前后提供插入点。有了这些插入点,用户就可以自己去编写类实现这些接口和方法,从而插入自己的逻辑,也就完成了我们所说的扩展。

Context?这又是什么?

Context,一般被称为上下文。一开始接触编程的我,对这个词一直是半懂不懂的。到底什么是上下文啊(恼),明明是汉字为什么我却看不懂啊(恼)。然后,就到了今天,我依然是半懂不懂(笑)。如果去百度搜索上下文,它会告诉你:将一长串的文字或内容,从其中分析出该个段落的摘要以及大意,甚至更进一步,将整篇文章的文意整理出来。此项技术可以应用在解读影片、音讯等档案,使得搜索引擎能够搜寻到文字以外的物件,方便使用者省去大量时间观看影片、聆听音讯,同时也可以帮助使用者提前了解影片与音讯的内容。说人话就是:故事梗概在保留核心内容的情况下,对整体内容进行精简。那么对于Spring来说,我们知道有大量的工厂、bean定义、注册等等功能,我们也需要这么一个梗概,来帮助我们对Spring有一个大体的了解,方便我们的使用。那么这个梗概,就是上下文。

package com.akitsuki.springframework.context;

import com.akitsuki.springframework.beans.factory.ListableBeanFactory;

/**
 * 上下文接口
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:15
 */
public interface ApplicationContext extends ListableBeanFactory {
}

ApplicationContext在Spring中,可以算得上是绝对重量级的角色。用户一般操作Spring,都是在和它打交道。虽然目前内部还没有添加方法,但是会在后面逐步进行完善。

虽然这里还没有方法,但是我们注意到,它继承了一个接口,ListableBeanFactory。我们来看看这个接口的内容

package com.akitsuki.springframework.beans.factory;

import com.akitsuki.springframework.beans.exception.BeanException;

import java.util.Map;

/**
 * 可以列出bean实例的beanFactory,而不是通过名称去查找
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:44
 */
public interface ListableBeanFactory extends BeanFactory {

    /**
     * 按照类型返回 Bean 实例
     *
     * @param type bean type
     * @param <T>  type
     * @return bean
     * @throws BeanException e
     */
    <T> Map<String, T> getBeansOfType(Class<T> type) throws BeanException;

    /**
     * 返回注册表中所有的Bean名称
     *
     * @return bean name list
     */
    String[] getBeanDefinitionNames();
}

嗯,按类型获取bean实例,以及获取所有bean的名称。还有不要忘了继承自BeanFactory的getBean方法。那么对于用户来说,实际上就可以通过ApplicationContext,来获取bean了。而那些bean定义的注册、bean的创建等过程,都被隐藏起来了。

接下来是对ApplicationContext的一个扩充

package com.akitsuki.springframework.context;

import com.akitsuki.springframework.beans.exception.BeanException;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:15
 */
public interface ConfigurableApplicationContext extends ApplicationContext {

    /**
     * 刷新容器
     *
     * @throws BeanException e
     */
    void refresh() throws BeanException;
}

可以看到,它在继承ApplicationContext的同时,提供了一个刷新容器的方法。这是一个重要的方法,那么它究竟会做什么事情呢?我们下面来揭晓。

package com.akitsuki.springframework.context.support;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.context.ConfigurableApplicationContext;
import com.akitsuki.springframework.core.io.DefaultResourceLoader;

import java.util.Map;

/**
 * 应用上下文类抽象实现
 * 继承DefaultResourceLoader是为了处理配置文件资源的加载
 * 实现了ConfigurableApplicationContext接口的刷新方法
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 15:06
 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    @Override
    public void refresh() throws BeanException {
        //创建beanFactory,加载beanDefinition
        refreshBeanFactory();
        //获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //在bean实例化之前,执行BeanFactoryPostProcessor
        invokeBeanFactoryPostProcessors(beanFactory);
        //注册BeanPostProcessor
        registerBeanPostProcessors(beanFactory);
        //提前实例化单例bean对象
        beanFactory.preInstantiateSingletons();
    }

    /**
     * 刷新beanFactory
     *
     * @throws BeanException
     */
    protected abstract void refreshBeanFactory() throws BeanException;

    /**
     * 获取beanFactory
     *
     * @return
     */
    protected abstract ConfigurableListableBeanFactory getBeanFactory();

    /**
     * 调用beanFactoryPostProcessor
     *
     * @param beanFactory
     * @throws BeanException
     */
    private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) throws BeanException {
        for (BeanFactoryPostProcessor processor : beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()) {
            processor.postProcessBeanFactory(beanFactory);
        }
    }

    /**
     * 注册beanPostProcessor
     *
     * @param beanFactory
     * @throws BeanException
     */
    private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) throws BeanException {
        for (BeanPostProcessor processor : beanFactory.getBeansOfType(BeanPostProcessor.class).values()) {
            beanFactory.addBeanPostProcessor(processor);
        }
    }

    @Override
    public Object getBean(String beanName, Object... args) throws BeanException {
        return getBeanFactory().getBean(beanName, args);
    }

    @Override
    public <T> T getBean(String beanName, Class<T> requiredType) throws BeanException {
        return getBeanFactory().getBean(beanName, requiredType);
    }

    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeanException {
        return getBeanFactory().getBeansOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }
}

好大的一个抽象类!我们可以看到它实现了我们刚才说的refresh方法,方法的内容则是:刷新bean工厂同时加载bean定义->获取bean工厂->执行bean定义后置处理器->注册bean后置处理器->提前实例化单例对象。其他的方法实现都比较简单,这里就不再详细说明。而刷新bean工厂和获取bean工厂两个方法是抽象方法,交给子类来具体实现。但这里出现了一个我们前面没有见过的类:ConfigurableListableBeanFactory,并且调用了一些基于这个类的方法,所以这里可能阅读起来会有些困难。我们接下来就介绍一下这个类。顺带一提,这里继承了 DefaultResourceLoader,是为了处理配置资源的加载。

package com.akitsuki.springframework.beans.factory;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.config.AutowireCapableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.ConfigurableBeanFactory;

/**
 * 提供分析和修改Bean以及预先实例化的操作接口
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:56
 */
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {

    /**
     * 获取BeanDefinition
     *
     * @param beanName bean名称
     * @return beanDefinition
     * @throws BeanException e
     */
    BeanDefinition getBeanDefinition(String beanName) throws BeanException;

    /**
     * 提前实例化单例bean对象
     *
     * @throws BeanException
     */
    void preInstantiateSingletons() throws BeanException;
}

好混乱!这个接口继承了多个接口,而且名字也很长很奇怪。不过从名字也可以推断出来,它既有 ListableBeanFactory的功能,也有 ConfigurableBeanFactory的功能。而且还继承了 AutowireCapableBeanFactory,一下子多出来2个没有见过的接口,难免有些无所适从。我们来逐个分析。

首先是 ConfigurableBeanFactory接口

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.factory.HierarchicalBeanFactory;

/**
 * 可获取 BeanPostProcessor、BeanClassLoader等的一个配置化接口
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:54
 */
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {

    String SCOPE_SINGLETON = "singleton";

    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 添加一个processor
     *
     * @param processor processor
     */
    void addBeanPostProcessor(BeanPostProcessor processor);
}

又是继承!SingletonBeanRegistry,这个我们已经了解,是单例bean的注册接口。但是又多了一个 HierarchicalBeanFactory。到底有完没完了!再忍一忍,胜利的道路就在眼前了!

package com.akitsuki.springframework.beans.factory;

/**
 * 分等级的BeanFactory
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:48
 */
public interface HierarchicalBeanFactory extends BeanFactory {
}

还好,这里只是为了满足Spring的结构而创建的一个接口,暂时还没有什么内容,可以先放下心来。

我们回到上面,ConfigurableBeanFactory这个接口提供了添加bean后置处理器的功能,意味着我们可以通过实现这个接口来为bean添加后置处理器,从而实现bean的扩展。

我们再来看看 AutowireCapableBeanFactory

package com.akitsuki.springframework.beans.factory.config;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.BeanFactory;

/**
 * 自动化处理bean工厂配置的接口
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 14:52
 */
public interface AutowireCapableBeanFactory extends BeanFactory {


    /**
     * 执行 BeanPostProcessors 接口实现类的 postProcessBeforeInitialization 方法
     *
     * @param existingBean bean
     * @param beanName     bean名称
     * @return bean
     * @throws BeanException e
     */
    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeanException;

    /**
     * 执行 BeanPostProcessors 接口实现类的 postProcessorsAfterInitialization 方法
     *
     * @param existingBean bean
     * @param beanName     bean名称
     * @return bean
     * @throws BeanException e
     */
    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeanException;
}

如果说上个接口是添加处理器,那么这个接口就是执行处理器了。分别在bean初始化前后来执行,也是我们这次要实现的核心功能。

介绍到这里,我们来分别列举一下 ConfigurableListableBeanFactory接口以及它所继承的多个接口,分别掌握哪些功能,来对 ConfigurableListableBeanFactory接口有一个整体的认知。

ConfigurableListableBeanFactory所继承的接口有:

  1. ListableBeanFactory:按类型获取bean实例、获取所有beanName
  2. ConfigurableBeanFactory:添加bean后置处理器
  3. HierarchicalBeanFactory:目前没有实际作用
  4. AutowireCapableBeanFactory:执行bean后置处理器
  5. SingletonBeanRegistry:单例bean注册
  6. BeanFactory:获取bean

再加上自己所拥有的获取bean定义、提前实例化单例bean两个方法,可以说是从bean定义到bean的实例化再到后置处理,一手包办了所有功能。

再一次,抽象!逐级继承!

经过前面的几次练习,想必我们已经对Spring的这套抽象类分别实现一部分的方法,再逐步继承直到落实到一个最终的类上面这种玩法,已经逐渐熟悉了。刚才我们也见识到了,有那么庞大的一个接口 ConfigurableListableBeanFactory,这个接口又被 AbstractApplicationContext所实现。在一个类中实现全部的功能是不现实的。所以我们这次又要拿出传统艺能,抽象,继承,逐级实现!首先我们看第一位选手:

package com.akitsuki.springframework.context.support;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * 实现了父类的刷新bean工厂的方法以及获取bean工厂的方法
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 16:16
 */
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

    private DefaultListableBeanFactory beanFactory;

    @Override
    protected void refreshBeanFactory() throws BeanException {
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        //读取beanDefinition
        loadBeanDefinitions(defaultListableBeanFactory);
        this.beanFactory = defaultListableBeanFactory;
    }

    @Override
    protected ConfigurableListableBeanFactory getBeanFactory() {
        return beanFactory;
    }

    /**
     * 读取beanDefinition
     *
     * @param beanFactory factory
     */
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory);
}

这里的关键,在于实现了刷新beanFactory的方法。我们可以看到,每次刷新beanFactory,都会新建一个DefaultListableBeanFactory,并且重新加载bean定义。这个操作,的确很有刷新的感觉。而如何重新加载bean定义,这里并没有具体实现,留给子类来进行。接下来是第二位选手

package com.akitsuki.springframework.context.support;

import com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory;
import com.akitsuki.springframework.beans.factory.xml.XmlBeanDefinitionReader;

/**
 * 抽象的通过xml完成beanDefinition读取注册的类
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 16:31
 */
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory, this);
        String[] configLocations = getConfigLocations();
        if (null != configLocations) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

    /**
     * 获取配置文件的路径
     *
     * @return
     */
    protected abstract String[] getConfigLocations();
}

到这里,其实就比刚才的抽象,要更加明确了一分。如果说刚才的抽象类,是【可刷新的应用上下文】,那么这个类,就明确到了【用xml实现的】可刷新的应用上下文,虽然依然抽象,但还是明确了一些。

这里终于实现了加载bean定义的方法。可以看到,这里利用 XmlBeanDefinitionReader来读取bean定义。而这里有趣的是,在新建 XmlBeanDefinitionReader的时候,将自身 this,作为参数传递了过去。而这个参数,所接受的类型是ResourceLoader。还记得遥远的 AbstractApplicationContext 吗?这个抽象类正继承了 DefaultResourceLoader,所以作为它的多级子类的 AbstractXmlApplicationContext,自然也可以作为 ResourceLoader来使用。

这里的实现,其实就是通过reader,传入配置路径,再由其解析bean定义并进行加载。还记得上一次的自动化配置吗?做的就是这一块的工作。至于这里的获取配置路径,依然是一个抽象方法,因为就像上一次的内容中,我们可以通过多种途径来获取配置内容,所以依然需要一个更具体的类来实现。来吧,我们来看看最终实现者。

package com.akitsuki.springframework.context.support;

/**
 * 具体对外给用户提供的应用上下文方法
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/10 16:46
 */
public class ClasspathXmlApplicationContext extends AbstractXmlApplicationContext {

    private String[] configLocations;

    public ClasspathXmlApplicationContext() {
        this("classpath:spring.xml");
    }

    public ClasspathXmlApplicationContext(String configLocation) {
        this(new String[]{configLocation});
    }

    public ClasspathXmlApplicationContext(String[] configLocations) {
        this.configLocations = configLocations;
        refresh();
    }

    @Override
    protected String[] getConfigLocations() {
        return configLocations;
    }
}

终于,我们明确了,是【classpath下的、用xml文件实现的、可刷新的、应用上下文】,这就是我们最终通过逐级继承实现所得到的结论。留给它的发挥空间已经不多了,只实现简单的路径获取方法就可以。但小伙子可是个富N代,有大把的功能通过继承得来,真是让人羡慕!

查漏补缺,完善接口

刚才我们完成了荡气回肠的一大套代码,但好像还有一些零碎被我们遗忘了。比如bean实例化后置处理的真正调用节点(根本不零碎好嘛!多重要的功能!)。说到这里,还记得我们的bean实例化后置处理是由哪个接口提供的吗?对了,是 AutowireCapableBeanFactory。那么还记得,在前面的几次练习,有个名字很奇怪的抽象类一直在那里碍眼吗?这回终于排场用场了。出来吧!AbstractAutowireCapableBeanFactory

package com.akitsuki.springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.config.*;

import java.lang.reflect.Constructor;

/**
 * 抽象的实例化Bean类,提供创建Bean的能力,创建完成后,放入单例bean map中进行缓存
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:12
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    /**
     * 实例化策略,在构造方法中进行初始化
     */
    private final InstantiationStrategy strategy;

    protected AbstractAutowireCapableBeanFactory(Class<? extends InstantiationStrategy> clazz) {
        try {
            strategy = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeanException("设置实例化策略出错", e);
        }
    }

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException {
        Object bean;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            //设置bean属性
            applyPropertyValues(beanName, beanDefinition, bean);
            //初始化bean,执行beanPostProcessor的前置和后置方法
            initializeBean(beanName, bean, beanDefinition);
            //创建好的单例bean,放入缓存
            addSingleton(beanName, bean);
        } catch (Exception e) {
            throw new BeanException("创建bean失败", e);
        }
        return bean;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) throws BeanException {
        //拿到所有的构造方法
        Constructor<?>[] declaredConstructors = beanDefinition.getBeanClass().getDeclaredConstructors();
        //开始循环,找到和参数类型一致的方法
        for (Constructor<?> c : declaredConstructors) {
            if (c.getParameterTypes().length == args.length) {
                boolean flag = true;
                for (int i = 0; i < c.getParameterTypes().length; i++) {
                    if (!args[i].getClass().equals(c.getParameterTypes()[i])) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    //调用策略来进行实例化
                    return strategy.instantiate(beanDefinition, beanName, c, args);
                }
            }
        }
        throw new BeanException("创建bean出错,未找到对应构造方法");
    }

    /**
     * 为生成的bean设置属性
     *
     * @param beanName       bean的名称
     * @param beanDefinition bean的定义
     * @param bean           等待设置属性的bean
     */
    protected void applyPropertyValues(String beanName, BeanDefinition beanDefinition, Object bean) throws BeanException {
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue pv : propertyValues.getPropertyValues()) {
            String name = pv.getName();
            Object value = pv.getValue();
            if (value instanceof BeanReference) {
                try {
                    value = getBean(((BeanReference) value).getName());
                } catch (Exception e) {
                    throw new BeanException("设置bean属性时异常:" + beanName, e);
                }
            }
            BeanUtil.setFieldValue(bean, name, value);
        }
    }

    /**
     * 初始化bean
     *
     * @param beanName       bean名称
     * @param bean           待初始化bean
     * @param beanDefinition bean定义
     * @return bean
     */
    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        //执行beanPostProcessor before处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        //执行bean初始化内容
        invokeInitMethod(beanName, wrappedBean, beanDefinition);
        //执行beanPostProcessor after处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }


    private void invokeInitMethod(String beanName, Object bean, BeanDefinition beanDefinition) {
        //todo 暂时为空
    }

    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeanException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(existingBean, beanName);
            if (null == current) {
                return result;
            }
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeanException {
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(existingBean, beanName);
            if (null == current) {
                return result;
            }
            result = current;
        }
        return result;
    }
}

终于,直到今天,我们终于知道了这个类如此命名的原因。原来这个接口是要交给它来实现的。

首先我们查看变化的部分。在 createBean方法中,加入了 initializeBean步骤。那我们来看看这个方法具体做了什么。其实非常单纯,分三步走:调用前置处理->初始化->调用后置处理。非常的简单易懂。那么我们会注意到,这里的【初始化】操作,是留空的。这其实是下一节的内容(开始埋坑了!),暂时先不要在意这些细节。重要的是,这里完成了前置后置处理的切入时间点。

说到这里,我想插入一段讨论。到目前为止,我们的bean实例化步骤,到底有哪些步骤?我觉得是下面这些:

读取配置文件->加载bean定义->调用构造方法创建bean(有参/无参)->设置bean依赖属性(普通/其他bean)->前置处理->初始化(目前还没有内容)->后置处理->添加进单例bean缓存->结束。

我们回到代码的分析,其实一直以来说前置处理、后置处理,是不够准确的,它们都属于后置处理器:PostProcessor。更准确的描述是:【bean初始化前】后置处理器、【bean初始化后】后置处理器。那么我们来看这两个方法的实现,其实两个方法的实现步骤是类似的,都是通过调用 getBeanProcessors方法,拿到所有的处理器,再去循环调用初始化前/初始化后处理。嗯?不对…不对不对不对,这个方法哪来的!前面说了那么多,都没有见过这个方法是哪里来的。而且我的这些处理器都存到哪里去了!虽然前面在 AbstractApplicationContext那里,有注册处理器的地方,调用了beanFactory的 addBeanPostProcessor,但是这个方法又是哪来的啊!!!不要在意这些细节,这一章的内容可能的确比较容易引起混乱,主要是增加了太多的接口和方法,方法的分布又比较分散所导致的。那么这两个方法究竟在哪里呢?答案很原始,在 AbstractBeanFactory中。至于为什么放在这里,我想是因为所有的BeanFactory都要用到吧,所以需要放在一个很父类的地方。

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.beans.factory.config.ConfigurableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.DefaultSingletonBeanRegistry;

import java.util.ArrayList;
import java.util.List;

/**
 * 抽象bean工厂,实现了BeanFactory,提供获取Bean的实现
 * 在获取Bean的方法中,调用了两个抽象方法:获取定义、创建Bean
 * 这两个抽象方法由子类来实现,这里只定义过程
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:04
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {

    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    @Override
    public Object getBean(String beanName, Object... args) throws BeanException {
        //这里先尝试获取单例Bean,如果可以获取到就直接返回
        Object bean = getSingleton(beanName);
        if (null != bean) {
            return bean;
        }
        //这里是上面获取单例Bean失败的情况,需要先调用抽象的获取bean定义的方法,拿到bean的定义,然后再通过这个来新创建一个Bean
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        return createBean(beanName, beanDefinition, args);
    }

    @Override
    public <T> T getBean(String beanName, Class<T> requiredType) throws BeanException {
        Object bean = getBean(beanName);
        return requiredType.cast(bean);
    }

    /**
     * 获取Bean的定义
     *
     * @param beanName bean的名字
     * @return beanDefinition
     * @throws BeanException exception
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeanException;

    /**
     * 创建Bean
     *
     * @param beanName       bean的名字
     * @param beanDefinition bean的定义
     * @param args           构造方法参数
     * @return bean
     * @throws BeanException exception
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeanException;

    @Override
    public void addBeanPostProcessor(BeanPostProcessor processor) {
        this.beanPostProcessors.remove(processor);
        this.beanPostProcessors.add(processor);
    }

    public List<BeanPostProcessor> getBeanPostProcessors() {
        return beanPostProcessors;
    }
}

首先可以看到,它实现了 ConfigurableBeanFactory接口,意味着它要肩负起注册processor的任务。在类中维护了一个list,作为processor的存放点。要注册的时候,就先移除,再添加。很好理解。而光有注册还不行,还得往外提供,于是就有了get方法。这样就把上面所挖的坑填平了。

道阻且长,终于要测试了

看到这里,不知道大家是否觉得脑子一团浆糊。反正我一开始学到这里的时候,是一团浆糊了。跟着教程把demo写出来,但是感觉自己完全没有理解,只是模仿了它的形,没有领会到它的神。所以我也是学到这里,才决定,要停下来,沉淀总结一下了,于是便有了这个系列。停下来,思考每一步为什么要这样设计,融入自己的理解,再把它写成文章,分享出来。如果我的理解和总结能帮到哪怕一个人,那我觉得也值得了。

好了,废话不多说,我们来开始测试。既然这次的主角是后置处理器,那么我们自然要给我们的测试加上它。

package com.akitsuki.springframework.test.common;

import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.PropertyValue;
import com.akitsuki.springframework.beans.factory.config.PropertyValues;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/11 16:08
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        propertyValues.addPropertyValue(new PropertyValue("dummyString", "翻斗花园一棵树,我叫英俊你记住"));
    }
}

首先是我们的BeanFactory后置处理器。还记得这个的切入时间点是什么时候吗?对了,是在beanDefinition加载完成后,还没有实例化bean的时候。那在这里,我们修改bean的属性dummyString为一段霸气十足的话!

package com.akitsuki.springframework.test.common;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.test.bean.UserService;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/11 16:08
 */
public class MyBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {
        if ("userService".equals(beanName)) {
            UserService userService = (UserService) bean;
            userService.setDummyInt(1919810);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {
        return bean;
    }
}

然后是我们的bean实例化后置处理器,我们在初始化前进行修改,把我们的userService中的dummyInt修改为一串意义不明的数字。

到这里,准备工作还不算完。光是这样实现接口是毫无用处的,我们还需要将这两个后置处理器注册成bean,这样才能被容器识别。

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="userDao" class="com.akitsuki.springframework.test.bean.UserDao"/>

    <bean id="userService" class="com.akitsuki.springframework.test.bean.UserService">
        <property name="dummyString" value="dummy"/>
        <property name="dummyInt" value="114514"/>
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean class="com.akitsuki.springframework.test.common.MyBeanFactoryPostProcessor"/>
    <bean class="com.akitsuki.springframework.test.common.MyBeanPostProcessor"/>
</beans>

干净麻利地写到注册文件中,到了这个时候才更加感觉出自动配置的好处,比我们手动注册要强太多了。

来,开始我们的主要测试类

package com.akitsuki.springframework.test;

import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/11 16:06
 */
public class ApiTest {

    @Test
    public void test() {
        ClasspathXmlApplicationContext applicationContext = new ClasspathXmlApplicationContext();
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.queryUserInfo(1L);
        userService.queryUserInfo(2L);
        userService.queryUserInfo(114L);
    }
}

测试结果

dummyString:翻斗花园一棵树,我叫英俊你记住
dummyInt:1919810
用户名:akitsuki
dummyString:翻斗花园一棵树,我叫英俊你记住
dummyInt:1919810
用户名:toyosaki
dummyString:翻斗花园一棵树,我叫英俊你记住
dummyInt:1919810
用户未找到>_<

Process finished with exit code 0

!!!!!

看到这个,不由得潸然泪下。比起前面几次的,简直是简洁太多了!而且和真正的Spring越来越像了。手拿ApplicationContext,别的什么都不用管,getBean!让我访问!就可以了。

看到这里,我们来推导一下简洁的表象下,每一步的步骤,来感受一下精妙的设计吧。

一切的开始,来自于 ClasspathXmlApplicationContext的新建。这里的无参构造方法中,会默认配置文件为 classpath下的 spring.xml。然后兜兜转转,执行到了 refresh方法。一旦执行到了 refresh方法,那就意味着打开了开关。刷新bean工厂加载bean定义->调用bean定义后置处理器(在这一步会先创建好后置处理器的bean)->注册bean后置处理器->实例化单例bean对象(在这一步中会执行bean的后置处理器)。而这一切,都藏在一个 new ClasspathXmlApplicationContext()下面,实在是精妙无比。

那么这次的内容,到这里也就结束了。回顾一下,真的是挺大的一章。下一章我们会填上遗留的那个【bean初始化】的坑,敬请期待。

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-06

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值