侧边栏壁纸
博主头像
昂洋编程 博主等级

鸟随鸾凤飞腾远,人伴贤良品自高

  • 累计撰写 71 篇文章
  • 累计创建 79 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Springboot自定义starter(以发送企业微信应用消息为例)

Administrator
2022-12-23 / 0 评论 / 0 点赞 / 38 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-06-14,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Springboot加载starter的原理

springboot通过一个@SpringBootApplication注解启动项目,springboot在项目启动的时候,会将项目中所有声明为Bean对象(注解、xml)的实例信息全部加载到ioc容器当中。 除此之外也会将所有依赖到的starter里的bean信息加载到ioc容器中,从而做到所谓的零配置,开箱即用。

具体就是加载spring.factories文件里声明的bean信息

知道了原理,我们就知道只要在spring.factories文件里声明我们自定义的类,那么Spring就会自动装在到IOC容器里

自定义Stater

新建一个Springboot项目

创建Springboot项目hugo-tools-spring-boot-starter,注意命名不要和Springboot官方的命名重合比如:server, management, spring 等等),pom文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.hugobiotech</groupId>
    <artifactId>hugo-tools-spring-boot-starter</artifactId>
    <version>${system.version}</version>
    <name>Hugo-tool</name>
    <description>予果工具类服务</description>

    <properties>
        <system.version>1.0.0</system.version>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <commons-httpclient.version>3.1</commons-httpclient.version>
        <fastjson.version>1.1.31</fastjson.version>
        <httpclient.version>4.5.13</httpclient.version>
        <hutool.version>5.0.5</hutool.version>
    </properties>

    <dependencies>
        <!--httpclient-->
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>${commons-httpclient.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>${httpclient.version}</version>
        </dependency>

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

企业微信发送消息采用的是http请求的方式,所以这里添加httpclient工具类,fastjson用作json序列化,尤其要添加关于Spring的configuration相关的三个包
image-1671762905210

创建好项目之后删掉主启动类,然后在resources目录下新增目录META-INF并新增spring.factories文件
image-1671763070663

几个工具类

CloseUtil

package com.hugobiotech.utils;

import java.io.Closeable;

/**
 * @author zhangmy
 * @description 用于关闭各种连接,缺啥补啥
 * @date 2021-03-05
 **/
public class CloseUtil {

    public static void close(Closeable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            } catch (Exception e) {
                // 静默关闭
            }
        }
    }

    public static void close(AutoCloseable closeable) {
        if (null != closeable) {
            try {
                closeable.close();
            } catch (Exception e) {
                // 静默关闭
            }
        }
    }
}

EmptyUtil

package com.hugobiotech.utils;

import java.util.Collection;
import java.util.Map;

/**
 * @author zhangmy
 * @date 2021/11/12 16:21
 * @description 空校验工具类
 */
public class EmptyUtil {

    /**
     * 字符串为空判断
     * @param str
     * @return
     */
    public static boolean isEmpty(final String str) {
        if (null == str || str.trim().length() == 0 || "".equals(str)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 字符串为不空判断
     * @param str
     * @return
     */
    public static boolean isNotEmpty(final String str) {
        return !isEmpty(str);
    }

    /**
     * StringBuilder 为空判断
     * @param obj
     * @return
     */
    public static boolean isEmpty(final StringBuilder obj) {
        if (null == obj || obj.length() == 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * StringBuilder 不为空判断
     * @param obj
     * @return
     */
    public static boolean isNotEmpty(final StringBuilder obj) {
        return !isEmpty(obj);
    }

    /**
     * 判断Collection集合为空
     * @param collection
     * @return
     */
    public static boolean isEmpty(final Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 判断Collection集合不为空
     * @param collection
     * @return
     */
    public static boolean isNotEmpty(final Collection<?> collection) {
        return (collection != null && collection.size() > 0);
    }

    /**
     * 判断Map集合为空
     * @param map
     * @return
     */
    public static boolean isEmpty(final Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

    /**
     * 判断Map集合不为空
     * @param map
     * @return
     */
    public static boolean isNotEmpty(final Map<?, ?> map) {
        return !(map == null || map.isEmpty());
    }

    /**
     * 判断实体类为空
     * @param obj
     * @return
     */
    public static boolean isEmpty(final Object obj) {
        return null == obj;
    }

    /**
     * 判断实体类不为空
     * @param obj
     * @return
     */
    public static boolean isNotEmpty(final Object obj) {
        return !isEmpty(obj);
    }
}

HttpClientUtil

package com.hugobiotech.utils;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author zhangmy
 * @date 2021/12/27 9:31
 * @description http请求工具类
 */
public class HttpClientUtil {

    /**
     * 发送get请求
     *
     * @param url   请求url
     * @param param 参数
     * @return
     */
    public static String httpGet(String url, Map<String, String> param) {
        // 结果字符串
        String resultString = "";
        // 创建Httpclient对象
        CloseableHttpClient httpclient = null;
        CloseableHttpResponse response = null;
        try {
            httpclient = HttpClients.createDefault();
            // 创建uri并格式化
            URIBuilder builder = new URIBuilder(url.replace(" ", "%20"));
            if (param != null) {
                for (String key : param.keySet()) {
                    builder.addParameter(key, param.get(key));
                }
            }
            URI uri = builder.build();
            // 创建http GET请求
            HttpGet httpGet = new HttpGet(uri);
            // 执行请求
            response = httpclient.execute(httpGet);
            // 判断返回状态是否为200
            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            System.out.println("httpGet异常,原因: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭连接
            CloseUtil.close(response);
            CloseUtil.close(httpclient);
        }
        return resultString;
    }

    /**
     * 发送get请求
     *
     * @param url
     * @return
     */
    public static String httpGet(String url) {
        return httpGet(url.replace(" ", "%20"), null);
    }

    /**
     * 发送post请求
     *
     * @param url   请求url
     * @param param 请求参数
     * @return
     */
    public static String httpPost(String url, Map<String, String> param) {
        // 结果字符串
        String resultString = "";
        // 创建Httpclient对象
        CloseableHttpClient httpclient = null;
        CloseableHttpResponse response = null;
        try {
            // 创建Httpclient对象
            httpclient = HttpClients.createDefault();
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url.replace(" ", "%20"));
            // 创建参数列表
            if (param != null) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (String key : param.keySet()) {
                    paramList.add(new BasicNameValuePair(key, param.get(key)));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8");
                httpPost.setEntity(entity);
            }
            // 执行http请求
            response = httpclient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            System.out.println("httpPost异常,原因: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭连接
            CloseUtil.close(response);
            CloseUtil.close(httpclient);
        }
        return resultString;
    }

    /**
     * 发送post请求
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static String httpPost(String url) throws IOException {
        return httpPost(url.replace(" ", "%20"), null);
    }

    /**
     * 发送post json参数请求
     *
     * @param url  请求url
     * @param json 骑牛参数
     * @return
     */
    public static String httpPostJson(String url, String json) {
        // 结果字符串
        String resultString = "";
        // 创建Httpclient对象
        CloseableHttpClient httpclient = null;
        CloseableHttpResponse response = null;
        try {
            // 创建Httpclient对象
            httpclient = HttpClients.createDefault();
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url.replace(" ", "%20"));
            // 创建请求内容
            StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            // 执行http请求
            response = httpclient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            System.out.println("httpPost异常,原因: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭连接
            CloseUtil.close(response);
            CloseUtil.close(httpclient);
        }
        return resultString;
    }

    /**
     * 发送post json请求并携带请求头
     *
     * @param url
     * @param jsonParams
     * @param headers
     * @param jsonParams
     * @return
     */
    public static String httpPostJsonWithHeaders(String url, String jsonParams, Map<String, String> headers) {
        // 结果字符串
        String resultString = "";
        // 创建Httpclient对象
        CloseableHttpClient httpclient = null;
        CloseableHttpResponse response = null;
        try {
            // 创建Httpclient对象
            httpclient = HttpClients.createDefault();
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url.replace(" ", "%20"));
            // 创建请求内容
            StringEntity entity = new StringEntity(jsonParams, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            for (String header : headers.keySet()) {
                httpPost.addHeader(new BasicHeader(header, headers.get(header)));
            }
            // 执行http请求
            response = httpclient.execute(httpPost);
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            System.out.println("httpPost异常,原因: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 关闭连接
            CloseUtil.close(response);
            CloseUtil.close(httpclient);
        }
        return resultString;
    }
}

新建服务及配置

发送企业微信消息参考官方文档: https://developer.work.weixin.qq.com/document/path/90236#10167
根据官方文档,我们先定义几个请求实体类及返回类

TestMsg

package com.hugobiotech.wxwork.entity;

import com.alibaba.fastjson.annotation.JSONField;

/**
 * @author zhangmy
 * @date 2022/12/23 9:41
 * @description
 */
public class TestMsg {

    /**
     * 消息内容
     */
    @JSONField(name = "content")
    private String content;

    public TestMsg(String content) {
        this.content = content;
    }

    public TestMsg() {
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "TestMsg{" +
                "content='" + content + '\'' +
                '}';
    }
}

SendTextMsgRequest

package com.hugobiotech.wxwork.entity;

import com.alibaba.fastjson.annotation.JSONField;

/**
 * @author zhangmy
 * @date 2022/12/22 18:06
 * @description
 */
public class SendTextMsgRequest {

    /**
     * 指定接收消息的成员,成员ID列表(多个接收者用‘|’分隔,最多支持1000个)。
     * 特殊情况:指定为"@all",则向该企业应用的全部成员发送
     */
    @JSONField(name = "touser")
    private String touser;

    /**
     * 指定接收消息的部门,部门ID列表,多个接收者用‘|’分隔,最多支持100个。
     * 当touser为"@all"时忽略本参数
     */
    @JSONField(name = "toparty")
    private String toparty;

    /**
     * 指定接收消息的标签,标签ID列表,多个接收者用‘|’分隔,最多支持100个。
     * 当touser为"@all"时忽略本参数
     */
    @JSONField(name = "totag")
    private String totag;

    /**
     * 消息类型,此时固定为:text
     */
    @JSONField(name = "msgtype")
    private String msgtype = "text";

    /**
     * 企业应用的id
     */
    @JSONField(name = "agentid")
    private Long agentid;

    /**
     * 消息文本内容
     */
    @JSONField(serialize = false)
    private String content;

    /**
     * 消息内容,最长不超过2048个字节,超过将截断(支持id转译)
     */
    @JSONField(name = "text")
    private TestMsg text;

    /**
     * 表示是否是保密消息,0表示可对外分享,1表示不能分享且内容显示水印,默认为0
     */
    @JSONField(name = "safe")
    private Integer safe = 0;

    /**
     * 表示是否开启id转译,0表示否,1表示是,默认0。仅第三方应用需要用到,企业自建应用可以忽略。
     */
    @JSONField(name = "enable_id_trans")
    private Integer enable_id_trans = 0;

    /**
     * 表示是否开启重复消息检查,0表示否,1表示是,默认0
     */
    @JSONField(name = "enable_duplicate_check")
    private Integer enable_duplicate_check = 0;

    /**
     * 表示是否重复消息检查的时间间隔,默认1800s,最大不超过4小时
     */
    @JSONField(name = "duplicate_check_interval")
    private Integer duplicate_check_interval = 1800;

    public SendTextMsgRequest() {
    }

    public SendTextMsgRequest(String touser, String toparty, String totag, Long agentid, String content) {
        this.touser = touser;
        this.toparty = toparty;
        this.totag = totag;
        this.agentid = agentid;
        this.content = content;
        this.text = new TestMsg(content);
    }

    public String getTouser() {
        return touser;
    }

    public void setTouser(String touser) {
        this.touser = touser;
    }

    public String getToparty() {
        return toparty;
    }

    public void setToparty(String toparty) {
        this.toparty = toparty;
    }

    public String getTotag() {
        return totag;
    }

    public void setTotag(String totag) {
        this.totag = totag;
    }

    public String getMsgtype() {
        return msgtype;
    }

    public void setMsgtype(String msgtype) {
        this.msgtype = msgtype;
    }

    public Long getAgentid() {
        return agentid;
    }

    public void setAgentid(Long agentid) {
        this.agentid = agentid;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public TestMsg getText() {
        return text;
    }

    public void setText(TestMsg text) {
        this.text = text;
    }

    public Integer getSafe() {
        return safe;
    }

    public void setSafe(Integer safe) {
        this.safe = safe;
    }

    public Integer getEnable_id_trans() {
        return enable_id_trans;
    }

    public void setEnable_id_trans(Integer enable_id_trans) {
        this.enable_id_trans = enable_id_trans;
    }

    public Integer getEnable_duplicate_check() {
        return enable_duplicate_check;
    }

    public void setEnable_duplicate_check(Integer enable_duplicate_check) {
        this.enable_duplicate_check = enable_duplicate_check;
    }

    public Integer getDuplicate_check_interval() {
        return duplicate_check_interval;
    }

    public void setDuplicate_check_interval(Integer duplicate_check_interval) {
        this.duplicate_check_interval = duplicate_check_interval;
    }

    @Override
    public String toString() {
        return "SendTextMsgRequest{" +
                "touser='" + touser + '\'' +
                ", toparty='" + toparty + '\'' +
                ", totag='" + totag + '\'' +
                ", msgtype='" + msgtype + '\'' +
                ", agentid=" + agentid +
                ", content='" + content + '\'' +
                ", text=" + text +
                ", safe=" + safe +
                ", enable_id_trans=" + enable_id_trans +
                ", enable_duplicate_check=" + enable_duplicate_check +
                ", duplicate_check_interval=" + duplicate_check_interval +
                '}';
    }
}

SendTextMsgResponse

package com.hugobiotech.wxwork.entity;

/**
 * @author zhangmy
 * @date 2022/12/22 18:28
 * @description 发送应用消息返回实体
 */
public class SendTextMsgResponse {

    /**
     * 返回码
     * 示例: 0
     */
    private Integer errcode;

    /**
     * 对返回码的文本描述内容
     * 示例: ok
     */
    private String errmsg;

    /**
     * 不合法的userid,不区分大小写,统一转为小写
     * 示例: userid1|userid2
     */
    private String invaliduser;

    /**
     * 不合法的partyid
     * 示例: partyid1|partyid2
     */
    private String invalidparty;

    /**
     * 不合法的标签id
     * 示例: tagid1|tagid2
     */
    private String invalidtag;

    /**
     * 没有基础接口许可(包含已过期)的userid
     * 示例: userid3|userid4
     */
    private String unlicenseduser;

    /**
     * 消息id,用于撤回应用消息
     * 示例: xxxx
     */
    private String msgid;

    /**
     * 仅消息类型为“按钮交互型”,“投票选择型”和“多项选择型”的模板卡片消息返回,应用可使用response_code调用更新模版卡片消息接口,72小时内有效,且只能使用一次
     * 示例: xyzxyz
     */
    private String response_code;

    @Override
    public String toString() {
        return "SendTextMsgResponse{" +
                "errcode=" + errcode +
                ", errmsg='" + errmsg + '\'' +
                ", invaliduser='" + invaliduser + '\'' +
                ", invalidparty='" + invalidparty + '\'' +
                ", invalidtag='" + invalidtag + '\'' +
                ", unlicenseduser='" + unlicenseduser + '\'' +
                ", msgid='" + msgid + '\'' +
                ", response_code='" + response_code + '\'' +
                '}';
    }

    public SendTextMsgResponse(Integer errcode, String errmsg, String invaliduser, String invalidparty, String invalidtag, String unlicenseduser, String msgid, String response_code) {
        this.errcode = errcode;
        this.errmsg = errmsg;
        this.invaliduser = invaliduser;
        this.invalidparty = invalidparty;
        this.invalidtag = invalidtag;
        this.unlicenseduser = unlicenseduser;
        this.msgid = msgid;
        this.response_code = response_code;
    }

    public SendTextMsgResponse() {
    }

    public Integer getErrcode() {
        return errcode;
    }

    public void setErrcode(Integer errcode) {
        this.errcode = errcode;
    }

    public String getErrmsg() {
        return errmsg;
    }

    public void setErrmsg(String errmsg) {
        this.errmsg = errmsg;
    }

    public String getInvaliduser() {
        return invaliduser;
    }

    public void setInvaliduser(String invaliduser) {
        this.invaliduser = invaliduser;
    }

    public String getInvalidparty() {
        return invalidparty;
    }

    public void setInvalidparty(String invalidparty) {
        this.invalidparty = invalidparty;
    }

    public String getInvalidtag() {
        return invalidtag;
    }

    public void setInvalidtag(String invalidtag) {
        this.invalidtag = invalidtag;
    }

    public String getUnlicenseduser() {
        return unlicenseduser;
    }

    public void setUnlicenseduser(String unlicenseduser) {
        this.unlicenseduser = unlicenseduser;
    }

    public String getMsgid() {
        return msgid;
    }

    public void setMsgid(String msgid) {
        this.msgid = msgid;
    }

    public String getResponse_code() {
        return response_code;
    }

    public void setResponse_code(String response_code) {
        this.response_code = response_code;
    }
}

注意发送企业微信应用消息的请求体是这样的

image-1671763783298

所以我们需要定义TestMsg和SendTextMsgRequest这两个类,然后返回是这样的

image-1671763884184

另外这里有个小知识点,在SendTextMsgRequest中每个字段都加上了@JSONField(name = "xxx")
这是为了防止JSON在序列化时将下滑先解析成驼峰命名,这样就不符合请求参数体了
@JSONField(serialize = false) 表示不参与序列化

定义Starter配置(重点)

企业微信参数配置类

package com.hugobiotech.wxwork.config;

import com.hugobiotech.wxwork.entity.WxWorkApp;
import com.hugobiotech.wxwork.service.WxWorkService;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author zhangmy
 * @date 2022/12/22 16:46
 * @description
 */
@Component
@ConfigurationProperties("hugo.tools.wx-work")
public class WxWorkProperties {

    /**
     * 企业id
     */
    private String corpId;

    /**
     * 应用列表
     */
    private List<WxWorkApp> apps;

    public String getCorpId() {
        return corpId;
    }

    public void setCorpId(String corpId) {
        this.corpId = corpId;
    }

    public List<WxWorkApp> getApps() {
        return apps;
    }

    public void setApps(List<WxWorkApp> apps) {
        this.apps = apps;
    }

    @Override
    public String toString() {
        return "WxWorkProperties{" +
                "corpId='" + corpId + '\'' +
                ", apps=" + apps +
                '}';
    }

    /**
     * 注册Bean
     * @return
     */
    @Bean
    public WxWorkService wxWorkService() {
        return new WxWorkService(corpId, apps);
    }
}

这里定义了配置的前缀为"hugo.tools.wx-work",那么在引用本starter的项目中就可以在application.yml中按照这个格式添加配置。这里还需要一个服务配置类,在这个类里边我们定义自己的发送消息的方法

企业微信服务类

package com.hugobiotech.wxwork.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hugobiotech.utils.EmptyUtil;
import com.hugobiotech.utils.HttpClientUtil;
import com.hugobiotech.wxwork.entity.SendTextMsgRequest;
import com.hugobiotech.wxwork.entity.SendTextMsgResponse;
import com.hugobiotech.wxwork.entity.WxWorkApp;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhangmy
 * @date 2022/12/22 16:59
 * @description
 */
public class WxWorkService {

    /**
     * 获取access_token的url
     */
    private final String ACCESS_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";

    /**
     * 发送应用消息url
     */
    private final String SENT_MSG_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=";

    /**
     * 企业id
     */
    private String corpId;

    /**
     * 应用app列表
     */
    private List<WxWorkApp> apps;

    public WxWorkService() {
    }

    @Override
    public String toString() {
        return "WxWorkService{" +
                "corpId='" + corpId + '\'' +
                ", apps=" + apps +
                '}';
    }

    public String getCorpId() {
        return corpId;
    }

    public void setCorpId(String corpId) {
        this.corpId = corpId;
    }

    public List<WxWorkApp> getApps() {
        return apps;
    }

    public void setApps(List<WxWorkApp> apps) {
        this.apps = apps;
    }

    public WxWorkService(String corpId, List<WxWorkApp> apps) {
        this.corpId = corpId;
        this.apps = apps;
    }

    /**
     * 发送文本消息
     * @param param 请求实体
     * @return
     */
    public SendTextMsgResponse sendTestMsg(SendTextMsgRequest param) {
        // 根据应用id查询应用配置信息
        List<WxWorkApp> appNameList = apps.stream().filter(app -> param.getAgentid().equals(app.getAgentId())).collect(Collectors.toList());
        if (EmptyUtil.isEmpty(appNameList)) {
            throw new RuntimeException("应用配置不存在");
        }
        WxWorkApp appInfo = appNameList.get(0);
        // 发送请求获取access_token
        JSONObject accessTokenObj = getAccessToken(appInfo.getCorpSecret());
        String accessToken = accessTokenObj.getString("access_token");

        // 发送应用文本消息
        System.out.println("请求参数:" + JSON.toJSONString(param));
        String responseStr =  HttpClientUtil.httpPostJson(SENT_MSG_URL + accessToken, JSON.toJSONString(param));
        return JSONObject.parseObject(responseStr, SendTextMsgResponse.class);
    }

    /**
     * 内部方法 -- 请求获取access_token
     * @param corpSecret 应用密钥
     * @return
     */
    private JSONObject getAccessToken(String corpSecret) {
        Map<String, String> params = new HashMap<>(2);
        params.put("corpid", corpId);
        params.put("corpsecret", corpSecret);
        String token = HttpClientUtil.httpGet(ACCESS_TOKEN_URL, params);
        return JSONObject.parseObject(token);
    }
}

WxWorkService这个类里边我们定义和WxWorkProperties类里相同的参数,主要原因用这些参数进行消息发送处理,比如sendTestMsg方法。另外当中的发送应用消息的方法都是按照官方文档进行的

逻辑呢就是WxWorkProperties拿到咱们项目的自定义配置信息,然后将这些项目配置赋值给WxWorkService,然后再将WxWorkService注入到IOC容器中,这样只要其他项目依赖本starter项目,就可以通过类似@Autowired的注解直接使用WxWorkService服务

spring.factories

最后别忘了在spring.factories中添加咱们自定义配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.hugobiotech.wxwork.config.WxWorkProperties

比如在项目A中引入本starter项目,spring.factories的作用就是将WxWorkProperties当做是A项目的一个配置类,然后就可以获取A项目的配置并注入WxWorkService服务

一切就绪,通过maven install之后在我们的本地maven仓库中就会出现本starter
image-1671765089335

下面我们测试一下

测试Starter

在另一个项目中(可以自己新建)引入hugo-tools-spring-boot-starter

<!--测试予果工具类start-->
        <dependency>
            <groupId>com.hugobiotech</groupId>
            <artifactId>hugo-tools-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>

然后记得在配置文件里添加企业微信配置
image-1671765200465
app这块可以写多个,发送消息时根据agentId会找到对应的app秘钥

使用springboot测试类进行测试

    @Autowired
    private WxWorkService wxWorkService;
  
    @Test
    public void wxWorkSendMsgTest() {
        SendTextMsgRequest param = new SendTextMsgRequest("AngYang", null, null, 1000002L, "测试消息发送hello");
        SendTextMsgResponse resp = wxWorkService.sendTestMsg(param);
        System.out.println("返回消息:" + resp);
    }

控制台输出
image-1671765439606
企业微信截图
image-1671765499922

总结

自定义starter有三个步骤

  • 1.创建配置类(文中WxWorkProperties类)
  • 2.创建服务类(文中WxWorkService)
  • 3.spring.factories配置

需要项目配置写在WxWorkProperties配置类中,拿到项目配置之后需要干什么写在WxWorkService服务中,最后将配置类及服务类通过spring.factories配置交给IOC管理(文中WxWorkService是直接通过WxWorkProperties注入的,所以不用单独写在spring.factories中)

附:

参考:

  • https://blog.csdn.net/qq_42394044/article/details/123906401
  • https://www.cnblogs.com/cjsblog/p/10926408.html

企业微信官方文档:

  • https://developer.work.weixin.qq.com/document/path/90236#10167

企业微信配置应用参考 Springboot实现企业微信扫码登录

自定义Starter的好处就是可以获取项目配置然后提供服务,如果不需要项目配置,那么就没有必要用starter,直接写普通工具类即可

0

评论区