上回书说到,我们用Aware接口实现了感知,让bean能感受到Spring组件的一部分。其实从这里我们也可以看出,Spring不仅为我们提供了自身的功能,同时也给我们留出了许多扩展的接口。那么这一次,我们就要实现FactoryBean接口。这个接口可以让我们自己编写一个Factory用来生产bean。而这个Factory,本身也是Spring中的一个bean。所以就有了标题中的【我是工厂,也是Bean】这么一个说法。
那么我们可以通过这个接口做什么事情呢?不知道大家有没有好奇过,我们在使用Mybatis的时候,mapper明明是一个接口,并没有对应的实现类,那么它为什么可以作为一个Bean被Spring注入并且使用呢?其实Mybatis的核心,就用到了这里的FactoryBean,它实现了一个生产bean的工厂,用于生产mapper所对应的bean。在这次的测试中,我们会试着实现一个简化版的流程,来帮助理解。
除此之外,这次我们还会扩展一个小功能,那就是bean的生产模式。之前我们生产的bean,都默认是单例的。但实际上Spring不止可以生产单例bean,这次我们就来扩展这个功能。
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─akitsuki
│ │ │ └─springframework
│ │ │ ├─beans
│ │ │ │ ├─exception
│ │ │ │ │ BeanException.java
│ │ │ │ │
│ │ │ │ └─factory
│ │ │ │ │ Aware.java
│ │ │ │ │ BeanClassLoaderAware.java
│ │ │ │ │ BeanFactory.java
│ │ │ │ │ BeanFactoryAware.java
│ │ │ │ │ BeanNameAware.java
│ │ │ │ │ ConfigurableListableBeanFactory.java
│ │ │ │ │ DisposableBean.java
│ │ │ │ │ FactoryBean.java
│ │ │ │ │ HierarchicalBeanFactory.java
│ │ │ │ │ InitializingBean.java
│ │ │ │ │ ListableBeanFactory.java
│ │ │ │ │
│ │ │ │ ├─config
│ │ │ │ │ AutowireCapableBeanFactory.java
│ │ │ │ │ BeanDefinition.java
│ │ │ │ │ BeanDefinitionRegistryPostProcessor.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
│ │ │ │ │ DisposableBeanAdapter.java
│ │ │ │ │ FactoryBeanRegistrySupport.java
│ │ │ │ │ InstantiationStrategy.java
│ │ │ │ │ SimpleInstantiationStrategy.java
│ │ │ │ │
│ │ │ │ └─xml
│ │ │ │ XmlBeanDefinitionReader.java
│ │ │ │
│ │ │ ├─context
│ │ │ │ │ ApplicationContext.java
│ │ │ │ │ ApplicationContextAware.java
│ │ │ │ │ ConfigurableApplicationContext.java
│ │ │ │ │
│ │ │ │ └─support
│ │ │ │ AbstractApplicationContext.java
│ │ │ │ AbstractRefreshableApplicationContext.java
│ │ │ │ AbstractXmlApplicationContext.java
│ │ │ │ ApplicationContextAwareProcessor.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
│ │ │ MapperFactoryBean.java
│ │ │ UserMapper.java
│ │ │ UserService.java
│ │ │
│ │ └─common
│ │ MapperRegistryPostProcessor.java
│ │
│ └─resources
│ spring.xml
新增的文件不算多,但我觉得这次的实现还蛮有意思的。那么,我们开始吧。
我们之前的bean,都是单例的。今天开始,我们有了选择权!我们即将增加一个新的功能:原型模式。实际上就是每次获取bean的时候,都重新生产一个bean。那么我们首先要修改bean定义,来加入这个作用范围的属性。
package com.akitsuki.springframework.beans.factory.config;
import lombok.Getter;
import lombok.Setter;
/**
* Bean的定义,包含bean的一些基本信息
*
* @author ziling.wang@hand-china.com
* @date 2022/11/7 9:54
*/
@Getter
@Setter
public class BeanDefinition {
/**
* bean的class
*/
private final Class<?> beanClass;
/**
* bean的属性和值
*/
private PropertyValues propertyValues = new PropertyValues();
/**
* 初始化方法名称
*/
private String initMethodName;
/**
* 销毁方法名称
*/
private String destroyMethodName;
/**
* bean作用范围,默认为单例
*/
private String scope = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* 默认启用单例模式
*/
private boolean singleton = true;
/**
* 默认不适用原型模式
*/
private boolean prototype = false;
public BeanDefinition(Class<?> beanClass) {
this.beanClass = beanClass;
}
public BeanDefinition(Class<?> beanClass, PropertyValues propertyValues) {
this.beanClass = beanClass;
this.propertyValues = propertyValues;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
this.singleton = ConfigurableBeanFactory.SCOPE_SINGLETON.equals(scope);
this.prototype = ConfigurableBeanFactory.SCOPE_PROTOTYPE.equals(scope);
}
}
这里要注意,我们的 setScope
中,进行了操作,对 scope
进行操作时,会同时改变 singleton
和 prototype
属性。
既然这里已经加入了属性,那么我们加载bean定义的地方,自然也要与时俱进。在XmlBeanDefinitionReader中,添加关于 scope
的操作
/**
* 真正通过xml读取bean定义的方法实现
*
* @param inputStream xml配置文件输入流
* @throws BeanException e
* @throws ClassNotFoundException e
*/
private void doLoadBeanDefinitions(InputStream inputStream) throws BeanException, ClassNotFoundException {
Document doc = XmlUtil.readXML(inputStream);
Element root = doc.getDocumentElement();
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
//如果不是bean,则跳过
if (!isBean(childNodes.item(i))) {
continue;
}
// 解析标签
Element bean = (Element) childNodes.item(i);
String id = bean.getAttribute("id");
String name = bean.getAttribute("name");
String className = bean.getAttribute("class");
String initMethodName = bean.getAttribute("init-method");
String destroyMethodName = bean.getAttribute("destroy-method");
String scope = bean.getAttribute("scope");
// 获取 Class,方便获取类中的名称
Class<?> clazz = Class.forName(className);
// 优先级 id > name
String beanName = StrUtil.isNotEmpty(id) ? id : name;
if (StrUtil.isEmpty(beanName)) {
beanName = StrUtil.lowerFirst(clazz.getSimpleName());
}
// 定义Bean
BeanDefinition beanDefinition = new BeanDefinition(clazz);
beanDefinition.setInitMethodName(initMethodName);
beanDefinition.setDestroyMethodName(destroyMethodName);
// 读取属性并填充
buildProperty(bean, beanDefinition);
if (getRegistry().containsBeanDefinition(beanName)) {
throw new BeanException("Duplicate beanName[" + beanName + "] is not allowed");
}
if (StrUtil.isNotBlank(scope)) {
beanDefinition.setScope(scope);
}
// 注册 BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
}
好了,现在配置可以被正确加载了,也可以正确放到bean定义中了。那么我们怎么用呢?答案是在创建的时候进行操作。因为我们知道,getBean的时候,会先尝试获取单例bean,如果没有获取到,那么就创建bean。所以我们就需要在创建bean的时候进行判断,只有在单例的时候才放入缓存。所以我们修改 AbstractAutowireCapableBeanFactory
。
@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);
//注册实现了DisposableBean接口的对象
registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
if (beanDefinition.isSingleton()) {
//创建好的单例bean,放入缓存
addSingleton(beanName, bean);
}
} catch (Exception e) {
throw new BeanException("创建bean失败", e);
}
return bean;
}
/**
* 在需要的情况下,注册销毁方法
*
* @param beanName
* @param bean
* @param beanDefinition
*/
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
//只有单例bean才需要销毁
if (!beanDefinition.isSingleton()) {
return;
}
if (bean instanceof DisposableBean || StrUtil.isNotBlank(beanDefinition.getDestroyMethodName())) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
}
}
这里我们不仅在创建bean的时候进行了判断,还在销毁方法处进行了处理。因为只有单例bean才需要进行销毁。
上面我们稍微扩充了bean的范围,但对于这一章来说,只是一个开胃小菜。我们这一章真正要实现的,是FactoryBean。我们上面也说了,FactoryBean是作为一个工厂来使用的。那么作为一个工厂,最重要的是什么呢?自然是生产对象了。不仅如此,我们还需要提供对象的类型,以及对象是否为单例的方法。
package com.akitsuki.springframework.beans.factory;
/**
* 工厂bean接口,实现了此接口的bean可以作为生产bean的工厂来使用
* @author ziling.wang@hand-china.com
* @date 2022/11/28 16:26
*/
public interface FactoryBean<T> {
/**
* 获取对象
* @return 对象
* @throws Exception e
*/
T getObject() throws Exception;
/**
* 获取对象类型
* @return 对象类型class
*/
Class<?> getObjectType();
/**
* 是否为单例
* @return
*/
boolean isSingleton();
}
从这个泛型我们也可以看出,一个FactoryBean,一般只用来生产一种Bean。
有了工厂Bean,我们还需要一个工厂Bean的注册支持。就像我们很久之前写的那个单例bean的注册类一样,工厂Bean也需要被注册起来。这主要是为了区分于其他的功能,各个领域模块各自负责自己的事情,避免由于扩展导致类膨胀,难以维护。
package com.akitsuki.springframework.beans.factory.support;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.FactoryBean;
import com.akitsuki.springframework.beans.factory.config.DefaultSingletonBeanRegistry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/28 16:28
*/
public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>();
protected Object getCachedObjectForFactoryBean(String beanName) {
Object obj = factoryBeanObjectCache.get(beanName);
return obj == NULL_OBJECT ? null : obj;
}
protected Object getObjectFromFactoryBean(FactoryBean<?> factoryBean, String beanName) {
if (factoryBean.isSingleton()) {
Object obj = getCachedObjectForFactoryBean(beanName);
if (null == obj) {
obj = doGetObjectFromFactoryBean(factoryBean, beanName);
factoryBeanObjectCache.put(beanName, null == obj ? NULL_OBJECT: obj);
}
return obj == NULL_OBJECT ? null : obj;
} else {
return doGetObjectFromFactoryBean(factoryBean, beanName);
}
}
private Object doGetObjectFromFactoryBean(FactoryBean<?> factoryBean, String beanName) {
try {
return factoryBean.getObject();
} catch (Exception e) {
throw new BeanException("factory bean threw exception on object [" + beanName + "] creation.");
}
}
}
首先我们可以看到,它继承了 DefaultSingletonBeanRegistry
,这意味着它也拥有了单例bean注册的功能。在此之上,再扩展自己的功能。
随后我们看到它用一个线程安全的map来缓存已经生产好了的bean。也就意味着它生产好了的单例bean,会在这里被缓存起来。我们再接着往下看,这里有个 NULL_OBJECT
,这个是声明在 DefaultSingletonBeanRegistry
中被继承下来的,它本质上是一个 new Object()
。它的主要作用是防止null值被放入 ConcurrentHashMap
,因为这个map不支持null值,所以用空对象来代替。
我们看整体的实现,实际上就是通过调用factoryBean的getObject方法,来生产对象,如果是单例则放入缓存,如果不是,则直接返回。所以并不是很难理解。
接下来,重点来了。我们要如何用它来生产bean呢?看起来一顿操作猛如虎,但突然有些疑惑,怎么用才是关键。这里我们要追溯到 getBean
方法中。要对FactoryBean方法进行一些特殊判断。如果发现getBean获取到的类型是FactoryBean,就不直接返回,而是特殊处理。下面我们来看看 AbstractBeanFactory
的变化
package com.akitsuki.springframework.beans.factory.support;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.FactoryBean;
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.util.ClassUtils;
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 FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
@Override
public Object getBean(String beanName, Object... args) throws BeanException {
//这里先尝试获取单例Bean
Object bean = getSingleton(beanName);
if (null != bean) {
return getObjectForBeanInstance(bean, beanName);
}
//这里是上面获取单例Bean失败的情况,需要先调用抽象的获取bean定义的方法,拿到bean的定义,然后再通过这个来新创建一个Bean
BeanDefinition beanDefinition = getBeanDefinition(beanName);
bean = createBean(beanName, beanDefinition, args);
return getObjectForBeanInstance(bean, beanName);
}
@Override
public <T> T getBean(String beanName, Class<T> requiredType) throws BeanException {
Object bean = getBean(beanName);
return requiredType.cast(bean);
}
private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
if (beanInstance instanceof FactoryBean) {
return getObjectFromFactoryBean(((FactoryBean<?>) beanInstance), beanName);
}
return beanInstance;
}
}
// 其他方法、属性
这里只列出了必要的内容。我们从上往下分析。我们知道,在一开始,AbstractBeanFactory
是继承自 DefaultSingletonBeanRegistry
的,而现在转为继承 FactoryBeanRegistrySupport
,这就意味着,它扩展了自己的功能。因为 FactoryBeanRegistrySupport
本身也是继承自 DefaultSingletonBeanRegistry
的,所以对于 AbstractBeanFactory
来说,功能并没有受到影响,而且多了一些操作 FactoryBean
的方法。
我们可以看到,这里在获取到bean之后,并不直接返回了,而是先交给 getObjectForBeanInstance
来处理。而这里会对 BeanFactory
类型进行处理,调用我们上面实现的 getObjectFromFactoryBean
方法。而我们知道,这个方法的具体实现,则是通过调用 factoryBean
的 getObject
方法,来获取真正的bean。这样,我们就搞清楚Spring是如何将 FactoryBean
偷梁换柱,生产真正bean的了。
看过第一章的都知道,我的这一系列手写Spring,其实都是跟着 小傅哥(https://bugstack.cn)
的教程来的,里面加上了我自己的理解和一些小的修改。在小傅哥的教程中,这一章的内容就到此为止了,下面则是针对FactoryBean的一些测试。不过通过这次的学习,我也了解到,大名鼎鼎的Mybatis,正是通过FactroyBean来实现将接口形式的Mapper,生产出真正的bean的。如果到这里就结束,始终觉得有些遗憾,不把Mybatis的实现搞懂,心里总是痒痒的。
通过查找资料可以知道,Mybatis不仅使用了FactoryBean,还有另外一个利器:Bean定义注册后置处理器。关于后置处理器的内容,我们在前面的章节也已经学习过了。之前我们实现了Bean和Bean工厂的后置处理器,而实际上Spring还有形形色色的各种处理器,这次说的Bean定义注册后置处理器也是其中之一。它的作用时间点是在 Bean定义注册完成后,创建Bean之前
。给我们提供了Bean定义的注册机会。
那么Mybatis是如何实现的呢?可以这么概括:**它会在Bean定义注册后置处理器中,将接口形式的Mapper,以BeanFactory的方式,注册到bean定义中。**一句话就概括完了,对不对?但你可能还是云里雾里的。我们下面就来详细解析。
经过之前的后置处理器章节,这里我们也不用过多介绍了,直接上代码
package com.akitsuki.springframework.beans.factory.config;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.support.BeanDefinitionRegistry;
/**
* bean定义注册后置处理器,在bean定义加载完成后,提供修改扩充功能
* @author ziling.wang@hand-china.com
* @date 2022/11/29 16:54
*/
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* 在bean定义加载完成后,提供修改扩充机制
* @param registry bean定义注册类
* @throws BeanException e
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeanException;
}
嗯,可以看到提供的方法,将 BeanDefinitionRegistry
传了进去。那么显然,我们可以使用它来注册我们自己的Bean定义。
还有一点需要注意的是,它继承了 BeanFactoryPostProcessor
,意味着它还同时具有Bean工厂后置处理器的功能。对于这个,我个人的理解是,两个处理器的处理时间点实际上是一起的,都是Bean定义加载完成后,但还没有开始创建Bean的时候。所以放在一起可能方便处理。
而且,既然它继承了 BeanFactoryPostProcessor
,也就意味着,我们可以在处理 BeanFactoryPostProcessor
的时候,顺便处理它。我们之前处理的方法在AbstractApplicationContext中,下面我们来看看修改:
/**
* 调用beanFactoryPostProcessor
*
* @param beanFactory
* @throws BeanException
*/
private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) throws BeanException {
for (BeanFactoryPostProcessor processor : beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()) {
//如果处理器为bean定义后置处理器,则先处理这个
if (processor instanceof BeanDefinitionRegistryPostProcessor) {
((BeanDefinitionRegistryPostProcessor) processor).postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) beanFactory);
}
processor.postProcessBeanFactory(beanFactory);
}
}
可以看到,这次针对processor,多了一个判断。如果是 BeanDefinitionRegistryPostProcessor
,则调用其方法,对bean定义注册后置处理器进行处理。同时,bean工厂后置处理器逻辑不变。这样,我们就把这个后置处理器的逻辑插入进来了。
到此位置,我们的Spring框架部分的改造,就算大功告成了。好像也没有多很多东西,只是加了个后置处理器以及处理器的插入点而已。但有了这些,我们就可以开始我们的手写Mybatis大业了。
来吧,终于到了测试环节了。前面的那么多章,测试都像一个陪衬环节。但是这次的测试,占到了重头。可以通过这次的测试,了解到Mybatis是如何实现的(虽然简化了很多)。
首先,我们的UserDao小朋友,一去不复返了。取而代之的是我们的UserMapper,嗯,更有Mybatis的感觉了。
package com.akitsuki.springframework.test.bean;
import java.util.HashMap;
import java.util.Map;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/8 14:42
*/
public interface UserMapper {
String queryUserName(Long id);
}
很好,味道很冲。不过这次我们为了简化,就不搞mapper的xml解析了。毕竟我们的大头是Spring,而不是Mybatis。
相应的,我们的UserService,也要跟着做出修改。将UserDao换成UserMapper。
接下来,是我们的重头戏,FactoryBean!
package com.akitsuki.springframework.test.bean;
import com.akitsuki.springframework.beans.factory.FactoryBean;
import com.akitsuki.springframework.test.common.ProxyMapperGenerator;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/29 10:23
*/
@AllArgsConstructor
@NoArgsConstructor
public class MapperFactoryBean<T> implements FactoryBean<T> {
private Class<T> mapperInterface;
private ProxyMapperGenerator<T> proxyMapperGenerator;
@Override
public T getObject() throws Exception {
//调用mapper生成器,来生产真正的bean
return proxyMapperGenerator.generateMapper(mapperInterface);
}
@Override
public Class<?> getObjectType() {
return mapperInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
嗯…看起来很逊?好像内容并不多。但是它值得我们好好唠一唠。
首先,我们看到它是一个泛型的类,这也是可以理解的,因为它要负责生产所有的mapper。类中有2个属性,mapperInterface是它要生产的mapper类型,这个很好理解。而ProxyMapperGenerator,接下来会介绍。它是用来具体生产mapper的,根据传入的mapper类型,去具体匹配对应的配置文件或者注解等信息,来生产出来对应的bean。这里原本的Mybatis是有一套很复杂的实现的,我这里简化处理了一下。
package com.akitsuki.springframework.test.common;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.test.bean.UserMapper;
import java.util.HashMap;
import java.util.Map;
/**
* 模拟代理生产mapper的类
* @author ziling.wang@hand-china.com
* @date 2022/11/30 13:51
*/
public class ProxyMapperGenerator<T> {
/**
* 根据mapper类型来生产对应的bean
* 这里做了简化写死了
* 实际上应该从配置文件进行解析读取
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public T generateMapper(Class<T> clazz) {
if (UserMapper.class.isAssignableFrom(clazz)) {
return (T) new UserMapper(){
private final Map<Long, String> userMap = new HashMap<>();
{
userMap.put(1L, "akitsuki");
userMap.put(2L, "toyosaki");
userMap.put(3L, "kugimiya");
userMap.put(4L, "hanazawa");
userMap.put(5L, "momonogi");
}
@Override
public String queryUserName(Long id) {
return userMap.get(id);
}
};
}
throw new BeanException("未找到对应mapper");
}
}
可以看到,我们把原来的UserDao的内容,搬了进来(换汤不换药(恼,而且实现也是通过接口的匿名内部类实现的。而Mybatis则是通过JDK动态代理来生产的。虽然咱技术是落后了一些,好歹意思是到位了。从FactoryBean,到具体的生产,都落实了。那么我们的后置处理器呢?这就来了。
package com.akitsuki.springframework.test.common;
import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.BeanDefinitionRegistryPostProcessor;
import com.akitsuki.springframework.beans.factory.config.PropertyValue;
import com.akitsuki.springframework.beans.factory.config.PropertyValues;
import com.akitsuki.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.akitsuki.springframework.test.bean.MapperFactoryBean;
import com.akitsuki.springframework.test.bean.UserMapper;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/29 17:01
*/
public class MapperRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeanException {
//动态的mapper生成器
ProxyMapperGenerator<?> mapperGenerator = new ProxyMapperGenerator<>();
//这里模拟mybatis根据接口动态生成bean
BeanDefinition definition = new BeanDefinition(MapperFactoryBean.class);
PropertyValues propertyValues = new PropertyValues();
PropertyValue mapperInterface = new PropertyValue("mapperInterface", UserMapper.class);
PropertyValue proxyMapperGenerator = new PropertyValue("proxyMapperGenerator", mapperGenerator);
propertyValues.addPropertyValue(mapperInterface);
propertyValues.addPropertyValue(proxyMapperGenerator);
definition.setPropertyValues(propertyValues);
registry.registerBeanDefinition("userMapper", definition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
}
精华都在这了。可以看到,这里根据 UserMapper
,创建了 BeanDefinition
。这里暂时先只考虑这一个的情况,如果多了,是需要进行扫描然后统一处理的。在创建的时候,将 mapperInterface
和 proxyMapperGenerator
属性进行填充,这里也用到了前面第四章所学到的 PropertyValues
。而且最关键的是,这里的beanClass,放入的是MapperFactoryBean,也就是我们这章的主角。最后,再将这个bean定义,注册进Spring即可。这波啊,这波是走后门特殊照顾。在配置文件之外的地方,将bean定义注册进去,小爷我就是不走官方渠道!
最后,把这个后置处理器注册成bean,我们的工作就算完成了。
<beans>
<bean id="userService" class="com.akitsuki.springframework.test.bean.UserService" scope="prototype">
<property name="dummyString" value="dummy"/>
<property name="dummyInt" value="114514"/>
<property name="userMapper" ref="userMapper"/>
bean>
<bean id="MapperRegistryPostProcessor" class="com.akitsuki.springframework.test.common.MapperRegistryPostProcessor"/>
beans>
可以看到,这里也移除了UserDao的注册,因为它的创建,已经交给我们的Mybatis·极致青春版(伪)了。而且,为了象征性的测试一下,这里也把userService的创建改为了prototype类型。
主要测试类没什么变化,每次最闲的就是它了
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/15 13:58
*/
public class ApiTest {
@Test
public void test() {
//初始化BeanFactory
ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
context.registerShutdownHook();
//获取bean,测试
UserService userService = context.getBean("userService", UserService.class);
userService.queryUserInfo(1L);
userService.queryUserInfo(4L);
userService.queryUserInfo(114L);
}
}
测试结果:
userService的afterPropertiesSet执行了
userService的afterPropertiesSet执行了
dummyString:dummy
dummyInt:114514
用户名:akitsuki
dummyString:dummy
dummyInt:114514
用户名:hanazawa
dummyString:dummy
dummyInt:114514
用户未找到>_<
Process finished with exit code 0
嗯,很好,很好…不对啊!userService的afterPropertiesSet执行了
这句话怎么输出了两遍!虽然我们这次将 UserService
改为了原型模式,但是也只调用了一次才对。这里一定有蹊跷!找了半天,才发现是 DefaultListableBeanFactory
中的 preInstantiateSingletons
方法在搞鬼。我们知道,新建 ApplicationContext
的时候,会调用刷新 refresh
方法,方法的最后一步,就是调用 preInstantiateSingletons
,提前对所有的单例对象进行初始化。这个方法原本的实现很简单,就是对所有的bean定义中的对象,进行一次 getBean
操作。但这次我们加入了原型模式,就不能再像从前一样了,需要加入对单例的判断。
@Override
public void preInstantiateSingletons() throws BeanException {
beanDefinitionMap.entrySet().stream().filter(entry -> entry.getValue().isSingleton())
.forEach(entry -> this.getBean(entry.getKey()));
// beanDefinitionMap.keySet().forEach(this::getBean);
}
再测试一次
userService的afterPropertiesSet执行了
dummyString:dummy
dummyInt:114514
用户名:akitsuki
dummyString:dummy
dummyInt:114514
用户名:hanazawa
dummyString:dummy
dummyInt:114514
用户未找到>_<
Process finished with exit code 0
很好,这次正常了。而且通过上面的问题,我们也看出来,prototype模式的确生效了。
那么,在最后,我们推导一下这次的bean生成顺序吧。
创建ApplicationContext->执行刷新方法->刷新工厂->加载配置文件中的bean定义->执行bean定义注册后置处理器->(创建UserMapper的bean定义)->(注册到Spring中)->执行bean工厂后置处理器->注册bean后置处理器->提前实例化单例对象->(准备实例化UserMapper)->(调用getBean)->(判断为FactoryBean类型)->(调用FactoryBean的getObject方法)->(调用mapper生成器生成具体的mapper)->(成功创建出一个mapper返回)->(放入单例bean缓存)->其他操作
括号中的内容,就是我们这次的重点内容了,可以看到,真是一个漫长的调用链,不由得又为Spring的精妙而感叹。
相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring
,这里对应的代码是mini-spring-09