开发者日记 - 2025年9月
type: channel
link: https://blog.web3er.cn/2025/09/
description: 戏人看戏博客网，揭秘互联网真相戏人看戏学无止尽，教大家各种网络技术分享个人生活，给大家带来无限体验！
items: 分享一次Maven钻石依赖的解决思路
items: 线程池中线程异常后：销毁还是复用？
items: 这是？？？
items: SpringBoot源码角度深入浅出IOC依赖注入过程

分享一次Maven钻石依赖的解决思路
type: item
date: 2025-09-25T14:11:28+08:00
description: 前言：项目管理上有一个著名的概念叫着「钻石依赖」，是指软件依赖导致同一个软件包的两个版本需要共存而不能冲突。我们平时使用的 maven 是这样解决钻石依赖的，它会从多个冲突的版本中选择一个来使用，如果不同的版本之间兼容性很糟糕，那么程序将无法正常编译运行。Maven 这种形式叫「扁平化」依赖管理。我以我的这个项目为例，可以发现一个Spring的默认的BeanFacotry的接口，重复实现了呢4遍（实际上4个都是同样的，因为多引用了3个maven工具包导致Springboot 包被重复引用了）。这时候就要排查引入的Maven包的问题，因为这个3个工具包也是我自己实现的，然后推送到了Maven中央仓库就可以自己在项目中引入使用了。1.看问题现在来看问题看一下Maven包里的引用操作进来发现！我去，这还得了？我只引用你这个工具，结果你自己给我引入一堆spring-boot的包，并且还给我制定了版本！那我要是用的Spring-boot 3这不炸了？赶紧解决(我开源的扳手工具地址：https://github.com/nb-sb/whn-wrench)2.解决方案a.自己在项目中排除依赖b.修改扳手项目中的依赖scope类型(因为这扳手项目也是我自己实现的，所以为了通用型肯定直接修改扳手项目是最好的，但是如果你引入的项目是其他人的包，他的包引入的依赖和你的依赖冲突的，就要使用a办法，排除依赖，这里就不过多阐述了，网上很多教程)首先先修改scope范围，改为provided可以看一下scope可以填写哪些参数（这里不过多阐述，这篇主要来解决钻石依赖问题的）作用域编译classpath运行classpath测试classpath打包到产品compile✅✅✅✅provided✅❌✅❌runtime❌✅✅✅test❌❌✅❌system✅✅✅✅然而看后果结果还是没有完全排除，还是可以引入进来？什么鬼？继续排查经过不懈查找，破案了，原来是这个组件引入了Redission，Redission又依赖了spring-context-support，导致的重复引用。由于引用的版本和我项目版本一样所以没有异常，如果引入的版本和我项目的版本不一致，包不全会有依赖冲突问题OK，发现问题就好解决了，直接排除这个redission中的spring的冲突的依赖查看perfect ！！！没有重复引入的问题了结尾：提交代码到dev分支。。在合并到main分支 ->推送到Maven中央仓库总结：遇到钻石依赖一般可以直接排除，但是这里由于依赖的包是我自己写的，所以直接可以修改原包即可，大家如果遇到钻石依赖问题可以按照我这个思路排查1.查看依赖所在的pom文件2.分析版本问题所在的包中3.常见解决思路：排除依赖、版本锁定原则（定义指定版本）、父子模块覆盖原则（父模块里声明的依赖,子模块里重复声明。子模块内会覆盖父模块的依赖）

线程池中线程异常后：销毁还是复用？
type: item
date: 2025-09-22T13:43:00+08:00
description: 前言下面测试是使用的JUC包下的 java.util.concurrent.ThreadPoolExecutor，本文将围绕验证，阅读源码俩方面来解析这个问题。这里使用两种提交方式分别验证，两种提交方式不一样结果也不一样！如果连这个都不知道，别跟我说你用过线程池好吗？如果线上出bug异常被吞掉，不知道怎么排查你就老实了。。1.验证execute提交线程池中public class ThreadPoolExecutorDeadTest { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = buildThreadPoolExecutor(); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute-exception")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); Thread.sleep(5000); System.out.println("再次执行任务======================="); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); executorService.execute(() -> exeTask("execute")); } public static ExecutorService buildThreadPoolExecutor() { return new ThreadPoolExecutor(5, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactoryBuilder().setNamePrefix("test-").build() , new ThreadPoolExecutor.CallerRunsPolicy()); } private static void exeTask(String name) { String printStr = "[thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name + "]"; if ("execute-exception".equals(name)) { throw new RuntimeException(printStr + ", 我抛异常了"); } else { System.out.println(printStr); } } }运行后发现，test-2的线程不见了，多出来一个test-3线程结论：execute 提交到线程池的方式，如果执行中抛出异常，并且没有在执行逻辑中catch，那么会抛出异常，并且移除抛出异常的线程，创建新的线程放入到线程池中。2.验证submit提交线程池中public static void main(String[] args) throws InterruptedException { ExecutorService executorService = buildThreadPoolExecutor(); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute-exception")); executorService.submit(() -> exeTask("execute")); executorService.submit(() -> exeTask("execute")); Thread.s…

这是？？？
type: item
date: 2025-09-13T20:50:00+08:00
description: 哈哈哈，每天都有垃圾评论，系统自动过滤掉了，不好意思，之前因为没有垃圾评论过滤和审核好多人发广告，不好意思哈～现在这个站点是前台是伪静态，缓存30天到腾讯云CDN EO节点上，所以可能时效性比较低，比如浏览量、评论(需要我手动审核)等，好处就是访问速度非常快，资源全在CDN上，而且是伪静态的，查询直接缓存到CDN上不走数据库。这样一些小黑子之前打我网站的，现在打不动了哈哈哈，DDOS找不到我原IP，CC根本打不动，因为伪静态查询不走数据库全打CDN上了，对我一点影响没有。打的量全被清洗了哈，脚本小子别费力了

SpringBoot源码角度深入浅出IOC依赖注入过程
type: item
date: 2025-09-13T20:45:00+08:00
description: 前置知识：下面先用一个从ApplicationContext中getBean的一个案例熟悉一下用OkHttpClient注入到了ICO容器中并获取为例打断点进来发现，如果是无参构造的话则会执行if里面的，protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { // 1. 解析 beanName（去掉别名，转为真正的名字） String beanName = this.transformedBeanName(name); // 2. 尝试从单例池里拿（一级缓存） Object sharedInstance = this.getSingleton(beanName); Object beanInstance; if (sharedInstance != null && args == null) { // 说明这个 Bean 已经创建过了（缓存命中） // 如果是循环依赖提前暴露的 Bean，这里会打印 trace 日志 if (this.logger.isTraceEnabled()) { if (this.isSingletonCurrentlyInCreation(beanName)) { this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } // 3. 如果是 FactoryBean，还需要处理 getObject() 包装 beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null); } }可以发现sharedInstance和beanInstance指向了同一个地址如果下次进行getBean的时候，并且这个Bean对象设置原型模式，就会新疆一个Bean，否则则直接返回原来的对象Ioc容器加载过程：1. new ApplicationContext(配置类/xml)； @SpringBootApplication @Configurable public class Main { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(Main.class); Object bean = applicationContext.getBean("IService"); } } //这里进行new一个ConfigurableApplicationContext public class SpringApplication { public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); } }2. 解析配置--->扫描包--->bean的定义信息读取进来--->BeanDefinition-->mapBeanDefinition = 把类的配置信息登记到 Spring 容器（BeanFactory）的“元数据字典”里，方便后续创建 Bean。BeanDefinition，里面写着：beanClass = UserServicescope = singletonlazyInit = false3.循环一个个创建bean--->条件判断--->懒加载的bean\多例Bean\抽象bean条件判断的时候会判断是否是单例Bean，是单例Bean的话进行一个Bean对象的创建，因为BeanFactory#getBean4. 通过beanfactory.getBean创建bean对象创建之前先…