• 手写Spring-第九章-FactoryBean:我是工厂,也是Bean


    前言

    上回书说到,我们用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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    新增的文件不算多,但我觉得这次的实现还蛮有意思的。那么,我们开始吧。

    扩充:原型模式

    我们之前的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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    这里要注意,我们的 setScope中,进行了操作,对 scope进行操作时,会同时改变 singletonprototype属性。

    既然这里已经加入了属性,那么我们加载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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    好了,现在配置可以被正确加载了,也可以正确放到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));
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    这里我们不仅在创建bean的时候进行了判断,还在销毁方法处进行了处理。因为只有单例bean才需要进行销毁。

    进入正题:FactoryBean

    上面我们稍微扩充了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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    从这个泛型我们也可以看出,一个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.");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    首先我们可以看到,它继承了 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;
        }
    }
    
    
    // 其他方法、属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    这里只列出了必要的内容。我们从上往下分析。我们知道,在一开始,AbstractBeanFactory是继承自 DefaultSingletonBeanRegistry 的,而现在转为继承 FactoryBeanRegistrySupport,这就意味着,它扩展了自己的功能。因为 FactoryBeanRegistrySupport本身也是继承自 DefaultSingletonBeanRegistry的,所以对于 AbstractBeanFactory来说,功能并没有受到影响,而且多了一些操作 FactoryBean的方法。

    我们可以看到,这里在获取到bean之后,并不直接返回了,而是先交给 getObjectForBeanInstance来处理。而这里会对 BeanFactory类型进行处理,调用我们上面实现的 getObjectFromFactoryBean方法。而我们知道,这个方法的具体实现,则是通过调用 factoryBeangetObject方法,来获取真正的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定义中。**一句话就概括完了,对不对?但你可能还是云里雾里的。我们下面就来详细解析。

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    嗯,可以看到提供的方法,将 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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    可以看到,这次针对processor,多了一个判断。如果是 BeanDefinitionRegistryPostProcessor,则调用其方法,对bean定义注册后置处理器进行处理。同时,bean工厂后置处理器逻辑不变。这样,我们就把这个后置处理器的逻辑插入进来了。

    到此位置,我们的Spring框架部分的改造,就算大功告成了。好像也没有多很多东西,只是加了个后置处理器以及处理器的插入点而已。但有了这些,我们就可以开始我们的手写Mybatis大业了。

    测试!这次连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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    很好,味道很冲。不过这次我们为了简化,就不搞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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    嗯…看起来很逊?好像内容并不多。但是它值得我们好好唠一唠。

    首先,我们看到它是一个泛型的类,这也是可以理解的,因为它要负责生产所有的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");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    可以看到,我们把原来的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) {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    精华都在这了。可以看到,这里根据 UserMapper,创建了 BeanDefinition。这里暂时先只考虑这一个的情况,如果多了,是需要进行扫描然后统一处理的。在创建的时候,将 mapperInterfaceproxyMapperGenerator属性进行填充,这里也用到了前面第四章所学到的 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到,这里也移除了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);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    测试结果:

    userService的afterPropertiesSet执行了
    userService的afterPropertiesSet执行了
    dummyString:dummy
    dummyInt:114514
    用户名:akitsuki
    dummyString:dummy
    dummyInt:114514
    用户名:hanazawa
    dummyString:dummy
    dummyInt:114514
    用户未找到>_<
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    嗯,很好,很好…不对啊!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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    再测试一次

    userService的afterPropertiesSet执行了
    dummyString:dummy
    dummyInt:114514
    用户名:akitsuki
    dummyString:dummy
    dummyInt:114514
    用户名:hanazawa
    dummyString:dummy
    dummyInt:114514
    用户未找到>_<
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    很好,这次正常了。而且通过上面的问题,我们也看出来,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

  • 相关阅读:
    SpringBoot启动时加载
    Vue.js的快速入门(Vue、axios、node、js、npm、wepback的快速入门)
    【Git】手把手教你在Windows系统下安装Git
    LeetCode 356. Line Reflection【数学,哈希表】中等
    【C++】侯捷:C++面向对象-笔记-01
    磁盘调度算法
    AcWing 800. 数组元素的目标和——算法基础课题解
    vb.net 实时监控双门双向门禁控制板源代码
    VUE的10个常用指令
    Vue 中使用事件总线来进行组件间通信($emit()、$on() 和 $off())
  • 原文地址:https://blog.csdn.net/Forget_Re/article/details/128117528