1.开始
首先肯定要说一下SpringBoot的四大核心了:
· 自动装配:简单配置甚至零配置即可运行项目
· 起步依赖:场景启动器
· Actuator:指标监控
· 命令行界面:命令行
关于四大核心中的自动装配,可以看下这篇文章:SpringBoot——四大核心之自动装配(源码解析)_张起灵-小哥的博客-CSDN博客
这篇文章呢,我来和大家聊聊起步依赖这个东西。
2.聊聊起步依赖
首先呢,我们来看一个SpringBoot项目中最常见的依赖项。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
这个我们都知道,一般称为 SpringBoot框架web项目的起步依赖,那么为什么我们添加这个依赖之后,之前在SpringMVC中的一些依赖就不需要再添加了呢?点进入这个依赖看看。
可以看到这个依赖中,还管理着其他依赖,其中就有tomcat、spring-webmvc这些,这不都是之前学SpringMVC需要配置tomcat以及其他依赖项吗?而spring-boot-starter-web这个起步依赖已经帮我们做好了,你就不需要再添加很多其他依赖了,是不是很方便呢?
在我们创建一个SpringBoot项目之后,向其中的pom文件添加依赖的时候,我们会发现,添加的很多依赖都是以 spring-boot-start-XXX 这样的形式,我当时就按照字面意思去理解:springboot-开始-XXX的依赖。
那么,我们现在说的规范一点:spring-boot-start-XXX就是spring-boot的场景启动器。
spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;
spring-boot-starter-thymeleaf:帮我们导入了thymeleaf模板引擎正常运行所依赖的组件;
SpringBoot就是将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来,我们要用什么功能就导入什么样的场景启动器即可;我们未来也可以自己自定义 starter。
说的形象一点,我们之前学SSM的时候,在pom文件中加入依赖,是不是有spring-context、spring-core、spring-webmvc等等。而现在不需要了,你就可以直接写一个spring-boot-start-XXX完事了。之前SSM那种方式就是点了一份又一份的菜,而现在SpringBoot直接就是点了一个套餐,这个套餐中有很多你需要的菜名。
我们可以在spring.factories配置文件中,随便找一个自动配置类点进去看看。
可以看到,在SpringBoot官方的starter中,先是定义了一个XXXAutoConfiguration自动配置类,然后这个类上标注的一些注解:是个配置类、什么条件下生效,导入了哪些类,以及开启对@ConfigurationProperties 注解的支持,在这个@EnableConfigurationProperties注解中,还有一个XXXProperties的属性配置类,在这个类中,上面有一个配置前缀信息的注解,而这个类中有一些属性、set/get方法,而这个前缀 + 属性就是我们在application.properties配置文件中所写的属性名。
3.自定义starter
上面说的是SpringBoot官方的starter,那么我们也可以自定义starter。
starter的命名有一种习惯,官方的starter一般都是spring-boot-starter-xxx,而我们自定义的starter一般都是xxx-spring-boot-starter。
先来看一下,完成整个自定义starter需要哪几个项目
· hello-spring-boot-starter:在项目开发中真正要引入的场景启动器。场景启动器引入了自动配置包模块hello-spring-boot-autoconfigure。
· hello-spring-boot-autoconfigure:真正的自动配置包模块,它实现的才是springboot的自动装配原理。
· boot-hello-test:测试模块。
3.1 hello-spring-boot-autoconfigure(普通SpringBoot项目)
我们自定义starter就是为了将某些热门的业务功能给抽取出来,做成一个个的场景启动器,为开发做准备。
所以这里先写一个service。
package com.szh.boot.service; import com.szh.boot.bean.HelloProperties; import org.springframework.beans.factory.annotation.Autowired; /** * 默认不要放在IoC容器中 */ public class HelloService { @Autowired private HelloProperties helloProperties; public String sayHello() { return "盗墓者姓名: " + helloProperties.getName() + ", 盗墓者年龄: " + helloProperties.getAge(); } }
由于service中的业务,是我们未来开发中要调用的,从中获取一些属性、执行一定的业务逻辑,所以我们要在service中注入XxxProeprties属性类。
下面来编写这个属性类。
package com.szh.boot.bean; import org.springframework.boot.context.properties.ConfigurationProperties; /** * */ @ConfigurationProperties(prefix = "szh.info") public class HelloProperties { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
又因为springboot的自动装配原理是:读取META-INF\spring.factories 文件,加载这其中的所有组件,之后再根据XXXAutoConfiguration 自动配置类中的注解属性来决定哪些自动配置类生效、哪些不生效,生效之后再根据某些注解属性条件来决定向容器中添加哪些组件(@Bean),同时将自动配置类与属性类进行绑定,这样我们就可以通过service完成对XxxProeprties属性类的注入和使用。
所以,下面来编写核心的 XXXAutoConfiguration 自动配置类。
package com.szh.boot.auto; import com.szh.boot.bean.HelloProperties; import com.szh.boot.service.HelloService; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Configuration: 声明该类为配置类 * @ConditionalOnMissingBean: 当容器中没有HelloService这个Bean时, 这个自动配置类才生效 * @EnableConfigurationProperties: 与相应的XxxProperties属性类进行绑定, 并放入容器中 */ @Configuration @ConditionalOnMissingBean(HelloService.class) @EnableConfigurationProperties(HelloProperties.class) public class HelloAutoConfiguration { @Bean public HelloService helloService() { return new HelloService(); } }
因为要读取 META-INF\spring.factories 文件,所以这里来创建好META-INF\spring.factories 文件,位于 resources 目录下。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.szh.boot.auto.HelloAutoConfiguration
这个模块的代码编写已经完成,下面将这个模块进行 clean、install 打包到maven本地仓库,以便我们在 starter 场景启动器中可以引入。
3.2 hello-spring-boot-starter(普通maven项目)
因为上面已经完成对自动装配模块的编写,同时也已经 maven install 了,所以在场景启动器模块中直接引入就行。
<dependencies> <dependency> <groupId>com.szh.boot</groupId> <artifactId>hello-spring-boot-autoconfigure</artifactId> <version>1.0.0</version> </dependency> </dependencies>
依赖添加之后,将我们的starter场景启动器同样 maven clean、install打包到本地仓库中,以便测试模块中进行依赖的导入。
3.3 boot-hello-test(SpringBoot Web项目)
最后来写我们的测试模块,根据以往学习springboot的经历,我们引入依赖肯定大部分都是引入了官方定义好的starter(spring-boot-starter-xxx),在这些场景启动器中有相关的 autoconfigure 自动配置依赖。
所以我们自定义starter也是一样的,将相关的 autoconfigure 自动配置依赖打包,然后在 starter 中进行引入,最后在开发测试的模块中只需要将starter引入就可以了。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.szh.boot</groupId> <artifactId>hello-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> </dependencies>
在测试模块中,先写一个 controller。在其中注入了我们在自动装配模块中写好的服务类HelloService,在HelloService中已经注入了XXXProperties属性类,所以这里我们一旦注入HelloService,也就有了相应的自动配置类、属性类。
package com.szh.boot.controller; import com.szh.boot.service.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * */ @RestController public class HelloController { @Autowired private HelloService helloService; @GetMapping(value = "/hello") public String getInfo() { return helloService.sayHello(); } }
下面,就可以在核心配置文件中定义XXXProperties属性类中的配置项的值了。
szh.info.name=张起灵 szh.info.age=18
最后,启动测试。
package com.szh.boot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class BootHelloTestApplication { public static void main(String[] args) { SpringApplication.run(BootHelloTestApplication.class, args); } }
可以看到,成功的获取到了配置文件中的属性值。
至此,我们的自定义starter的所有步骤已经全部完成了。