extern 定义_如何优雅的构建自定义SpringBoot Starter

a8c9a2d0bbd2bddd7a1a6dd1bbbbce7b.png

Spring

作为一名合格的程序猿,相信大家都对Spring比较熟悉。在Spring的演进历史中,我们一般都经历了SSH(Spring+Struts+Hibernate),Spring MVC,Spring Boot,Spring Cloud等一系列Spring的优秀作品。

在目前软件开发公司特别是互联网企业,项目开发周期一般都很短,项目必须采用快速迭代以满足不断变化的市场需求。所以作为企业的项目开发团队必须选择一个能够快速构建项目工程的框架,能够让整个项目团队的学习成本比较低,上手容易,而且框架扩展性比较强和稳定性比较好。SpringBoot一般是大部分开发团队的不二之选。

为什么SpringBoot如此深受开发者的青睐呢?纵观Spring的官网文档和Spring的开发者社区,SpringBoot提供了大多数场景的脚手架,方便开发人员上手,学习成本比较低,而且开发更容易,能够让开发人员从框架层面解脱出来,全身心的投入到业务逻辑层面的实现。

那么SpringBoot到底都有哪些比较有实用价值的脚手架呢?我们随便找到一个简单的SpringBoot工程,来看看它都引入了哪些stater

adaca76f423d7ad1d5f4ad58e9a10eb6.png

SpringBoot Starter

通过上面的截图我们可以看到,一个简单的SpringBoot一般都引入了数据库操作脚手架,日志脚手架,安全脚手架,模板引擎脚手架等。然而这只是冰山一角。纵观Spring开发者社区和GitHub我们发现有许多脚手架starter是社区提供的,我们可以直接拿来使用。

342bba918fefd30a2a8edfcbf08e782c.png

自定义Starter


言归正传,下面我们通过一个简单的例子来演示下如何构建一个我们自定义的SpringBoot Starter,该示例通过一个Starter向钉钉机器人发送告警信息:

构建dingtalk-robot-starter项目工程:

项目工程结构如下:

f3218eabcd3299d6fa6900a0882e273b.png

工程结构

pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>4.0.0org.springframework.bootspring-boot-starter-parent2.3.0.RELEASEorg.springframework.bootdingtalk-robot-starter0.0.1dingtalk-robot-starterdingtalk robot send message starter1.8org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-devtoolsruntimetrueorg.springframework.bootspring-boot-configuration-processortrueorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-enginecom.dingtalk.opentaobao-sdk-java-auto1479188381469-20200609com.alibabafastjson1.2.47

这里我们首先需要安装钉钉官方网站提供的SDK工具

cd dingtalk-robot-startermvn install:install-file -DgroupId=com.dingtalk.open -DartifactId=taobao-sdk-java-auto -Dversion=1479188381469-20200609 -Dpackaging=jar -Dfile=lib/taobao-sdk-java-auto_1479188381469-20200609.jar

DingTalkAutoConfiguration.java内容如下:

package org.springframework.dingtalk.robot;import lombok.extern.log4j.Log4j2;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;import javax.annotation.Resource;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.nio.charset.StandardCharsets;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.util.Base64;@Log4j2@Configuration@EnableConfigurationProperties(DingTalkProperties.class)public class DingTalkAutoConfiguration {    // 使用配置    @Resource    private DingTalkProperties dingTalkProperties;    // 在Spring上下文中创建一个对象    @Bean    @ConditionalOnMissingBean    public DingTalkRobotClient dingTalkRobotClient() throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {        String secret = dingTalkProperties.getSecret();        StringBuilder stringBuilder = new StringBuilder(dingTalkProperties.getWebhook());        stringBuilder.append("×tamp=");        Long timestamp = System.currentTimeMillis();        stringBuilder.append(timestamp);        String stringToSign = timestamp + Const.NEW_LINE + secret;        Mac mac = Mac.getInstance(Const.ALGORITHM);        mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Const.ALGORITHM));        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));        String sign = URLEncoder.encode(new String(Base64.getEncoder().encode(signData)),"UTF-8");        stringBuilder.append("&sign=");        stringBuilder.append(sign);        String url = stringBuilder.toString();        log.debug("url:{}", url);        return new DingTalkRobotClient(url, dingTalkProperties.isEnable());    }}

DingTalkProperties.java

package org.springframework.dingtalk.robot;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;@Data@ConfigurationProperties(prefix = "dingtalk.robot")public class DingTalkProperties {    private String webhook = "https://oapi.dingtalk.com/robot/send";    private String secret;    private boolean enable = true;}

DingTalkRobotClient.java

package org.springframework.dingtalk.robot;import com.alibaba.fastjson.JSON;import com.dingtalk.api.DefaultDingTalkClient;import com.dingtalk.api.request.OapiRobotSendRequest;import com.dingtalk.api.response.OapiRobotSendResponse;import com.taobao.api.ApiException;import lombok.extern.log4j.Log4j2;import org.springframework.scheduling.annotation.Async;import org.springframework.scheduling.annotation.EnableAsync;import java.net.InetAddress;import java.net.UnknownHostException;import java.util.List;@Log4j2@EnableAsyncpublic class DingTalkRobotClient extends DefaultDingTalkClient {    private boolean enable;    public DingTalkRobotClient(String serverUrl, boolean enable) {        super(serverUrl);        this.enable = enable;    }    private String getHostName() {        String hostName = "localhost";        try {            hostName = InetAddress.getLocalHost().getHostName();        } catch (UnknownHostException e) {            log.error("getHostName", e);        }        return "[" + hostName + "]:";    }    @Async    public void sendTextMessage(String message) {        if (enable) {            message = getHostName() + message;            OapiRobotSendRequest request = new OapiRobotSendRequest();            request.setMsgtype("text");            OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();            text.setContent(message);            request.setText(text);            try {                OapiRobotSendResponse response = execute(request);                log.debug("sendTextMessage:{}", JSON.toJSONString(response));            } catch (ApiException e) {                log.error("ApiException", e);            }        }    }    @Async    public void sendTextMessage(String message, boolean isAtAll, List mobiles) {        if (enable) {            message = getHostName() + message;            OapiRobotSendRequest request = new OapiRobotSendRequest();            request.setMsgtype("text");            OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();            text.setContent(message);            request.setText(text);            if (isAtAll) {                OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();                at.setAtMobiles(mobiles);                // isAtAll类型如果不为Boolean,请升级至最新SDK                at.setIsAtAll(isAtAll);                request.setAt(at);            }            try {                OapiRobotSendResponse response = execute(request);                log.debug("sendTextMessage:{}", JSON.toJSONString(response));            } catch (ApiException e) {                log.error("ApiException", e);            }        }    }    @Async    public void sendLinkMessage(String message, String title, String messageUrl, String picUrl) {        if (enable) {            message = getHostName() + message;            OapiRobotSendRequest request = new OapiRobotSendRequest();            request.setMsgtype("link");            OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();            link.setMessageUrl(messageUrl);            link.setPicUrl(picUrl);            link.setTitle(title);            link.setText(message);            request.setLink(link);            try {                OapiRobotSendResponse response = execute(request);                log.debug("sendLinkMessage:{}", JSON.toJSONString(response));            } catch (ApiException e) {                log.error("ApiException", e);            }        }    }    @Async    public void sendLinkMessage(String message, String title, String messageUrl, String picUrl, boolean isAtAll, List mobiles) {        if (enable) {            message = getHostName() + message;            OapiRobotSendRequest request = new OapiRobotSendRequest();            request.setMsgtype("link");            OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();            link.setMessageUrl(messageUrl);            link.setPicUrl(picUrl);            link.setTitle(title);            link.setText(message);            request.setLink(link);            if (isAtAll) {                OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();                at.setAtMobiles(mobiles);                // isAtAll类型如果不为Boolean,请升级至最新SDK                at.setIsAtAll(isAtAll);                request.setAt(at);            }            try {                OapiRobotSendResponse response = execute(request);                log.debug("sendLinkMessage:{}", JSON.toJSONString(response));            } catch (ApiException e) {                log.error("ApiException", e);            }        }    }}

到处,源代码编写完毕,我们需要增加Starter的配置文件,能够在SpringBoot启动的时候加载到我们的DingTalkAutoConfiguration并且将DingTalkRobotClient自动注入到SpringBoot容器中。


在resources资源文件夹下新建META-INF文件夹,创建spring.factories内容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.dingtalk.robot.DingTalkAutoConfiguration

通过该配置文件,SpringBoot在启动的时候会去查找这个文件并且根据该文件的配置项,自动装配Java Bean。该配置文件可以定义多个类似的配置项,在这里我们只定义了一个配置项。

下面我们定义另一个配置文件,同样在工程的resources/META-INF文件夹下,文件名是additional-spring-configuration-metadata.json,内容如下:

{  "properties": [    {      "sourceType": "org.springframework.dingtalk.robot.DingTalkProperties",      "name": "dingtalk.robot.webhook",      "type": "java.lang.String",      "defaultValue": "https://oapi.dingtalk.com/robot/send",      "description": "Dingtalk WebHook"    },    {      "sourceType": "org.springframework.dingtalk.robot.DingTalkProperties",      "name": "dingtalk.robot.secret",      "type": "java.lang.String",      "description": "Dingtalk secret"    },    {      "sourceType": "org.springframework.dingtalk.robot.DingTalkProperties",      "name": "dingtalk.robot.enable",      "type": "java.lang.Boolean",      "defaultValue": "true",      "description": "enable or disable Dingtalk"    }  ]}

这个配置文件主要起到配置项提示的功能。到这里Starter已经构建完成,下面介绍如何使用我们自定义的Starter。


370c37efc947b34a152d7418318e44fc.png

使用Starter

使用Starter

先将Starter安装到本地的Maven仓库中

cd dingtalk-robot-startermvn clean install -Dmaven.test.skip=true

在其他需要使用该自定义Starter的地方,将该Starter的maven dependency引入

org.springframework.boot    dingtalk-robot-starter    0.0.1

在application.properties或者application.yml文件中添加该Starter的配置项:

dingtalk:  robot:    webhook: https://oapi.dingtalk.com/robot/send?access_token=this is access token    secret: this is secret    enable: true

源代码使用方法如下:

@Autowiredprivate DingTalkRobotClient dingTalkRobotClient;dingTalkRobotClient.sendTextMessage(message);

通过这种简单的配置,我们就可以将一些警告信息或者错误信息发送到钉钉机器人了。

钉钉机器人的创建过程在这里就不再赘述,可以自行参考官网文档整个创建过程都有详细的介绍。


32f39b997699c2be9a4b62194fba5cec.png

介绍完毕

大概总结下整个过程:

  1. 构建一个自定义的Starter项目工程;
  2. 编写AutoConfiguration自动装配配置类;
  3. 实现Starter需要实现的功能;
  4. 编写spring.factories配置文件,告诉SpringBoot在启动的时候需要扫描的配置类;
  5. 在需要使用该Starter的项目中引入maven dependency;
  6. 在需要使用该Starter的项目中添加该Starter的配置项;
  7. 在需要使用该Starter的项目中使用@Autowired注入Java Bean,并且调用该Java Bean里面的方法;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值