前言

上一次我们实现了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 {@Overridepublic void refresh() throws BeanException {//创建beanFactory,加载beanDefinitionrefreshBeanFactory();//获取beanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();//在bean实例化之前,执行BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(beanFactory);//注册BeanPostProcessorregisterBeanPostProcessors(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);}}@Overridepublic Object getBean(String beanName, Object... args) throws BeanException {return getBeanFactory().getBean(beanName, args);}@Overridepublic <T> T getBean(String beanName, Class<T> requiredType) throws BeanException {return getBeanFactory().getBean(beanName, requiredType);}@Overridepublic <T> Map<String, T> getBeansOfType(Class<T> type) throws BeanException {return getBeanFactory().getBeansOfType(type);}@Overridepublic 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;@Overrideprotected void refreshBeanFactory() throws BeanException {DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();//读取beanDefinitionloadBeanDefinitions(defaultListableBeanFactory);this.beanFactory = defaultListableBeanFactory;}@Overrideprotected 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 {@Overrideprotected 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();}@Overrideprotected 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);}}@Overrideprotected 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 暂时为空}@Overridepublic 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;}@Overridepublic 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<>();@Overridepublic Object getBean(String beanName, Object... args) throws BeanException {//这里先尝试获取单例Bean,如果可以获取到就直接返回Object bean = getSingleton(beanName);if (null != bean) {return bean;}//这里是上面获取单例Bean失败的情况,需要先调用抽象的获取bean定义的方法,拿到bean的定义,然后再通过这个来新创建一个BeanBeanDefinition beanDefinition = getBeanDefinition(beanName);return createBean(beanName, beanDefinition, args);}@Overridepublic <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;@Overridepublic 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 {@Overridepublic 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 {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {if ("userService".equals(beanName)) {UserService userService = (UserService) bean;userService.setDummyInt(1919810);}return bean;}@Overridepublic 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 {@Testpublic 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

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

  1. 手写Spring-第十六章-旋转吧雪月花!用三级缓存解决循环依赖

    前言 循环依赖,一直是一个令人头疼的问题.虽然我们一般情况下会尽量避免这种情况的发生,但很多时候它会在无意识的情况下出现.比如隔了好几个bean之后,发现循环起来了.那么什么是循环依赖呢?其实就是A依 ...

  2. 手写Spring-第十二章-Engage!将AOP和Spring生命周期结合起来

    前言 这里先说一点题外话.由于明年一月份火焰纹章系列新作-火焰纹章Engage就要发售了,我作为一个老火纹厨表示,这个2022年真的一秒钟也待不下去了(恼).所以就用了这么个标题.也算是强行契合了这次 ...

  3. 手写Spring DI依赖注入,嘿,你的益达!

    手写DI 提前实例化单例Bean DI分析 DI的实现 构造参数依赖 一:定义分析 二:定义一个类BeanReference 三:BeanDefinition接口及其实现类 四:DefaultBean ...

  4. 手写 Spring 事务、IOC、DI 和 MVC

    Spring AOP 原理 什么是 AOP? AOP 即面向切面编程,利用 AOP 可以对业务进行解耦,提高重用性,提高开发效率 应用场景:日志记录,性能统计,安全控制,事务处理,异常处理 AOP 底 ...

  5. JAVA项目代码手写吗_一个老程序员是如何手写Spring MVC的

    见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十多 ...

  6. 记录一次阿里架构师全程手写Spring MVC

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  7. 十年java架构师分享:我是这样手写Spring的

    人见人爱的 Spring 已然不仅仅只是一个框架了.如今,Spring 已然成为了一个生态.但深入了解 Spring 的却寥寥无几.这里,我带大家一起来看看,我是如何手写 Spring 的.我将结合对 ...

  8. 从头开始实现一个小型spring框架——手写Spring之集成Tomcat服务器

    手写Spring之集成Tomcat与Servlet 写在前面 一.Web服务模型及servlet 1.1 Web服务器 1.2 请求流程 二.实现 三.小结 写在前面 最近学习了一下spring的相关 ...

  9. 手把手教你手写Spring框架

    手写spring准备工作: 新建一个maven工程: 架构 新建类: package com.spring;public class keweiqinApplicationContext {priva ...

最新文章

  1. 华为静态、默认、备用路由配置
  2. linux游戏脚本,ubuntu 新手一键配置脚本
  3. 112页数学知识整理!机器学习-数学基础回顾.pptx
  4. Mysql(7)——auto_increment简介
  5. 【解决】ERROR in xxx.js from UglifyJs
  6. 安装“消息队列 (MSMQ)”
  7. java接口中多继承的问题
  8. 使用Javascript 实现类
  9. ArcEngine中多边形内外环的处理(转)
  10. Python+django网页设计入门(20):使用WebSocket创建多人在线聊天室
  11. 高中理科不好学计算机,高一理科很差但想学理咋办
  12. ECSHOP4.0 H5端源码运行环境安装
  13. ps把模糊图片变清晰
  14. Android锁屏壁纸 代码,android 锁屏壁纸和桌面壁纸的设置实现
  15. 2017 CCPC 秦皇岛 G 题 ZOJ 3987 - Numbers (高精度+贪心)
  16. 瑞芯微rv1126/1109软硬件解压缩对比---附:关于内存对齐的那些事
  17. Kubernetes中配置livenessProbe、readinessProbe和startupProbe
  18. 使用三目运算求三个数的最大值、最小值和中间值
  19. Java学习-使用文本编辑器编辑Java源代码
  20. Java中文编程开发,让Java编写更加容易

热门文章

  1. oracle核销预付账款,采购、接收、应付会计分录和功能认识
  2. DIY个人智能家庭网关—— 路由器篇之刷机
  3. 如何用curl做PUT请求?
  4. 【技术应用】合合信息与长江金租合作部署TextIn财报机器人,录入一页财报仅需2-3秒
  5. git操作---分支的创建和合并
  6. python语音朗读
  7. python操作MySQL----增删改查
  8. 叮咚~您有一份GitHub2020年度报告待查收
  9. ntp服务器搭建及客户端配置-使用阿里云
  10. STC15W408AS最小系统开发板注意事项与烧写实际操作