作者:永恆の_☆ 地址:http://blog.csdn.net/chenghui0317/article/details/9413401
一、什麼是JavaEE MVC
JavaEE MVC 字面的意思是模型(M),視圖(V),控制器(C)這三塊。MVC是一種面向對象的架構模式,它的基本思想是把程序界面和業務邏輯分開,這樣便於軟件的後期維護,同時也方便了開發時期的分工和管理。 MVC這種開發模式已經 被直接或者間接的應用於各個web開發中,不管是JavaEE還是.NET 都被廣泛運用。
二、三層開發模式和MVC 有什麼區別
最開始以爲三層開發模式和MVC 是一個概念,認爲三層架構是MVC,MVC是三層架構。概念性的東西多少有點混淆。
其實不然,三層架構分爲: 表示層,邏輯層,數據訪問層。三層架構從結構來說是縱向的分層,上層依賴於下層,而下層不依賴於上層,即單項依賴。並且表示層和我們MVC的視圖 都是將程序運行的結果呈現給用戶,業務邏輯層和持久層是爲了程序的可移植性我們把邏輯層和專門數據訪問層的權限全部交給我們MVC的控制器來管理和操作。
三、MVC實現思路
JavaEE的 MVC 的思路是:加載配置文件信息在服務器啓動的時候,然後用戶發送的請求由一個核心控制器攔截所有的url,然後服務器根據請求的字符串拆分出控制器的實現類 以及對應的action方法,服務器根據這個控制器的實現類的完整限定名 反射得出該類的實例 ,執行該類中的action方法之後 會返回一個邏輯路徑,服務器然後根據這個邏輯路徑,跳轉到邏輯路徑對應的頁面將結果顯示出來。
接下來按照MVC架構模擬一個登錄的功能。
首先需要一個核心控制器ActionServlet.java
@Override
public void init(ServletConfig config) throws ServletException {
//初始化 讀取struts.xml
String configName = config.getInitParameter("configName");
actionMappingManager = new ActionMappingManager(configName.split(","));
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲得請求的url然後映射出請求的action
BaseAction baseAction = null;
//獲得action名稱
String actionPath = request.getRequestURL().toString();
String actionName = actionPath.substring(actionPath.lastIndexOf("/") + 1, actionPath.indexOf(".action"));
if(actionName!=null){
ActionMapping actionMapping = actionMappingManager.getActionMapping(actionName);
if(actionMapping!=null){
try {
Class clazz = Class.forName(actionMapping.getClassName());
baseAction = (BaseAction)clazz.newInstance();
//獲得執行完 execute()返回的邏輯路徑
String logicPath = baseAction.execute(request, response);
if(logicPath!=null){
String reallPath = actionMapping.getResultMap().get(logicPath).getValue();
if(actionMapping.getResultMap().get(logicPath).isRedirect()){
response.sendRedirect(reallPath);
}else{
request.getRequestDispatcher(reallPath).forward(request, response);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
在web.xml中需要配置這個核心控制器,因爲是啓動服務器的時候就要加載,所以具體如下:
<!-- 核心控制器 -->
<servlet>
<servlet-name>actionServlet</servlet-name>
<servlet-class>com.mvc.controller.ActionServlet</servlet-class>
<init-param>
<param-name>configName</param-name>
<param-value>/struts.xml</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>actionServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
從上面的代碼中可以看到將拆分出來的actionName 傳遞進去獲得了一個ActionMapping 對象,這個對象的獲得是先從由核心控制器在init()方法中 讀取struts.xml配置文件,然後從裏面獲取 Action類名,Action方法,ActionResult 這樣子一個一一對應的關係的actionMappings集合中,需要獲取這個集合中的對應關係只需要傳遞一個actionName 去獲取就好了,它就會返回如上面說的ActionMapping對象。ActionMapping對象獲取到了之後 裏面保存了action類的限定名,我們這裏利用反射得到實例,然後調用action方法,根據這個方法返回的邏輯名稱logicPath
,從ActionMapping對象中得到一個真實的路徑,然後再從ActionMapping對象中獲取本地請求的請求方式 是重定向還是請求轉發,跳轉後顯示,本次請求完畢。
那麼核心控制器在init()方法中是如何讀取struts.xml中的信息呢?
配置文件的定義規範都是事先全部定義好的,然後是由dom4j 來讀取xml的根目錄,一層一層往裏面獲取節點對象以及節點的綁定值。每次解析完配置 就封裝到已經定義好的ActionMapping對象中,然後將着所有的對象全部放在ActionMappings這個大的集合中。怎麼實現,代碼如下:
package com.mvc.controller;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.ActionMap;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* action manager
*
* @author Administrator
*
*/
public class ActionMappingManager {
public Map<String, ActionMapping> actionMappings = new HashMap<String, ActionMapping>();
/**
* 實例化的時候就加載struts.xml
*
* @param configNames
*/
public ActionMappingManager(String[] configNames) {
for (int i = 0; i < configNames.length; i++) {
initConfig(configNames[i]);
}
}
/**
* 使用dom4j解析配置文件
* @param configName
*/
public void initConfig(String configName) {
try {
if (configName == null || configName.isEmpty()) {
throw new Exception("配置文件不能爲空");
}
//獲得文件流,加載配置文件
InputStream inputStream = this.getClass().getResourceAsStream(configName);
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
Iterator<Element> actionsIterator = rootElement.elements("actions").iterator();
while(actionsIterator.hasNext()){
Element actionElement = actionsIterator.next();
Iterator<Element> actionIterator = actionElement.elements("action").iterator();
//循環單個action
while(actionIterator.hasNext()){
Element signActionElement= actionIterator.next();
//一個action對應ActionMapping 對象
ActionMapping actionMapping = new ActionMapping();
actionMapping.setName(signActionElement.attributeValue("name"));
actionMapping.setClassName(signActionElement.attributeValue("class"));
Iterator<Element> resultIterator = signActionElement.elementIterator("result");
while(resultIterator.hasNext()){
Element resultElement = resultIterator.next();
//使用Result封裝
Result result = new Result();
result.setName(resultElement.attributeValue("name"));
result.setValue(resultElement.getText());
result.setRedirect(Boolean.valueOf(resultElement.attributeValue("redirect")));
actionMapping.addResultMap(result.getName(),result);
}
actionMappings.put(actionMapping.getName(), actionMapping);
}
}
for (ActionMapping actionMapping : actionMappings.values()) {
System.out.println("name:"+actionMapping.getName()+" ,className:"+actionMapping.getClassName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根據actionName獲得ActionMapping
* @param actionName
* @return
*/
public ActionMapping getActionMapping(String actionName){
return actionMappings.get(actionName);
}
}
ActionMapping.java中 定義的name 就是我們從請求url拆分出來的actionPath,className是這個action類的 完整限定名,然後resultMap這裏面保存的則是 action方法執行完畢後返回的邏輯名稱對應的之後所有跳轉情況的集合。
具體代碼如下:
package com.mvc.controller;
import java.util.HashMap;
import java.util.Map;
/**
* struts.xml映射ActionMapping
* @author Administrator
*
*/
public class ActionMapping {
private String name;
private String className;
private Map<String, Result> resultMap = new HashMap<String, Result>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public Map<String, Result> getResultMap() {
return resultMap;
}
public void addResultMap(String name, Result result) {
this.resultMap.put(name, result);
}
}
在Result.java類中定義的是邏輯名稱,實際跳轉頁面以及跳轉的方式等等。
具體代碼如下:
package com.mvc.controller;
public class Result {
private String name;
private String value;
private boolean isRedirect = false; //默認請求轉發
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public boolean isRedirect() {
return isRedirect;
}
public void setRedirect(boolean isRedirect) {
this.isRedirect = isRedirect;
}
}
這樣子配置之後基本可以完成所有符合規範的請求。
那麼接下來一起來走一個MVC的請求。
struts.xml中的定義如下:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定義配置文件 每一對action標籤保存用戶提交的請求所跳轉的Action頁面及返回頁面的信息 -->
<struts>
<actions>
<action name="login" class="com.mvc.action.LoginAction">
<result name="success" redirect="true">loginSuccess.jsp</result>
<result name="fail">/loginFail.jsp</result>
</action>
<action name="register" class="com.mvc.action.RegisterAction">
<result name="success" redirect="true">/registerSuccess.jsp</result>
<result name="fail">/registerFail.jsp</result>
</action>
</actions>
</struts>
我們根據這個配置,就可以創建一個名稱爲LoginAction 的類,並且完整限定名是 com.mvc.action.LoginAction,login則是請求路徑,因爲沒有指定具體的方法名method屬性,所以我們會調用默認的方法execute(),登錄操作 有成功 或者失敗兩種請求,在<result>標籤也體現出來了,以第一個爲例,success爲我們上面經常提到的邏輯名稱,然後後面有一個跳轉的方式, 在這個邏輯名稱的標籤內綁定的是實際路徑。
根據這個思路我們定義了LoginAction.java,具體代碼如下:
package com.mvc.action;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.jni.User;
/**
* 登錄action的處理action
* @author Administrator
*
*/
public class LoginAction extends BaseAction{
@Override
public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
String name = request.getParameter("name");
String password = request.getParameter("password");
if(name!=null && password!=null){
if(name.equals("chenghui") && password.equals("password")){
User user = new User();
request.getSession().setAttribute("user", user);
return "success";
}
}
return "fail";
}
}
它繼承了BaseAction 這個基類,基類裏面除了一個execute()空方法以外 什麼都沒有。
根據上面Action類中的代碼體驗接收的參數,所以視圖層的話很簡單了,如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'login.jsp' starting page</title>
</head>
<body>
<h1>用戶登錄</h1>
<form action="${pageContext.request.contextPath}/login.action" method="post" name="loginForm">
姓名:<input type="text" name="name" /><br/>
密碼:<input type="text" name="password" /><br/>
<input type="submit" value="登錄" >
</form>
</body>
</html>
這樣子,基本上完成了MVC架構模式的開發。
當非常熟悉這種架構模式之後就會覺得很好很強大,不用每一次請求 都定義那麼多servlet 來處理請求,而是採用核心控制器來集中處理和管理。它的好處還需在以後的開發和學習中慢慢去體會。