手写spring第三章-重构,使用依赖关系完善实例化bean操作

文章目录

前言

在之前的文章中,我们手写了一个基于单一职责原则以及模板方法模式设计的spring简版框架,但是在使用过程中我们发现,若需要装入容器的bean是带参的时,创建bean的时候会出现问题。对此,在本章,我们就需要针对这个问题对框架进行相应调整。

目的

通过本次的代码修改,使得容器不仅仅可以存取无参bean,也能存取带参bean。

设计思路

由于这个需求需要对容器进行较大的调整,笔者在重构时,会进行自顶向下的分析。相应类图如下所示:
手写spring第三章-重构,使用依赖关系完善实例化bean操作

BeanFactory改造点分析

通过查看之前的代码,我们发现BeanFactorygetBean方法,参数只有一个name,假设我的项目已经上线且这个函数在非带参情况下使用得很好,那么在实现本文需求时,我们不应该去改动这个函数,而是增加一个新的getBean方法,只不过参数是(String name,Object… agrs),这也算是在另一个角度上体现了开闭原则( 对修改关闭,对拓展开放)。

AbstractBeanFactory改造点分析

完成了最顶层的思路分析后,我们就开始看看,规范容器行为的抽象类AbstractBeanFactory,在本次重构中,我们在继承BeanFactory接口时,添加了一个新的getBean方法,编写完成后的代码如下:

public Object getBean(String name,Object... args) throws BeansException {
		Object bean = getSingleton(name);
        if (bean != null) {
            return (T) bean;
        }

        BeanDefinition beanDefinition = getBeanDefinition(name);
        return (T) createBean(name, beanDefinition, args);
       }

这时候我们发现一个问题,这段代码不就和**getBean(String name)**的代码差不多嘛?

public Object getBean(String name) throws BeansException {
        Object singleton = getSingleton(name);
        if (singleton!=null){
            return singleton;
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name,beanDefinition);
    }

本着代码简洁之道,对于这种情况我们就应该分析两个getBean的共性,然后抽取出一段通过代码。通过分析我们可以看出,这两个getBean的区别仅仅是创建对象时,是否需要传构造函数参数而已,所以我们索性写一个doGetBean方法,函数参数为**(String name, Object… args),这样以来,无参的getBean方法,args传null就行了。
经过这样一段手术之后,之前的抽象方法createBean(String beanName, BeanDefinition beanDefinition)明显不适用了(我们既然可能要取出带参的bean,那么我就应该要能创建带参的bean),所以新的createBean应该是
createBean(String beanName, BeanDefinition beanDefinition, Object[] args)**

实例化工具设计

考虑到实例化bean的方法有很多,如jdk、cglib等,所以在进行实例化bean工具编写的时候,我们就应该考虑到长远的发展,要保证代码能够随时扩展出新的工具,所以我们将实例化bean的这种工具统一用一个接口来限制他们的工作,同时我们也能够很好的遵循依赖倒置原则,高层模块不应该依赖细节模块,而是依赖其抽象,故创建一个接口InstantiationStrategy,使得其他高层模块需要用到这个工具时,只需将其作为私有成员变量即可,这样后续该策略具体实现代码无论如何修改都不会影响到高层模块其他逻辑,且保证各司其职。代码如下:

public interface InstantiationStrategy {

    Object instantiate(String beanName, BeanDefinition beanDefinition, Constructor ctor,Object[] args) throws BeansException;
}

之后我们在编写两个实例化bean的类,一个基于jdk的SimpleInstantiationStrategy,另一个则是基于cglib的CglibSubclassingInstantiationStrategy

AbstractAutowireCapableBeanFactory改造点分析

我们之前的createBean方法代码如下所示:

@Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        Object bean = null;
        try {
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        addSingleton(beanName, bean);
        return bean;
    }

考虑到创建bean可能会出现有参也可能会出现无参的情况,我们在重构时,就需要对代码进行通用性的设计了,因为在上文中我们已经在AbstractBeanFactory做了相应处理,代码如下:

 public Object getBean(String name) throws BeansException {
       return doGetBean(name,null);
    }


    public Object getBean(String name,Object... args) throws BeansException {
        return doGetBean(name,args);
    }

对于本文,我们只需判断构造函数是否带参,若带参则传构造函数和参数给实例化策略,若无参则直接传空构造函数即可。代码如下所示,细心的读者可以看出,对于创建bean的工作,笔者又创建一个函数createBeanInstance,这是为什么呢?代码简洁之道曾说过:

同一个函数中的代码应该属于同一层级

由于之前的代码已经有了一个addSingleton方法,为了让代码层次保持一致,所以创建bean的具体细节也放到一个函数中,即createBeanInstance

private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanName, beanDefinition, args);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }


        addSingleton(beanName, bean);
        return bean;
    }


    protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructor = beanClass.getDeclaredConstructors();
        for (Constructor<?> ctor : declaredConstructor) {
            if (args != null && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }

        return getInstantiationStrategy().instantiate(beanName, beanDefinition, constructorToUse, args);
    }


    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

类图

手写spring第三章-重构,使用依赖关系完善实例化bean操作

代码结构

手写spring第三章-重构,使用依赖关系完善实例化bean操作

代码

BeanFactory

package cn.shark.springframework.beans.factory;

import cn.shark.springframework.beans.BeansException;

/**
 * 将bean工厂设置为接口,便于后续进行各式各样的扩展
 */
public interface BeanFactory {

    Object getBean(String name) throws BeansException;


    Object getBean(String name,Object... agrs) throws BeansException;



}

AbstractBeanFactory

package cn.shark.springframework.beans.factory.support;


import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.BeanFactory;
import cn.shark.springframework.beans.factory.config.BeanDefinition;


public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {


    public Object getBean(String name) throws BeansException {
       return doGetBean(name,null);
    }


    public Object getBean(String name,Object... args) throws BeansException {
        return doGetBean(name,args);
    }

    /**
     * 使用模板方法模式保证后面的bean容器只需专注自己的责任,以及统一bean容器规范
     *
     * @param name
     * @return
     * @throws BeansException
     */
    protected <T> T doGetBean(String name, Object... args) {
        Object bean = getSingleton(name);
        if (bean != null) {
            return (T) bean;
        }

        BeanDefinition beanDefinition = getBeanDefinition(name);
        return (T) createBean(name, beanDefinition, args);
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;


    /**
     * 创建bean,并且放到单例容器中
     *
     * @param beanName
     * @param beanDefinition
     * @return
     * @throws BeansException
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;


}

InstantiationStrategy

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;

public interface InstantiationStrategy {

    Object instantiate(String beanName, BeanDefinition beanDefinition, Constructor ctor,Object[] args) throws BeansException;
}

SimpleInstantiationStrategy

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SimpleInstantiationStrategy implements InstantiationStrategy {
    @Override
    public Object instantiate(String beanName, BeanDefinition beanDefinition, Constructor ctor, Object[] args) throws BeansException {
        Class clazz = beanDefinition.getBeanClass();
        try {
            if (ctor != null) {

                return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);

            } else {
                return clazz.getDeclaredConstructor().newInstance();
            }
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
        }
    }
}

CglibSubclassingInstantiationStrategy

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;

import java.lang.reflect.Constructor;

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
    @Override
    public Object instantiate(String beanName, BeanDefinition beanDefinition, Constructor ctor, Object[] args) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());
        /**
         * 在cglib中Callback是一个标记接口,Enhancer使用的回调就是cglib中Callback接口的子接口
         */
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (ctor == null) {
            return enhancer.create();
        }

        return enhancer.create(ctor.getParameterTypes(), args);
    }
}

AbstractAutowireCapableBeanFactory

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanName, beanDefinition, args);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }


        addSingleton(beanName, bean);
        return bean;
    }


    protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructor = beanClass.getDeclaredConstructors();
        for (Constructor<?> ctor : declaredConstructor) {
            if (args != null && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }

        return getInstantiationStrategy().instantiate(beanName, beanDefinition, constructorToUse, args);
    }


    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }
}

测试用例

package cn.shark.springframework;

import cn.shark.springframework.bean.UserService;
import cn.shark.springframework.beans.factory.config.BeanDefinition;
import cn.shark.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.junit.Test;

public class ApiTest {

    @Test
    public void test(){
        DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();

        BeanDefinition beanDefinition=new BeanDefinition(UserService.class);
        beanFactory.registerBeanDefinition("userService",beanDefinition);

        UserService userService= (UserService) beanFactory.getBean("userService","小明");
        userService.queryUserInfo();
    }
}


手写spring第三章-重构,使用依赖关系完善实例化bean操作

总结

通过本次重构我们可以看出,正是因为良好的结构设计,严格遵守软件设计原则,重构的代码改动不是很大,我们在本次重构中完完全全没有动到BeanDefinition注册逻辑,即BeanDefinitionRegistry相关接口和继承类,也正是由于单一职责的架构设计DefaultListableBeanFactory只专注于自己个性化定制的或者beanDefinition和注册beanDefinition,而创建bean的工作由其继承的抽象类AbstractAutowireCapableBeanFactory完成了,所以本次重构中,DefaultListableBeanFactory完完全全不需要任何的改动。这就是软件设计原则的重要性。

参考文章

软件架构设计的七大原则

开闭原则- 对修改关闭,对拓展开放

手写spring第二章-运用设计模式编写可扩展的容器

代码整洁之道

《Spring 手撸专栏》第 4 章:崭露头角,基于Cglib实现含构造函数的类实例化策略

上一篇:简单手写模拟spring底层原理


下一篇:IoC之控制反转引入