9.depend-on干什么的?

本文详细探讨了Spring容器中bean的创建和销毁顺序,包括无依赖时的默认顺序、通过构造函数强依赖的情况以及使用`depend-on`属性干预顺序。

本文主要讨论一下bean的创建和销毁的顺序,如何来干预bean的创建和销毁的顺序。

无依赖bean创建和销毁的顺序

我们先来看一下没有任何依赖的bean的创建和销毁的顺序。

下面的xml中定义了3个bean:


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
  6. <bean id="bean3" class="com.javacode2018.lesson001.demo7.NormalBean$Bean3"/>
  7. <bean id="bean2" class="com.javacode2018.lesson001.demo7.NormalBean$Bean2"/>
  8. <bean id="bean1" class="com.javacode2018.lesson001.demo7.NormalBean$Bean1"/>
  9. </beans>

注意上面xml中bean定义顺序是:bean3、bean2、bean1。

对应java代码如下:


  1. package com.javacode2018.lesson001.demo7;
  2. import org.springframework.beans.factory.DisposableBean;
  3. /**
  4. * 无任何依赖的bean创建的顺序
  5. */
  6. public class NormalBean {
  7. public static class Bean1 implements DisposableBean {
  8. public Bean1() {
  9. System.out.println(this.getClass() + " constructor!");
  10. }
  11. @Override
  12. public void destroy() throws Exception {
  13. System.out.println(this.getClass() + " destroy()");
  14. }
  15. }
  16. public static class Bean2 implements DisposableBean {
  17. public Bean2() {
  18. System.out.println(this.getClass() + " constructor!");
  19. }
  20. @Override
  21. public void destroy() throws Exception {
  22. System.out.println(this.getClass() + " destroy()");
  23. }
  24. }
  25. public static class Bean3 implements DisposableBean {
  26. public Bean3() {
  27. System.out.println(this.getClass() + " constructor!");
  28. }
  29. @Override
  30. public void destroy() throws Exception {
  31. System.out.println(this.getClass() + " destroy()");
  32. }
  33. }
  34. }

上面代码中使用到了DisposableBean接口,这个是spring容器提供的一个接口,这个接口中有个destroy方法,我们的bean类可以实现这个接口,当我们调用容器的close方法关闭容器的时候,spring会调用容器中所有bean的destory方法,用来做一些清理的工作,这个以后还会细讲的。

上面几个类中构造方法和destory方法中都有输出。

下面我们来搞个测试用例看一下spring容器启动和关闭的过程中,定义的3个bean的创建和销毁的顺序。


  1. package com.javacode2018.lesson001.demo7;
  2. import com.javacode2018.lesson001.demo5.IocUtils;
  3. import org.junit.Test;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
  7. * dependon详解
  8. */
  9. public class DependOnTest {
  10. /**
  11. * 无依赖的bean创建和销毁的顺序
  12. */
  13. @Test
  14. public void normalBean() {
  15. System.out.println("容器启动中!");
  16. String beanXml = "classpath:/com/javacode2018/lesson001/demo7/normalBean.xml";
  17. ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
  18. System.out.println("容器启动完毕,准备关闭spring容器!");
  19. //关闭容器
  20. context.close();
  21. System.out.println("spring容器已关闭!");
  22. }
  23. }

运行上面的normalBean方法,输出:


  1. 容器启动中!
  2. class com.javacode2018.lesson001.demo7.NormalBean$Bean3 constructor!
  3. class com.javacode2018.lesson001.demo7.NormalBean$Bean2 constructor!
  4. class com.javacode2018.lesson001.demo7.NormalBean$Bean1 constructor!
  5. 容器启动完毕,准备关闭spring容器!
  6. class com.javacode2018.lesson001.demo7.NormalBean$Bean1 destroy()
  7. class com.javacode2018.lesson001.demo7.NormalBean$Bean2 destroy()
  8. class com.javacode2018.lesson001.demo7.NormalBean$Bean3 destroy()
  9. spring容器已关闭!

bean的定义结合上面输出我们来对比一下:

bean定义顺序创建顺序销毁顺序
bean3bean3bean1
bean2bean2bean2
bean1bean1bean3

从输出中可以得到2点结论:

  1. bean对象的创建顺序和bean xml中定义的顺序一致
  2. bean销毁的顺序和bean xml中定义的顺序相反

通过构造器强依赖bean创建和销毁顺序

我们将上面案例改造一下,通过构造函数注入的方式使bean之间产生强依赖。


  1. package com.javacode2018.lesson001.demo7;
  2. import org.springframework.beans.factory.DisposableBean;
  3. /**
  4. * 强依赖的bean创建和销毁顺序
  5. */
  6. public class StrongDependenceBean {
  7. public static class Bean1 implements DisposableBean {
  8. public Bean1() {
  9. System.out.println(this.getClass() + " constructor!");
  10. }
  11. @Override
  12. public void destroy() throws Exception {
  13. System.out.println(this.getClass() + " destroy()");
  14. }
  15. }
  16. public static class Bean2 implements DisposableBean {
  17. private Bean1 bean1;
  18. public Bean2(Bean1 bean1) { //@1
  19. this.bean1 = bean1;
  20. System.out.println(this.getClass() + " constructor!");
  21. }
  22. @Override
  23. public void destroy() throws Exception {
  24. System.out.println(this.getClass() + " destroy()");
  25. }
  26. }
  27. public static class Bean3 implements DisposableBean {
  28. private Bean2 bean2;
  29. public Bean3(Bean2 bean2) { //@2
  30. this.bean2 = bean2;
  31. System.out.println(this.getClass() + " constructor!");
  32. }
  33. @Override
  34. public void destroy() throws Exception {
  35. System.out.println(this.getClass() + " destroy()");
  36. }
  37. }
  38. }

代码解释:

@1:创建Bean2的时候需要传入一个bean1对象,对bean1产生了强依赖

@2:创建Bean3的时候需要传入一个bean2对象,对bean2产生了强依赖

依赖关系是:


  1. bean3->bean2->bean1

对应的配置(strongDependenceBean.xml):


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
  6. <bean id="bean3" class="com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean3">
  7. <constructor-arg index="0" ref="bean2"/> //@1
  8. </bean>
  9. <bean id="bean2" class="com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean2">
  10. <constructor-arg index="0" ref="bean1"/> //@2
  11. </bean>
  12. <bean id="bean1" class="com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean1">
  13. </bean>
  14. </beans>

注意上面xml中bean定义顺序是:bean3、bean2、bean1。

@1:bean3中通过构造器注入bean2

@2:bean2中通过构造器注入bean1

DependOnTest中创建一个测试用例,如下:


  1. /**
  2. * 强依赖的bean的创建和销毁顺序测试
  3. */
  4. @Test
  5. public void strongDependenceBean() {
  6. System.out.println("容器启动中!");
  7. String beanXml = "classpath:/com/javacode2018/lesson001/demo7/strongDependenceBean.xml";
  8. ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
  9. System.out.println("容器启动完毕,准备关闭spring容器!");
  10. context.close();
  11. System.out.println("spring容器已关闭!");
  12. }

运行strongDependenceBean方法输出:


  1. 容器启动中!
  2. class com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean1 constructor!
  3. class com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean2 constructor!
  4. class com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean3 constructor!
  5. 容器启动完毕,准备关闭spring容器!
  6. class com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean3 destroy()
  7. class com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean2 destroy()
  8. class com.javacode2018.lesson001.demo7.StrongDependenceBean$Bean1 destroy()
  9. spring容器已关闭!

bean的定义结合上面输出我们来对比一下:

bean定义顺序依赖顺序(下面依赖上面的)创建顺序销毁顺序
bean3bean1bean1bean3
bean2bean2bean2bean2
bean1bean3bean3bean1

从输出中可以得到2点结论:

  1. bean对象的创建顺序和bean依赖的顺序一致
  2. bean销毁的顺序和bean创建的顺序相反

通过depend-on干预bean创建和销毁顺序

上面看到了对于无依赖的bean,通过定义的顺序确实可以干预bean的创建顺序,通过强依赖也可以干预bean的创建顺序。

那么如果xml中定义的bean特别多,而有些bean之间也没有强依赖关系,此时如果想去调整bean的创建和销毁的顺序,得去调整xml中bean的定义顺序,或者去加强依赖,这样是非常不好的,spring中可以通过depend-on来解决这些问题,在不调整bean的定义顺序和强加依赖的情况下,可以通过通过depend-on属性来设置当前bean的依赖于哪些bean,那么可以保证depend-on指定的bean在当前bean之前先创建好,销毁的时候在当前bean之后进行销毁。

depend-on使用方式:


  1. <bean id="bean1" class="" depend-on="bean2,bean3; bean4" />

depend-on:设置当前bean依赖的bean名称,可以指定多个,多个之间可以用”,;空格“进行分割

上面不管bean2,bean2,bean4在任何地方定义,都可以确保在bean1创建之前,会先将bean2,bean3,bean4创建好,表示bean1依赖于这3个bean,可能bean1需要用到bean2、bean3、bean4中生成的一些资源或者其他的功能等,但是又没有强制去在bean1类中通过属性定义强依赖的方式去依赖于bean2、bean3、bean4;当然销毁的时候也会先销毁当前bean,再去销毁被依赖的bean,即先销毁bean1,再去销毁depend-on指定的bean。

下面我们来个案例看一下:

dependOnBean.xml


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
  6. <bean id="bean3" class="com.javacode2018.lesson001.demo7.NormalBean$Bean3" depends-on="bean2,bean1"/>
  7. <bean id="bean2" class="com.javacode2018.lesson001.demo7.NormalBean$Bean2"/>
  8. <bean id="bean1" class="com.javacode2018.lesson001.demo7.NormalBean$Bean1"/>
  9. </beans>

上面xml中先定义的bean3,然后定义了bean2和bean1,并且指定了bean3的depend-on=“bean2,bean1”,根据depend-on的规则,所以会先创建bean2和bean1,然后再创建bean3,销毁的时候,会按照和创建相反的顺序来,即:bean1、bean2、bean3,下面我们来看看效果是不是这样:

上面xml对应的java代码如下:


  1. package com.javacode2018.lesson001.demo7;
  2. import org.springframework.beans.factory.DisposableBean;
  3. /**
  4. * 通过depend-on来干预bean创建和销毁顺序
  5. */
  6. public class DependOnBean {
  7. public static class Bean1 implements DisposableBean {
  8. public Bean1() {
  9. System.out.println(this.getClass() + " constructor!");
  10. }
  11. @Override
  12. public void destroy() throws Exception {
  13. System.out.println(this.getClass() + " destroy()");
  14. }
  15. }
  16. public static class Bean2 implements DisposableBean {
  17. public Bean2() {
  18. System.out.println(this.getClass() + " constructor!");
  19. }
  20. @Override
  21. public void destroy() throws Exception {
  22. System.out.println(this.getClass() + " destroy()");
  23. }
  24. }
  25. public static class Bean3 implements DisposableBean {
  26. public Bean3() {
  27. System.out.println(this.getClass() + " constructor!");
  28. }
  29. @Override
  30. public void destroy() throws Exception {
  31. System.out.println(this.getClass() + " destroy()");
  32. }
  33. }
  34. }

DependOnTest中创建测试用例:


  1. /**
  2. * 通过depend-on来干预bean创建和销毁顺序
  3. */
  4. @Test
  5. public void dependOnBean() {
  6. System.out.println("容器启动中!");
  7. String beanXml = "classpath:/com/javacode2018/lesson001/demo7/dependOnBean.xml";
  8. ClassPathXmlApplicationContext context = IocUtils.context(beanXml);
  9. System.out.println("容器启动完毕,准备关闭spring容器!");
  10. context.close();
  11. System.out.println("spring容器已关闭!");
  12. }

运行dependOnBean方法输出:


  1. 容器启动中!
  2. class com.javacode2018.lesson001.demo7.NormalBean$Bean2 constructor!
  3. class com.javacode2018.lesson001.demo7.NormalBean$Bean1 constructor!
  4. class com.javacode2018.lesson001.demo7.NormalBean$Bean3 constructor!
  5. 容器启动完毕,准备关闭spring容器!
  6. class com.javacode2018.lesson001.demo7.NormalBean$Bean3 destroy()
  7. class com.javacode2018.lesson001.demo7.NormalBean$Bean1 destroy()
  8. class com.javacode2018.lesson001.demo7.NormalBean$Bean2 destroy()
  9. spring容器已关闭!

总结

  1. 无依赖的bean创建顺序和定义的顺序一致,销毁顺序刚好相反
  2. 通过构造器强依赖的bean,会先创建构造器参数中对应的bean,然后才会创建当前bean,销毁顺序刚好相反
  3. depend-on可以指定档期bean依赖的bean,通过这个可以确保depend-on指定的bean在当前bean创建之前先创建好,销毁顺序刚好相反

问题?

通过setter方式注入依赖的bean的时候,bean的创建顺序和销毁的顺序是什么样的?大家可以研究一下,欢迎留言。

案例源码


  1. 链接:https://pan.baidu.com/s/1p6rcfKOeWQIVkuhVybzZmQ
  2. 提取码:zr99
cmake --build . --target help The following are some of the valid targets for this Makefile: ... all (the default if no target is provided) ... clean ... depend ... edit_cache ... install ... install/local ... install/strip ... list_install_components ... rebuild_cache ... test ... Continuous ... ContinuousBuild ... ContinuousConfigure ... ContinuousCoverage ... ContinuousMemCheck ... ContinuousStart ... ContinuousSubmit ... ContinuousTest ... ContinuousUpdate ... Experimental ... ExperimentalBuild ... ExperimentalConfigure ... ExperimentalCoverage ... ExperimentalMemCheck ... ExperimentalStart ... ExperimentalSubmit ... ExperimentalTest ... ExperimentalUpdate ... Nightly ... NightlyBuild ... NightlyConfigure ... NightlyCoverage ... NightlyMemCheck ... NightlyMemoryCheck ... NightlyStart ... NightlySubmit ... NightlyTest ... NightlyUpdate ... build_info ... common ... ggml ... ggml-base ... ggml-cpu ... llama ... llama-batched ... llama-batched-bench ... llama-bench ... llama-cli ... llama-convert-llama2c-to-ggml ... llama-cvector-generator ... llama-diffusion-cli ... llama-embedding ... llama-eval-callback ... llama-export-lora ... llama-finetune ... llama-gemma3-cli ... llama-gen-docs ... llama-gguf ... llama-gguf-hash ... llama-gguf-split ... llama-imatrix ... llama-llava-cli ... llama-logits ... llama-lookahead ... llama-lookup ... llama-lookup-create ... llama-lookup-merge ... llama-lookup-stats ... llama-minicpmv-cli ... llama-mtmd-cli ... llama-parallel ... llama-passkey ... llama-perplexity ... llama-q8dot ... llama-quantize ... llama-qwen2vl-cli ... llama-retrieval ... llama-run ... llama-save-load-state ... llama-server ... llama-simple ... llama-simple-chat ... llama-speculative ... llama-speculative-simple ... llama-tokenize ... llama-tts ... llama-vdot ... mtmd ... sha1 ... sha256 ... test-alloc ... test-arg-parser ... test-autorelease ... test-backend-ops ... test-barrier ... test-c ... test-chat ... test-chat-parser ... test-chat-template ... test-gbnf-validator ... test-gguf ... test-grammar-integration ... test-grammar-parser ... test-json-partial ... test-json-schema-to-grammar ... test-llama-grammar ... test-log ... test-model-load-cancel ... test-mtmd-c-api ... test-opt ... test-quantize-fns ... test-quantize-perf ... test-quantize-stats ... test-regex-partial ... test-rope ... test-sampling ... test-thread-safety ... test-tokenizer-0 ... test-tokenizer-1-bpe ... test-tokenizer-1-spm ... xxhash
10-04
你运行了: ```bash cmake --build . --target help ``` 输出列出了所有可用的构建目标(targets),其中包括: - `llama-cli` - `llama-server` - `llama-quantize` - `llama-perplexity` - `test-chat` - 等等 但 **没有 `main` 和 `server`**。 --- ## ✅ 正确答案:在当前版本的 `llama.cpp` 中 > 🔴 `main` 已被重命名为 `llama-cli` > 🔴 `server` 已被重命名为 `llama-server` 所以你看到的是最新命名规范的结果! --- ## 📌 如何构建命令行推理程序? 使用: ```bash cmake --build . --target llama-cli -j ``` ✅ 生成可执行文件: ``` build/examples/llama-cli ``` 📌 功能:加载模型并进行文本推理(即旧版 `main` 的功能) --- ## 📌 如何构建 HTTP 服务端? 使用: ```bash cmake --build . --target llama-server -j ``` ✅ 生成可执行文件: ``` build/examples/llama-server ``` 📌 功能:提供类似 OpenAI API 的 HTTP 接口,支持 `/completion`, `/embedding` 等。 > ⚠️ 注意:需要启用 `-DLLAMA_CURL=ON` 才能完整支持网络功能。 --- ## ✅ 完整推荐构建流程 ```bash cd llama.cpp/build rm -rf * # 配置项目(建议开启 curl 支持 server) cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DLLAMA_CURL=ON \ -DLLAMA_METAL=ON # macOS M系列芯片加速 -DLLAMA_CUDA=ON # NVIDIA GPU 加速(可选) -DLLAMA_BLAS=ON # 启用 BLAS 数学库优化 # 查看所有目标(可选) cmake --build . --target help # 构建核心工具 cmake --build . --target llama-cli llama-server llama-quantize -j ``` --- ## 🧩 为什么改名?背景说明 从 2023 年底开始,`llama.cpp` 社区对代码结构进行了重构: | 旧名称 | 新名称 | 原因 | |-------|--------|------| | `main` | `llama-cli` | 更明确表示是“命令行接口” | | `server` | `llama-server` | 统一命名风格,避免歧义 | | `quantize` | `llama-quantize` | 模块化命名,便于扩展 | 👉 所有 example 程序现在都加上了 `llama-` 前缀,提升一致性。 --- ## ✅ 验证是否成功构建 ```bash ./examples/llama-cli --help ./examples/llama-server --port 8080 ``` 你应该能看到帮助信息或启动日志。 示例运行: ```bash # 运行一个本地推理 ./examples/llama-cli \ -m ./models/llama3-8b-instruct.Q4_K_M.gguf \ -p "Hello, how are you?" \ -n 128 # 启动服务器 ./examples/llama-server \ -m ./models/llama3-8b-instruct.Q4_K_M.gguf \ --port 8080 ``` 然后你可以通过 curl 测试: ```bash curl http://localhost:8080/completion \ -d &#39;{"prompt": "Hello, who are you?", "temperature": 0.7}&#39; ``` --- ## 💡 小技巧:创建别名方便使用 ```bash ln -s examples/llama-cli main ln -s examples/llama-server server ln -s examples/llama-quantize quantize ./main --help ./server --port 8080 ``` 这样就可以继续用旧习惯命名。 --- ## ❌ 常见错误总结 | 错误操作 | 结果 | 正确做法 | |--------|------|---------| | `cmake --build . --target main` | 报错:no such target | 改为 `llama-cli` | | `cmake --build . --target server` | 报错:no such target | 改为 `llama-server` | | 忽略 `-DLLAMA_CURL=ON` | server 缺少 HTTPS 下载支持 | 构建时开启 | | 使用过时教程中的 `BUILD_MAIN=ON` | 变量未被使用 | 不再需要 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值