Spring手撸源码系列-支持构造函数入参方式的类的实例化

Spring手撸源码系列-支持构造函数入参方式的类的实例化,第1张

Spring手撸源码系列-支持构造函数入参方式的类的实例

上一章节我们实现了可以对Bean对象进行定义、注册、获取Bean对象的功能,这一章节在此基础上改造可以支持加载类的构造函数以及入参方式来实例化Bean。

 首先上一章其实在UserService类里添加有参构造函数然后运行是会报错的。因为创建bean方法时实例化beanDefinition.getBeanClass().newInstance();实例化方式并没有考虑构造函数的入参,所以就会报错

public class UserService {
    private String name;

    public UserService(String name) {
        this.name = name;
    }  

    public void queryUserInfo(){
        System.out.println("查询用户信息");
    }
}

 这一章节就来填平这个错误

1.工程实现

 

2.UML类图

就像我们写的系统程序一样,新添加一个需求总会需要修改对应的类与方法,那么下面标红的就是在原有的类上面添加的方法与属性。

BeanFactory更改点:

我们考虑一下我们要加入参,入参参数肯定是用户给我们的,用户什么时候给的呢,就是在getBean()时将参数给我们的,这样我们才能将数据一一向各个实现的子类传递,最后进行实例化 *** 作,所以现在BeanFactory这里重载支持参数的方法Object getBean(String name,Object... args)你就应该能理解

AbstractBeanFactory更改点:

上节是模板方法,像个大集合,把该做的都聚集一起,但是具体实现由各个子类实现,自己只做模板,哈哈,这节还是这样,但是上节只支持无参,这节需要既能支持有参又能支持无参所以,也是添加了重载方法getBean(),抽取了添加doGetBean()做模板方法,还是上一节一样的方式。

AbstractAutowireCapableBeanFactory更改点:

这个类里就是更改了实现createBean()添加了入参的参数,然后又添加了createBeanInstance()用来可以获取构造方法的参数获取方式。但是你也看见了,这个类里依赖了一个类为InstantiationStrategy,这个类之前是没有的,后创建的作用是什么呢,回到思路上我们是获取了构造函数等参数什么的,但是我们还没有实例化啊,怎么实例化呢?所以InstantiationStrategy接口就是干这个用的。在此类中依赖实例化策略类接口,想使用Cglib方式用Cglib,想用Jdk方式就用jdk方式,这样的设计不需要if,else,

InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

InstantiationStrategy:

本章节新类-实例化的策略类接口,只有一个功能方法实例化instantiate()

CglibSubclassingInstantiationStrategy:

具体实例化实现类,这个类采用的是Cglib进行的实例化。

SimpleInstantiationStrategy:

具体实例化实现类,这个类采用的是JDK支持的实例化。

有逻辑有设计的类是不是在新需求的添加也不会乱呢,感觉对修改有条理、有章法,针对于获取和实例化时做了更改,其余的Bean的定义、注册、单例都没有任何更改,妙啊,也没有在设计是否调用某个模块采用if,else了。

3.代码实现

BeanFactory:可以支持获取bean对象时入参

// 定义bean工厂接口
public interface BeanFactory {
    // 获取bean
    Object getBean(String name) throws BeansException;

    // 04章节-添加,可以传参数
    Object getBean(String name,Object... args);
}

 AbstractBeanFactory:按上一章稍有变动,由于既无参又有有参但是要做的事情是一样的所以抽离出来doGetBean做上一章需要做的事情(获取单例对象,获取不到获取bean定义对象,获取到了进行创建Bean了)

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
 
    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null);
    }

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

    // 还是模板方法
    protected  T doGetBean(final String name, final Object[] args) {
        // 获取单例bean
        Object bean = getSingleton(name);
        if (bean != null) {
            System.out.println("因为存在对象,从单例容器中取出对象");
            return (T) bean;
        }
        BeanDefinition beanDefination = getBeanDefinition(name);
        return (T) createBean(name, beanDefination, args);
    }
    // *************************第4节添加--可传入参***********************

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    // 此方法第四节传添加了一个参数为Object[] args
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException;

}

InstantiationStrategy:定义实例化的接口,是采用cglib方式啊还是采用JDK的方式啊,

// 实例化策略接口
public interface InstantiationStrategy {
    // 添加必要的入参信息,还有Constructor:必要的类信息,目的是为了拿到符合入参信息相对应的构造函数
    // args具体的入参信息,最终实例化时候会用到
    Object instantiate(BeanDefinition beanDefination, String beanName, Constructor ctro, Object[] args);
}

CglibSubclassingInstantiationStrategy:Cglib方式去做实例化 *** 作。 

// Cglib的实例化
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
    @Override
    public Object instantiate(BeanDefinition beanDefination, String beanName, Constructor ctro, Object[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefination.getBeanClass());
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (null == ctro) return enhancer.create();
        return enhancer.create(ctro.getParameterTypes(), args);
    }
}

SimpleInstantiationStrategy:jdk方式进行实例化

// jdk实例化
public class SimpleInstantiationStrategy implements InstantiationStrategy {
    @Override
    public Object instantiate(BeanDefinition beanDefination, String beanName, Constructor ctro, Object[] args) {
        Class clazz = beanDefination.getBeanClass();
        try {
            if (null != clazz) {
                // 实例化有参构造函数
                // 把入参
                return clazz.getDeclaredConstructor(ctro.getParameterTypes()).newInstance(args);
            } else {
                // 实例化无参构造函数
                return clazz.getDeclaredConstructor().newInstance();
            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new BeansException("failed to instantiate [" + clazz.getName() + "]", e);
        }
    }
}

 AbstractAutowireCapableBeanFactory:抽取出createBeanInstance()用来获取有参或无参的构造函数,之后开始决定是需要Cglib方式实例化还是jdk实例化,我这里是选择了Cglib,可以通过这个代码去指定不同的策略:InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

// 真正实例化Bean类,也就是说真正的获取类实例对象了
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(beanDefinition, beanName, args);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        // 获取bean实例以后添加到单例对象中
        addSingleton(beanName, bean);
        return bean;
    }
  
    // 抽取createBeanInstance方法,通过beanClass.getDeclaredConstructors()获取构造函数的集合
    // 循环对比构造函数集合与入参信息的匹配情况,这里只是对一种函数数量对比,实际spring源码还需要比对入参类型
    // 否则相同数量不同入参类型的情况,就会抛异常了
    // 构造方法填充
    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor constructorToUse = null;
        Class beanClass = beanDefinition.getBeanClass();
        Constructor[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }
    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }
}
4.测试

准备条件,将需要bean容器管理的类弄好

public class UserService {

    private String name;

    public UserService(String name) {
        this.name = name;
    }

    public void queryUserInfo() {
        System.out.println("查询用户信息:"+name);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("");
        sb.append("").append(name);
        return sb.toString();
    }
}
public class ApiTest04 {
    @Test
    public void test_BeanFactory() {
        // 1.初始化 BeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 2.注册 bean
        BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
        beanFactory.registerBeanDefinition("userService", beanDefinition);
        // 3.获取 bean
        UserService userService = (UserService) beanFactory.getBean("userService","xdf");
        userService.queryUserInfo();
    }
}

运行结果:可以自己切换jdk或者是Cglib运行结果都是一致的 

 这篇博客就到这里了啊!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5482258.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存