SpringBoot 配置 okhttp3的操作
1. Maven 添加依賴
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>3.10.0</version> </dependency>
2. application.properties 配置文件
ok.http.connect-timeout=30 ok.http.read-timeout=30 ok.http.write-timeout=30 # 連接池中整體的空閑連接的最大數(shù)量 ok.http.max-idle-connections=200 # 連接空閑時(shí)間最多為 300 秒 ok.http.keep-alive-duration=300
3. OkHttpConfiguration 配置類
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
/**
* @author Answer.AI.L
* @date 2019-04-09
*/
@Configuration
public class OkHttpConfiguration {
@Value("${ok.http.connect-timeout}")
private Integer connectTimeout;
@Value("${ok.http.read-timeout}")
private Integer readTimeout;
@Value("${ok.http.write-timeout}")
private Integer writeTimeout;
@Value("${ok.http.max-idle-connections}")
private Integer maxIdleConnections;
@Value("${ok.http.keep-alive-duration}")
private Long keepAliveDuration;
@Bean
public OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory(), x509TrustManager())
// 是否開(kāi)啟緩存
.retryOnConnectionFailure(false)
.connectionPool(pool())
.connectTimeout(connectTimeout, TimeUnit.SECONDS)
.readTimeout(readTimeout, TimeUnit.SECONDS)
.writeTimeout(writeTimeout,TimeUnit.SECONDS)
.hostnameVerifier((hostname, session) -> true)
// 設(shè)置代理
// .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
// 攔截器
// .addInterceptor()
.build();
}
@Bean
public X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
@Bean
public SSLSocketFactory sslSocketFactory() {
try {
// 信任任何鏈接
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return null;
}
@Bean
public ConnectionPool pool() {
return new ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.SECONDS);
}
}
4. OkHttp 類
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author Answer.AI.L
* @date 2019-04-09
*/
@Slf4j
@Component
public class OkHttpCli {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final MediaType XML = MediaType.parse("application/xml; charset=utf-8");
@Autowired
private OkHttpClient okHttpClient;
/**
* get 請(qǐng)求
* @param url 請(qǐng)求url地址
* @return string
* */
public String doGet(String url) {
return doGet(url, null, null);
}
/**
* get 請(qǐng)求
* @param url 請(qǐng)求url地址
* @param params 請(qǐng)求參數(shù) map
* @return string
* */
public String doGet(String url, Map<String, String> params) {
return doGet(url, params, null);
}
/**
* get 請(qǐng)求
* @param url 請(qǐng)求url地址
* @param headers 請(qǐng)求頭字段 {k1, v1 k2, v2, ...}
* @return string
* */
public String doGet(String url, String[] headers) {
return doGet(url, null, headers);
}
/**
* get 請(qǐng)求
* @param url 請(qǐng)求url地址
* @param params 請(qǐng)求參數(shù) map
* @param headers 請(qǐng)求頭字段 {k1, v1 k2, v2, ...}
* @return string
* */
public String doGet(String url, Map<String, String> params, String[] headers) {
StringBuilder sb = new StringBuilder(url);
if (params != null && params.keySet().size() > 0) {
boolean firstFlag = true;
for (String key : params.keySet()) {
if (firstFlag) {
sb.append("?").append(key).append("=").append(params.get(key));
firstFlag = false;
} else {
sb.append("&").append(key).append("=").append(params.get(key));
}
}
}
Request.Builder builder = new Request.Builder();
if (headers != null && headers.length > 0) {
if (headers.length % 2 == 0) {
for (int i = 0; i < headers.length; i = i + 2) {
builder.addHeader(headers[i], headers[i + 1]);
}
} else {
log.warn("headers's length[{}] is error.", headers.length);
}
}
Request request = builder.url(sb.toString()).build();
log.info("do get request and url[{}]", sb.toString());
return execute(request);
}
/**
* post 請(qǐng)求
* @param url 請(qǐng)求url地址
* @param params 請(qǐng)求參數(shù) map
* @return string
*/
public String doPost(String url, Map<String, String> params) {
FormBody.Builder builder = new FormBody.Builder();
if (params != null && params.keySet().size() > 0) {
for (String key : params.keySet()) {
builder.add(key, params.get(key));
}
}
Request request = new Request.Builder().url(url).post(builder.build()).build();
log.info("do post request and url[{}]", url);
return execute(request);
}
/**
* post 請(qǐng)求, 請(qǐng)求數(shù)據(jù)為 json 的字符串
* @param url 請(qǐng)求url地址
* @param json 請(qǐng)求數(shù)據(jù), json 字符串
* @return string
*/
public String doPostJson(String url, String json) {
log.info("do post request and url[{}]", url);
return exectePost(url, json, JSON);
}
/**
* post 請(qǐng)求, 請(qǐng)求數(shù)據(jù)為 xml 的字符串
* @param url 請(qǐng)求url地址
* @param xml 請(qǐng)求數(shù)據(jù), xml 字符串
* @return string
*/
public String doPostXml(String url, String xml) {
log.info("do post request and url[{}]", url);
return exectePost(url, xml, XML);
}
private String exectePost(String url, String data, MediaType contentType) {
RequestBody requestBody = RequestBody.create(contentType, data);
Request request = new Request.Builder().url(url).post(requestBody).build();
return execute(request);
}
private String execute(Request request) {
Response response = null;
try {
response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
}
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
} finally {
if (response != null) {
response.close();
}
}
return "";
}
}
5. 使用驗(yàn)證
@RestController
public class AnswerController {
@Autowired
private OkHttpCli okHttpCli;
@RequestMapping(value = "show", method = RequestMethod.GET)
public String show() {
String url = "https://www.baidu.com/";
String message = okHttpCli.doGet(url);
return message;
}
}
6. 雙向認(rèn)證(待證)
@Bean
public SSLSocketFactory sslSocketFactory() {
String certPath = "";
String caPath = "";
String certPwd = "";
String caPwd = "";
try {
ClassPathResource selfcertPath = new ClassPathResource(certPath);
ClassPathResource trustcaPath = new ClassPathResource(caPath);
KeyStore selfCert = KeyStore.getInstance("pkcs12");
selfCert.load(selfcertPath.getInputStream(), certPwd.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(selfCert, certPwd.toCharArray());
KeyStore caCert = KeyStore.getInstance("jks");
caCert.load(trustcaPath.getInputStream(), caPwd.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
tmf.init(caCert);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
補(bǔ)充:Spring Cloud Feign 總結(jié)問(wèn)題,注意點(diǎn),性能調(diào)優(yōu),切換okhttp3
Feign常見(jiàn)問(wèn)題總結(jié)
FeignClient接口如使用@PathVariable ,必須指定value屬性
//在一些早期版本中, @PathVariable("id") 中的 "id" ,也就是value屬性,必須指定,不能省略。
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
...
}
Java代碼自定義Feign Client的注意點(diǎn)與坑
@FeignClient(name = "microservice-provider-user", configuration = UserFeignConfig.class)
public interface UserFeignClient {
@GetMapping("/users/{id}")
User findById(@PathVariable("id") Long id);
}
/**
* 該Feign Client的配置類,注意:
* 1. 該類可以獨(dú)立出去;
* 2. 該類上也可添加@Configuration聲明是一個(gè)配置類;
* 配置類上也可添加@Configuration注解,聲明這是一個(gè)配置類;
* 但此時(shí)千萬(wàn)別將該放置在主應(yīng)用程序上下文@ComponentScan所掃描的包中,
* 否則,該配置將會(huì)被所有Feign Client共享,無(wú)法實(shí)現(xiàn)細(xì)粒度配置!
* 個(gè)人建議:像我一樣,不加@Configuration注解
*
* @author zhouli
*/
class UserFeignConfig {
@Bean
public Logger.Level logger() {
return Logger.Level.FULL;
}
}
配置類上也可添加@Configuraiton 注解,聲明這是一個(gè)配置類;但此時(shí)千萬(wàn)別將該放置在主應(yīng)用程序上下文@ComponentScan 所掃描的包中,否則,該配置將會(huì)被所有Feign Client共享(相當(dāng)于變成了通用配置,其實(shí)本質(zhì)還是Spring父子上下文掃描包重疊導(dǎo)致的問(wèn)題),無(wú)法實(shí)現(xiàn)細(xì)粒度配置!
個(gè)人建議:像我一樣,不加@Configuration注解,省得進(jìn)坑。
最佳實(shí)踐:盡量用配置屬性自定義Feign的配置?。?!
@FeignClient 注解屬性
//@FeignClient(name = "microservice-provider-user")
//在早期的Spring Cloud版本中,無(wú)需提供name屬性,從Brixton版開(kāi)始,@FeignClient必須提供name屬性,否則應(yīng)用將無(wú)法正常啟動(dòng)!
//另外,name、url等屬性支持占位符。例如:
@FeignClient(name = "${feign.name}", url = "${feign.url}")
類級(jí)別的@RequestMapping會(huì)被Spring MVC加載
@RequestMapping("/users")
@FeignClient(name = "microservice-user")
public class TestFeignClient {
// ...
}
類上的@RequestMapping 注解也會(huì)被Spring MVC加載。該問(wèn)題現(xiàn)已經(jīng)被解決,早期的版本有兩種解決方案:方案1:不在類上加@RequestMapping 注解;方案2:添加如下代碼:
@Configuration
@ConditionalOnClass({ Feign.class })
public class FeignMappingDefaultConfiguration {
@Bean
public WebMvcRegistrations feignWebRegistrations() {
return new WebMvcRegistrationsAdapter() {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new FeignFilterRequestMappingHandlerMapping();
}
};
}
private static class FeignFilterRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected boolean isHandler(Class<?> beanType) {
return super.isHandler(beanType) && !beanType.isInterface();
}
}
}
首次請(qǐng)求失敗Ribbon的饑餓加載(eager-load)模式
如需產(chǎn)生Hystrix Stream監(jiān)控信息,需要做一些額外操作Feign本身已經(jīng)整合了Hystrix,可直接使用@FeignClient(value = "microservice-provider-user", fallback = XXX.class) 來(lái)指定fallback類,fallback類繼承@FeignClient所標(biāo)注的接口即可。
但是假設(shè)如需使用Hystrix Stream進(jìn)行監(jiān)控,默認(rèn)情況下,訪問(wèn)http://IP:PORT/actuator/hystrix.stream 是會(huì)返回404,這是因?yàn)镕eign雖然整合了Hystrix,但并沒(méi)有整合Hystrix的監(jiān)控。如何添加監(jiān)控支持呢?需要以下幾步:
第一步:添加依賴,示例:
<!-- 整合hystrix,其實(shí)feign中自帶了hystrix,引入該依賴主要是為了使用其中的hystrix-metrics-event-stream,用于dashboard --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
第二步:在啟動(dòng)類上添加@EnableCircuitBreaker 注解,示例:
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableCircuitBreaker
public class MovieFeignHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(MovieFeignHystrixApplication.class, args);
}
}
第三步:在application.yml中添加如下內(nèi)容,暴露hystrix.stream端點(diǎn):
management: endpoints: web: exposure: include: 'hystrix.stream'
這樣,訪問(wèn)任意Feign Client接口的API后,再訪問(wèn)http://IP:PORT/actuator/hystrix.stream ,就會(huì)展示一大堆Hystrix監(jiān)控?cái)?shù)據(jù)了。
Feign 上傳文件
加依賴
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.0.3</version> </dependency>
編寫Feign Client
@FeignClient(name = "ms-content-sample", configuration = UploadFeignClient.MultipartSupportConfig.class)
public interface UploadFeignClient {
@RequestMapping(value = "/upload", method = RequestMethod.POST,
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
class MultipartSupportConfig {
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}
}
如代碼所示,在這個(gè)Feign Client中,我們引用了配置類MultipartSupportConfig ,在MultipartSupportConfig 中,我們實(shí)例化了SpringFormEncoder 。這樣這個(gè)Feign Client就能夠上傳啦。
注意點(diǎn)
//RequestMapping注解中的produeces 、consumes 不能少;
@RequestMapping(value = "/upload", method = RequestMethod.POST,
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
接口定義中的注解@RequestPart(value = "file") 不能寫成@RequestParam(value = "file") 。
最好將Hystrix的超時(shí)時(shí)間設(shè)長(zhǎng)一點(diǎn),例如5秒,否則可能文件還沒(méi)上傳完,Hystrix就超時(shí)了,從而導(dǎo)致客戶端側(cè)的報(bào)錯(cuò)。
Feign實(shí)現(xiàn)Form表單提交
添加依賴:
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.2.2</version> </dependency>
Feign Client示例:
@FeignClient(name = "xxx", url = "http://www.itmuch.com/", configuration = TestFeignClient.FormSupportConfig.class)
public interface TestFeignClient {
@PostMapping(value = "/test",
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
)
void post(Map<String, ?> queryParam);
class FormSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
// new一個(gè)form編碼器,實(shí)現(xiàn)支持form表單提交
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
// 開(kāi)啟Feign的日志
@Bean
public Logger.Level logger() {
return Logger.Level.FULL;
}
}
}
調(diào)用示例:
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
HashMap<String, String> param = Maps.newHashMap();
param.put("username","zhangsan");
param.put("password","pwd");
this.testFeignClient.post(param);
return new User();
}
日志:
...[TestFeignClient#post] ---> POST http://www.baidu.com/test HTTP/1.1 ...[TestFeignClient#post] Accept: application/json;charset=UTF-8 ...[TestFeignClient#post] Content-Type: application/x-www-form-urlencoded; charset=UTF-8 ...[TestFeignClient#post] Content-Length: 30 ...[TestFeignClient#post] ...[TestFeignClient#post] password=pwd&username=zhangsan ...[TestFeignClient#post] ---> END HTTP (30-byte body)
由日志可知,此時(shí)Feign已能使用Form表單方式提交數(shù)據(jù)。
Feign GET請(qǐng)求如何構(gòu)造多參數(shù)
假設(shè)需請(qǐng)求的URL包含多個(gè)參數(shù),例如http://microservice-provider-user/get?id=1&username=張三 ,該如何使用Feign構(gòu)造呢?我們知道,Spring Cloud為Feign添加了Spring MVC的注解支持,那么我們不妨按照Spring MVC的寫法嘗試一下:
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get0(User user);
}
然而,這種寫法并不正確,控制臺(tái)會(huì)輸出類似如下的異常。
feign.FeignException: status 405 reading UserFeignClient#get0(User); content:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/get"}
由異??芍?,盡管我們指定了GET方法,F(xiàn)eign依然會(huì)使用POST方法發(fā)送請(qǐng)求。于是導(dǎo)致了異常。正確寫法如下
方法一[推薦]注意:使用該方法無(wú)法使用Fegin的繼承模式
@FeignClient("microservice-provider-user")
public interface UserFeignClient {
@GetMapping("/get")
public User get0(@SpringQueryMap User user);
}
方法二[推薦]
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
}
這是最為直觀的方式,URL有幾個(gè)參數(shù),F(xiàn)eign接口中的方法就有幾個(gè)參數(shù)。使用@RequestParam注解指定請(qǐng)求的參數(shù)是什么。
方法三[不推薦]多參數(shù)的URL也可使用Map來(lái)構(gòu)建。當(dāng)目標(biāo)URL參數(shù)非常多的時(shí)候,可使用這種方式簡(jiǎn)化Feign接口的編寫。
@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
@RequestMapping(value = "/get", method = RequestMethod.GET)
public User get2(@RequestParam Map<String, Object> map);
}
在調(diào)用時(shí),可使用類似以下的代碼。
public User get(String username, String password) {
HashMap<String, Object> map = Maps.newHashMap();
map.put("id", "1");
map.put("username", "張三");
return this.userFeignClient.get2(map);
}
注意:這種方式不建議使用。主要是因?yàn)榭勺x性不好,而且如果參數(shù)為空的時(shí)候會(huì)有一些問(wèn)題,例如map.put("username", null); 會(huì)導(dǎo)致服務(wù)調(diào)用方(消費(fèi)者服務(wù))接收到的username是"" ,而不是null。
切換為 Okhttp3 提升 QPS 性能優(yōu)化
加依賴引入okhttp3
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>${version}</version>
</dependency>
寫配置
feign: # feign啟用hystrix,才能熔斷、降級(jí) # hystrix: # enabled: true # 啟用 okhttp 關(guān)閉默認(rèn) httpclient httpclient: enabled: false #關(guān)閉httpclient # 配置連接池 max-connections: 200 #feign的最大連接數(shù) max-connections-per-route: 50 #fegin單個(gè)路徑的最大連接數(shù) okhttp: enabled: true # 請(qǐng)求與響應(yīng)的壓縮以提高通信效率 compression: request: enabled: true min-request-size: 2048 mime-types: text/xml,application/xml,application/json response: enabled: true
參數(shù)配置
/**
* 配置 okhttp 與連接池
* ConnectionPool 默認(rèn)創(chuàng)建5個(gè)線程,保持5分鐘長(zhǎng)連接
*/
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class) //SpringBoot自動(dòng)配置
public class OkHttpConfig {
// 默認(rèn)老外留給你彩蛋中文亂碼,加上它就 OK
@Bean
public Encoder encoder() {
return new FormEncoder();
}
@Bean
public okhttp3.OkHttpClient okHttpClient() {
return new okhttp3.OkHttpClient.Builder()
//設(shè)置連接超時(shí)
.connectTimeout(10, TimeUnit.SECONDS)
//設(shè)置讀超時(shí)
.readTimeout(10, TimeUnit.SECONDS)
//設(shè)置寫超時(shí)
.writeTimeout(10, TimeUnit.SECONDS)
//是否自動(dòng)重連
.retryOnConnectionFailure(true)
.connectionPool(new ConnectionPool(10, 5L, TimeUnit.MINUTES))
.build();
}
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)與面試題匯總
由于順序表的插入刪除操作需要移動(dòng)大量的元素,影響了運(yùn)行效率,因此引入了線性表的鏈?zhǔn)酱鎯?chǔ)——單鏈表。本文為大家介紹了單鏈表的實(shí)現(xiàn)與面試題匯總,感興趣的可以了解一下2022-10-10
mybatis中關(guān)于mapper的使用以及注意事項(xiàng)
這篇文章主要介紹了mybatis中關(guān)于mapper的使用以及注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
mybatis-plus?查詢傳入?yún)?shù)Map,返回List<Map>方式
這篇文章主要介紹了mybatis-plus?查詢傳入?yún)?shù)Map,返回List<Map>方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
Java讀取Excel文件并寫入數(shù)據(jù)庫(kù)的示例代碼
這篇文章主要介紹了Java讀取Excel文件并寫入數(shù)據(jù)庫(kù)的示例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-06-06
SWT(JFace) 體驗(yàn)之FontRegistry
測(cè)試代碼如下:2009-06-06
Springboot Websocket Stomp 消息訂閱推送
本文主要介紹了Springboot Websocket Stomp 消息訂閱推送,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
SpringBoot+MyBatis實(shí)現(xiàn)MD5加密數(shù)據(jù)庫(kù)用戶密碼的方法
MD5技術(shù)主要用于對(duì)用戶密碼加密,增加賬戶的安全性,他具有不可逆的特性,不會(huì)被輕易解密,這篇文章給大家介紹SpringBoot+MyBatis實(shí)現(xiàn)MD5加密數(shù)據(jù)庫(kù)用戶密碼的方法,感興趣的朋友跟隨小編一起看看吧2024-03-03
2020macOS Big Sur配置Java開(kāi)發(fā)環(huán)境之jdk安裝過(guò)程
這篇文章主要介紹了2020macOS Big Sur配置Java開(kāi)發(fā)環(huán)境之jdk安裝,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02

