曾彪彪的个人网站
首页
文章列表
>>
文章详情
创建spring boot starter
作者:
曾彪彪
日期:
2023-05-16 05:14:54
阅读(873)
分类:
### 背景介绍 Spring Boot为开发提供了极大的便利,开发者可以使用Spring Boot快速构建自己的应用。比如需要在Spring Boot中使用Rabbit MQ,我们只需要引入依赖以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 然后在application.properties或application.yml中添加以下数据库配置信息: ```yaml spring: rabbitmq: addresses: ${RABBIT_ADDRESS:localhost:5672} username: ${RABBIT_USERNAME:guest} password: ${RABBIT_PASSWORD:guest} ``` 然后我们就可以自动注入RabbitTemplate了。 ```java @Service public class MessageService { @Autowired RabbitTemplate rabbitTemplate; } ``` 这一切的核心,就是自动装配。 ---- ### Spring Boot 自动装配原理 Spring Boot依赖spring-boot-autoconfigure,这个jar包是帮助spring boot自动装配所有需要的依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> ``` 创建一个spring boot工程,系统会自动引入这个jar包,查看jar包下的META-INF --> spring.factories文件,会发现默认自动添加了许多模块。比如我们上面讲到的RabbitAutoConfiguration。 ```properties # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ # 注意看这里... org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ # 其它的省略了 ``` **重要的事情说三遍,以下内容为:重点,重点,重点!** Spring Boot在启动时,会解析这些配置文件。我们看到,org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration这个类被加进去了,我们以这个作为列子讲解。 展开spring-boot-autoconfigure下的org.springframework.boot.autoconfigure --> amqp,找到RabbitAutoConfiguration,打开其源代码: ```java @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ RabbitTemplate.class, Channel.class }) // 参见下边注释1 @EnableConfigurationProperties(RabbitProperties.class) // 参见下边注释2 @Import(RabbitAnnotationDrivenConfiguration.class) public class RabbitAutoConfiguration { // 其它代码省略... @Bean @ConditionalOnSingleCandidate(ConnectionFactory.class) @ConditionalOnMissingBean(RabbitOperations.class) public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) { RabbitTemplate template = new RabbitTemplate(); configurer.configure(template, connectionFactory); return template; } @Bean @ConditionalOnSingleCandidate(ConnectionFactory.class) @ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true) @ConditionalOnMissingBean // 参见下边注释3 public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) { return new RabbitAdmin(connectionFactory); } } } ``` 可以看到,在这个类里边,我们注入了RabbitTemplate和AmqpAdmin,这就是我们可以在自己的代码中自动注入这两个类实列的原因。 这里需要解析几个注解: 1. *@ConditionalOnClass({ RabbitTemplate.class, Channel.class })* 这个注解的意思是,如果Spring Boot应用程序中包含RabbitTemplate和Channel这两个类,RabbitAutoConfiguration配置文件才生效,否则不解析配置文件。所以当我们引入spring-boot-starter-amqp依赖的时候,这个配置就生效了,Spring Boot就会帮我们创建RabbitTemplate的实例。 2. *@EnableConfigurationProperties(RabbitProperties.class)* 这个注解就是注入RabbitProperties实例,这是一个读取配置文件的的配置类,代码如下: ```java @ConfigurationProperties(prefix = "spring.rabbitmq") public class RabbitProperties { private static final int DEFAULT_PORT = 5672; private static final int DEFAULT_PORT_SECURE = 5671; /** * RabbitMQ host. Ignored if an address is set. */ private String host = "localhost"; /** * RabbitMQ port. Ignored if an address is set. Default to 5672, or 5671 if SSL is * enabled. */ private Integer port; /** * Login user to authenticate to the broker. */ private String username = "guest"; // 省略其它代码... ``` 这个类就是从我们的yml配置文件中读取Rabbit 连接等配置信息。 3. *@ConditionalOnMissingBean*, 这是放在创建Bean的方法上测注解,意思是如果Amqpadmin这个类存在,就不创建了。这给用户灵活的选择,可以创建自己的Ampqadmin来管理Rabbit MQ,也可以不创建,使用系统默认的。 **重点结束啦,到这里,是不是理解了Spring Boot自动装配的原理了,这么核心的东西,是不是觉得也不难~~** ---- ### 创建spring boot starter 接下来手把手带领大家一起创建一个spring boot starter项目。 ##### 应用场景 为什么要创建一个spring boot starter项目呢,目的是为了使用方便和代码复用。举个例子,加入你有个监控系统叫monitor-center,是基于Socket IO开发的,很多系统都需要调用这个监控系统获取监控数据。这个监控系统使用OAuth 2.0进行授权的,在使用监控系统前,需要先使用账户密码登录安全中心,获取token,然后使用token访问监控系统。如果token过期,又需要访问安全中心,刷新token。之后是访问监控系统获取监控数据。 上边细节还挺繁琐的,如果每个监控应用都实现一遍,工作任务就太大了,于是我们考虑做一个monitor-boot-starter,包装所有细节。然后提供一个MonitorService给客户使用。 ##### 创建项目 下面开始创建项目: 第一步,创建一个Spring boot工程,取名monitor-boot-starter,引入如下依赖: ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zengbiaobiao.demo</groupId> <artifactId>monitor-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> <name>monitor-boot-starter</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> ``` 这里需要注意两个细节: - 命名规范,Spring官方的starter,都叫spring-boot-starter-<模块名称>,我们自己定义的start,都命名为 <模块名称>-boot-starter。 - 添加了另一个依赖spring-boot-configuration-processor,并且在plugin的configuration中排除它,这在后边会讲到。 第二部,创建一个config包,添加以下三个类: ```java package com.zengbiaobiao.demo.monitor.autoconfig; /** * @author zengsam * 这是自动装配类,初始化monitor-boot-starter中所有需要用到的bean。 */ @Configuration @ConditionalOnClass(MonitorService.class) @EnableConfigurationProperties(MonitorProperties.class) public class MonitorAutoConfiguration { @Autowired private MonitorProperties properties; @Bean @ConditionalOnMissingBean public MonitorService monitorService() { return new MonitorService(properties); } } ``` ```java /** * @author zengsam * 这是配置类,用于从application.yml或application.properties中读取配置信息 */ @ConfigurationProperties(prefix = "demo.monitor") public class MonitorProperties { private String loginUrl; private String username; private String password; private String serverUrl; // 省略getter,setter方法... } ``` ```java /** * @author zengsam * 这是我们这个应用的核心类,应该在自动装配类中创建。 */ public class MonitorService { private MonitorProperties properties; public MonitorService(MonitorProperties properties) { this.properties = properties; } public void subscribe(String url, Consumer<String> callback) { System.out.println("login to security center:"); System.out.println("loginUrl=" + properties.getLoginUrl()); System.out.println("username=" + properties.getUsername()); System.out.println("password=" + properties.getPassword()); System.out.println("connect to monitor:"); System.out.println("serverUrl=" + properties.getServerUrl()); System.out.println("receive monitor data"); callback.accept("current time:" + new Date().toString()); callback.accept("current time:" + new Date().toString()); callback.accept("current time:" + new Date().toString()); } public void unsubscribe(String url) { System.out.println("unsubscribe:" + url); } } ``` 每个类的功能和作用,都做了注释。 第三步,在resources目录下,创建META-INF文件夹,然后创建文件spring.factories,添加以下内容。 ```properties # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.zengbiaobiao.demo.monitor.autoconfig.MonitorAutoConfiguration ``` Spring boot starter启动的时候,这个文件的的内容会被合并到spring-boot-autoconfigure的spring.factories中。这就告诉Spring Boot,系统启动时,除了扫描默认的自动装配类,也扫描com.zengbiaobiao.demo.monitor.autoconfig.MonitorAutoConfiguration这个类。 好啦,这个项目已经创建结束了,现在只需要打包安装就可以使用了。执行以下命令: ```shell mvn clean compile install ``` ---- ### 使用spring boot starter 我们来创建一个项目,使用刚才我们自己创建的monitor-boot-starter。 第一步,创建Spring Boot工程,取名demo,添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.zengbiaobiao.demo</groupId> <artifactId>monitor-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> ``` 第二部,在application.yml中,添加以下内容: ```yaml demo: monitor: server-url: https://www.demo-monitor.com username: samzeng password: 123456 ``` 当你在application.yml中输入demo的时候,是不是还带自动提示功能的,惊不惊喜,意不意外^..^。 第三步,在DemoApplication.java类中,注入MonitorService并调用subscribe方法。 ```java @SpringBootApplication public class DemoApplication implements CommandLineRunner { @Autowired private MonitorService monitorService; public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Override public void run(String... args) throws Exception { monitorService.subscribe("abc", data -> System.out.println("receive data:" + data)); } } ``` 运行程序,得到输入如下: ```shell login to security center: loginUrl=https://www.demo-login.com username=samzeng password=123456 connect to monitor: serverUrl=https://www.demo-monitor.com receive monitor data receive data:current time:Fri Sep 11 13:53:05 CST 2020 receive data:current time:Fri Sep 11 13:53:05 CST 2020 receive data:current time:Fri Sep 11 13:53:05 CST 2020 ``` 很好,虽然功能很简单,但一切都在我的掌握之中。 ---- ### Spring Boot Configuration Metadata 刚才我们在使用monitor-boot-starter的时候,居然还带自动提示的,这么骚包。这是为什么呢,现在来解释一下。 在我们工程monitor-boot-starter的pom.xml中,我们添加了pring-boot-configuration-processor这个依赖,这个依赖的作用是,在编译时,为所有的添加了@ConfigurationProperties的类添加元数据,这些元数据是可以被IDE读到的,我们在application.yml中输入demo的时候,就会自动提示demo.monitor.login-url。 ```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zengbiaobiao.demo</groupId> <artifactId>monitor-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> <name>monitor-boot-starter</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> ``` 当然根据官方的要求,需要在spring-boot-maven-plugin中排除掉spring-boot-configuration-processor,估计是为了避免版本冲突。具体请参见[Configuring the Annotation Processor](https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-configuration-metadata.html#configuration-metadata-annotation-processor-setup) Configuration Metadata还可以做的更加智能,不但给出智能提示,还把可选项都可以列出来。大家知道有这个功能就行了,用的时候再去查。这其实是另一个知识点,具体参考[Configuration Metadata](https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-configuration-metadata.html) ---- ### 总结 好吧,讲完了,总结一下,如果要创建一个spring boot starter,可以参考以下步骤: 1. 首先需要引入spring-boot-autoconfigure和spring-boot-configuration-processor依赖,后边这个依赖是用来产生配置文件元数据的。 2. 创建AutoConguration类,这里需要理解两个注解,@ConditionalOnClass(MonitorService.class)和@ConditionalOnMissingBean,第一个注解是放在自动装配类上的,表示只有当MonitorService类存在的时候才执行自动装配。第二个类是放在创建Bean的方法上,表示只有当前类实例不存在的时候才创建,这给用户很大的灵活性。可以自己创建,也可以使用系统默认的Bean。 3. 创建配置文件类,从application.yml中读取配置文件信息。 4. 在resources目录下,创建META-INF文件加,并在该文件夹下创建spring.properties,添加需要自动装配的类。Spring Boot会在启动时,合并spring.properties中的内容。 5. 最后就是打包安装,然后使用啦。 实例代码在[github](https://github.com/zengbiaobiao/demo)上,希望可以帮到你。 ---- 所有文章在[我的博客](https://www.zengbiaobiao.com)上同步。
评论(0)
评论(必填)
名称(必填)
联系方式(可选)
验证码(必填)
提交
评论(必填)
名称(必填)
联系方式(可选)
验证码(必填)