Java创建对象和spring创建对象的过程和区别
暮乘白帝过重山
从new到IoC的演进,体现了软件工程从"怎么做"到"做什么"的思维转变。理解Java对象创建的底层机制,是写出高性能代码的基础;掌握Spring的Bean管理哲学,则是构建可维护大型系统的关键。二者如同汽车的发动机与智能驾驶系统——前者保证基础性能,后者提供高阶能力,共同推动Java生态持续发展。
从new到IoC:揭秘Java与Spring对象创建的差异与本质
一、Java原生对象创建全流程剖析
先来个王炸:Java的new关键字实际上是语法糖,其底层通过bytecode new + invokespecial两条指令协作完成。这种设计既保持了语言简洁性,又为JVM优化(如逃逸分析、栈上分配)留足了空间
1.1 new关键字背着我们都做了些什么???
当我们写下UserService service = new UserService()时,JVM会执行以下精密流程:
public class UserService {private int id; // 默认0private String name; // 默认null{System.out.println("初始化块执行"); // 第1步}public UserService() {System.out.println("构造函数执行"); // 第2步}
}
-
类加载检查:
-
JVM检查方法区是否已加载类元数据
-
未加载则触发类加载机制(双亲委派模型)
-
-
内存分配:
-
计算对象大小(指针压缩影响)
-
选择分配方式(指针碰撞/空闲列表)
-
堆内存中划分对象空间
-
-
零值初始化:
-
所有基本类型赋默认值(int=0, boolean=false)
-
引用类型置null
-
-
对象头设置:
-
MarkWord(哈希码、GC年龄、锁状态)
-
类型指针(指向类元数据)
-
-
初始化执行:
-
初始化块(按代码顺序)
-
构造函数(显式初始化)
-
1.2 反射创建:灵活背后的代价
通过反射API创建对象的典型流程:
Class<?> clazz = Class.forName("com.example.UserService");
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
UserService instance = (UserService) constructor.newInstance();
执行步骤:
-
类加载器加载目标类
-
获取构造器对象并绕过访问检查
-
通过Unsafe类分配内存
-
直接调用构造器(跳过常规初始化顺序)
1.3 其他创建方式对比
| 创建方式 | 适用场景 | 注意事项 |
|---|---|---|
| Cloneable | 对象拷贝场景 | 深拷贝/浅拷贝问题 |
| 反序列化 | 网络传输、持久化存储 | 绕开构造器执行 |
| Unsafe.allocate | 极高性能需求 | 可能破坏JVM内存模型 |
一些补充:
1.4 JavaBean规范
- 必须是公共类,类访问权限需为
public,以便外部访问。- 提供无参公共构造方法,必须有一个
public的无参数构造函数(默认构造方法),便于反射实例化。- 属性私有化,属性(字段)必须用
private修饰,通过公共方法访问。通过 getter/setter 访问属性,对每个private属性,提供标准的getXxx()和setXxx()方法(布尔属性可用isXxx())。- 实现
Serializable接口(可选但常见),支持序列化,便于网络传输或持久化存储。
二、Spring IoC容器对象创建全解析
一样的,先来手王炸:Spring IoC容器对象创建
Spring创建对象的本质是通过BeanDefinition元数据驱动IoC容器执行一套标准化的对象生命周期管理流程,在运行时动态组装、增强和管理对象依赖关系,实现控制反转和面向切面编程的统一治理。
什么?你有点被炸懵了?那我换种说法
Spring创建对象的核心机制就是IoC(控制反转)容器在运作,
但更准确的说法是:
"Spring创建对象 = IoC容器管理 + 依赖注入 + 扩展增强"
通俗说就是:
-
IoC容器是"工厂":它负责根据你的配置(注解/XML)生产对象(比如
@Service标注的类)。 -
依赖注入是"自动装配":对象需要的其他组件(比如
@Autowired的成员),Spring会自动塞进去。 -
扩展增强是"增值服务":AOP代理、生命周期回调等(比如
@Transactional事务控制)。
为什么说"不只是IoC"?
-
单纯IoC:只是"不用你自己new"(控制权反转)。
-
Spring的完整流程:还包括依赖注入(DI)、AOP、作用域管理(单例/原型)等,比传统IoC更强大。
(就像网购:IoC是"商家替你发货",而Spring是"发货+送货上门+七天无理由+赠品"一套完整服务)
2.1 Bean生命周期全景图
Spring Bean 的创建过程之所以远比直接 new 对象复杂,是因为它在底层构建了一套完整的对象生命周期管理体系。让我们通过一个生活中的比喻来理解这个精密过程:想象我们现在要建造房子(Bean),而 Spring 容器就像一个全能的建筑管理局,不仅负责砖瓦堆砌(对象创建),还要统筹水电布线(依赖注入)、安全监控(AOP)、装修验收(生命周期回调)等全套流程。OK,我们说一说建房子的一套简单流程
第1阶段:图纸审批(加载配置元数据)
-
现实场景:向城建局提交房屋设计图(XML/注解/JavaConfig)
-
技术实现:
-
扫描
@ComponentScan指定的包路径 -
解析
@Configuration类中的@Bean方法 -
读取 XML 中的
<bean>定义 -
处理
@Import导入的其他配置
-
-
关键机制:兄弟们记住:
BeanDefinitionReader任务就是将不同格式的配置统一转化为内存中的 Bean 然后能进行下一步
第2阶段:地基浇筑(也就是实例化Bean)
-
现实场景:我们花钱请来一批施工队,然后施工队的任务是不是就是根据图纸打地基?(调用构造方法)
-
技术细节:
// AbstractAutowireCapableBeanFactory protected Object createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 构造器推断算法:智能匹配参数最多的构造器Constructor<?> constructorToUse = determineConstructor(beanName, mbd);// 通过反射实例化或CGLIB动态生成子类return instantiateBean(beanName, mbd, constructorToUse, args); } -
我说一下,有种特殊特殊场景:记住是spring!!!
当遇到循环依赖(A依赖B,B又依赖A)时,Spring 的"三级缓存"开始运作:-
施工许可证公示(三级缓存 singletonFactories):提前暴露半成品对象
-
毛坯房展示(二级缓存 earlySingletonObjects):存放未装修的原始对象
-
精装房交付(一级缓存 singletonObjects):最终成品
-
第3阶段:毛坯的人生,精装的朋友圈,房子一样,毛坯房建好了,接下俩就是请水电师傅,叫他们水电安装(也就是属性填充)
-
现实场景:给房屋安装水管(@Autowired)和电线(@Value)
-
技术流程:
// 处理自动装配的核心逻辑 for (PropertyValue pv : mbd.getPropertyValues().getPropertyValues()) {// 1. 解决依赖:在容器中查找匹配的BeanObject resolvedValue = resolveDependency(pv);// 2. 反射注入:通过Field.set()或setter方法ReflectionUtils.makeAccessible(field);field.set(beanInstance, resolvedValue); } -
智能处理:
-
遇到
@Lazy注解时延迟加载 -
对
@Qualifier指定的 Bean 进行精确匹配 -
处理
@Primary标注的优先候选对象
-
第4阶段:最后要叫人来看看房子达不达标,也就是安全验收(BeanPostProcessor处理)
-
现实场景:消防检查(前置处理)和环保认证(后置处理)
-
代码示例:
public interface BeanPostProcessor {// 装修前检查(如@PostConstruct处理)default Object postProcessBeforeInitialization(Object bean, String name) { return bean; }// 装修后验收(如AOP代理包装)default Object postProcessAfterInitialization(Object bean, String name) { return bean; } } -
开发中经常遇到的情况处理
-
AutowiredAnnotationBeanPostProcessor处理自动装配 -
CommonAnnotationBeanPostProcessor解析@PostConstruct -
AbstractAdvisingBeanPostProcessor准备AOP代理
-
第5阶段:精装修(初始化方法执行)
-
现实场景:安装智能家居系统(初始化逻辑)
-
执行顺序:
-
@PostConstruct标注的方法 → 2.InitializingBean接口的afterPropertiesSet()→ 3. XML 中配置的init-method
-
-
代码演示:
public class SmartHomeService implements InitializingBean {@PostConstructpublic void connectIoT() {System.out.println("连接智能设备...");}@Overridepublic void afterPropertiesSet() {System.out.println("启动环境监测系统...");}public void initSceneMode() {System.out.println("配置情景模式...");} }
第6阶段:安防系统部署(AOP代理)
-
现实场景:安装监控摄像头(切面逻辑)
-
代理策略:
public DefaultAopProxyFactory createAopProxy(AdvisedSupport config) {// 条件判断:目标类是否有接口?是否是CGLIB代理?if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {return new ObjenesisCglibAopProxy(config);} else {return new JdkDynamicAopProxy(config);} } -
动态代理示例:
// JDK代理示例 public class $Proxy12 extends Proxy implements UserService {public final void saveUser() {// 1. 执行@Before advice// 2. 调用target.saveUser()// 3. 执行@After advice} }
第7阶段:房产登记(注册单例池)
-
现实场景:将房屋信息录入不动产登记中心
-
最终存储:
// DefaultSingletonBeanRegistry private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);} }
2.2 核心阶段详解
阶段1:实例化(Instantiation)
-
通过反射或CGLIB创建原始对象
-
构造器解析策略:构造器参数匹配算法
阶段2:属性填充(Population)
-
处理@Autowired/@Value注解
-
解决循环依赖的三级缓存机制:
-
singletonFactories(三级)
-
earlySingletonObjects(二级)
-
singletonObjects(一级)
-
阶段3:初始化(Initialization)
public class UserService implements InitializingBean {@PostConstructpublic void customInit() {// 注解方式初始化}@Overridepublic void afterPropertiesSet() {// 接口方式初始化}public void xmlInit() {// XML配置的初始化方法}
}
初始化顺序:
@PostConstruct
InitializingBean接口
XML配置的init-method
2.3 代理与包装阶段(了解)
AOP代理的两种实现方式:
-
JDK动态代理:基于接口(生成$Proxy类)
-
CGLIB代理:基于继承(生成Enhancer子类)
代理时机选择:
// AbstractAutoProxyCreator
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {return bean;}// 创建代理逻辑...
}
三、核心差异对比与选型指南
3.1 核心机制对比表
| 维度 | Java原生创建 | Spring Bean创建 |
|---|---|---|
| 控制权 | 开发者直接控制 | 容器控制(IoC) |
| 生命周期 | 由JVM管理 | 完整生命周期管理 |
| 依赖注入 | 手动设置 | 自动装配 |
| 对象类型 | 原始类型 | 可能被代理包装 |
| 创建成本 | 低(约5ns) | 高(约5000ns) |
| 作用域 | 单一实例 | 支持多种作用域 |
3.2 性能对比数据
通过JMH测试(纳秒/操作):
| 操作 | 平均耗时 | 99%分位 |
|---|---|---|
| new创建 | 5.2 | 7.1 |
| 反射创建 | 18.7 | 25.3 |
| Spring单例获取 | 42.3 | 55.9 |
| Spring原型创建 | 4832.6 | 5214.8 |
3.3 最佳实践建议
适合Java原生创建的场景:
-
高频创建的对象(如DTO)
-
不需要依赖管理的工具类
-
对启动速度敏感的应用
适合Spring管理的场景:
-
需要依赖注入的业务组件
-
需要AOP增强的服务类
-
需要复杂生命周期的资源对象
四、深度思考:
4.1 对象管理范式的转变
Java原生方式体现命令式编程思想:
// 开发者全权控制
DBConn conn = new MySQLConn(config);
UserDAO dao = new UserDAO(conn);
Service service = new Service(dao);
Spring IoC体现声明式编程哲学:
@Configuration
public class AppConfig {@Beanpublic DBConn dbConn() { /*...*/ }@Beanpublic UserDAO userDAO(DBConn conn) { /*...*/ }@Beanpublic Service service(UserDAO dao) { /*...*/ }
}
4.2 扩展机制对比
Java原生扩展方式:
继承
组合
SPI机制
Spring扩展方式:
BeanPostProcessor
BeanFactoryPostProcessor
ImportSelector
条件化配置
4.3 现代编程趋势影响
响应式编程对对象创建的影响(Project Reactor的延迟创建)
Serverless环境下的轻量化需求(Spring Native)
云原生时代的配置管理(Kubernetes与Spring Cloud整合)
五、做个总结
理解两种创建方式的本质差异,我们可以:
在传统单体架构中合理选择对象管理方式
在微服务架构中优化Bean初始化顺序
在云原生环境下平衡启动速度与便利性
深度定制Spring容器满足特殊需求
相关文章:
Java创建对象和spring创建对象的过程和区别
暮乘白帝过重山 从new到IoC的演进,体现了软件工程从"怎么做"到"做什么"的思维转变。理解Java对象创建的底层机制,是写出高性能代码的基础;掌握Spring的Bean管理哲学,则是构建可维护大型系统的关键。二者如同…...
RabbitMQ应用2
RabbitMQ应用2 一.实际业务逻辑订单系统中使用MQ(不写订单系统逻辑)1.项目的创建和准备2.代码实现ControllerConfigurationproperties 二.物流系统使用MQ(不实现物流系统业务)1.项目创建同订单(一样)2.代码…...
Windows 实战-evtx 文件分析--笔记
Windows 取证之EVTX日志 - 蚁景网安实验室 - 博客园 一.evtx日志文件是什么 从 Windows NT 6.0(也就是 Windows Vista 和 Windows Server 2008)开始,微软引入了一种全新的日志文件格式,称为 evtx。这种格式取代了之前 Windows 系…...
Vue3的组件通信
父子通信 父传子 1.父组件给子组件添加属性传值 const myCount ref(10) ... <son :count"myCount"/>2.子组件通过defineProps编译器宏接收 const props defineProps({count: Number })3.子组件使用 {{count}}子传父 1. 父组件实现处理函数 const getM…...
【postgresql】锁概览
常规锁 场景测试案例...
python中的 f 是什么意思,f‘{username}_log_archive_{int(time.time())}.txt‘
python中的 f 是什么意思,f’{username}log_archive{int(time.time())}.txt’ 在 Python 中,f 是一种字符串前缀,用于创建格式化字符串(也称为 f-string),它是 Python 3.6 及更高版本引入的一种方便的字符串格式化方式。 基本语法和功能 当你在字符串前加上 f 前缀时,…...
子组件使用:visible.sync=“visible“进行双向的绑定导致该弹窗与其他弹窗同时显示的问题
问题描述:最近写代码时遇到了一个问题:点击A弹窗后关闭,继续点击B弹窗,这时会同时弹窗A、B两个弹窗。经过排查后发现在子组件定义时使用了:visible.sync"visible"属性进行双向的数据绑定 <template> <el-dial…...
【AI产品分享】面向图片的原始位置翻译功能
1. 背景 在撰写文字材料时,往往需要配套图像以增强表达效果。然而,有时自己绘制的图可能达不到理想的质量,而在其他文献材料中却能发现更清晰、直观的示例。希望在“站在巨人的肩膀上”优化自己的图像时,通常希望在保留原始图像的…...
存储型XSS漏洞解析
一、存储型XSS漏洞的核心原理 定义与攻击流程 存储型XSS(Stored XSS)是一种将恶意脚本永久存储在服务器端(如数据库、文件系统)的跨站脚本攻击方式。其攻击流程分为四步: 注入阶段:攻击者通过输入点&…...
【无标题】跨网段耦合器解决欧姆龙CJ系列PLC通讯问题案例
欧姆龙CJ系列PLC不同网段的通讯问题 一、项目背景 某大型制造企业的生产车间内,采用了多台欧姆龙CJ系列PLC对生产设备进行控制。随着企业智能化改造的推进,需要将这些PLC接入工厂的工业以太网,以便实现生产数据的实时采集、远程监控以及与企业…...
K8S学习之基础七十二:Ingress基于Https代理pod
Ingress基于Https代理pod 1、构建TLS站点 (1)准备证书,在xianchaomaster1节点操作 cd /root/ openssl genrsa -out tls.key 2048 openssl req -new -x509 -key tls.key -out tls.crt -subj /CCN/STBeijing/LBeijing/ODevOps/CNak.lucky.com…...
node.js版本管理
概述 遇到了版本升级后,以前项目不兼容的问题。 下载一个node.js的版本管理工具,官网下载地址,可以选择版本下载,我选择的1.11.1版本的。下载完成后点击安装,分别选择nvm安装目录和nodejs的安装目录,点击安…...
Gartner预计2025年AI支出达6440亿美元:数据中心与服务器市场的关键驱动与挑战
根据Gartner最新预测,2025年全球生成式人工智能(GenAI)支出将达到6440亿美元,较2024年增长76.4%,其中80%的支出将集中于硬件领域,尤其是集成AI能力的服务器、智能手机和PC等设备。这一增长的核心驱动力来自…...
clickhouse集群版本部署文档
集群版本介绍 clickhouse是表级别的集群,一个clickhouse实例可以有分布式表,也可以有本地表。本文介绍4个节点的clickhouse情况下部署配置。 分布式表数据分成2个分片,2个副本,总共4份数据: 节点1数据:分…...
AI提示词:好评生成器
提示说明 生成一段幽默的好评 提示词 # Role: 好评生成器# Profile: - author: xxx - version: 1.0 - language: 中文 - description: 生成一段幽默的好评## Goals: - 根据用户提供的体验优点生成一段幽默的好评 - 视角采用第一人称来描述(站在用户的视角) - 用词口语化、语…...
重新安装VMware tools为灰色无法点击问题解决|读取电脑文件的共享文件夹方法
1.问题VMware tools为灰色 sudo systemctl status vmware-tools 显示:Unit vmware-tools.service could not be found. 改 检测方式 弹出(之前没有) 在重启的瞬间点安装 弹出: 双击打开 右键打开终端,解压 cd ~ ta…...
构造超小程序
文章目录 构造超小程序1 编译器-大小优化2 编译器-移除 C 异常3 链接器-移除所有依赖库4 移除所有函数依赖_RTC_InitBase() _RTC_Shutdown()__security_cookie __security_check_cookie()__chkstk() 5 链接器-移除清单文件6 链接器-移除调试信息7 链接器-关闭随机基址8 移除异常…...
mycat --分片规则--
文章目录 MyCat分片规则详解1. rule1 (基于id的func1算法)2. sharding-by-date (按日期分片)3. rule2 (基于user_id的func1算法)4. sharding-by-intfile (基于枚举值分片)5. auto-sharding-long (长整型范围分片)6. mod-long (取模分片)7. sharding-by-murmur (MurmurHash分片)…...
wireshark抓包分析数据怎么看 wireshark使用教程_wireshark怎么看
Wireshark与Sniff Master:网络抓包工具使用指南 网络抓包分析是开发测试和网络故障排查中不可或缺的技能。在众多抓包工具中,Wireshark无疑是最流行且功能强大的选择,而Sniff Master作为后起之秀,也因其简洁高效的特点受到许多专…...
Outlook客户端无法连接到服务器,添加账户显示“无网络连接,请检查你的网络设置,然后重试。[2603]”
1、先切换一下到手机热点或者其他网络,判断是不是现在所连接的网络的问题。如果有VPN代理软件,网银软件,加密软件在后台运行,麻烦退出一下。 2、打开电脑上的 控制面板——网络和Internet——Internet选项——高级——先点击还原…...
LlamaIndex实现RAG增强:融合检索(Fusion Retrieval)与混合检索(Hybrid Search)
🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创…...
递归(实践版)
这篇博客我不会写太多细节,我只做一件事,那就是教你如何写好一个递归. 二叉树的后序遍历 public void dfs(TreeNode root){if(rootnull)return;dfs(root.left);dfs(root.right);System.out.println(root.val); } 归并排序 public void merge(int[] nums,int left,int right)…...
JavaScript instanceof 运算符全解析
JavaScript instanceof 运算符全解析 核心语义: 判断一个对象(object)是否属于某个构造函数(constructor)或类的实例,基于原型链(prototype chain)实现类型检测。 一、JavaScript 中的基础用法 1. 语法结构 object instanceof constructor 返回值:布尔值(true/fal…...
蓝桥杯冲刺:一维前缀和
系列文章目录 蓝桥杯系列:一维前缀和 文章目录 系列文章目录前言一、暴力的写法:二、一维前缀和的模板: 具体实现: 三、具体例题:求和 1.题目参考:2.以下是具体代码实现: 总结 前言 上次我介绍…...
Ubuntu24.04-中文输入法的切换
Ubuntu24.04在安装后自带中文全拼输入法。。 根据官方的说明,需使用 shift super 空格 切换输入法,但在之前使用windows或者ubuntu的早些版本,多使用 Ctrl 空格 的方式切换输入法,本文就介绍如何进行输入法快捷键切换的配置&a…...
技术回顾day3
1.获取文件信息、获取视频信息 走的都是同一个方法:baseController里面的getFile。 在getFile方法里面进行判断文件的类型,判断是不是m3u8类型或者ts类型做一些额外的处理。 获取信息底层就是读取文件,然后写入response的OutputStream ou…...
埃文科技企业AI大模型一体机——昇腾体系+DeepSeek+RAG一站式解决方案
面对企业级市场海量数据资产与复杂业务场景深度耦合的刚需,埃文科技重磅推出基于华为昇腾算力DeepSeek大模型的企业一体机产品,提供DeepSeek多版本大模型一体机选择,为企业提供本地昇腾算力DeepSeek大模型RAG知识库的一体化解决方案ÿ…...
SAP-ABAP:ABAP `LEAVE LIST-PROCESSING` 深度解析
ABAP LEAVE LIST-PROCESSING 深度解析 核心机制 模式切换(Dialog → List) 中断屏幕流 强制终止当前Dialog程序的PBO/PAI处理,脱离屏幕序列控制(如事务码SE38执行的程序)。触发报表事件 激活类报表程序的事件链:INITIALIZATION → AT SELECTION-SCREEN → START-OF-SEL…...
JavaWeb开发基础知识-Servlet终极入门指南(曼波萌新版)
(✪▽✪)曼波~~~~!欢迎来到Servlet新手村!准备好开启Web开发的奇妙冒险了吗?让曼波用最有趣的方式带你飞~ 🚀 🌈 第①章 什么是Servlet? // 本质就是一个Java类! public class HelloServlet e…...
游戏引擎学习第198天
回顾并为今天的内容设定 今天我们有一些代码需要处理。昨天我们进行了一些调试界面的整合工作,之前我们做了一些临时的、粗糙的操作,将一些东西读进来并放到调试界面中。今天,我们并不打算进行大规模的工作,更多的是对之前的代码…...
