前言
上一次我们实现了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
所继承的接口有:
- ListableBeanFactory:按类型获取bean实例、获取所有beanName
- ConfigurableBeanFactory:添加bean后置处理器
- HierarchicalBeanFactory:目前没有实际作用
- AutowireCapableBeanFactory:执行bean后置处理器
- SingletonBeanRegistry:单例bean注册
- 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