【手撸Spring】03 基于Cglib实现含构造函数的类实例化策略

Spring03 手写AOP——代理模式 什么是AOP 使用 logback 日志框架 手写AOP Spring的AOP通知类型(了解) 阅读详情

基于Cglib实现含构造函数的类实例化策略

目标

这一章节的目标主要是为了解决上一章节我们埋下的坑,那是什么坑呢?其实就是一个关于 Bean 对象在含有构造函数进行实例化的坑。

在上一章节我们扩充了 Bean 容器的功能,把实例化对象交给容器来统一处理,但在我们实例化对象的代码里并没有考虑对象类是否含构造函数,也就是说如果我们去实例化一个含有构造函数的对象那么就要抛异常了。

怎么验证?其实就是把 UserService 添加一个含入参信息的构造函数就可以,如下:

public class UserService {

    private String name;

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

    // ...
}

报错如下:

java.lang.InstantiationException: cn.bugstack.springframework.test.bean.UserService

	at java.lang.Class.newInstance(Class.java:427)
	at cn.strongwind.springframework.test.ApiTest.test_newInstance(ApiTest.java:51)
	...

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

设计

填平这个坑的技术设计主要考虑两部分,一个是串流程从哪合理的把构造函数的入参信息传递到实例化操作里,另外一个是怎么去实例化含有构造函数的对象。
在这里插入图片描述

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

实现

1、目录结构

small-spring-step-03
└── src
    ├── main
    │   └── java
    │       └── cn.strongwind.springframework.beans
    │           ├── factory
    │           │   ├── config
    │           │   │   ├── BeanDefinition.java
    │           │   │   └── SingletonBeanRegistry.java
    │           │   ├── support
    │           │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   ├── AbstractBeanFactory.java
    │           │   │   ├── BeanDefinitionRegistry.java
    │           │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   ├── DefaultListableBeanFactory.java
    │           │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   ├── InstantiationStrategy.java
    │           │   │   └── SimpleInstantiationStrategy.java
    │           │   └── BeanFactory.java
    │           └── BeansException.java
    └── test
        └── java
            └── cn.strongwind.springframework.test
                ├── bean
                │   └── UserService.java
                └── ApiTest.java

在这里插入图片描述
本章节“填坑”主要是在现有工程中添加 InstantiationStrategy 实例化策略接口,以及补充相应的 getBean 入参信息,让外部调用时可以传递构造函数的入参并顺利实例化。

2、新增getBean接口

public interface BeanFactory {

    Object getBean(String name) throws BeansException;

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

}

  • BeanFactory 中我们重载了一个含有入参信息 args 的 getBean 方法,这样就可以方便的传递入参给构造函数实例化了。

3、定义实例化策略接口

public interface InstantiationStrategy {

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

}
  • 在实例化接口 instantiate 方法中添加必要的入参信息,包括:beanDefinition、 beanName、ctor、args
  • 其中 Constructor 你可能会有一点陌生,它是 java.lang.reflect 包下的 Constructor 类,里面包含了一些必要的类信息,有这个参数的目的就是为了拿到符合入参信息相对应的构造函数。
  • 而 args 就是一个具体的入参信息了,最终实例化时候会用到

4、JDK实例化

public class SimpleInstantiationStrategy implements InstantiationStrategy {

    @Override
    public 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()).newInstance(args);,把入参信息传递给 newInstance 进行实例化。

5、Cglib实例化

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {

    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        if (null == ctor) return enhancer.create();
        return enhancer.create(ctor.getParameterTypes(), args);
    }

}

6、创建策略调用

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);
        }

        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);
    }

}

  • 首先在 AbstractAutowireCapableBeanFactory 抽象类中定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类。
  • 接下来抽取 createBeanInstance 方法,在这个方法中需要注意 Constructor 代表了你有多少个构造函数,通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合。
  • 接下来就需要循环比对出构造函数集合与入参信息 args 的匹配情况,这里我们对比的方式比较简单,只是一个数量对比,而实际 Spring 源码中还需要比对入参类型,否则相同数量不同入参类型的情况,就会抛异常了。

参考链接

基于Cglib实现含构造函数的实例化策略

第 04 章:崭露头角,基于Cglib实现含构造函数的类实例化策略 作者:小傅哥 博客:https://bugstack.cn 沉淀、分享、成长,让自己和他人都能有所收获!???? 一、前言 技术成长,是对场景设计细节不断的雕刻! 你觉得自己的技术什么时候得到了快速的提高,是CRUD写的多了以后吗?想都不要想,绝对不可能!CRUD写的再多也只是能满足你作为一个搬砖工具人,敲击少逻辑流水代码的速度而已,而编程能力这一块,除了最开始的从不熟练到熟练以外,就很少再有其他提升了。 那你可能会想什么才是编程能力提升?其实更多的编程能力的提升是你对复杂场景的架构把控以及对每一个技术 阅读详情

相关推荐

【Spring】实战记录:Spring Boot 动态注册 Bean 遭遇 CGLIB 代理失败全复盘

JDK 动态代理:基于接口。如果目标类实现了接口,默认优先使用此方式。CGLIB 代理:基于继承。如果目标类没有实现接口,或者强制配置了,Spring 会使用 CGLIB 生成目标类的子类。在本案例中,动态注册的逻辑可能隐式地强制了 CGLIB 代理,或者该类虽然实现了接口但在动态注册上下文中被判定为需要类代理。整个排查与修复的流程可以用以下流程图概括:fill:#333;important;important;fill:none;color:#333;color:#333;important;

九师兄 280

Spring手撸04:使用Cglib实现含有构造函数的类实现策略

上回书说到Spring 实现Bean定义/注册/创建的一个流程,但在创建的时候其实埋了个坑,今天这回书,就把这个坑填掉! 主要就是在实例化这个过程,只是草率的使用class.newInstance(); 但其实这个方法已经被废弃掉了,因为这个方法必须要有无参构造方法,所以很不灵活. 所以今天就来重写一下获取实例化的方法. BeanFactory 首先因为每个Bean的构造函数是不固定的,所以可以用这个方式来扩展一下getBean() public interface BeanFactory { O

weixin_45369440的博客 658

Python实战:用SkillMetrics库5分钟搞定泰勒图绘制(附完整代码)

本文详细介绍了如何使用Python的SkillMetrics库快速绘制专业泰勒图,以直观对比多个模型的预测性能。通过整合相关系数、标准差和中心化均方根误差三个核心指标,该工具能帮助数据科学家和研究者高效完成模型评估与可视化,提升分析报告质量。文章提供了从环境配置、数据准备到高级定制与子图对比的完整代码示例。

7z8x2c4v6b9的博客 372

【手撸Spring】第三节:扩展实例化Bean策略,增加Cglib的方式

链接: lite-spring在上一节中,使用Class.newInstance的方式创建了Bean,调用的无参构造函数去创建,那在本节中,增加Cglib的方式去创建,并且增加有参的构造函数创建Bean,并且是使用到了策略模式去调用Bean创建的方式 定义Bean实例化策略接口实现类 JDK实现类 Cglib实现类 在Bean实例化调用类中注入我们的策略类 以上增加Cglib的方式就实现了

weixin_43100952的博客 196

ChatBI实战:如何用Vanna和WrenAI搭建企业级智能问数系统(附避坑指南)

本文详细介绍了如何利用Vanna和WrenAI搭建企业级智能问数系统(ChatBI),解决多源异构数据整合、语义理解准确率和系统性能安全等核心挑战。通过实战案例和优化策略,展示了这一组合方案在提升查询效率、实现业务自主分析和动态探索式分析方面的显著优势,特别适合企业级数据查询需求。

weixin_29047919的博客 400

V.PhyloMaker实战:从物种列表到维管束植物系统发育树的完整流程

本文详细介绍了使用V.PhyloMaker工具包,从物种列表快速构建维管束植物系统发育树的完整实战流程。内容涵盖R包安装、物种名标准化、核心函数phylo.maker的三种构建方案选择、结果树的可视化与导出,以及与picante包的联动分析,为生态学和生物地理学研究提供了高效解决方案。

weixin_29057237的博客 376

手写spring 03 - Cglib 构造函数实例化对象

文章目录目标设计实现新增getBean接口定义实例化策略接口JDK 实例化Cglib 实例化创建策略调用测试测试有参的构造实例创建测试无构造函数的测试有构造函数实例化获取构造函数信息测试Cglib实例化测试Cglib的策略实例化 目标 实现有参构造函数实例化对象. 设计 参考Spring Bean的容器源码实现方式, 在BeanFactory中添加Object getBean(String name, Object... args) 创建有参构造bean对象的两种方式 基于jdk的DeclaredCons

Java持续实践 1530

CGLIB原理及实现机制

CGLIB原理及实现机制 什么是CGLIB CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。 为什么使用CGLIB CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么

weixin_40516305的博客 2164

AUTOSAR/汽车行业

AUTOSAR架构及汽车产业链相关介绍

m0_54254513的博客 1万+

【深度学习基础】Temporal Fusion Transformer

从下向上看:全连接(Dense) → ELU(指数线性激活函数(比relu好点,有利于模型收敛)) → 全连接(Dense) → 门控线性单元(Gate)(Gated Linear Units (GLUs)) → Layernorm。:一方面,它可以进行静态上下文变量和动态变量的融合,另一方面,通过门控进行了特征选择操作,门控体现在GLU(门控线性单元)中,用了sigmoid控制信息的通过程度,具有良好的适应性,能够自动选择有效信息,自适应地调整网络复杂性。输入:主输入,可选的上下文向量c。

weixin_41838721的博客 1227

深入理解 CGLIB 类代理:原理剖析与实践应用

在 Java 编程中,代理技术是一种强大的手段,广泛应用于日志记录、权限控制、事务管理等场景。Java 提供了两种主要的代理方式:JDK 动态代理和 CGLIB 动态代理。JDK 动态代理基于接口,而 CGLIB 动态代理则基于子类继承,可以对没有接口的类进行代理。

微笑听雨 1310

VM下安装linux虚拟机(ubuntu)和Intel parallel XE 2013(ifort2013)

VM下安装ubuntu和ifort2013跑自己的模式 由于新冠疫情,本人只能在家工作,很多重要的文件和程序都留在了办公室的linux机器上,工作无法正常开展。本来准备去办公室把机器搬回家不曾想学校所有门禁都关闭,不许进入,何时解禁也没有明确日期。科研不能等人,没条件科研只能想办法创造条件了。 以前在自己的笔记本(windows)上装过一次VM和ifort但无奈程序不能正常跑,所以之前就放弃了。本...

pangdahai3366的博客 1498

Spring IOC 基于Cglib实现含构造函数的类实例化策略

Spring IOC 基于Cglib实现含构造函数的类实例化策略

小凯的博客 1122

【Envi遥感图像处理】022:envi时空分析工具的使用

本文介绍了ENVI软件中的时空分析工具应用,以美国拉斯维加斯1975-2014年的9期Landsat数据为例。主要内容包括:1)时空分析概念,即对空间相关时间序列数据的建模分析;2)构建时序数据流程,通过添加栅格、时间排序等步骤生成.series格式时序文件;3)时序数据查看方法,包括动画播放、添加注记及保存视频;4)多源数据统一处理工具,解决坐标系、空间范围不一致问题。该工具可用于冰川变化、植被覆盖等时空变化研究。

智慧空间实验室 270

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Spring AOP动态代理选型指南:JDK vs CGLIB 深度解析

在Spring的代理世界里,选择正确的代理方式就像为你的应用挑选完美战衣——既要灵活轻便,又要防护周全”

全栈开发工程师 1925

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

spring AOP策略模式使用

1.策略模式 The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it       ...

261

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

【转】Apache BeanUtils、PropertyUtils,Spring BeanUtils,Cglib BeanCopier性能比较

比较的是四种复制的方式,分别为Apache的BeanUtils和PropertyUtils,Spring的BeanUtils,Cglib的BeanCopier。做法是在Eclipse新建了一个Project,专门用于专门测试几种代码的性能。

qw9874123的专栏 546

chapter03_Bean的实例化与策略模式

先扩展一下BeanFactory接口,获取bean的时候,可以传入参数,这样就可以使用带参数的构造器。由于实例化的方式很多,我们这里使用策略模式,便于以后不断地扩展。改造doCreateBean方法,灵活切换对象实例化的方式。使用传入的构造器和传入的参数表,创建CGLIB代理对象。使用传入的构造器和传入的参数表,直接new对象。给测试的Bean,新建空参构造器和有参构造器。

亮晶晶的博客 307

Spring-03 Aop简介,实现原理,基于ProxyFactoryBean实现Aop,基于AspectJ开发的实现

Spring-03 1. SpringAop简介 AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。 aop 解决的问题就是将共同重复的代码就行抽离(日志,事务,鉴权) 什么是切面?切面就是一个类,类里面包含类某一类具体业务(日志,事务,鉴权),用这个类 去拦截/增强 你需要监控的方法或者业务 1.1类与切面的关系 AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务

qq_41520945的博客 413

框架学习 三 Spring 、03、aop(maven,mybatis,spring,springmvc)

1.什么是AOP? AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。 aop 就是面向切面编程 aop 解决的问题就是将共同重复的代码就行抽离(日志,事务,鉴权) 什么是切面?切面就是一个类,类里面包含类某一类具体业务(日志,事务,鉴权),用这个类 去拦截/增强 你需要监控的方法或者业务 作用: 1.将共同/重复代码抽取 提高开发i效率 2.调高代码课维护性 切入点:就是

JinMujun的博客 265

每周小记——Spring框架

本文解析了Spring框架的四大核心机制:工厂模式通过BeanFactory和ApplicationContext实现Bean生命周期管理,奠定IoC容器基础;控制反转与依赖注入将对象创建权移交容器,通过配置解耦组件,提升系统灵活性;面向切面编程基于代理模式(静态/JDK动态/CGLIB)实现横切关注点(如日志、事务)与业务逻辑分离,并总结了五种通知类型及其配置方法。文章系统阐述了Spring如何通过设计模式优化Java企业级开发,强化代码可维护性与模块化,凸显其在现代架构中的关键作用

weixin_57655687的博客 1553

demo03:mini-spring项目梳理

spring相关的异常BeanDefinition类:Bean的定义(核心),不包含Bean的具体信息BeanDefinitionRegistry接口:注册Bean(核心),定义注册Bean方法BeanFactory接口:IoC容器的核心接口,定义获取Bean的方法(核心)SingletonBeanRegistry接口:定义了一个获取单例对象的接口。

qq_74924951的博客 867

学习笔记_Spring_day03

AOP相关概念 AOP相关术语 Joinpoint(连接点): 被拦截到的方法. Pointcut(切入点): 我们对其进行增强的方法. Advice(通知/增强): 对切入点进行的增强操作 包括前置通知,后置通知,异常通知,最终通知,环绕通知 Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 Aspect(切面): 是切入点和通知的结合 AOP思想的实现一般都是基于代...

naerjiajia207的博客 254

spring03_AOP的简述与配置

AOP概述 AOP的优势及作用 作用: 在程序运行期间,不修改源码对已有方法进行增强。 优势: 减少重复代码 提高开发效率 维护方便 AOP的实现 使用动态代理技术 动态代理的特点 字节码随用随创建,随用随加载。 它与静态代理的区别也在于此。因为静态代理是字节码一上来就创建好,并完成加载。 装饰者模式就是静态代理的一种体现。 动态代理常用的有两种方式 基于接口的动态代理 提供...

sl1130398099的博客 181

【Spring系列-04】AOP 架构与实现原理

本文深入解析了Spring框架中AOP(面向切面编程)的实现原理,重点分析了代理对象的创建流程。文章首先介绍了AOP如何将日志记录、事务管理等通用功能从业务代码中解耦,然后详细阐述了Spring创建AOP代理的三个关键步骤:通过ProxyFactory获取代理入口、根据条件选择JDK动态代理或CGLIB实现方式,以及最终生成代理对象的具体过程。对于JDK动态代理和CGLIB两种技术,文章分别给出了核心代码示例并加以解释说明,帮助读者理解Spring AOP的底层实现机制。

专注后端技术架构,AI技术分享 743
上一篇: 代码随想录day7
下一篇: 代码随想录day8
Strongwind_
博客等级 码龄7年 139粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值