Spring Boot 对比 Spring MVC 最大的优点就是使用简单,约定大于配置。不会像之前用 Spring MVC 的时候,时不时被 xml 配置文件搞的晕头转向,冷不防还因为 xml 配置上的一点疏忽,导致整个项目莫名其妙的不可用,顿感生活无所依恋,简称生无可恋。

这要归功于组成了 Spring Boot 的各种各样的 starters,有官方提供的,也有第三方开源出来。可以这么说,基本上你打算用的功能都可以找到,如果没有找到,那就再找一找。

用 Spring Boot 的功能组件(例如 spring-boot-starter-actuator、 spring-boot-starter-data-redis 等)的步骤非常简单,用著名的把大象放冰箱的方法来概括的话,有以下三步就可以完成组件功能的使用:

STEP 1

在 pom 文件中引入对应的包,例如:

<dependency> <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
复制代码

STEP 2

在应用配置文件中加入相应的配置,配置都是组件约定好的,需要查看官方文档或者相关说明。有些比较复杂的组件,对应的参数和规则也相应的较多,有点可能多大几十上百了。

STEP 3

以上两步都正常的情况下,我们就可以使用组件提供的相关接口来开发业务功能了。

没错吧,这个过程我们在日常的开发中不知道已经实践了多少遍。那么 Spring Boot 为什么能做到如此简单易用呢,它内部是什么样的工作机制呢,不知道你有没有研究过。

以下是为了理解 Spring Boot 组件的实现机制而制作的一个 demo starter。理解其中的原理,对我们日后的工作有什么意义呢?

  1. 遇到问题的时候,可以帮助我们更有头绪的排查问题;
  2. 可以帮助我们正确的阅读源代码,组件的切入口在哪儿,配置属性是什么等等;

开始实现一个 Spring Boot Starter

下面我们来实现这个简单的 starter,这个 starter 并没有什么实际的功能,只是为了做个演示而已。

开始之前,我们要理解一下 spring boot starter 是什么呢?

实际上 starter 并不会包含多少功能代码,我们可以把它理解成一个「连接包」(我自己造的概念),按照这个概念来说:
它首先是一个包,一个集合,它把需要用的其他功能组件囊括进来,放到自己的 pom 文件中。
然后它是一个连接,把它引入的组件和我们的项目做一个连接,并且在中间帮我们省去复杂的配置,力图做到使用最简单。

实现一个 starter 有四个要素:

  1. starter 命名 ;
  2. 自动配置类,用来初始化相关的 bean ;
  3. 指明自动配置类的配置文件 spring.factories ;
  4. 自定义属性实体类,声明 starter 的应用配置属性 ;

好了,开始实现我们的 demo

1. 给 starter 起个名字

也就是我们使用它的时候在 pom 中引用的 artifactId。命名有有规则的,官方规定:

官方的 starter 的命名格式为 spring-boot-starter-{name} ,例如上面提到的 spring-boot-starter-actuator。

非官方的 starter 的命名格式为 {name}-spring-boot-starter,我们把自定的 starter 命名为 kite-spring-boot-starter,命名在 pom 文件里。

<groupId>kite.springcloud</groupId>
<artifactId>kite-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
复制代码

2. 引入自动配置包及其它相关依赖包

实现 starter 主要依赖自动配置注解,所以要在 pom 中引入自动配置相关的两个 jar 包

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
复制代码

除此之外,依赖的其他包当然也要引进来。

3. 创建 spring.factories 文件

在 resource/META-INF 目录下创建名称为 spring.factories 的文件,为什么在这里?当 Spring Boot 启动的时候,会在 classpath 下寻找所有名称为 spring.factories 的文件,然后运行里面的配置指定的自动加载类,将指定类(一个或多个)中的相关 bean 初始化。

例如本例中的配置信息是这样的:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=kite.springcloud.boot.starter.example.KiteAutoConfigure
复制代码

等号前面是固定的写法,后面就是我们自定义的自动配置类了,如果有多个的话,用英文逗号分隔开。

4. 编写自动配置类

自动配置类是用来初始化 starter 中的相关 bean 的。可以说是实现 starter 最核心的功能。

@Configuration
@ConditionalOnClass(KiteService.class)
@EnableConfigurationProperties(KiteProperties.class)
@Slf4j
public class KiteAutoConfigure {@Autowiredprivate KiteProperties kiteProperties;@Bean@ConditionalOnMissingBean(KiteService.class)@ConditionalOnProperty(prefix = "kite.example",value = "enabled", havingValue = "true")KiteService kiteService(){return new KiteService(kiteProperties);}
}
复制代码

代码非常简单,放眼望去,最多的就是各种注解。

@Configuration 这个不用解释,表示这是个自动配置类,我们平时做项目时也会用到,一般是用作读取配置文件的时候。

@ConditionalOnClass(KiteService.class) :

只有在 classpath 中找到 KiteService 类的情况下,才会解析此自动配置类,否则不解析。

@EnableConfigurationProperties(KiteProperties.class):

启用配置类。

@Bean:实例化一个 bean 。

@ConditionalOnMissingBean(KiteService.class):

与 @Bean 配合使用,只有在当前上下文中不存在某个 bean 的情况下才会执行所注解的代码块,也就是当前上下文还没有 KiteService 的 bean 实例的情况下,才会执行 kiteService() 方法,从而实例化一个 bean 实例出来。

@ConditionalOnProperty:

当应用配置文件中有相关的配置才会执行其所注解的代码块。

这个类的整体含义就是: 当 classpath 中存在 KiteService 类时解析此配置类,什么情况下才会在 classpath 中存在呢,就是项目引用了相关的 jar 包。并且在上下文中没有 KiteService 的 bean 实例的情况下,new 一个实例出来,并且将应用配置中的相关配置值传入。

5. 实现属性配置类

@Data
@ConfigurationProperties("kite.example")
public class KiteProperties {private String host;private int port;
}
复制代码

配置类很简单,只有两个属性,一个 host ,一个 port 。配置参数以 kite.example 作为前缀。稍后我们在使用这个 starter 的时候会看到如何声明配置属性。

6. 实现相关功能类

也就是前面一直提到的 KiteService,其实严格来讲,这个业务功能类不应该放在 starter 中,应该放在单独的 jar 包里,但是此处 demo 非常简单,也就在这里写了。

@Slf4j
public class KiteService {private String host;private int port;public KiteService(KiteProperties kiteProperties){this.host = kiteProperties.getHost();this.port = kiteProperties.getPort();}public void print(){log.info(this.host + ":" +this.port);}
}
复制代码

一个构造函数和一个 print 方法。

7. 打包

通过 maven 命令将这个 starter 安装到本地 maven 仓库

mvn install
复制代码

也可以通过 mvn package deploy 发布到你的私服

或者发布到中央仓库。

使用刚创建的 starter

上面已经完成了 starter 的开发,并安装到了本地仓库,然后就是在我们的项目中使用它了。

1. 创建项目,在 pom 中引用

<dependency><groupId>kite.springcloud</groupId><artifactId>kite-spring-boot-starter</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
复制代码

2. 应用配置项

创建 application.yml ,配置如下:

server:port: 3801
kite:example:enabled: true  # 开启才生效host: 127.0.0.1port: 3801
复制代码

3. 调用 KiteService 的服务方法

@RestController
@RequestMapping(value = "use")
public class UseController {@Autowiredprivate KiteService kiteService;@GetMapping(value = "print")public void print(){kiteService.print();}
}
复制代码

4. 启动服务,并访问接口

访问 /use/print 接口,会发现在日志中打印出了配置信息

2019-05-24 16:45:04.234  INFO 36687 --- [nio-3801-exec-1] k.s.boot.starter.example.KiteService     : 127.0.0.1:3801
复制代码

抛砖引玉,如何阅读其他 starter 源码

顺着上面的思路,我们来看一下官方的 starters 的结构。先来把 Spring Boot 从 github 上 clone 一份下来。用 idea 打开,可以看到项目结构如下

Spring-boot-starters 中就是官方提供的主要 starters,比如 jdbc、redis、security、web 等等。

我们拿 spring-boot-starter-data-redis 这个 starter 作为例子,来说一说官方是怎么组织项目结构的,以及阅读源码的顺序应该是怎样的。

1. 展开 Spring-boot-staters 下的 redis starter,我们看到目录结构如下

其中并没有 Java 代码,只有一个 spring.provides 文件,里面的内容如下:

provides: spring-data-redis,lettuce-core
复制代码

意思就是说,本项目依赖 spring-data-redis 和 lettuce-core 这两个包,并且在 pom 文件中引用了。其目的就是告知使用者在引用此包的时候,不必再引用 provides 中的依赖包了。

2. 然后就是自动注解了,所有 stater 的自动注解类、属性配置类都放到了 spring-boot-autoconfigure 这个项目下

看到熟悉的 spring.factories 没有,前面我们自己实现过。这个内容比较多,我们只看 redis 相关的

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration
复制代码

包含三个自动配置文件,然后顺着配置,我们找到所在 package

然后就可以开始阅读代码了。其他的 starter 也是同样的结构。

壮士且慢,先给点个赞吧,总是被白嫖,身体吃不消!

一个项目有两个pom_实现一个Spring Boot Starter超简单,读 Starter 源码也不在话下...相关推荐

  1. vue项目跨域的问题(一个项目对接两个不同的域名、端口接口导致跨域,最好的办法是后端解决)

    vue项目跨域的问题前端解决方法(一个项目对接两个不同的域名.端口接口导致跨域,最好的办法是后端解决) 前端解决方法: ①打开config文件---->index.js文件,找到 proxyTa ...

  2. phpstudy一个域名配置两个网站(一个是thinkphp5,一个是原生php)

    phpstudy一个域名配置两个网站(一个是thinkphp5,一个是原生php) 一.总结 一句话总结:把原生php的网站直接放到thinkphp5的public目录下可以解决以stem.aaaa. ...

  3. “正话反说”:A和B在玩一个游戏,两人轮流说一句话,这句话正读反读都一样,如adgda,谁先说错,谁出局,另一个人胜出。编写一个函数用于判断这句话是否符合要求,符合要求时,函数返回1,否则函数返回0

    "正话反说":A和B在玩一个游戏,两人轮流说一句话,这句话正读反读都一样,如adgda,谁先说错,谁出局,另一个人胜出. 编写一个函数用于判断这句话是否符合要求,符合要求时,函数返 ...

  4. 【web前端特效源码】使用HTML5+CSS3制作一个会动的电脑桌面+昼夜变化动画效果~~适合初学者~超简单~ |前端开发

    b站视频演示效果: [web前端特效源码]使用HTML5+CSS3制作一个会动的电脑桌面+昼夜变化动画效果~~适合初学者~超简单~ |前端开发|IT软件 效果图: 完整代码: <!DOCTYPE ...

  5. 搭建Spring Boot2.X集成Hibernate5项目,并集成传统SSH老项目的安全认证组件,以Spring Boot方式开发项目并集成到老系统

    搭建Spring Boot2.X集成Hibernate5项目,并集成传统SSH老项目的安全认证组件,以Spring Boot方式开发项目并集成到老系统 场景 可行性分析 搭建Spring Boot集成 ...

  6. pycharm里怎么关闭一个项目_【周末分享】一个完整的项目复盘到底要怎么做?...

    点击"阅读原文",注册会员,海量活动方案免费拿 作者 | 杨阳(广告创意主笔) 来源 | 广告创意(ID:idea1408) 字数:3099 推荐阅读时长:5min 从计划到执行到 ...

  7. 两个栈实现一个队列,两个队列实现一个栈

    题目:用两个栈实现一个队列,用两个队列实现一个栈. 首先要了解栈和队列这两种数据结构各自的特点,栈是一种后入先出(Last In First Out,LIFO)的数据结构,队列是一种先进先出(Firs ...

  8. java实现-两个栈实现一个队列和两个队列实现一个栈

    1.两个栈实现一个队列 思路:压入元素直接入stack1,删除元素先判断stack2中是否为空,如果不为空直接弹出:为空则将stack1中的元素取出压入 stack2中再弹出. 代码: import ...

  9. 两个栈实现一个队列与两个队列实现一个栈

    http://blog.csdn.net/z84616995z/article/details/19204529 两个栈实现一个队列: 原理方法:用一个栈为主栈,一个栈为辅助栈存放临时元素. 入队:将 ...

最新文章

  1. pjsip学习笔记二
  2. ubuntu下python+tornado+supervisor+nginx部署
  3. 绘图: matplotlib Basemap简介
  4. tkinter中鼠标与键盘事件
  5. Lua require搜索路径指定方法
  6. C++ ofstream/ifstream读写文件demo
  7. 关于利用exchange server 2003搭建邮件服务器:小进步……
  8. 巴比特独家 | 我们梳理98家新三板公司年报,发现企业布局区块链6大特点
  9. 蜂窝移动电话定位技术与应用(转)
  10. 杨歌:金融电路与 Web3 经济模型原理 (转载及导言)
  11. tkinter做界面的一点心得(丑而简单)
  12. arcgis 触屏实现键盘模拟
  13. 计算机发展的各个阶段是以什么作为标志的,计算机发展的各个阶段是以什么作为标志的?...
  14. 微信域名防封PHP程序强制跳转到浏览器打开
  15. 从“来现场POC”到“去线下店体验”:我的数据治理产品选型经历
  16. 如何运用计算机制作合同书,Word 2007 制作一份专业合同书实例WORD2007 -电脑资料...
  17. 使用Example_where_Cause出现 Column 'goods_id' in where clause is ambiguous解决办法
  18. [生存志] 第109节 秦始皇初玩叠人塔
  19. python字符串操作作业_Python基础(7)——字符串作业
  20. 打开转盘锁 ,易懂的BFS解法

热门文章

  1. 《Effective-Ruby》读书笔记
  2. 重入锁:ReentrantLock 详解
  3. 软件重构过程中的思维转换: 遗留代码如何变废为宝
  4. Java集合的使用:List与Map
  5. 浅谈机器学习的职业发展方向
  6. 系统设计面试题思路综述
  7. 鸟哥的Linux私房菜(基础篇)-第一章、Linux是什么(一.4. 重点回顾)
  8. 【ubuntu+opencv3】ubuntu16.04+qt5+opencv3.2.0编译与安装
  9. 混合云计算和联合云计算
  10. SQL日期时间和字符串函数