一、目标

这一章节的目标主要是为了解决上一章节我们埋下的坑,那是什么坑呢?其实就是一个关于 Bean 对象在含有构造函数进行实例化的坑。在上一章节我们扩充了 Bean 容器的功能,把实例化对象交给容器来统一处理,但在我们实例化对象的代码里并没有考虑对象类是否含构造函数,也就是说如果我们去实例化一个含有构造函数的对象那么就要抛异常了。怎么验证?其实就是把 UserService 添加一个含入参信息的构造函数就可以。

  • 验证
public class UserService {private String name;public void queryUserInfo(){System.out.println("查询用户信息");}public UserService(String name) { this.name = name; }
}


发生这一现象的主要原因就是因为 beanDefinition.getBeanClass().newInstance(); 实例化方式并没有考虑构造函数的入参,所以就这个坑就在这等着你了!那么我们的目标就很明显了,来把这个坑填平!

二、设计

填平这个坑的技术设计主要考虑两部分

  • 一个是串流程从哪合理的把构造函数的入参信息传递到实例化操作里
  • 另外一个是怎么去实例化含有构造函数的对象。
  • 参考 Spring Bean 容器源码的实现方式,在 BeanFactory 中添加 Object getBean(String name, Object... args) 接口,这样就可以在获取 Bean 时把构造函数的入参信息传递进去了。
  • 另外一个核心的内容是使用什么方式来创建含有构造函数的 Bean 对象呢?这里有两种方式可以选择,一个是基于 Java 本身自带的方法DeclaredConstructor,另外一个是使用 Cglib 来动态创建 Bean 对象。 Cglib 是基于字节码框架 ASM 实现,所以你也可以直接通过 ASM 操作指令码来 创建对象

关于代理的文章

三、实现

  1. 工程结构
  2. 类依赖图

相比于第二章的类依赖图,主要变化:

  • 在现有工程中添加 InstantiationStrategy 实例化策略接口
  • 以及在BeanFactory接口中补充相应的 getBean 入参信息,让外部调用时可以传递构造函数的入参并顺利实例化。
  • BeanFactory
//BeanFactory中我们重载了一个含有入参信息 args 的 getBean 方法,这样就可以方便的传递入参给构造函数实例化了
public interface BeanFactory {Object getBean(String name) throws BeansException;Object getBean(String name, Object... args) throws BeansException;
}
  • 定义实例化策略接口
//在实例化接口 instantiate 方法中添加必要的入参信息,包括:beanDefinition、beanName、ctor、args
//其中 Constructor 你可能会有一点陌生,它是 java.lang.reflect 包下的Constructor 类,里面包含了一些必要的类信息,有这个参数的目的就是为了拿到符合入参信息相对应的构造函数。
//而 args 就是一个具体的入参信息了,最终实例化时候会用到。
public interface InstantiationStrategy {Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;
}
  • 实例化(使用上面提到的两种方式)

JDK方式:

public class SimpleInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Class clazz = beanDefinition.getBeanClass();try {if (null != ctor) {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);}}
}
  • 首先通过 beanDefinition 获取 Class 信息,这个 Class 信息是在 Bean 定义的时候传递进去的。
  • 接下来判断 ctor 是否为空,如果为空则是无构造函数实例化,否则就是需要有构造函数的实例化
  • 这里我们重点关注有构造函数的实例化,实例化方式为clazz.getDeclaredConstructor(ctor.getParameterTypes()).ne wInstance(args);把入参信息传递给 newInstance 进行实例化。

CGLIB方式:

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {@Overridepublic Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(beanDefinition.getBeanClass());enhancer.setCallback(new NoOp() {@Overridepublic int hashCode() {return super.hashCode();}});if (null == ctor) return enhancer.create();return enhancer.create(ctor.getParameterTypes(), args);}
}
  1. 创建策略调用
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();@Overrideprotected 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);}addSingleton(beanName, bean);return bean;}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;}public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {ths.instantiationStrategy = instantiationStrategy;}
}
  • 首先在 AbstractAutowireCapableBeanFactory 抽象类中定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类
  • 接下来抽取 createBeanInstance 方法,在这个方法中需要注意 Constructor代表了你有多少个构造函数,通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合。
  • 接下来就需要循环比对出构造函数集合与入参信息 args 的匹配情况,这里我们对比的方式比较简单,只是一个数量对比,而实际 Spring 源码中还需要比对入参类型,否则相同数量不同入参类型的情况,就会抛异常了。
  1. 测试
    @Testpublic void test_BeanFactory() {// 1.初始化 BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 2. 注入beanBeanDefinition beanDefinition = new BeanDefinition(UserService.class);beanFactory.registerBeanDefinition("userService", beanDefinition);// 3.获取beanUserService userService = (UserService) beanFactory.getBean("userService", "小傅哥");userService.queryUserInfo();}

四、总结

  • 本章节的主要以完善实例化操作,增加 InstantiationStrategy 实例化策略接口,并新增了两个实例化类。这部分类的名称与实现方式基本是 Spring 框架的一个缩小版,大家在学习过程中也可以从 Spring 源码找到对应的代码。
  • 从我们不断的完善增加需求可以看到的,当你的代码结构设计的较为合理的时候,就可以非常容易且方便的进行扩展不同属性的类职责,而不会因为需求的增加导致类结构混乱。所以在我们自己业务需求实现的过程中,也要尽可能的去考虑一个良 好的扩展性以及拆分好类的职责。

手写简版spring --3--对象实例化策略相关推荐

  1. 手写简版spring --8--Aware感知容器对象Aware感知容器对象

    一.目标 目前已实现的 Spring 框架,在 Bean 操作上能提供出的能力,包括:Bean 对象的定义和注册,以及在操作 Bean 对象过程中执行的,BeanFactoryPostProcesso ...

  2. 手写简版spring --5--资源加载器解析文件注册对象

    一.目标 在完成 Spring 的框架雏形后,现在我们可以通过单元测试进行手动操作Bean对象的定义.注册和属性填充,以及最终获取对象调用方法.但这里会有一个问题,就是如果实际使用这个 Spring ...

  3. 手写简版spring --4--注入属性和依赖对象

    一.目标 首先我们回顾下这几章节都完成了什么,包括:实现一个容器.定义和注册Bean.实例化Bean,按照是否包含构造函数实现不同的实例化策略,那么在创建对象实例化这我们还缺少什么?其实还缺少一个关于 ...

  4. 手写简版spring --10--容器事件和事件监听器

    一.降低耦合 解耦场景在互联网开发的设计中使用的也是非常频繁,如:这里需要一个注册完成事件推送消息.用户下单我会发送一个MQ.收到我的支付消息就可以发货了等等,都是依靠事件订阅和发布以及MQ消息这样的 ...

  5. 手写简版spring --9--对象作用域和FactoryBean

    一.目标 交给 Spring 管理的 Bean 对象,一定就是我们用类创建出来的 Bean 吗?创建出来的 Bean 就永远是单例的吗,没有可能是原型模式吗?在集合 Spring 框架下,我们使用的 ...

  6. 手写简版spring --2--实现Bean的定义、注册、获取

    一.目标 在上一章节我们初步依照 Spring Bean 容器的概念,实现了一个粗糙版本的代码实现.那么本章节我们需要结合已实现的 Spring Bean 容器进行功能完善,实现 Bean 容器关于 ...

  7. 手写简版spring --6--应用上下文(BeanPostProcessor 和 BeanFactoryPostProcessor)

    一.目标 如果你在自己的实际工作中开发过基于 Spring 的技术组件,或者学习过关于SpringBoot 中间件设计和开发等内容.那么你一定会继承或者实现了 Spring对外暴露的类或接口,在接口的 ...

  8. 手写简版spring --1--创建简单的Bean容器

    一.声明 这个系列是我自己的学习笔记,为了在学习的过程中巩固知识而记录的,好强迫自己用心拜读,而不是进收藏夹.本系列都是基于小缚哥的文章和代码的,想要深入了解,请移步小缚哥博客 二.spring-Be ...

  9. 手写简版spring --7--初始化方法和销毁方法

    一.目标 当我们的类创建的 Bean 对象,交给 Spring 容器管理以后,这个类对象就可以被赋予更多的使用能力.就像我们在上一章节已经给类对象添加了修改注册Bean定义未实例化前的属性信息修改和实 ...

最新文章

  1. 成功抓取douban 所有电影
  2. Filebeat的下载(图文讲解)
  3. java:UDP通信
  4. angularjs移除不必要的$watch
  5. oracle 绑定变量模糊查询,求助-ACTIVE DG 异常shutdown
  6. java中抽象类,abstract关键字
  7. matlab的m函数入门2
  8. 【linux】linux shell if 多条件 并行 字符串判断
  9. CNN中各类卷积总结:残差、shuffle、空洞卷积、变形卷积核、可分离卷积等
  10. LeetCode每日一题——两数相加
  11. 一文轻松搞懂-条件随机场CRF
  12. 华为鸿蒙系统多而能使用吗,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  13. pe修改rpc服务器不可用,电脑rpc服务器不可用,教你电脑rpc服务器不可用怎么解决...
  14. CVPR-2020 AAAI2020 CVPR-2019 NIPS-2019 ICCV-2019 IJCAI-2019 论文超级大合集下载,整理好累,拿走不谢
  15. 打造双网卡负载均衡服务器(转)
  16. windows全局消息钩子的一个BUG
  17. 图片嵌在文字里首行缩进
  18. GMap地图的加载和现实
  19. GBU810-ASEMI整流桥GBU810
  20. 根据psy输出sql

热门文章

  1. C# 网站静态页面生成器 for 多线程版
  2. 北风设计模式课程---13、享元模式
  3. 借助JVM生日的时机,说说关于JVM你所不知道的那些事
  4. 前端编码规范,个人感觉bootstrap总结的不错,拿出来给大家分享
  5. Unity3d webplayer发布的问题和100%自适应浏览器
  6. 高精度加减法 1000阶乘求法
  7. GPS-nmealib学习
  8. CUDA从入门到精通(零):写在前面
  9. 在C/C++中嵌入Python
  10. scatter() 散点图样式