static关键字真能提高Bean的优先级吗?答:真能(上)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: static关键字真能提高Bean的优先级吗?答:真能(上)

前言


关于Spring初始化Bean的顺序问题,是个老生常谈的话题了,结论可总结为一句话:全局无序,局部有序。Spring Bean整体上是无序的,而现实是大多数情况下我们真的无需关心,无序就无序呗,无所谓喽。但是(此处应该有但是哈),我有理由相信,对于有一定从业经验的Javaer来说,或多或少都经历过Bean初始化顺序带来的“困扰”,也许是因为没有对你的功能造成影响,也许可能是你全然“不知情”,所以最终就不了了之~


隐患终归隐患,依照墨菲定律来讲,担心的事它总归是会发生的。A哥经常“教唆”程序员要面向工资编程,虽然这价值观有点扭曲,但不可否认很多小伙伴真是这么想的(命中你了没有😄),稍加粉饰了而已。话粗理不粗哦,almost所有的Javaer都在用Spring,你凭什么工资比你身边同事的高呢?


Spring对Bean的(生命周期)管理是它最为核心的能力,同时也是很复杂、很难掌握的一个知识点。现在就可以启动你的工程,有木有这句日志:


/

"Bean 'xxx' of type [xxxx] is not eligible for getting processed by all BeanPostProcessors" 
  + "(for example: not eligible for auto-proxying)"


这是一个典型的Spring Bean过早初始化问题,搜搜看你日志里是否有此句喽。这句日志是由Spring的BeanPostProcessorChecker这个类负责输出,含义为:你的Bean xxx不能被所有的BeanPostProcessors处理到(有的生命周期触达不到),提醒你注意。此句日志在低些的版本里是warn警告级别,在本文约定的版本里官方把它改为了info级别。


绝大多数情况下,此句日志的输出不会对你的功能造成影响,因此无需搭理。这也是Spring官方为何把它从warn调低为info级别的原因


我在CSDN上写过一篇“Spring Bean过早初始化导致的误伤”的文章,访问量达近4w:


image.png

从这个数据(访问量)上来看,这件事“并不简单”,遇到此麻烦的小伙伴不在少数且确实难倒了一众人。关于Spring Bean的顺序,全局是不可控的,但是局部上它提供了多种方式来方便使用者提高/降低优先级(比如前面的使用@AutoConfigureBefore调整配置顺序竟没生效?这篇文章),本文就聊聊static关键字对于提供Bean的优先级的功效。


版本约定


本文内容若没做特殊说明,均基于以下版本:


  • JDK:1.8
  • Spring Framework:5.2.2.RELEASE


正文


本文采用从 问题提出-结果分析-解决方案-原理剖析 这4个步骤,层层递进的去感受static关键字在Spring Bean上的魅力~


警告一:来自BeanPostProcessorChecker


这是最为常见的一种警告,特别当你的工程使用了shiro做鉴权框架的时候。在我记忆中这一年来有N多位小伙伴问过我此问题,可见一斑。


@Configuration
class AppConfig {
    AppConfig() {
        System.out.println("AppConfig init...");
    }
    @Bean
    BeanPostProcessor postProcessor() {
        return new MyBeanPostProcessor();
    }
}
class MyBeanPostProcessor implements BeanPostProcessor {
    MyBeanPostProcessor() {
        System.out.println("MyBeanPostProcessor init...");
    }
}


运行程序,输出结果:


AppConfig init...
2020-05-31 07:40:50.979  INFO 15740 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'appConfig'
   of type [com.yourbatman.config.AppConfig$$EnhancerBySpringCGLIB$$29b523c8] is not eligible for getting 
   processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
MyBeanPostProcessor init...
...


结果分析(问题点/冲突点):


1.AppConfig优先于MyBeanPostProcessor进行实例化

  1. 常识是:MyBeanPostProcessor作为一个后置处理器理应是先被初始化的,而AppConfig仅仅是个普通Bean而已,初始化理应靠后


2.出现了BeanPostProcessorChecker日志:表示AppConfig这个Bena不能被所有的BeanPostProcessors处理,所以有可能会让它“错过”容器对Bean的某些生命周期管理,因此可能损失某些能力(比如不能被自动代理),存在隐患

  1. 但凡只要你工程里出现了BeanPostProcessorChecker输出日志,理应都得引起你的注意,因为这属于Spring的警告日志(虽然新版本已下调为了info级别)


说明:这是一个Info日志,并非warn/error级别。绝大多数情况下你确实无需关注,但是如果你是一个容器开发者,建议请务必解决此问题(毕竟貌似大多数中间件开发者都有一定代码洁癖😄)


解决方案:static关键字提升优先级


基于上例,我们仅需做如下小改动:


AppConfig:
//@Bean
//BeanPostProcessor postProcessor() {
//    return new MyBeanPostProcessor();
//}
// 方法前面加上static关键字
@Bean
static BeanPostProcessor postProcessor() {
    return new MyBeanPostProcessor();
}


运行程序,结果输出:


MyBeanPostProcessor init...
...
AppConfig init...
...


那个烦人的BeanPostProcessorChecker日志就不见了,清爽了很多。同时亦可发现AppConfig是在MyBeanPostProcessor之后实例化的,这才符合我们所想的“正常”逻辑嘛。


警告二:Configuration配置类增强失败


这个“警告”就比上一个严重得多了,它有极大的可能导致你程序错误,并且你还很难定位问题所在。

@Configuration
class AppConfig {
    AppConfig() {
        System.out.println("AppConfig init...");
    }
    @Bean
    BeanDefinitionRegistryPostProcessor postProcessor() {
        return new MyBeanDefinitionRegistryPostProcessor();
    }
  ///
    @Bean
    Son son(){
        return new Son();
    }
    @Bean
    Parent parent(){
        return new Parent(son());
    }
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    MyBeanDefinitionRegistryPostProcessor() {
        System.out.println("MyBeanDefinitionRegistryPostProcessor init...");
    }
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}


运行程序,结果输出:

AppConfig init...
MyBeanDefinitionRegistryPostProcessor init...
2020-05-31 07:59:06.363  INFO 37512 --- [           main] o.s.c.a.ConfigurationClassPostProcessor  : Cannot enhance
   @Configuration bean definition 'appConfig' since its singleton instance has been created too early. The typical
   cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring
   such methods as 'static'.
...
son init...hashCode() = 1300528434
son init...hashCode() = 1598434875
Parent init...


结果分析(问题点/冲突点):


  1. AppConfig竟然比MyBeanDefinitionRegistryPostProcessor的初始化时机还早,这本就不合理
  2. 从ConfigurationClassPostProcessor的日志中可看到:AppConfig配置类enhance增强失败
  3. Son对象竟然被创建了两个不同的实例,这将会直接导致功能性错误
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
20天前
|
存储 人工智能 测试技术
小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
141061 20
小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
|
19天前
|
人工智能 运维 前端开发
基于阿里百炼的DeepSeek-R1满血版模型调用【零门槛保姆级2084小游戏开发实战】
本文介绍基于阿里百炼的DeepSeek-R1满血版模型调用,提供零门槛保姆级2048小游戏开发实战。文章分为三部分:定位与核心优势、实战部署操作指南、辅助实战开发。通过详细步骤和案例展示,帮助开发者高效利用DeepSeek-R1的强大推理能力,优化游戏逻辑与视觉效果,解决官网响应延迟问题,提升开发效率和用户体验。适合企业开发者、教育行业及多模态探索者使用。
70896 17
基于阿里百炼的DeepSeek-R1满血版模型调用【零门槛保姆级2084小游戏开发实战】
|
27天前
|
人工智能 自然语言处理 Shell
深度评测 | 仅用3分钟,百炼调用满血版 Deepseek-r1 API,百万Token免费用,简直不要太爽。
仅用3分钟,百炼调用满血版Deepseek-r1 API,享受百万免费Token。阿里云提供零门槛、快速部署的解决方案,支持云控制台和Cloud Shell两种方式,操作简便。Deepseek-r1满血版在推理能力上表现出色,尤其擅长数学、代码和自然语言处理任务,使用过程中无卡顿,体验丝滑。结合Chatbox工具,用户可轻松掌控模型,提升工作效率。阿里云大模型服务平台百炼不仅速度快,还确保数据安全,值得信赖。
358010 62
深度评测 | 仅用3分钟,百炼调用满血版 Deepseek-r1 API,百万Token免费用,简直不要太爽。
|
23天前
|
人工智能 自然语言处理 API
快速使用 DeepSeek-R1 满血版
DeepSeek是一款基于Transformer架构的先进大语言模型,以其强大的自然语言处理能力和高效的推理速度著称。近年来,DeepSeek不断迭代,从DeepSeek-V2到参数达6710亿的DeepSeek-V3,再到性能比肩GPT-4的DeepSeek-R1,每次都带来重大技术突破。其开源策略降低了AI应用门槛,推动了AI普惠化。通过阿里云百炼调用满血版API,用户可以快速部署DeepSeek,享受高效、低成本的云端服务,最快10分钟完成部署,且提供免费token,极大简化了开发流程。
191013 23
快速使用 DeepSeek-R1 满血版
|
8天前
|
人工智能 搜索推荐 数据可视化
Manus:或将成为AI Agent领域的标杆
随着人工智能技术的飞速发展,AI Agent(智能体)作为人工智能领域的重要分支,正逐渐从概念走向现实,并在各行各业展现出巨大的应用潜力。在众多AI Agent产品中,Manus以其独特的技术优势和市场表现,有望成为该领域的标杆。作为资深AI工程师,本文将深入探讨Manus的背景知识、主要业务场景、底层原理、功能的优缺点,并尝试使用Java搭建一个属于自己的Manus助手,以期为AI Agent技术的发展和应用提供参考。
11071 13
|
8天前
|
机器学习/深度学习 人工智能 测试技术
阿里云百炼已上线超强推理开源模型QwQ-32B,尺寸更小,性能比肩DeepSeek满血版
通义千问团队推出了320亿参数的QwQ-32B模型,通过大规模强化学习和多阶段训练,在数学、编程及通用能力上达到或超越了DeepSeek-R1等先进模型。QwQ-32B模型已在阿里云百炼上线,支持API调用,用户可通过官方文档了解详细使用方法。未来,团队将继续探索智能体与RL集成,推动人工通用智能的发展。
|
25天前
|
机器学习/深度学习 人工智能 自然语言处理
快来零门槛、即刻拥有 DeepSeek-R1 满血版
随着人工智能技术的发展,DeepSeek作为一款新兴推理模型,凭借强大的技术实力和广泛的应用场景崭露头角。本文基于阿里云提供的零门槛解决方案,评测DeepSeek的部署与使用。该方案支持多模态任务,涵盖文本生成、代码补全等,融合NLP、IR和ML技术,提供快速实现AI应用的便利。用户无需编码,最快5分钟、最低0元即可部署DeepSeek模型。阿里云还提供100万免费Token,适合预算有限的个人或小型团队试用。通过Chatbox客户端配置API,用户可轻松体验智能交互功能,如数学提问和代码书写等。
37603 5
|
20天前
|
人工智能 编解码 算法
DeepSeek加持的通义灵码2.0 AI程序员实战案例:助力嵌入式开发中的算法生成革新
本文介绍了通义灵码2.0 AI程序员在嵌入式开发中的实战应用。通过安装VS Code插件并登录阿里云账号,用户可切换至DeepSeek V3模型,利用其强大的代码生成能力。实战案例中,AI程序员根据自然语言描述快速生成了C语言的base64编解码算法,包括源代码、头文件、测试代码和CMake编译脚本。即使在编译错误和需求迭代的情况下,AI程序员也能迅速分析问题并修复代码,最终成功实现功能。作者认为,通义灵码2.0显著提升了开发效率,打破了编程语言限制,是AI编程从辅助工具向工程级协同开发转变的重要标志,值得开发者广泛使用。
7908 68
DeepSeek加持的通义灵码2.0 AI程序员实战案例:助力嵌入式开发中的算法生成革新
|
7天前
|
机器学习/深度学习 弹性计算 搜索推荐
真正的0代码,0脚本,0门槛,QwQ-32B一键部署!
阿里云最新发布的QwQ-32B模型通过强化学习显著提升了推理能力,在多个核心指标上达到DeepSeek-R1满血版水平,超越了DeepSeek-R1-Distill-Qwen-32B。用户可通过阿里云系统运维管理(OOS)的公共扩展功能,一键部署OpenWebUI+Ollama至ECS,轻松运行QwQ-32B模型。该方案支持本地部署和连接阿里云百炼在线模型,无需编写代码,操作简便,适合新手尝试。具体步骤包括:在阿里云控制台安装OpenWebUI扩展、选择ECS实例并创建、等待几分钟后获取URL链接,即可开始使用。此外,还提供了详细的配置指南和高级玩法介绍,帮助用户更好地利用该模型。
|
10天前
|
开发者 异构计算
高效部署通义万相Wan2.1:ComfyUI文生/图生视频实战,工作流直取!
通义万相Wan2.1开源不到一周,已登顶HuggingFace Model 和 Space 榜双榜首,在HuggingFace和ModelScope平台的累计下载量突破100万次,社区热度持续攀升!为响应小伙伴们对ComfyUI工作流运行Wan2.1的强烈需求,社区开发者整理了实战教程👇
1287 21
高效部署通义万相Wan2.1:ComfyUI文生/图生视频实战,工作流直取!