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相关的三个包
创建好项目之后删掉主启动类,然后在resources目录下新增目录META-INF并新增spring.factories文件
几个工具类
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;
}
}
注意发送企业微信应用消息的请求体是这样的
所以我们需要定义TestMsg和SendTextMsgRequest这两个类,然后返回是这样的
另外这里有个小知识点,在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
下面我们测试一下
测试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>
然后记得在配置文件里添加企业微信配置
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);
}
控制台输出
企业微信截图
总结
自定义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,直接写普通工具类即可
评论区