自定义spring boot starter

前段时间在面试的过程中遇到面试官的一个问题,怎么自己定义一个spring boot的starter,当时其实自己是有点懵的,说实话以前真的没有注意过这个问题,不过好在自己看过一些具体的starter依赖,然后大概回答了一下。然后趁着周末决定来看一下到底是怎么一回事。
在spring boot项目中我们经常会使用到各种starter,比如spring-boot-starter-webspring-boot-starter-data-jpa等等,其实如果你深入的跟踪过这些pom.xml的话你会发现,其实这些所谓的starter不过是一个依赖的集合而已,可以理解为对所需要的依赖进行了一层包装。这样的好处就是相对更加灵活一些,每一个starter可以看作是一个组件,这个组件包了相关的依赖,以spring-boot-starter-web为例,我们看下它的组成到底是怎么样的:

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.8.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.1.8.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.1.8.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.17.Final</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.9.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.9.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

根据上面的依赖可以知道,其实它也不过是引用了一些其他相关的依赖而已,它只是将这些依赖集中起来而已。比如有spring-webmvc、有spring-boot-starter-tomcat(你可以接着向下看它的组成)。这样它就做成了一个组件,不管是引用还是移除我们只需要关心它就行了。

一、自定义一个starter

根据上面的这些如果我们想自定义一个starter我们应该怎么做呢?在工作中我也在项目中使用过我们公司自己定义的一些starter,比如ES、redis等等。接下来尝试自己定义一个很简单的starter

1、创建一个maven项目

使用IDE创建一个maven工程,然后定义好相关的 groupIdartifactIdversion,然后在pom文件引入相关的依赖,主要是spring boot自动配置相关的依赖,pom.xm文件依赖如下:

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
      <version>2.1.8.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <version>2.1.8.RELEASE</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    </dependency>
  </dependencies>

然后我们需要定义一个配置类ServiceConfigProperties,其实如果不会的话参考一下其他的starter,代码如下:

@ConfigurationProperties(prefix = "com.ypc.service")
public class ServiceConfigProperties {

    private String name = "starter";

    private String url = "localhost";

    private String username;

    private String password;

    private Integer count = 1;

    // 省略get set
    ......
}

然后我们需要定义一个自动配置类ServiceAutoConfiguration,这个自动配置的作用就是对外提供一个或多个相关的bean,以便可以直接注入到spring容器中。比如RedisAutoConfiguration会创建RedisTemplateStringRedisTemplate两个bean。所以我们也需要创建一个具体的服务类(这个根据具体的starter决定)StarterService,这个类本身在这里并不具备很具体的功能,仅仅是作为一个例子,代码如下:

public class StarterService {

    private String serviceName;

    private String url;

    private String password;

    private Integer count;

    public StarterService() {
    }

    public StarterService(String serviceName, String url, String password, Integer count) {
        this.serviceName = serviceName;
        this.url = url;
        this.password = password;
        this.count = count;
    }
    
    // 省略get set方法
    .......
}

最后就是自动配置类的代码,这个配置我们需要spring boot能自动进行配置,代码如下:

@Configuration
@EnableConfigurationProperties(ServiceConfigProperties.class)
public class ServiceAutoConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAutoConfiguration.class);

    @Autowired
    private ServiceConfigProperties serviceConfigProperties;

    @Bean("starterService")
    public StarterService starterService() {
        LOGGER.info(">>>> starter service start <<<<");
        StarterService starterService = new StarterService();

        starterService.setCount(serviceConfigProperties.getCount());
        starterService.setUrl(serviceConfigProperties.getUrl());
        starterService.setPassword(serviceConfigProperties.getPassword());
        starterService.setServiceName(serviceConfigProperties.getName());

        return starterService;
    }
}

需要注意一点就是在该类上使用@EnableAutoConfiguration注解并不能让spring去注入我们创建的bean。一开始我只是添加了@EnableAutoConfiguration,但是在引入到项目中启动时报错,显示无法注入自定义的bean——StarterService。后来网上找了下相关资料发现需要在resources目录下创建名为META-INF文件夹,并新建一个文件spring.factories,然后添加下面的内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ypc.springboot.starter.ServiceAutoConfiguration

详情可以参考Spring-boot @EnableAutoConfiguration源码分析

2、在其他项目中使用starter

然后我们需要将项目打包,并安装到我们本地的maven仓库,执行下面的maven命令:

mvn clean install

或者直接使用maven插件进行也可以。
接下来我们需要新创建一个spring boot的项目,并在该项目中引入自定义的starter,我的依赖如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 引入自定义的service starter -->
        <dependency>
            <groupId>com.ypc.springboot.custom</groupId>
            <artifactId>service-starter</artifactId>
            <version>1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

然后在application.properties配置自定义starter配置类的相关内容,如下:

图-1.png

编写一个简单的接口,并在相应代码中注入依赖使用的bean——StarterService,然后调用一个方法即可,代码如下:

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private StarterService starterService;

    @Override
    public UserEntity queryByUserName(String userName) {
        String result = starterService.hello();
        log.info(">>>> starter service hello={} <<<<",result);

        UserEntity userEntity = userRepository.findByUserName(userName);
        return userEntity;
    }
}

看下日志输出如下:

图-2.png

说明自定义的starter在项目中是可以正常使用的,当然这个例子有点过于简单了,也并没有什么实际的价值,只是提供了一种自定义starter的思路。如果在实际开发中需要自定义starter其实参考spring boot提供那些starter就可以了。

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

推荐阅读更多精彩内容