问题提出:如何优雅地分离出应用程序的状态、用户交互和数据表现?如何通过框架体现工程的高性能、高灵活性、高响应性?

MVC定义:model、view、controller三者的有机组合,分别表示:模型、视图和控制。

这个模式认为:程序不论简单还是复杂,从结构上来看,都可以分为三个层次。

下图展示了MVC程序框架在Android应用程序中的使用,以及各个层次使用哪些组件担当:

1)最上面一层,是直接面向于最终用户的“视图层”(View)。它是提供给用户的操作界面,是程序的外壳。界面就是各种UI组件(XML布局或者Java自定义控件对象)。只负责展示数据,同时接收控制器传过来的结果。

2)最底下的一层,是核心的“数据层”(Model),也就是程序需要操作的数据或信息(系统中的业务逻辑部分)。通常是数据库SQLite、网络请求的JSON、本地XML或者Java对象数据。它代表了一些实体类,用来描述业务逻辑怎么组合,同时也为数据定义业务规则;

3)中间的一层,就是“控制层”(controller),负责根据用户从“视图层”输入的指令,选取“数据层”中的数据,然后对其进行相应的操作,产生最终的结果(可以分派用户的请求并选择恰当的视图以用于显示,同时也可以解释用户的数据并将它们映射为模型层可执行的操作)。控制器是与应用程序相关联的动作集合,负责处理待响应的请求。通过界面响应用户输入,通过模型层处理数据,最后返回结果给界面。控制器扮演着模型和界面的粘合剂角色。

抽象一点,上述模型可以抽象为下述结果:

展示了从Activity接收用于点击输入,控制器响应用户输入并发起Internet请求数据(网络请求),响应结果经过模型层转换,最后控制器取到模型层数据并通知界面进行刷新。

更加简化的MVC模型如下:

或者是下述结果:

在Android中,View和Model也是有关联的,从而抽象为下述图:

上述的三个层次是紧密联系,且是相互独立的,每一层的变化不影响其他层次。每一层都对外提供接口,供上面一层调用。软件因而实现模块化,修改外观或者变更数据都不用修改其他层次,大大方便了维护和升级。

一个逻辑模型可以对于多种视图模型,比如一批统计数据可以分别用柱状图、饼状图来显示结果;一种视图模型也可以对应多种逻辑模型。使用MVC的目的就是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式,而Controller存在的目的则是确保M改变,V应该同步更新。

使用计算器这个例子作为实例进行分析:外部的按钮和上面的显示条,就是“视图层”;需要运算的数字就是“数据层”;执行加减乘除的内部运算步骤就是“控制层”。每一层执行不同的功能,整个程序的结构很清晰。

MVC的好处在于:从用户的角度出发,用户可以根据自己的需求,选择自己合适浏览数据的方式。比如说,对于一篇在线文档,用户可以选择以HTML网页的方式阅读,也可以选择以pdf的方式阅读。从开发者的角度来看,MVC把应用程序的逻辑层与界面完全分开。

Android应用程序中,MVC框架是如何实现的?都充当什么角色?

1. View接受用户的交互请求;

2. View将请求转交给Controller;

3. Controller(用户做的动作比如:update数据,删除指定名字的学生等等)操作Model进行数据更新(根据用户指示,执行底层的数据动作等等);

4. 数据更新之后,Model通知View数据变化;

5. View显示更新之后的数据;

M层适合做一些业务逻辑处理,比如数据库存取操作、网络操作、复杂的算法等耗时操作;

V层显示数据部分,XML布局可以视为是V层,显示Model层的数据结果;

C层适合使用Activity担当,Android中Activity用于处理用户交互问题(发起业务请求),读取用户输入(等待业务处理结果),响应用户点击等等事件。

MainActivity.java代码如下:

package com.demo.controller;

	import static com.demo.controller.ControllerProtocol.C_DATA;
import static com.demo.controller.ControllerProtocol.C_QUIT;
import static com.demo.controller.ControllerProtocol.C_UPDATE_FINISHED;
import static com.demo.controller.ControllerProtocol.C_UPDATE_STARTED;
import static com.demo.controller.ControllerProtocol.V_REQUEST_DATA;
import static com.demo.controller.ControllerProtocol.V_REQUEST_QUIT;
import static com.demo.controller.ControllerProtocol.V_REQUEST_UPDATE; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView; import com.demo.R;
import com.demo.model.Model;
import com.demo.model.ModelData;
import com.demo.utils.LogUtil; public class MvcActivity extends Activity implements Callback,
View.OnClickListener {
private static final String TAG = MvcActivity.class.getSimpleName(); private Controller controller; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); findViewById(R.id.update).setOnClickListener(this);
findViewById(R.id.quit).setOnClickListener(this); // 初始化层次结构中的交互
controller = new Controller(new Model());
// 使用MvcActivity所在的主线程初始化outBox中的Handler
controller.addOutboxHandler(new Handler(MvcActivity.this));
// Controller层发送请求数据的Message
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_DATA);
} @Override
public void onClick(View v) {
// 接收用户输入
switch (v.getId()) {
case R.id.update:
LogUtil.d(TAG, "User click input: R.id.update");
// 接收用户输入并得到Controller层响应,Controller层发送请求数据的Message
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_UPDATE);
break;
case R.id.quit:
LogUtil.d(TAG, "User click input: R.id.quit");
// 接收用户输入并得到Controller层响应,Controller层发送请求数据的Message
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_QUIT);
break;
}
} @Override
public boolean handleMessage(Message msg) {
Log.d(TAG, "Received message: " + msg); switch (msg.what) {
case C_QUIT:
// 接收Controller层输入,准备退出应用
onQuit();
return true;
case C_DATA:
// 将数据显示到View层
onData((ModelData) msg.obj);
return true;
case C_UPDATE_STARTED:
// (状态)正在后台请求数据
onUpdateStarted();
return true;
case C_UPDATE_FINISHED:
// (状态)后台请求数据结束,准备显示
onUpdateFinished();
return true;
}
return false;
} @Override
protected void onDestroy() {
try {
controller.dispose();
} catch (Throwable t) {
LogUtil.d(TAG, "Failed to destroy the controller");
} super.onDestroy();
} /**
* <功能描述> Controller层做出update响应,更新View层
*
* @return void [返回类型说明]
*/
private void onQuit() {
LogUtil.d(TAG, "Activity quitting");
finish();
} /**
* <功能描述> 从Controller层传递过来的Message,表明获取到返回的数据,更新View
*
* @param data [参数说明]
* @return void [返回类型说明]
*/
private void onData(ModelData data) {
LogUtil.d(TAG, "onData::running...");
TextView dataView = (TextView) findViewById(R.id.data_view);
dataView.setText("The answer is " + data.getAnswer());
} /**
* <功能描述> 开始Update数据显示ProgressBar
*
* @return void [返回类型说明]
*/
private void onUpdateStarted() {
LogUtil.d(TAG, "onUpdateStarted::running...");
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.VISIBLE);
} /**
* <功能描述> Update数据过程结束后,ProgressBar消失,显示数据
*
* @return void [返回类型说明]
*/
private void onUpdateFinished() {
LogUtil.d(TAG, "onUpdateFinished::running...");
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress_bar);
progressBar.setVisibility(View.GONE);
controller.getInboxHandler().sendEmptyMessage(V_REQUEST_DATA);
} }

Controller.java代码如下:

package com.demo.controller;

	import static com.demo.controller.ControllerProtocol.C_QUIT;

	import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message; import com.demo.model.Model;
import com.demo.utils.LogUtil; import java.util.ArrayList;
import java.util.List; public class Controller {
private static final String TAG = Controller.class.getSimpleName(); private final Model model; // 使用HandlerThread、Handler、Message机制实现不同层的消息通信
// inBox:用于接收从View层发送的Message
private final HandlerThread inboxHandlerThread;
private final Handler inboxHandler;
// outBox:用于Controller给View发送Message
private final List<Handler> outboxHandlers = new ArrayList<Handler>(); // 将消息处理委托给ControllerState,不在Controller中处理Message
private ControllerState state; /**
* <默认构造函数> Controller层初始化
*/
public Controller(Model model) {
this.model = model; // Handler就存在于该Thread子线程(也就是工作线程)中
inboxHandlerThread = new HandlerThread("Controller Inbox");
inboxHandlerThread.start();
// HandlerThread的getLooper()获取到Handler实例
inboxHandler = new Handler(inboxHandlerThread.getLooper()) { @Override
public void handleMessage(Message msg) {
Controller.this.handleMessage(msg);
}
}; // 初始化ControllerState,准备接受Message
this.state = new ReadyState(this);
} // 处理从View层传递过来的Message
private void handleMessage(Message msg) {
LogUtil.d(TAG, "handleMessage::Received message...msg.what=" + msg.what); // 把消息处理委托给它的 ControllerState
if (!state.handleMessage(msg)) {
LogUtil.d(TAG, "Unknown message: " + msg);
}
} /**
* <功能描述> inboxHandlerThread退出
*
* @return void [返回类型说明]
*/
public final void dispose() {
inboxHandlerThread.getLooper().quit();
} /**
* <功能描述>获取inBox的Handler实例,并返回该实例
*
* @return [参数说明]
* @return Handler [返回类型说明]
*/
public final Handler getInboxHandler() {
return inboxHandler;
} /**
* <功能描述> 增加Handler到outBox中;outBox用于Controller传送Message到View层
*
* @param handler [参数说明]
* @return void [返回类型说明]
*/
public final void addOutboxHandler(Handler handler) {
outboxHandlers.add(handler);
} /**
* <功能描述> 从outBox移除Handler
*
* @param handler [参数说明]
* @return void [返回类型说明]
*/
public final void removeOutboxHandler(Handler handler) {
outboxHandlers.remove(handler);
} /**
* <功能描述> 更新Controller的状态,ReadyState和UpdatingState之间的切换
*
* @param newState [参数说明]
* @return void [返回类型说明]
*/
final void changeState(ControllerState newState) {
LogUtil.d(TAG,
String.format("Changing state from %s to %s", state, newState));
state = newState;
} /**
* <功能描述> Controller层向View层发送Message
*
* @param what
* @param arg1
* @param arg2
* @param obj [参数说明]
* @return void [返回类型说明]
*/
final void notifyOutboxHandlers(int what, int arg1, int arg2, Object obj) {
LogUtil.d(TAG, "notifyOutboxHandlers::running...what=" + what); if (outboxHandlers.isEmpty()) {
LogUtil.d(TAG, String.format(
"No outbox handler to handle outgoing message (%d)", what));
} else {
for (Handler handler : outboxHandlers) {
Message msg = Message.obtain(handler, what, arg1, arg2, obj);
msg.sendToTarget();
}
}
} /**
* <功能描述> 获取到Model层
*
* @return [参数说明]
* @return Model [返回类型说明]
*/
final Model getModel() {
return model;
} /**
* <功能描述> 接收用户输入,准备退出应用
*
* @return void [返回类型说明]
*/
final void quit() {
notifyOutboxHandlers(C_QUIT, 0, 0, null);
}
}

ControllerState.java代码如下:

	package com.demo.controller;

	import android.os.Message;

	/**
* <功能描述> 将Controller层的消息处理逻辑转交给ControllerState处理;ControllerState有两个实现类:
* ReadyState和UpdatingState
*
* @author Administrator
*/
public interface ControllerState {
boolean handleMessage(Message msg);
}

ReadyState.java代码如下:

package com.demo.controller;

	import android.os.Message;

	import com.demo.utils.LogUtil;

	import static com.demo.controller.ControllerProtocol.*;

	/**
* <功能描述> 数据更新的状态之一:准备阶段,最前面的状态
*
* @author Administrator
*/
final class ReadyState implements ControllerState {
private static final String TAG = ReadyState.class.getSimpleName(); private final Controller controller; public ReadyState(Controller controller) {
this.controller = controller;
} @Override
public final boolean handleMessage(Message msg) {
LogUtil.d(TAG, "handleMessage::running...msg.what=" + msg.what); switch (msg.what) {
case V_REQUEST_QUIT:
// 从View层获取到的Quite消息
onRequestQuit();
return true;
case V_REQUEST_UPDATE:
// 从View层获取到的Update的消息
onRequestUpdate();
return true;
case V_REQUEST_DATA:
// 从View层获取到的更新数据的消息
onRequestData();
return true;
}
return false;
} /**
* <功能描述> 请求数据
*
* @return void [返回类型说明]
*/
private void onRequestData() {
LogUtil.d(TAG, "onRequestData::running...");
controller.notifyOutboxHandlers(C_DATA, 0, 0, controller.getModel()
.getData());
} /**
* <功能描述> 请求更新数据,并更改Controller的状态
*
* @return void [返回类型说明]
*/
private void onRequestUpdate() {
LogUtil.d(TAG, "onRequestUpdate::running...");
// 状态返回给Controller层,并将状态改变为UpdatingState
controller.changeState(new UpdatingState(controller));
} private void onRequestQuit() {
LogUtil.d(TAG, "onRequestQuit::running...");
controller.quit();
}
}

UpdatingState.java代码如下:

package com.demo.controller;

	import static com.demo.controller.ControllerProtocol.C_UPDATE_FINISHED;
import static com.demo.controller.ControllerProtocol.C_UPDATE_STARTED;
import static com.demo.controller.ControllerProtocol.V_REQUEST_QUIT; import android.os.Message; import com.demo.utils.LogUtil; /**
* <功能描述> 数据更新的状态之一:正在更新阶段
*
* @author Administrator
*/
final class UpdatingState implements ControllerState {
private static final String TAG = UpdatingState.class.getSimpleName(); private final Controller controller;
private final Thread updateThread; public UpdatingState(Controller controller) {
this.controller = controller; // 新建一个Thread工作线程
updateThread = new Thread("Model Update") { @Override
public void run() {
Controller controller = UpdatingState.this.controller;
try {
// 在工作线程中执行数据请求(抽象为相对耗时的操作),可为其他:数据库操作
controller.getModel().updateData();
} catch (Throwable t) {
LogUtil.d(TAG, "Error in the update thread");
} finally {
// controller.getModel().updateData()执行完后,反馈数据给Controller层
notifyControllerOfCompletion();
}
}
};
updateThread.start();
// Controller层向View层发送Message,开始更新数据
controller.notifyOutboxHandlers(C_UPDATE_STARTED, 0, 0, null);
} @Override
public boolean handleMessage(Message msg) {
LogUtil.d(TAG, "handleMessage::msg.what=" + msg.what); switch (msg.what) {
case V_REQUEST_QUIT:
onRequestQuit();
return true;
}
return false;
} /**
* <功能描述> 数据获取到后,最终会反馈给Controller层
*
* @return void [返回类型说明]
*/
private void notifyControllerOfCompletion() {
LogUtil.d(TAG, "notifyControllerOfCompletion::starting..."); controller.getInboxHandler().post(new Runnable() { @Override
public void run() {
// 将状态改变为ReadyState
controller.changeState(new ReadyState(controller));
// 并通知结束更新
controller.notifyOutboxHandlers(C_UPDATE_FINISHED, 0, 0, null);
}
});
} private void onRequestQuit() {
LogUtil.d(TAG, "onRequestQuit::starting..."); updateThread.interrupt();
controller.quit();
}
}

ControllerProtocal.java代码如下:

package com.demo.controller;

	public interface ControllerProtocol {
// 从View层传递的Message
int V_REQUEST_QUIT = 101;
int V_REQUEST_UPDATE = 102;
int V_REQUEST_DATA = 103; // 从Controller层传递的Message
int C_QUIT = 201;
int C_UPDATE_STARTED = 202;
int C_UPDATE_FINISHED = 203;
int C_DATA = 204; // obj = (ModelData) data
}

Model.java代码如下:

package com.demo.model;

	import java.util.ArrayList;
import java.util.List;
import java.util.Random; import android.os.SystemClock; import com.demo.utils.LogUtil; /**
* <功能描述> 专指Model层;使用ThreadSafe指示是线程安全的
*
* @author Administrator
*/
public class Model {
private static final String TAG = Model.class.getSimpleName(); public interface Listener {
void onModelStateUpdated(Model model);
} // Model模型中包含有ModelData
private ModelData data = new ModelData(0); private final List<Listener> listeners = new ArrayList<Listener>(); public Model() {
LogUtil.d(TAG, "Model constructer..");
} public final ModelData getData() {
synchronized (this) {
return data;
}
} /**
* <功能描述> 更新数据
*
* @return void [返回类型说明]
*/
public final void updateData() {
// 可能是请求服务器数据,执行繁重的计算
SystemClock.sleep(3000);
ModelData newData = new ModelData(new Random().nextInt(10) + 1); synchronized (this) {
data = newData;
} synchronized (listeners) {
for (Listener listener : listeners) {
listener.onModelStateUpdated(this);
}
}
} /**
* <功能描述>增加监听器
*
* @param listener [参数说明]
* @return void [返回类型说明]
*/
public final void addListener(Listener listener) {
synchronized (listeners) {
listeners.add(listener);
}
} /**
* <功能描述>移除监听器
*
* @param listener [参数说明]
* @return void [返回类型说明]
*/
public final void removeListener(Listener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
}

ModelData.java代码如下:

package com.demo.model;

	import com.demo.utils.LogUtil;

	import java.io.Serializable;

	/**
* <功能描述> Model层中的数据,指示数据层的状态;Immutable指示是不变的
*
* @author Administrator
*/
public final class ModelData implements Serializable {
private static final String TAG = ModelData.class.getSimpleName();
private static final long serialVersionUID = 1L; private final int answer; public ModelData(int answer) {
LogUtil.d(TAG, "ModelData::answer=" + answer);
this.answer = answer;
} public final int getAnswer() {
LogUtil.d(TAG, "getAnswer::answer=" + answer); return answer;
}
}

View代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.demo.controller.MvcActivity" > <TextView
android:id="@+id/data_view"
style="@android:style/TextAppearance.Large"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:gravity="center"
android:text="" /> <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2.0" > <Button
android:id="@+id/update"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:text="Update" /> <Button
android:id="@+id/quit"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:text="Quit" />
</LinearLayout> <ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:indeterminate="true"
android:visibility="gone" /> </LinearLayout>

更加详细的MPV模型解析:

Android应用程序MVC框架实例分析的更多相关文章

  1. PHP: 手把手编写自己的 MVC 框架实例教程

    1 什么是MVC MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller ...

  2. Spring MVC框架实例

    Spring  MVC 背景介绍 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC 架构,能够选择是使用内置的 Spring Web 框架还是 ...

  3. MVC模式在Java Web应用程序中的实例分析

    MVC在软件架构中是一种比较重要的架构思想,已经被广泛的应用在实际的java web项目开发中,我们所要了解和掌握的是mvc的架构思想和使用mvc模式来分析和解决问题的方法.当然相同或不同的项目都有各 ...

  4. Android应用程序启动过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程 ...

  5. Android 应用程序启动过程源代码分析

    本文转自:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程序的Activity的启动过程.在And ...

  6. php实现最简单的MVC框架实例教程

    本文以一个实例的形式讲述了PHP实现MVC框架的过程,比较浅显易懂.现分享给大家供大家参考之用.具体分析如下: 首先,在学习一个框架之前,基本上我们都需要知道什么是mvc,即model-view-co ...

  7. ThinkAndroid是简洁,快速的进行Android应用程序的框架

    ThinkAndroid简介ThinkAndroid是一个免费的开源的.简易的.遵循Apache2开源协议发布的Android开发框架,其开发宗旨是简单.快速的进行Android应用程序的开发,包含A ...

  8. Android Touch事件原理加实例分析

    Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. ...

  9. Android应用程序安装过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6766010 Android系统在启动的过程中, ...

随机推荐

  1. CF209C Trails and Glades

    题目链接 题意 有一个\(n\)个点\(m\)条边的无向图(可能有重边和自环)(不一定联通).问最少添加多少条边,使得可以从\(1\)号点出发,沿着每条边走一遍之后回到\(1\)号点. 思路 其实就是 ...

  2. PR视频剪辑

    PR视频剪辑 新手问题1: 将素材导入到Adobe Premiere Pro CC后为什么无法拖入到时间轴上 解决办法:没有建立有序列所致,CC不会一开始就让你新建序列,图中间处写的好清楚“无序列”. ...

  3. python去除html标签的几种方法

    import re from bs4 import BeautifulSoup from lxml import etree html = '<p>你好</p><br/& ...

  4. Hadoop生态圈-zookeeper完全分布式部署

    Hadoop生态圈-zookeeper完全分布式部署 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客部署是建立在Hadoop高可用基础之上的,关于Hadoop高可用部署请参 ...

  5. Slow ReadProcessor&amp;Error Slow BlockReceiver错误日志分析(转)

    1.总结 "Slow ReadProcessor" 和"Slow BlockReceiver"往往是因为集群负载比较高或者某些节点不健康导致的,本文主要是帮助你 ...

  6. LFYZ-OJ ID: 1021 邮票问题

    邮票问题 Problem Description 设有已知面额的邮票m种,每种有n张,用总数不超过n张的邮票,能从面额1开始,最多连续组成多少面额.(1≤m≤100,1≤n≤100,1≤邮票面额≤25 ...

  7. 第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)

    一. 什么是事务 我们通俗的理解事务就是一系列操作要么全部成功.要么全部失败(不可能存在部分成功,部分失败的情况). 举一个事务在我们日常生活中的经典例子:两张银行卡(甲.乙),甲向乙转钱,整个过程需 ...

  8. 深入理解 LINQ to SQL 生成的 SQL 语句

    Ø  简介 在 C# 中与数据交互最常用的语句就是 LINQ 了,而 LINQ to SQL 是最直接与数据库打交道的语句,它可以根据 LINQ 语法生成对应的 SQL 语句,在数据库中去执行.本文主 ...

  9. [转]C语言的int最值问题,以及原码反码及补码

    以2字节为例来说: 对于无符号的数值(原码反码及补码都一样),最大值为1111  1111  1111  1111=65535 最小值为0000  0000  0000  0000=0 对于有符号的来 ...

  10. JavaWeb - Apache与Tomcat有什么关系和区别

    总结: 1- apache是web服务器,侧重于http server: tomcat是应用(java)服务器,侧重于servlet引擎 2-合作过程详解,请看:JavaWeb - apache和to ...