SpringBoot制作Starter以及bean的自动装配

发布时间:2026/7/17 6:01:04
SpringBoot制作Starter以及bean的自动装配 什么是 Starter举个例子在 Spring Boot 项目中引入 spring-boot-starter-data-redis 的依赖就获得了一个全局可用的 StringRedisTemplate 类型的 bean 对象。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency从命名上就能看出来spring-boot-starter-data-redis 就是一个 starter。当然其实 spring-boot-starter-data-redis 并不是只有 StringRedisTemplate 这么一个 bean。在 starter 中可以声明多个 bean项目在启动的时候会加载需要的 bean这个就是 bean 的自动装配。在我的理解中starter 就是一个 jar 包只需要遵循 Spring 的约定大于配置的哲学根据 Spring 的规则制作 jar 包。在其他项目中引入这个 jar 包就可以直接获得 bean 对象了不需要二次配置。如何制作自己的 starter官方自己制作的 starter 的命名规范是spring-boot-starter-xxx。spring-boot-starter-webspring-boot-starter-data-redisspring-boot-starter-mail第三方制作 starter 的命名建议是xxx-spring-boot-starter跟官方命名的方向相反。mybatis-spring-boot-starter如果是自己制作的话不用管那么多规范因为蒙多想去哪就去哪。第一步创建新项目引入依赖groupIdorg.example/groupId artifactIda/artifactId version1.0-SNAPSHOT/version properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-autoconfigure/artifactId version3.0.0/version /dependency /dependencies第二步从配置文件获取数据还是以 spring-boot-starter-data-redis 为例我们在引入 jar 包后需要在配置文件中配置 redis 的相关信息。同样的我们制作 starter 的时候也可以从配置文件中获取数据。prefix a配置文件需要以 a 为前缀。ConfigurationProperties(prefix a) public class AProperties { private String A1; private String A2; public AProperties() { } public AProperties(String a1, String a2) { A1 a1; A2 a2; } public String getA1() { return A1; } public void setA1(String a1) { A1 a1; } public String getA2() { return A2; } public void setA2(String a2) { A2 a2; } }第三步编写业务类public class AService { private AProperties aProperties; public AService(AProperties aProperties) { this.aProperties aProperties; } public void work() { System.out.println(用户名 aProperties.getA1()); System.out.println(密码 aProperties.getA2()); System.out.println(starter业务执行); } }这里简单提供了一个 work 方法打印 A1、A2 对应的取值用来模拟从配置文件中获得地址、用户名、密码、数据库等信息。第四步编写自动配置类Configuration EnableConfigurationProperties(AProperties.class) public class AAutoConfig { Bean public AService aService(AProperties aProperties) { return new AService(aProperties); } }在这个自动配置类中通过方法返回一个 bean 对象。第五步服从 Spring 的命令在 resources 目录下创建 META-INF 目录。在 META-INF 目录下创建 spring 目录。在 spring 目录下创建 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。在这个什么 imports 文件中写入自动配置类的全类名。org.example.a.AAutoConfig这个是 Spring Boot 3 的写法还有一种写法是 Spring Boot 2.x 的但是我都没用过 2.x 的版本所以我也不知道怎么写。但是写法本身不重要也没必要研究茴香豆的茴字有多少种写法要用的时候查一下就行了。第六步打 jar 包并保存到本地仓库通过 maven 执行 install 命令将项目打成 jar 包并保存到本地仓库中。如果是用 IDEA 写 Java 的话IDEA 本身就集成了 maven就算自己安装了 maven 也会默认用 IDEA 自带的 maven仓库目录是C:\Users\admin\.m2\repository。验证 starter 是否可正常使用创建新项目引入 starter。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId version3.0.0/version /dependency dependency groupIdorg.example/groupId artifactIda/artifactId version1.0-SNAPSHOT/version /dependency /dependenciesserver: port: 8080 a: A1: 100 A2: 200RestController public class BController { Autowired private AService aService; GetMapping(/test) public int test() { aService.work(); return 200; } }接口正常返回数据业务方法正常执行。现在我们成功制作了一个自己的 starter之后还需要用 AService 的功能直接引入使用即可可以说是开袋即食。但是我们这么大费周章结果只是给 Spring 添加了一个 bean。我们不是可以用 Component 注解声明 bean 吗那就试试吧。Component 和配置类我在 starter 中添加了一个 bean。Component public class AComponent { public void work() { System.out.println(一个小组件); } }同时把 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 的目录和文件都删除掉了确保 bean 的加载跟上文不同。重新打包这次把版本号改成 2.0-SNAPSHOT。在测试项目中注入这个新的 bean。RestController public class BController { Autowired private AService aService; Autowired private AComponent aComponent; GetMapping(/test) public int test() { aService.work(); aComponent.work(); return 200; } }现在重新启动测试项目结果服务启动失败提示缺少 bean 对象。Description: Field aComponent in org.example.b.BController required a bean of type org.example.a.AComponent that could not be found. The injection point has the following annotations: - org.springframework.beans.factory.annotation.Autowired(requiredtrue) Action: Consider defining a bean of type org.example.a.AComponent in your configuration.为什么会出现这种情况呢因为 Spring 默认只会扫描启动类所在包和子包下面的类。我们的测试项目的包是 org.example.b而 starter 项目的包是 org.example.a在测试项目中根本就没有扫描到 org.example.a 下面的类自然不会把 AComponent 注册为 bean。解决方法很简单在启动类中添加 ComponentScan(org.example.a)指定要扫描的路径即可。SpringBootApplication ComponentScan(org.example.a) public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }现在服务启动成功了但是接口报错了响应码是 404。这个问题就出在了 ComponentScan(org.example.a) 这里因为我们指定了要扫描路径是 org.example.a所以它不会去扫描 org.example.b我写的 Controller 都没有被扫描到Controller 里面的 test 接口自然也不会开启。浏览器向一个不存在的接口发送请求当然是 404 资源不存在。所以我们需要继续修改要扫描的路径。SpringBootApplication ComponentScan({org.example.a, org.example.b}) public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }现在再启动项目试一下这次就顺利启动并且接口正常响应了。Spring Boot 中 bean 的自动装配在我看来bean 的创建有两种方式Component 注解声明的 beanConfiguration 配置类中 Bean 方法返回的 bean一个项目中 bean 的来源有两个途径项目本身声明的 bean项目需要的依赖中引入的 bean现在两两组合一下得到了四种取值项目自己通过 Component 注解被 ComponentScan 扫描注册为 bean项目自己通过 Configuration 配置类中 Bean 注解也是被 ComponentScan 扫描注册为 bean依赖中的 Component 注解也需要被 ComponentScan 扫描注册为 bean依赖中的配置类中的 Bean 注解要么被 ComponentScan 扫描注册为 bean要么遵循 Spring 的规则创建 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件并写上配置类的全类名如果是通过 ComponentScan 扫描配置类的话不需要这个 imports 文件。但是如果不用 ComponentScan 的话就必须用 imports 文件声明配置类的位置。在 Spring Boot 的启动类中有一个 SpringBootApplication 注解Ctrl B 进入这个类发现这个类上面还有其他注解。SpringBootApplicationSpringBootConfiguration这是一个配置类ComponentScan扫描的路径默认是扫描启动类所在包及其下面的子包EnableAutoConfiguration从依赖中的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中获取自动配置类的路径根据 Conditional 注解这些配置类中的 Bean 修饰的方法的返回值注册成 bean那现在问题出现了如果我们要用其他第三方的 bean通过 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 这个文件只能获得配置类中 Bean 方法返回的 bean不能获得 Component 声明的 bean。如果要获得 Component 声明的 bean需要配置 ComponentScan 的路径但是我们一配置这个路径就能直接获得 Component 和 Bean 声明的 bean 了也不需要这个 imports 文件了啊。其实直接通过 ComponentScan 去指定路径的话是比较麻烦的。用极限法来假设一下你要用 1000 亿个依赖每个依赖都有自己的包名那不就要在 ComponentScan 中手动填写 1000 亿次这多麻烦啊。用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 声明配置类的位置这个工作不是我们自己干的是制作这个依赖的人去完成的我们只需要引入依赖就可以使用了这就很方便。