Java Retrofit源碼層深入分析
提醒:看的過程一定要自己點(diǎn)開源碼,跟著一步步走,只看容易懵逼
一、自己對(duì)Retrofit的理解
Retrofit的中文翻譯是改造,改造什么呢?
我認(rèn)為是對(duì)OkHttp的使用、RxJava的使用進(jìn)行改造。
具體體現(xiàn)在哪里?
在使用OkHttp請(qǐng)求前:
1.用注解統(tǒng)一配置網(wǎng)絡(luò)請(qǐng)求頭和請(qǐng)求參數(shù)
2.通過動(dòng)態(tài)代理統(tǒng)一獲取注解的請(qǐng)求頭和請(qǐng)求參數(shù)然后一致組裝適配成請(qǐng)求的request,交給okHttp進(jìn)行請(qǐng)求
使用OkHttp結(jié)果返回后:
1.線程切換
線程切換分為兩種一種是用Retrofit默認(rèn)的,另一種是使用RxJava
2.把返回的數(shù)據(jù)json適配成javabean
簡(jiǎn)單總結(jié),Retrofit就是為了讓OkHttp、RxJava使用的更加簡(jiǎn)潔的一個(gè)封裝
二、Retrofit的簡(jiǎn)單使用
沒有添加RxJava
class RetrofitActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//初始化一個(gè)Retrofit對(duì)象
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClientProvider.client()) // 這個(gè)OkHttpClientProvider.client()是我自己封裝的,就是簡(jiǎn)單提供一個(gè)OkHttpClient
.build()
//創(chuàng)建出GitHubApiService這個(gè)GitHubApiService的動(dòng)態(tài)代理對(duì)象service
val service = retrofit.create(GitHubApiService::class.java)
//返回一個(gè)Okhttp的請(qǐng)求 Call 對(duì)象repos
val repos = service.listRepos("octocat")
//調(diào)用 enqueue 方法在回調(diào)方法里處理結(jié)果
repos.enqueue(object : Callback<List<Repo>?> {
override fun onFailure(call: Call<List<Repo>?>, t: Throwable) {
t.printStackTrace()
}
override fun onResponse(call: Call<List<Repo>?>, response: Response<List<Repo>?>) {
"response.code() = ${response.code()}".logE()
}
})
}
}
//自己定義的 API 請(qǐng)求接口
interface GitHubApiService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String?): Call<List<Repo>>
}三、請(qǐng)求前Retrofit所做的工作
以統(tǒng)一的方式為網(wǎng)絡(luò)請(qǐng)求準(zhǔn)備各種參數(shù)
(1).Retrofit的創(chuàng)建,把BaseUrl、OkHttpClient和Gson的實(shí)例保存在Retrofit對(duì)象中
Retrofit.Builder()的build()方法:
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}首先看這個(gè)callFactory,因?yàn)槲覀円呀?jīng).client(OkHttpClientProvider.client())方法設(shè)置了我們的okhttpclient,所以這里callFactory不為null:
public Builder client(OkHttpClient client) {
return callFactory(checkNotNull(client, "client == null"));
}
public Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}我們沒有添加CallbackExecutor(這個(gè)看名字就知道是用來(lái)處理返回值的),所以這里用的是默認(rèn)的:
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}記住這個(gè)CallbackExecutor,后面會(huì)用到
接下來(lái)重點(diǎn)看:
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
這個(gè)很重要,我們沒有調(diào)用addCallAdapterFactory方法另外添加AdapterFactory,所以這里第一句的this.adapterFactories是一個(gè)空集合,之后調(diào)用add方法添加了一個(gè)默認(rèn)的platform.defaultCallAdapterFactory(callbackExecutor),這個(gè)很重要需要記住。
接下來(lái)把我們的Gson解析器添加到了converterFactories中
最后創(chuàng)建了Retrofit對(duì)象,把上面的對(duì)象都維護(hù)在了Retrofit中,這些都是網(wǎng)絡(luò)請(qǐng)求需要的工具。
(2).用注解來(lái)表示請(qǐng)求頭和請(qǐng)求的參數(shù)等
如果不知道注解的本質(zhì)的小伙伴點(diǎn)擊傳送門:
看一下 注解的具體使用:
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String?): Call<List<Repo>>這個(gè)就是注解提供的請(qǐng)求方式、請(qǐng)求地址、請(qǐng)求參數(shù)等,這個(gè)是接口類中的一個(gè)方法,后面這個(gè)接口類會(huì)被動(dòng)態(tài)代理來(lái)代理這個(gè)接口。
(3).用動(dòng)態(tài)代理來(lái)把Retrofit和代理接口的方法上的請(qǐng)求信息,組裝成一個(gè)OkHttp的請(qǐng)求
如果不知道動(dòng)態(tài)代理本質(zhì)的小伙伴點(diǎn)擊傳送門:
使用動(dòng)態(tài)代理,組裝請(qǐng)求的代碼:
//創(chuàng)建出GitHubApiService這個(gè)GitHubApiService的動(dòng)態(tài)代理對(duì)象service
val service = retrofit.create(GitHubApiService::class.java)
//返回一個(gè)Okhttp的請(qǐng)求 Call 對(duì)象repos
val repos = service.listRepos("octocat")看Retrofit的create方法:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
......
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}可以看到就是創(chuàng)建了一個(gè)動(dòng)態(tài)代理,動(dòng)態(tài)代理的作用就是代理類的任何一個(gè)方法的調(diào)用,都會(huì)走這個(gè)動(dòng)態(tài)代理的invoke方法,即service.listRepos("octocat")的調(diào)用會(huì)走這個(gè)invoke方法。
下面我們具體分析,動(dòng)態(tài)代理的invoke方法如何把各種請(qǐng)求的信息,組裝成一個(gè)OkHttpCall的:
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
這個(gè)ServiceMethod是一個(gè)重點(diǎn),它是真正執(zhí)行把所有的請(qǐng)求信息,拼成一個(gè)Okhttp的請(qǐng)求Call的類:
ServiceMethod<?, ?> loadServiceMethod(Method method) {
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}這里有一個(gè)緩存,serviceMethodCache是一個(gè)ConcurrentHashMap來(lái)緩存serviceMethod。
然后是ServiceMethod的創(chuàng)建,主要看build方法:
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
......
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
......
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
Type parameterType = parameterTypes[p];
......
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
......
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
......
return new ServiceMethod<>(this);
}可以看出來(lái),這里都在把之前的注解、請(qǐng)求參數(shù)、請(qǐng)求地址等解析出來(lái),存到serviceMethod中:
ServiceMethod(Builder<R, T> builder) {
this.callFactory = builder.retrofit.callFactory();
this.callAdapter = builder.callAdapter;
this.baseUrl = builder.retrofit.baseUrl();
this.responseConverter = builder.responseConverter;
this.httpMethod = builder.httpMethod;
this.relativeUrl = builder.relativeUrl;
this.headers = builder.headers;
this.contentType = builder.contentType;
this.hasBody = builder.hasBody;
this.isFormEncoded = builder.isFormEncoded;
this.isMultipart = builder.isMultipart;
this.parameterHandlers = builder.parameterHandlers;
}這個(gè)ServiceMethod大家現(xiàn)在不用細(xì)看它,知道它是解析了我們之前統(tǒng)一用注解格式表示的請(qǐng)求信息,后保存在了自己serviceMethoed的對(duì)象中。后面用到的時(shí)候,大家自然能體會(huì)到。
然后回到動(dòng)態(tài)代理的invoke方法:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
這個(gè)OkHttpCall就是具體發(fā)出請(qǐng)求的類,它持有serviceMethod,這里我們先不分析它,繼續(xù)往下看:
return serviceMethod.callAdapter.adapt(okHttpCall);
這里就和我們調(diào)用的地方聯(lián)系起來(lái)了,這個(gè)return的是一個(gè)okHttp的call:
//創(chuàng)建出GitHubApiService這個(gè)GitHubApiService的動(dòng)態(tài)代理對(duì)象service
val service = retrofit.create(GitHubApiService::class.java)
//返回一個(gè)Okhttp的請(qǐng)求 Call 對(duì)象repos ???注意這里變化了
val repos = serviceMethod.callAdapter.adapt(okHttpCall)注意上面service.listRepos("octocat")等于動(dòng)態(tài)代理調(diào)用invoke方法的返回值即:
serviceMethod.callAdapter.adapt(okHttpCall)
那我們重點(diǎn)分析:
serviceMethod.callAdapter.adapt(okHttpCall)
首先這個(gè)serviceMethod.callAdapter是什么:
callAdapter是在serviceMethod中的build方法中賦值的:
public ServiceMethod build() {
callAdapter = createCallAdapter();
......
}
private CallAdapter<T, R> createCallAdapter() {
......
try {
//noinspection unchecked
return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
......
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
......
}上面是整個(gè)callAdapter的調(diào)用鏈,最后是:
adapterFactories.get(i).get(returnType, annotations, this);
這句確定的CallAdapter:
adapterFactoris之前我們提到過要記住的,在Retrofit的build方法中:
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
上面分析了,adapterFactories中只有platform.defaultCallAdapterFactory(callbackExecutor),這一個(gè)CallAdapter,adapterFactories.get(i)就是獲取platform.defaultCallAdapterFactory(callbackExecutor)的。
接下來(lái)看platform.defaultCallAdapterFactory(callbackExecutor)是什么:
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}所以我們得到結(jié)論,adapterFactories.get(i)是new ExecutorCallAdapterFactory(callbackExecutor)
注意我們要的是adapterFactories.get(i).get(returnType, annotations, this),所以我們要看ExecutorCallAdapterFactory的get方法:
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}返回的是:
new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};所以:
serviceMethod.callAdapter是adapterFactories.get(i).get(returnType, annotations, this)是上面這段代碼new CallAdapter
那我們要分析的serviceMethod.callAdapter.adapt(okHttpCall),所以我們要的是new CallAdapter的adaptger方法:它返回的是new ExecutorCallbackCall<>(callbackExecutor, call)
這里要注意,這兩個(gè)參數(shù):
callbackExecutor是我們上面提到過的處理返回值的:
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}call是:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
好現(xiàn)在我們知道了:
//創(chuàng)建出GitHubApiService這個(gè)GitHubApiService的動(dòng)態(tài)代理對(duì)象service
val service = retrofit.create(GitHubApiService::class.java)
//返回一個(gè)Okhttp的請(qǐng)求 Call 對(duì)象repos ???注意這里變化了
val repos = serviceMethod.callAdapter.adapt(okHttpCall)其實(shí)是:
//創(chuàng)建出GitHubApiService這個(gè)GitHubApiService的動(dòng)態(tài)代理對(duì)象service
val service = retrofit.create(GitHubApiService::class.java)
//返回一個(gè)Okhttp的請(qǐng)求 Call 對(duì)象repos ???注意這里變化了
Call repos = new ExecutorCallbackCall<>(callbackExecutor, call)到這里我們分析了第三小節(jié)的題目:如何用動(dòng)態(tài)代理來(lái)把Retrofit和代理接口的方法上的請(qǐng)求信息組裝成一個(gè)OkHttp的請(qǐng)求
(4).發(fā)起請(qǐng)求的調(diào)用鏈細(xì)節(jié)
我們知道發(fā)起請(qǐng)求的代碼:
val service = retrofit.create(GitHubApiService::class.java)
//返回一個(gè)Okhttp的請(qǐng)求 Call 對(duì)象repos
val repos = service.listRepos("octocat")
//調(diào)用 enqueue 方法在回調(diào)方法里處理結(jié)果
repos.enqueue(object : Callback<List<Repo>?> {
override fun onFailure(call: Call<List<Repo>?>, t: Throwable) {
t.printStackTrace()
}
override fun onResponse(call: Call<List<Repo>?>, response: Response<List<Repo>?>) {
"response.code() = ${response.code()}".logE()
}
})這里的repos已經(jīng)分析了是ExecutorCallbackCall,調(diào)用的enqueue方法,接下來(lái)我們來(lái)看調(diào)用鏈ExecutorCallbackCall的enqueue方法:
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}首先我們已經(jīng)知道構(gòu)造方法的兩個(gè)參數(shù)是什么:
delegate 是OkHttpCall
callbackExecutor是platform.defaultCallbackExecutor();
然后我們繼續(xù)看delegate即OkHttpCall的enqueue方法(上面提到的掠過的OkhttpCall這里用到了):
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//?? 重點(diǎn)
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//?? 重點(diǎn)
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
//?? 重點(diǎn)
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}上面我們標(biāo)記了三個(gè)重點(diǎn):
1.第一個(gè)重點(diǎn)call = rawCall = createRawCall();
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}這里用到了上面提到了serviceMethod,之前我們已經(jīng)總結(jié),它是保存了所有請(qǐng)求需要的信息和請(qǐng)求需要的工具,這里就要使用了:
Request request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request);
就這兩句,先把保存的請(qǐng)求頭和參數(shù)等信息,拼湊成OkHttpClient需要的Request,然后調(diào)用serviceMethod.callFactory.newCall(request),這個(gè)serviceMethod.callFactory就是我們之前設(shè)置的OkHttpClient,調(diào)用的它的newCall方法,得到一個(gè)OkHttp的Call請(qǐng)求對(duì)象,其實(shí)是RealCall對(duì)象
2.第二個(gè)重點(diǎn)call.enqueue(new okhttp3.Callback() {}
這是調(diào)用的OkHttpClient的enqueue方法,所以我們之前總結(jié)Retrofit就是對(duì)OkHttp請(qǐng)求之前和之后的工作的封裝,使其有一致、簡(jiǎn)單、可復(fù)用等等。
3.第三個(gè)重點(diǎn)callSuccess(response);
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}callback.onResponse(OkHttpCall.this, response);,這個(gè)callback是之前delegate調(diào)用enqueue方法時(shí)的參數(shù):
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}注意這個(gè)callback是delegate.enqueue方法的參數(shù),不是外層enqueue方法的callback,所以這個(gè)回調(diào)執(zhí)行的這個(gè)callback的onResponse方法代碼:
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});callbackExecutor之前提到過是platform.defaultCallbackExecutor():
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}可以看到就是把Runnable來(lái)交給handler執(zhí)行,實(shí)現(xiàn)的線程切換
到這里發(fā)起請(qǐng)求的調(diào)用邏輯分析清楚了。
到此這篇關(guān)于Java Retrofit源碼層深入分析的文章就介紹到這了,更多相關(guān)Java Retrofit內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目打包war包時(shí)無(wú)法運(yùn)行問題的解決方式
在開發(fā)工程中,使用啟動(dòng)類啟動(dòng)能夠正常啟動(dòng)并測(cè)試,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目打包war包時(shí)無(wú)法運(yùn)行問題的解決方式,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06
java如何從地址串中解析提取省市區(qū)(完美匹配中國(guó)所有地址)
這篇文章主要給大家介紹了關(guān)于java如何從地址串中解析提取省市區(qū)的相關(guān)資料,通過這個(gè)方法可以完美匹配中國(guó)所有地址,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
Java?Apache?common-pool對(duì)象池介紹
這篇文章主要介紹了Java Apache?common-pool對(duì)象池介紹,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-09-09
Spring MVC請(qǐng)求參數(shù)的傳遞方式
Spring MVC是一種基于Model-View-Controller(MVC)設(shè)計(jì)模式的輕量級(jí)Web框架,用于Java應(yīng)用程序的開發(fā),在處理HTTP請(qǐng)求時(shí),Spring MVC會(huì)涉及到請(qǐng)求參數(shù)的傳遞,本文給大家介紹了Spring MVC請(qǐng)求參數(shù)的傳遞方式,需要的朋友可以參考下2024-10-10
java swing 創(chuàng)建一個(gè)簡(jiǎn)單的QQ界面教程
這篇文章主要介紹了java swing 創(chuàng)建一個(gè)簡(jiǎn)單的QQ界面教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-09-09
Spring Cache + Caffeine的整合與使用示例詳解
對(duì)于一些項(xiàng)目里需要對(duì)數(shù)據(jù)庫(kù)里的某些數(shù)據(jù)一直重復(fù)請(qǐng)求的,且這些數(shù)據(jù)基本是固定的,在這種情況下,可以借助簡(jiǎn)單使用本地緩存來(lái)緩存這些數(shù)據(jù),本文介紹一下Spring Cache和Caffeine的使用,感興趣的朋友一起看看吧2023-12-12

