Android常用库源码解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: Android常用库源码解析图片加载框架比较共同优点都对多级缓存、线程池、缓存算法做了处理自适应程度高,根据系统性能初始化缓存配置、系统信息变更后动态调整策略。比如根据 CPU 核数确定最大并发数,根据可用内存确定内存缓存大小,网络状态变化时调整最大并发数等。

Android常用库源码解析
图片加载框架比较
共同优点

都对多级缓存、线程池、缓存算法做了处理
自适应程度高,根据系统性能初始化缓存配置、系统信息变更后动态调整策略。比如根据 CPU 核数确定最大并发数,根据可用内存确定内存缓存大小,网络状态变化时调整最大并发数等。
支持多种数据源支持多种数据源,网络、本地、资源、Assets 等
不同点

Picasso所能实现的功能,Glide都能做,无非是所需的设置不同。但是Picasso体积比起Glide小太多。
Glide 不仅是一个图片缓存,它支持 Gif、WebP、缩略图。Glide 支持加载 Gif 动态图,而 Picasso 不支持该特性
Fresco在5.0以下的内存优化非常好,代价就是体积也非常的大,按体积算Fresco>Glide>Picasso
UIL可以算是老牌最火的图片加载库了,该作者在项目中说明已经停止了对该项目的维护。这就意味着以后任何的 bug 都不会修复,任何的新特性都不会再继续开发,所以毫无疑问 UIL 不推荐在项目中使用了。
图片框架的缓存

MemorycCache图片内存缓存。默认使用了 LRU 算法。
DiskCache图片磁盘缓存,默认使用LruDiskCache算法,在缓存满时删除最近最少使用的图片
glide源码
一般看源码先看他的使用方法,通过使用的方法看对应的代码。
Glide.with(MainActivity.this).load(url).into(headerImage);

with方法把context传进去,返回GlideBuilder的对应,在这里做一些初始化操作,比如构建线程池(包括sourceExecutor ,diskCacheExecutor ),缓存大小和缓存器,默认的连接监听工厂(connectivityMonitorFactory ),Engine对象和RequestManagerRetriever 对象等等。
load(URL)方法没做什么事情,主要就是把URL传进去,获取RequestBuilder对象。
主要的操作都在into方法里(在这里会取lru缓存还是本地缓存,还是没有,告诉RequestBuilder)。RequestBuilder的into方法里开启了线程池进行加载资源。网络请求是通过url打开连接,返回一个HttpURLConnection对象,进行网络请求的。加载得资源后转换到主线程并进行回调(沒注意看這個)。设置给imageview
glide为什么有lru还会内存溢出。因为直接把整个大图片的整个内存加载进去了。对于大图可以下载下来,asdrawale来加载,drawable更省内存,Drawable应该不属于常驻内存的对象,不然的话,不可能不会出现OOM的~~
Glide内部处理了网络图片加载的错位或者闪烁(tag)。
public Request getRequest() {

    //本质还是getTag
    Object tag = getTag();
    Request request = null;
    if (tag != null) {
        if (tag instanceof Request) {
            request = (Request) tag;
        } else {
            throw new IllegalArgumentException("You must not call setTag() on a view 

Glide is targeting");

        }
    }
    return request;
}
@Override
public void setRequest(Request request) {
    //本质是setTag
    setTag(request);
}

对图片加载用到了LruCache(最少最近使用)算法
他会把内存控制在一定大小内,超过最大值时会自动回收,这个最大值可以自己定,一个太小的缓存空间,有可能造成图片频繁地被释放和重新加载,这并没有好处。而一个太大的缓存空间,则有可能还是会引起 java.lang.OutOfMemory 的异常。一般使用最大可用内存的1/8作为缓存的大小。LruCache的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap (频繁增删、不需要排序)中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。

public class BitmapCache implements ImageCache {

private LruCache<String, Bitmap> mCache;  

public BitmapCache() {  
    int maxSize = 10 * 1024 * 1024;  
    mCache = new LruCache<String, Bitmap>(maxSize) {  
        @Override  
        protected int sizeOf(String key, Bitmap bitmap) {  
            return bitmap.getRowBytes() * bitmap.getHeight();  
        }  
    };  
}  

@Override  
public Bitmap getBitmap(String url) {  
    return mCache.get(url);  
}  

@Override  
public void putBitmap(String url, Bitmap bitmap) {  
    mCache.put(url, bitmap);  
}  

}
网络框架比较

常用网络库使用方法

public interface netApi {

@GET("repos/{owner}/{repo}/contributors")
Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo);

}

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    volleyStringRequest();
    volleyJsonRequest();
    retrofitHttpRequest();

    try {
        okhttpAsyGet();
        OkHttpSyncGet();

    } catch (Exception e) {
        e.printStackTrace();
    }
}
//volley第一步
RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
private void volleyStringRequest() {

    //volley第二步
    StringRequest stringRequest = new StringRequest("http://www.baidu.com",
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.d("TAG", response);
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.e("TAG", error.getMessage(), error);
        }
    });
    //volley第三步
    mQueue.add(stringRequest);
}

private void volleyJsonRequest() {
    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.sina.com/sports/101010100.html", null,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    Log.d("TAG", response.toString());
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.e("TAG", error.getMessage(), error);
        }
    });
    mQueue.add(jsonObjectRequest);
}

//okhttp第一步
private final OkHttpClient client = new OkHttpClient();

public void okhttpAsyGet() throws Exception {
    //okhttp第二步
    Request request = new Request.Builder()
            .url("http://publicobject.com/helloworld.txt")
            .build();

    //okhttp第三步
    okhttp3.Response response = client.newCall(request).execute();

    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    Headers responseHeaders = response.headers();
    for (int i = 0; i < responseHeaders.size(); i++) {
        System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
    }

    System.out.println(response.body().string());
}

public void OkHttpSyncGet() throws Exception {
    Request request = new Request.Builder()
            .url("http://publicobject.com/helloworld.txt")
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
        @Override
        public void onResponse(Call call, okhttp3.Response response) throws IOException {
            if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

            Headers responseHeaders = response.headers();
            for (int i = 0, size = responseHeaders.size(); i < size; i++) {
                System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
            }
            System.out.println(response.body().string());//只能获取一次,可以用string保存
        }
    });
}

public void retrofitHttpRequest() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .build();

    netApi repo = retrofit.create(netApi.class);

    retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("userName", "path");
    call.enqueue(new retrofit2.Callback<ResponseBody>() {
        @Override
        public void onResponse(retrofit2.Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
            //response
        }

        @Override
        public void onFailure(retrofit2.Call<ResponseBody> call, Throwable t) {

        }
    });
}

}

String post(String url, String json) throws IOException {

 RequestBody formBody = new FormEncodingBuilder()
.add("platform", "android")
.add("name", "bug")
.add("subject", "XXXXXXXXXXXXXXX")
.build();

  Request request = new Request.Builder()
  .url(url)
  .post(body)
  .build();

  Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
    return response.body().string();
} else {
    throw new IOException("Unexpected code " + response);
}

}

HttpURLConnection和HttpClient。这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。
在Android 2.2版本之前,HttpClient是最好的选择。因为HttpURLConnection有一些bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能。
在Android 2.3版本及以后,HttpClientHttpURLConnection则是最佳的选择,HttpURLConnection的API提供的比较简单,可以更加容易地去使用和扩展它。而且速度快、节省电量。
OkHttp 处理了很多网络问题:自动重连、会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址,当第一个IP连接失败的时候,OkHttp会自动尝试下一个IP。OkHttp还处理了代理服务器问题和SSL握手失败问题。
volley的设计目标就是非常适合数据量小,通信量大的客户端,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。Volley停止了更新,而OkHttp得到了官方的认可,并在不断优化。因此我最终替换为了OkHttp
volley原理
主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。

为什么说Volley适合数据量小,通信频繁的网络操作
volley中为了提高请求处理的速度,采用了ByteArrayPool进行内存中的数据存储的,如果下载大量的数据,这个存储空间就会溢出,所以不适合大量的数据,但是由于他的这个存储空间是内存中分配的,当存储的时候优是从ByteArrayPool中取出一块已经分配的内存区域, 不必每次存数据都要进行内存分配,而是先查找缓冲池中有无适合的内存区域,如果有,直接拿来用,从而减少内存分配的次数 ,所以他比较适合据量小,通信量大网络数据交互情况。

Retrofit原理

Retrofit 2.0底层依赖OkHttp实现,也就是说Retrofit本质上就是对OkHttp的更进一步封装,还支持Rxjava。Retrofit和其它Http库最大区别在于通过大范围使用注解简化Http请求。Retrofit使用注解来描述HTTP请求(请求方式、请求参数)。
内部也是调用okhttp的网络请求方式
Retrofit主要是在create方法中采用动态代理模式实现接口方法,这个过程构建了一个ServiceMethod对象,根据方法注解获取请求方式,参数类型和参数注解拼接请求的链接,当一切都准备好之后会把数据添加到Retrofit的RequestBuilder中。然后当我们主动发起网络请求的时候会调用okhttp发起网络请求,okhttp的配置包括请求方式,URL等在Retrofit的RequestBuilder的build()方法中实现,并发起真正的网络请求。
网络请求的工作本质上是OkHttp完成,而 Retrofit 仅负责网络请求接口的封装。
自己写网络请求框架

volley,okHttp等,这类优秀的框架其底层的实现大部分也是基于系统的 线程池 和 httpClient 或 HttpUrlConnection的网络请求类框架,Android中是不能在主线程中(又称UI线程)进行网络操作的,那么框架中必不可少地要使用到子线程,可以使用简单的 Thread + Runnable + Handler或者重量级点的AsyncTask。
处理好并发操作,一个应用中往往要进行多线程操作,而Java虚拟机对于一个线程的内存分配大约在1M左右,具体多少要看它执行的任务而定。所有就要使用线程池,例如newFixdThreadPool 可以控制并发数量,且在整个APP运行过程中有几个常驻线程在,避免使用时反复地new,退出时再销毁,而 newCacheThreadPool 则会在任务完成后,自动回收线程,它会帮你释放线程内存,也就不会有常驻线程。
还要注意使接口分离,降低耦合,而且接口能够我们带来很大的方便。
okhttp源码
在构造器中利用建造者模式来构建 OkHttpClient 的对象,OkHttpClient 的构造器中主要是默认的配置。在newCall(Request request) (request是请求参数和URL)的时候,其实是里面创建了一个 RealCall 的对象,里面有execute() 方法。里面有getResponseWithInterceptorChain() ,添加了很多Interceptor,并返回 Response 对象的。
Interceptor 是 OkHttp 最核心的一个东西,它负责拦截请求进行一些额外的处理(例如 设置cookie),Interceptor有负责失败重试、重定向的(RetryAndFollowlnterceptor)、读取缓存、更新缓存的(Cachelnterceptor)、负责和服务器建立连接的(Connectlnterceptor)、负责向服务器发送请求数据(Bridgelnterceptor)、从服务器读取响应数据的(Networklnterceptor)。
每一个功能都只是一个 Interceptor,它们再连接成一个 Interceptor.Chain,环环相扣,最终完成一次网络请求。
根据响应码判断是否是重定向(3开头3xx)。RetryAndFollowUpInterceptor:如果不需要重定向,那么 followUp 为空,会释放资源,返回 response。 若为重定向就销毁旧连接,创建新连接,将重定向操作得到的新请求设置给 request。
同步请求通过Call.execute()直接返回当前的Response,而异步请求会把当前的请求Call.enqueue添加(AsyncCall)到请求队列中,并通过回调(Callback)的方式来获取最后结果。
@Override public Response execute() throws IOException {

synchronized (this) {
  if (executed) throw new IllegalStateException("Already Executed");
  executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
  client.dispatcher().executed(this);
  Response result = getResponseWithInterceptorChain();
  if (result == null) throw new IOException("Canceled");
  return result;
} catch (IOException e) {
  e = timeoutExit(e);
  eventListener.callFailed(this, e);
  throw e;
} finally {
  client.dispatcher().finished(this);
}

}
Response getResponseWithInterceptorChain() throws IOException {

// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
  interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
    originalRequest, this, eventListener, client.connectTimeoutMillis(),
    client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);

}
复用机制:Http中添加了一种KeepAlive机制,当数据传输完毕后仍然保持连接,等待下一次请求时直接复用该连接。
ConnectionPool :取到的话复用,没有取到放到连接池中。
ConnectionPool关键代码:
OkHttp 默认最大并发数 64,单域名最大并发 5,为了实现请求的并发,Dispatcher 配置了一个线程池,
//线程池,核心线程数为0,最大线程数为最大整数,线程空闲存活时间60s,//SynchronousQueue 直接提交策略
private static final Executor executor = new ThreadPoolExecutor(0,

  Integer.MAX_VALUE , 60L , TimeUnit.SECONDS,
  new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));

//空闲连接的最大连接数
private final int maxIdleConnections;
//保持连接的周期
private final long keepAliveDurationNs;
//双端队列,存放具体的连接
private final Deque connections = new ArrayDeque<>();
//用于记录连接失败的route
final RouteDatabase routeDatabase = new RouteDatabase();

//构造函数//从这里可以知道,空闲连接的最大连接数为5,保持连接的周期是5分钟

public ConnectionPool() {

this(5, 5, TimeUnit.MINUTES);

}

public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {

this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
  throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}

}
同步
Dispatcher会在同步执行任务队列中记录当前被执行过得任务Call,同时在当前线程中去执行Call的getResponseWithInterceptorChain()方法,直接获取当前的返回数据Response;
异步
Dispatcher内部实现了懒加载无边界限制的线程池方式,同时该线程池采用了SynchronousQueue这种阻塞队列。异步执行是通过Call.enqueue(Callback responseCallback)来执行,在Dispatcher中添加一个封装了Callback的Call的匿名内部类Runnable来执行当前的Call。这里一定要注意的地方这个AsyncCall是Call的匿名内部类。AsyncCall的execute方法仍然会回调到Call的getResponseWithInterceptorChain方法来完成请求,同时将返回数据或者状态通过Callback来完成。
使用缓存,有多种不同的缓存方式

自定义一个拦截器

class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {

Request request = chain.request();

long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
    request.url(), chain.connection(), request.headers()));

Response response = chain.proceed(request);

long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
    response.request().url(), (t2 - t1) / 1e6d, response.headers()));

return response;

}
}

其他库
LeakCanary原理解析

lifecycleCallbacks监听Activity的onDestroy方法,正常情况下activity在onDestroy后需要立即被回收,onActivityDestroyed方法最终会调用RefWatcher.watch方法:
通过将Activity包装到WeakReference(弱引用)中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收
如果Activity没有被回收,调用GcTigger.runGc方法运行GC,如果这时候还没有被回收,那就说明Activity可能已经泄露。
Evenbus是做什么的?和RXjava有什么区别?

采用EventBus作为事件管理,可以跨线程,跨组件通信。 以前我们做组件间的消息分发更新,一般会采用观察者模式,或者接口数据回调的相关方式。但是这样的做法虽然可以解决问题,但是组件之间的耦合比较严重,而且代码也不易阅读和相关维护。为了解决这样的问题我们可以使用消息总线EventBus框架。
EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
RxJava要比EventBus的应用更广泛,RxJava里面几乎可以做任何事情。做异步、网络的数据处理,写出来的代码比较优雅。
黏性事件
简单讲,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似,但是它只能收到最新的一次消息,比如说在未订阅之前已经发送了多条黏性消息了,然后再订阅只能收到最近的一条消息。

EventBus源码
register(this)就是去当前类,遍历所有的方法,找到onEvent开头的然后进行存储(把匹配的方法最终保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList ),eventType是我们方法参数的Class,Subscription中则保存着subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了执行改方法所需的一切),然后post的时候,根据post传入的参数,去找到匹配的方法,反射调用。数据传递是通过handler。
原文地址https://www.cnblogs.com/sixrain/p/11369003.html

相关文章
|
11天前
|
IDE Android开发 iOS开发
深入解析Android与iOS的系统架构及开发环境差异
本文旨在探讨Android和iOS两大主流移动操作系统在系统架构、开发环境和用户体验方面的显著差异。通过对比分析,我们将揭示这两种系统在设计理念、技术实现以及市场策略上的不同路径,帮助开发者更好地理解其特点,从而做出更合适的开发决策。
38 2
|
14天前
|
编解码 开发工具 UED
QT Widgets模块源码解析与实践
【9月更文挑战第20天】Qt Widgets 模块是 Qt 开发中至关重要的部分,提供了丰富的 GUI 组件,如按钮、文本框等,并支持布局管理、事件处理和窗口管理。这些组件基于信号与槽机制,实现灵活交互。通过对源码的解析及实践应用,可深入了解其类结构、布局管理和事件处理机制,掌握创建复杂 UI 界面的方法,提升开发效率和用户体验。
65 12
|
7天前
|
XML JSON 网络协议
超级好用的C++实用库之字节流解析器
超级好用的C++实用库之字节流解析器
11 3
|
13天前
|
存储 开发框架 数据可视化
深入解析Android应用开发中的四大核心组件
本文将探讨Android开发中的四大核心组件——Activity、Service、BroadcastReceiver和ContentProvider。我们将深入了解每个组件的定义、作用、使用方法及它们之间的交互方式,以帮助开发者更好地理解和应用这些组件,提升Android应用开发的能力和效率。
|
16天前
|
缓存 Android开发 开发者
Android RecycleView 深度解析与面试题梳理
本文详细介绍了Android开发中高效且功能强大的`RecyclerView`,包括其架构概览、工作流程及滑动优化机制,并解析了常见的面试题。通过理解`RecyclerView`的核心组件及其优化技巧,帮助开发者提升应用性能并应对技术面试。
40 8
|
16天前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
39 8
|
7天前
|
缓存 网络协议 分布式数据库
超级好用的C++实用库之DNS解析
超级好用的C++实用库之DNS解析
19 0
|
10天前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
17天前
|
搜索推荐 Linux Android开发
深入解析安卓与iOS系统架构设计差异
本文旨在探讨Android和iOS两大主流操作系统在架构设计上的根本差异。通过分析两种系统的设计理念、核心组件以及实际应用表现,揭示它们如何反映不同的开发哲学和用户体验策略。我们将从系统层级结构、内存管理机制、用户界面设计三个方面入手,逐一对比Android的开放性和灵活性如何与其对手iOS的封闭性和一致性相互辉映。
|
2月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件

推荐镜像

更多
下一篇
无影云桌面