Spring boot starter原理,手撸一个starter

  1. 什么是starter?

    starter是SpringBoot中的一个新发明,它有效的降低了项目开发过程的复杂程度,对于简化开发操作有着非常好的效果。提供一个开箱即用的组件。

  2. 手写一个starter

    假设我们有个需求,我们需要序列化一个对象,当工程中存在FastJson的com.alibaba.fastjson.JSON类时使用FastJson序列化,如果不存在就则进行简单的toString操作。

    我们首先定义一个接口有一个格式化方法

    public interface FormatProcessor {
        //定义一个序列化的方法
        <T> String format(T obj);
    }
    

    然后这个接口有两个实现类,分别做不同序列化操作。

    import com.alibaba.fastjson.JSON;
    
    public class JsonFormatProcessor implements FormatProcessor{
        @Override
        public <T> String format(T obj) {
            return "JsonFormatProcessor:"+ JSON.toJSONString(obj);
        }
    }
    
    import java.util.Objects;
    
    public class StringFormatProcessor implements FormatProcessor{
    
    
        @Override
        public <T> String format(T obj) {
            return "StringFormatProcessor:"+Objects.toString(obj);
        }
    }
    

    定义一个Configuration类。

    @Configuration
    public class FormatAutoConfiguration {
        @ConditionalOnMissingClass("com.alibaba.fastjson.JSON") // 如果不存在JSON类就加载
        @Bean
        @Primary //  如果有两个实现则优先加载这个
        public FormatProcessor stringFormat() {
            return new StringFormatProcessor();
        }
    
        @ConditionalOnClass(name = "com.alibaba.fastjson.JSON") // 如果存在JSON类
        @Bean
        public FormatProcessor jsonFormat() {
            return new JsonFormatProcessor();
        }
    }
    

    定义一个自动配置类。

    @Import(FormatAutoConfiguration.class)
    @Configuration
    public class HelloAutoConfiguration {
    
        @Bean
        public HelloFormatTemplate helloFormatTemplate( FormatProcessor formatProcessor) {
            return new HelloFormatTemplate(formatProcessor);
        }
    }
    

    template类

    public class HelloFormatTemplate {
    
        private FormatProcessor formatProcessor;
    
        public HelloFormatTemplate(FormatProcessor formatProcessor) {
            this.formatProcessor = formatProcessor;
        }
    
        public <T> String doFormat(T obj){
            StringBuilder stringBuilder=new StringBuilder();
            stringBuilder.append("begin:Execute format").append("<br/>");
            stringBuilder.append("Obj format result:").append(formatProcessor.format(obj)).append("<br/>");
            return stringBuilder.toString();
        }
    }
    

    使用spring boot的SPI机制把 HelloAutoConfiguration自动装配进spring容器。在spring.factories文件中定义

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.yhx.starter.autoconfiguration.HelloAutoConfiguration
    

    这样我们就写出了一个starter,实现了一个类似以RedisTemplate的HelloFormatTemplate的工具。那如果我们要使用外部化的配置也就要读取到application.yml文件的配置该怎么写呢?

    首先我们写一个Properties类,并加上@ConfigurationProperties注解。

    @ConfigurationProperties(prefix = HelloProperties.HELLO_FORMAT_PREFIX)
    public class HelloProperties {
        public static final String HELLO_FORMAT_PREFIX = "yhx.hello.format";
        private Map<String, Object> info;
        public Map<String, Object> getInfo() {
            return info;
        }
        public void setInfo(Map<String, Object> info) {
            this.info = info;
        }
    }
    

    然后在HelloAutoConfiguration的自动装配的类中加上@EnableConfigurationProperties(HelloProperties.class),此时HelloAutoConfiguration类为

    @Import(FormatAutoConfiguration.class)
    @EnableConfigurationProperties(HelloProperties.class)
    @Configuration
    public class HelloAutoConfiguration {
    
        @Bean
      // HelloProperties这时候的已经在spring容器中了,会自动去找查的
        public HelloFormatTemplate helloFormatTemplate(HelloProperties helloProperties, FormatProcessor formatProcessor) {  
            return new HelloFormatTemplate(helloProperties, formatProcessor);
        }
    }
    

    这时候我们的Template可以改写为如下的

    public class HelloFormatTemplate {
    
        private FormatProcessor formatProcessor;
    
        private HelloProperties helloProperties;
    
        public HelloFormatTemplate(HelloProperties helloProperties,FormatProcessor formatProcessor) {
            this.helloProperties=helloProperties;
            this.formatProcessor = formatProcessor;
        }
    
        public <T> String doFormat(T obj){
            StringBuilder stringBuilder=new StringBuilder();
            stringBuilder.append("begin:Execute format").append("<br/>");
           stringBuilder.append("HelloProperties:").append(formatProcessor.format(helloProperties.getInfo())).append("<br/>");
            stringBuilder.append("Obj format result:").append(formatProcessor.format(obj)).append("<br/>");
            return stringBuilder.toString();
    
        }
    }
    

    我们在使用这个这个starter的工程中在application.yml或者是application.properties中配置,就可以配置进这个序列化的bean中

    yhx.hello.format.info.country=CN
    yhx.hello.format.info.provice=HuNan
    yhx.hello.format.info.city=shanghai
    yhx.hello.format.info.org=xingren
    
  3. 常见的starter之logger

    我们在项目中加入logging starter的依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
        <version>2.3.4.RELEASE</version>
    </dependency>
    

    然后看一下spring-boot-starter-logging的jar包,发现里面并没有什么,这是为什么呢?

    spring-boot-starter-logging-2.3.4.RELEASE.jar
     META-INF
         LICENSE.txt
         MANIFEST.MF
         NOTICE.txt
    

    然后我们点进pom文件中的spring-boot-starter-logging看看

    <?xml version="1.0" encoding="UTF-8"?>
    <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <!-- This module was also published with a richer model, Gradle metadata,  -->
      <!-- which should be used instead. Do not delete the following line which  -->
      <!-- is to indicate to Gradle or any Gradle module metadata file consumer  -->
      <!-- that they should prefer consuming it instead. -->
      <!-- do_not_remove: published-with-gradle-metadata -->
      <modelVersion>4.0.0</modelVersion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-logging</artifactId>
      <version>2.3.4.RELEASE</version>
      <name>spring-boot-starter-logging</name>
      <description>Starter for logging using Logback. Default logging starter</description>
      <url>https://spring.io/projects/spring-boot</url>
      <organization>
        <name>Pivotal Software, Inc.</name>
        <url>https://spring.io</url>
      </organization>
      <licenses>
        <license>
          <name>Apache License, Version 2.0</name>
          <url>https://www.apache.org/licenses/LICENSE-2.0</url>
        </license>
      </licenses>
      <developers>
        <developer>
          <name>Pivotal</name>
          <email>info@pivotal.io</email>
          <organization>Pivotal Software, Inc.</organization>
          <organizationUrl>https://www.spring.io</organizationUrl>
        </developer>
      </developers>
      <scm>
        <connection>scm:git:git://github.com/spring-projects/spring-boot.git</connection>
        <developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-boot.git</developerConnection>
        <url>https://github.com/spring-projects/spring-boot</url>
      </scm>
      <issueManagement>
        <system>GitHub</system>
        <url>https://github.com/spring-projects/spring-boot/issues</url>
      </issueManagement>
      <dependencies>
        <dependency>
          <groupId>ch.qos.logback</groupId>
          <artifactId>logback-classic</artifactId>
          <version>1.2.3</version>
          <scope>compile</scope>
        </dependency>
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-to-slf4j</artifactId>
          <version>2.13.3</version>
          <scope>compile</scope>
        </dependency>
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>jul-to-slf4j</artifactId>
          <version>1.7.30</version>
          <scope>compile</scope>
        </dependency>
      </dependencies>
    </project>
    
    

    里面就是一堆依赖,为什么空的也能实现自动装配呢?因为spring boot官方提供了装配,如果相关的类或者一类存在的话,所以logging的starter只需要添加一些依赖就可以。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容