版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第12章新聞發(fā)布網站的設計12.1需求分析12.2ORM技術應用12.3MVC框架模式應用12.4實現(xiàn)網頁靜態(tài)化12.5應用AJAX實現(xiàn)快捷交互12.6應用Servlet實現(xiàn)驗證碼
【學習提示】本章通過對一個典型的新聞發(fā)布網站的設計思路進行剖析,展示了在網站設計和開發(fā)過程中的共性方法,著重分析了網站設計中的重點和難點,以便在實踐中更好地理解和學習網站設計開發(fā)的基本流程及主要技術。
網站代碼的編輯和系統(tǒng)調試開發(fā)工具為MyEclipse,在MyEclipse中配置集成Tomcat,使得對軟件進行調試更為方便。數(shù)據庫則可使用MySQL或SQLSERVER。
12.1.1網站基本功能描述
各類網站的設計中幾乎都包含新聞發(fā)布的功能。通過這一功能,網站的管理者可以方便地編排欄目和發(fā)布新聞信息,而網站的瀏覽者則可以方便地查看各欄目中的相關新聞,有些網站還提供了新聞評論的功能。本章所介紹的網站首頁如圖12-1所示。12.1需求分析
圖12-1
新聞網站主界面根據網站的規(guī)模不同,不同的信息發(fā)布網站的功能也有很大差異,但都包括如下最基本的功能:
●前臺信息瀏覽。這是系統(tǒng)面向廣大用戶最核心的功能,用戶可以瀏覽到自己感興趣的信息。前臺的網頁設計包括首頁面展示、欄目頁面展示和信息的詳細展示,顯然這些頁面都應當是由JSP動態(tài)生成的。
●后臺新聞維護。這部分功能是面向網站管理員的,管理員登錄驗證之后可以編輯多個欄目版塊新聞,可對新聞內容實施增、刪、改、查等操作。12.1.2總體設計
對于大型和復雜的網站系統(tǒng)設計工作,開發(fā)者通常會選擇三層體系結構作為系統(tǒng)的整體架構。三層體系結構包括表現(xiàn)層、業(yè)務邏輯層和數(shù)據層。通常,業(yè)務邏輯層既要處理業(yè)務邏輯,又要對關系數(shù)據庫中的數(shù)據表進行CRUD(Create、Read、Update、Delete)操作,這種方式會導致業(yè)務邏輯與關系數(shù)據庫的耦合性太強,降低了管理信息系統(tǒng)的靈活性和適應性。為了降低這種耦合性,可以將數(shù)據的訪問操作從業(yè)務邏輯層中分離出來,單獨作為一個數(shù)據持久層,如圖12-2所示。
圖12-2系統(tǒng)開發(fā)中的多層體系結構雖然本章所討論的新聞網站的設計案例只給出基本的功能,但作為網站開發(fā)實踐的案例,系統(tǒng)也采用了上述的多層體系結構。
在使用多層體系結構的前提下,網站的具體設計中使用了如下技術:運用ORM技術對后臺的數(shù)據庫操作進行封裝;使用MVC設計結構對網站各模塊進行劃分;為了提高網站的安全性能與用戶體驗,還進行了網頁靜態(tài)化以及基于AJAX的設計。系統(tǒng)中各個層次的劃分與所應用技術之間的關系大致如圖12-3所示。圖12-3各個層次的劃分與所應用技術之間的關系圖由上述可知,在設計開發(fā)過程中主要有如下技術難點:
(1)對象與關系映射的設計。我們所采用的數(shù)據庫是關系型數(shù)據庫(RDB),而用來開發(fā)系統(tǒng)的語言工具Java是一種面向對象語言,兩者看待數(shù)據的角度有很大差異。通過“對象/關系映射”(ObjectRelationalMapping,ORM)可以實現(xiàn)兩者的數(shù)據對接,因此系統(tǒng)實現(xiàn)中需要考慮如何采用ORM來實現(xiàn)數(shù)據持久層。
(2)基于MVC設計結構的設計。在學習了MVC設計結構的基礎知識之后,應考慮如何將MVC設計結構應用到具體的設計中,設計過程中需要結合具體的情況進行考慮和分析。
(3)動態(tài)網頁靜態(tài)化的設計。新聞網站中每一條新聞內容的網頁都是由JSP程序動態(tài)生成的,而這些頁面可能會被大量的用戶重復訪問,如果每次訪問都執(zhí)行相應的程序來生成HTML頁面,那就會消耗大量的系統(tǒng)資源、降低系統(tǒng)效率。但如果所有的新聞內容頁面都由網頁設計人員手工排版完成,也是不符合軟件工程的原則,是不可行的。系統(tǒng)需要提供動態(tài)網頁靜態(tài)化的功能,將每個新聞頁面一次性自動生成為靜態(tài)的HTML文件提供用戶訪問,從而提高系統(tǒng)的執(zhí)行效率。
(4)基于AJAX的友好用戶體驗設計。用戶體驗已經成為軟件系統(tǒng)成敗的非常重要的因素,AJAX技術可以幫助網站系統(tǒng)提供友好的用戶體驗,降低網絡帶寬消耗,實現(xiàn)用戶對數(shù)據進行快捷、方便的操作。
在下面的內容中,將會對上述技術難點進行逐一分析并深入探討。
12.2.1ORM技術簡介
網站設計與開發(fā)項目就是一種軟件工程項目,其開發(fā)過程符合軟件工程的一般原則和步驟。如何把復雜的系統(tǒng)逐步分解,形成大量、簡單的模塊是很多軟件工程方法的目標。對數(shù)據的增、刪、改、查操作是基于數(shù)據庫的網站設計中最基本、最通用的功能,把復雜的功能逐步分解為對數(shù)據的增、刪、改、查操作是網站系統(tǒng)需求分析和模塊分解的過程中非常重要的環(huán)節(jié)。12.2ORM技術應用在面向關系的數(shù)據庫中,描述事物關系的是表、記錄、字段;而在面向對象的程序設計中,描述事物的是類、對象、屬性。面向對象通過對現(xiàn)實事物進行抽象,用軟件工程化的方法進行描述;而關系數(shù)據庫則是建立在嚴格的數(shù)學理論上對事物的關系進行描述的。在描述事物同一級別上(如表對應類和記錄對應對象),兩者有所聯(lián)系卻又不能完全等同,這就構成了所謂的“阻抗不匹配”問題。
在新聞網站系統(tǒng)設計中所采用的數(shù)據庫是關系型數(shù)據庫(RDB),而用來開發(fā)系統(tǒng)的語言工具Java是一種面向對象語言,兩者看待數(shù)據的角度有很大差異。通過“對象/關系映射”可以實現(xiàn)兩者的數(shù)據對接。
ORM是一種數(shù)據持久層的技術,它實現(xiàn)了業(yè)務邏輯層中的對象模型與數(shù)據層中的關系模型之間的透明轉換,很好地解決了它們之間的“阻抗不匹配”問題。圖12-4顯示了ORM在多層系統(tǒng)架構中的作用。
圖12-4ORM在多層系統(tǒng)架構中的作用從圖中可以看出,ORM的實質在于:一方面將關系數(shù)據庫中的業(yè)務數(shù)據用對象的形式表示,并通過面向對象的方式將這些對象組織起來,實現(xiàn)系統(tǒng)業(yè)務邏輯功能的過程,即實現(xiàn)從關系模型到對象模型的映射;另一方面將業(yè)務邏輯中的業(yè)務對象以某種方式存儲到關系數(shù)據庫中,完成對象數(shù)據存儲的過程,即要實現(xiàn)對象模型到關系模型的映射。這其中最重要的概念就是映射(Mapping),通過這種映射可以將業(yè)務邏輯對象與數(shù)據庫分離開來,使管理信息系統(tǒng)開發(fā)人員既可利用面向對象程序設計語言的簡單易用性,又可利用關系數(shù)據庫的存儲優(yōu)勢。
ORM的映射模式分為三個層次,分別為屬性到字段的映射、類到表的映射和關系映射。下面將分別介紹這三個層次的映射規(guī)則。
●屬性到字段的映射。這是ORM中最基本的映射。對象的屬性可以映射到關系數(shù)據表中的零個或多個字段。一般情況下,對象的一個屬性對應到關系數(shù)據表中的一列,以下兩種情況除外:
一種情況是對象的有些屬性不需要持久化。例如,一個“學生”類的“年齡”屬性,是用出生日期計算出來的,不需要持久化存儲,所以這個屬性就沒有必要映射到數(shù)據表中的字段上。另一種情況是對象的某個或某些屬性本身也是對象。仍以“學生”類為例,它的“通信地址”屬性有可能也是一個對象,它本身就映射為一張數(shù)據庫表。“通信地址”的各個屬性(不同的地址)再分別映射到數(shù)據表中的字段。在這種情況下,對象的一個屬性映射成了多個字段。
●類到表的映射。一個對象的類可直接或間接的映射為數(shù)據庫中的表。當類結構比較簡單時(與其他類沒有繼承等關系),可直接映射為一張表。但當對象比較復雜時,特別是考慮到類的繼承問題,如何在數(shù)據庫中映射被繼承的屬性就成為此類映射的核心問題。針對該問題,一般有三種策略,分別為整個類層次一張表、每個具體類一張表以及每個類一張表?!耜P系映射。ORM不僅要將對象映射到關系數(shù)據庫中,而且對象之間的關系也需要映射到關系數(shù)據庫中。對象之間的關系主要包括繼承、關聯(lián)和聚合。其中,對象之間的繼承關系已經在類到表的映射中進行了處理,這里主要討論另外兩種關系。
關聯(lián)表達了對象之間的連接數(shù)量關系,包括一對一關聯(lián)、一對多關聯(lián)、多對多關聯(lián)和反射關聯(lián)。數(shù)據之間的關聯(lián)在關系型數(shù)據庫中通常通過外鍵和關聯(lián)表來實現(xiàn)。12.2.2數(shù)據庫表的設計
在進行ORM的具體設計時,可以先開發(fā)“對象”的部分(即O),也可以先開發(fā)“關系”的部分(即R),當然也可以由不同的開發(fā)者并行開發(fā)。在這里,我們先介紹新聞發(fā)布系統(tǒng)所需的數(shù)據庫結構。本系統(tǒng)最基本的數(shù)據關系如下:
●新聞欄目。包括欄目編號、欄目名稱等;
●新聞信息。包括新聞編號、新聞標題、新聞內容、新聞發(fā)布時間、作者、所屬欄目編號等。
上述的數(shù)據關系在數(shù)據庫系統(tǒng)中是由兩張表來實現(xiàn)的,分別是column表和article表。
column表存儲新聞欄目信息,欄目實體的引入是為了將新聞正確分類(本系統(tǒng)欄目不再有子欄目設置),欄目表結構如表12-1所示。表12-1column表結構
article表存儲新聞信息,包括新聞編號、新聞標題、內容、發(fā)布者、發(fā)布時間和所屬欄目等信息。表中所屬欄目column_id字段作為外碼與column表中的主碼對應。article表結構如表12-2所示。表12-2article表結構12.2.3數(shù)據對象的設計
本系統(tǒng)主要操作的兩類信息為新聞欄目和新聞信息,在面向對象的設計中可抽象為Column類和Article類。由于Column類與Article類以及其相關類在實現(xiàn)上大同小異,下面僅介紹Article類及與其相關的映射類,并結合相關代碼作分析說明。
首先對Article類進行實現(xiàn)。通過分析,對存在于article表中的字段可以將其一一對應設為Article類中的屬性,并以面向對象的思想對屬性進行了封裝,根據對數(shù)據的訪問需要分別對各屬性定義get與set方法。完整的代碼如下(為節(jié)省篇幅,對代碼中的縮進、空格和空行進行了縮減):
/************Article.java核心代碼***********************/
packagebean.article;
importjava.util.Date;
publicclassArticle{
privateintarticleid;
privateStringartitle;
privateintcolumnid;
privateStringartauthor;
privateStringcontent;
privateDateartupdateTime;
privateDateartaddTime;
privateintviewnum;
privateStringarthtmlurl;
privateStringartkeywords;
privateintartstatus;
privateStringartistop;
privateStringartiscomment;
publicintgetArticleid(){returnarticleid;}
publicvoidsetArticleid(intarticleid){this.articleid=articleid;}
publicStringgetArtitle(){returnartitle;}
publicvoidsetArtitle(Stringarttitle){this.artitle=arttitle;}
publicintgetColumnid(){ returncolumnid; }
publicvoidsetColumnid(intcolumnid){ this.columnid=columnid; }
publicStringgetArtauthor(){ returnartauthor; }
publicvoidsetArtauthor(Stringartauthor){ this.artauthor=artauthor; }
publicStringgetContent(){ returncontent; }
publicvoidsetContent(Stringcontent){ this.content=content; }
publicDategetArtupdateTime(){ returnartupdateTime; }
publicvoidsetArtupdateTime(DateartupdateTime){this.artupdateTime=artupdateTime;}
publicDategetArtaddTime(){ returnartaddTime; }
publicvoidsetArtaddTime(DateartaddTime){ this.artaddTime=artaddTime; }
publicintgetViewnum(){ returnviewnum; }
publicvoidsetViewnum(intviewnum){ this.viewnum=viewnum; }
publicStringgetArthtmlurl(){ returnarthtmlurl; }
publicvoidsetArthtmlurl(Stringarthtmlurl){ this.arthtmlurl=arthtmlurl; }
publicStringgetArtkeywords(){ returnartkeywords;}
publicvoidsetArtkeywords(Stringartkeywords){this.artkeywords=artkeywords; }
publicintgetArtstatus(){ returnartstatus; }
publicvoidsetArtstatus(intartstatus){ this.artstatus=artstatus; }
publicStringgetArtistop(){ returnartistop; }
publicvoidsetArtistop(Stringartistop){ this.artistop=artistop; }
publicStringgetArtiscomment(){ returnartiscomment; }
publicvoidsetArtiscomment(Stringartiscomment){this.artiscomment=artiscomment; }
}從代碼中可以看出,類定義中的很多屬性和方法的設計都非常簡單,事實上大多數(shù)代碼可以由開發(fā)工具幫助生成。12.2.4關系與對象的匹配
有了“R”和“O”,接下來就需要設計映射類“M”,以實現(xiàn)關系與對象的匹配?!癕”的作用、位置以及與“R”、“O”之間的關系如圖12-5所示。
圖12-5ORM各部分圖示從圖中可以看出,映射類“M”的主要任務就是在“R”和“O”之間建立適當?shù)挠成潢P系,搭建系統(tǒng)訪問數(shù)據庫的橋梁,因此也是ORM設計中工作的重心。對映射類“M”的命名通常體現(xiàn)了其主要的功能,即對“數(shù)據訪問對象(DataAccessObjects,DAO)”的實現(xiàn)。對應前文的Article類,下面的映射類命名為ArticleDaoImp類。
ArticleDaoImp類主要實現(xiàn)了對數(shù)據庫article表進行的CRUD操作。針對部分特定的需求,設計相應的代碼來完成所需功能,比如,根據文章編號查詢文章的功能用findArticle
ById(intid)方法實現(xiàn),獲取某欄目中前若干條新聞的功能用select(intid,inttopnum)方法實現(xiàn)等。ArticleDaoImp類的完整代碼如下:
/************ArticleDaoImp.java(數(shù)據持久化層(DAO層))代碼****************/
packagebean.article;
importjava.sql.CallableStatement;
importjava.sql.Connection;
importjava.sql.ResultSet;
importjava.sql.SQLException;
importjava.sql.Statement;
importjava.util.ArrayList;
importjava.util.List;
importmom.*;
importbean.DB; //該類主要封裝了對JDBC與數(shù)據庫的連接進行的初始化操作
publicclassArticleDAOImp{
//對數(shù)據進行插入操作
Publicstaticintinsert(Stringtitle,intcolumnid,Stringauthor,
Stringcontent,booleanistop,booleaniscomment){
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt1=null;
Statementstmt2=null;
ResultSetrs=null;
intnum=0;
intArticleid=0;
try{
stmt1=con.createStatement();
Stringsql="insertintoarticle(column_id,article_title,article_content,article_author,
article_istop,article_iscomment,article_addTime,article_updateTime)values('"+columnid+"','"+title+"','"+content+"','"+author+"',"+istop+","+iscomment+",now(),now())";
num=stmt1.executeUpdate(sql);
stmt2=con.createStatement();
rs=stmt2.executeQuery("SELECT@@IDENTITYAS'Identity'");
if(rs.next()){
Articleid=rs.getInt(1);
}
stmt1.close();
stmt2.close();
con.close();
}catch(SQLExceptione){
e.printStackTrace();
}
if(num>0&&Articleid>0){
returnArticleid;
}
return0;
}
//通過對滿足相應條件pageSize,currPage,clumnId的文章在數(shù)據庫中進行搜索,并返回結果
publicstaticList<Article>itemSelect(intpageSize,intcurrPage,intcolumnId){
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt=null;
ResultSetrs=null;
Stringsql="selectarticle_id,column_id,article_title,article_htmlurl,article_updateTime,
article_author,article_istop,article_iscomment,casewhenchar_length(article_content>200)thenconcat(substring(article_content,1,200),'...')elsearticle_contentendasarticle_content1fromarticlewherecolumn_id="+columnId+"orderbyarticle_istopdesc,article_iddesclimit"+pageSize*(curr1)+","+pageSize+"";
try{
stmt=con.createStatement();
rs=stmt.executeQuery(sql);
}catch(SQLExceptione){
e.printStackTrace();
}
List<Article>list=newArrayList<Article>();
try{
while(rs.next()){
Articlearticle=newArticle();
article.setArticleid(rs.getInt("article_id"));
article.setColumnid(rs.getInt("column_id"));
article.setArtistop(rs.getString("article_istop"));
article.setArtiscomment(rs.getString("article_iscomment"));
article.setArtitle(HtmlUtil.getStringtrim(rs.getString("article_title"),15));
article.setArthtmlurl(rs.getString("article_htmlurl"));
article.setArtupdateTime(rs.getTimestamp("article_updateTime"));
article.setArtauthor(rs.getString("article_author"));
article.setContent(rs.getString("article_content1"));
list.add(article);
}
}catch(NumberFormatExceptione){
e.printStackTrace();
}catch(SQLExceptione){
e.printStackTrace();
}
returnlist;
}
//通過欄目id對article表進行搜索,返回前topnum條數(shù)據
publicstaticList<Article>select(intid,inttopnum){
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt=null;
ResultSetrs=null;
Stringsql="SELECTarticle_id,article_title,column_id,article_author,article_content,
article_updateTime,article_addTime,"+
"article_viewnum,article_htmlurl,article_keywords,article_status,"+
"casewhenarticle_istop=1then'是'else'否'endarticle_istop,"+
"casewhenarticle_iscomment=1then'是'else'否'endarticle_iscommentFROMarticlewherecolumn_id='"+id+"'orderbyarticle_updateTimedesclimit"+topnum+"";
try{
stmt=con.createStatement();
rs=stmt.executeQuery(sql);
}catch(SQLExceptione){
e.printStackTrace();
}
List<Article>list=newArrayList<Article>();
try{
while(rs.next()){
Articlearticle=newArticle();
article.setArticleid(rs.getInt("article_id"));
article.setArtitle(HtmlUtil.getStringtrim(rs.getString("article_title"),15));
article.setColumnid(rs.getInt("column_id"));
article.setArtauthor(rs.getString("article_author"));
article.setContent(rs.getString("article_content"));
article.setArtupdateTime(rs.getTimestamp("article_updateTime"));
article.setArtaddTime(rs.getTimestamp("article_addTime"));
article.setViewnum(rs.getInt("article_viewnum"));
article.setArthtmlurl(rs.getString("article_htmlurl"));
article.setArtkeywords(rs.getString("article_keywords"));
article.setArtstatus(rs.getInt("article_status"));
article.setArtistop(rs.getString("article_istop"));
article.setArtiscomment(rs.getString("article_iscomment"));
list.add(article);
}
}catch(NumberFormatExceptione){
e.printStackTrace();
}catch(SQLExceptione){
e.printStackTrace();
}
returnlist;
}
//以文章id為搜索條件進行數(shù)據搜索
publicstaticArticlefindArticleById(intid){
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt=null;
ResultSetrs=null;
Stringsql="SELECTarticle_id,article_title,column_id,article_author,article_content,
article_updateTime,"+"article_addTime,article_viewnum,article_htmlurl,article_keywords,article_status,
"+
"casewhenarticle_istop=1then'是'else'否'endarticle_istop,casewhenarticle_iscomment=1"+
"then'是'else'否'endarticle_iscommentFROMarticlewherearticle_id='"+id+"'";
try{
stmt=con.createStatement();
rs=stmt.executeQuery(sql);
}catch(SQLExceptione){
e.printStackTrace();
}
Articlearticle=newArticle();
try{
while(rs.next()){
article.setArticleid(rs.getInt("article_id"));
article.setArtitle(rs.getString("article_title"));
article.setColumnid(rs.getInt("column_id"));
article.setArtauthor(rs.getString("article_author"));
article.setContent(rs.getString("article_content"));
article.setArtupdateTime(rs.getTimestamp("article_updateTime")); article.setArtaddTime(rs.getTimestamp("article_addTime"));
article.setViewnum(rs.getInt("article_viewnum"));
article.setArthtmlurl(rs.getString("article_htmlurl"));
article.setArtkeywords(rs.getString("article_htmlurl"));
article.setArtstatus(rs.getInt("article_status"));
article.setArtistop(rs.getString("article_istop")); article.setArtiscomment(rs.getString("article_iscomment"));
}
}catch(NumberFormatExceptione){
e.printStackTrace();
}catch(SQLExceptione){
e.printStackTrace();
}
returnarticle;
}
//以文章id作為條件對文章進行刪除
publicstaticbooleandelete(intid){
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt=null;
intn=0;
try{
stmt=con.createStatement();
Stringsql="deletefromarticlewherearticle_id="+id;
n=stmt.executeUpdate(sql);
stmt.close();
con.close();
}
catch(Exceptione){}
if(n>0)
{ returntrue;
}
returnfalse;
}
//更改相應文章id的數(shù)據內容
publicstaticbooleanupdate(Stringtitle,intcol_id,Stringauthor,
Stringcontent,intistop,intiscomment,intid){
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt=null;
intnum=0;
try
{
stmt=con.createStatement();
Stringsql="updatearticlesetarticle_title='"+title+"',column_id='"+col_id+"',
article_author='"+author+"',article_content='"+content+"',article_updateTime=now(),article_istop="+istop+",article_iscomment="+iscomment+"wherearticle_id='"+id+"'";
stmt.executeUpdate(sql);
num=stmt.executeUpdate(sql);
stmt.close();
con.close();
}
catch(Exceptione){}
if(num>0)
{
returntrue;
}
returnfalse;
}
//更改相應文章id的url字段內容
publicstaticvoidupdateUrl(intid,Stringurl)
{
DBdb=newDB();
Connectioncon=db.getCon();
Statementstmt=null;
try{
stmt=con.createStatement();
Stringsql="updatearticlesetarticle_htmlurl='"+url+"'wherearticle_id='"+id+"'";
stmt.executeUpdate(sql);
}
catch(Exceptione)
{
e.printStackTrace();
}
}
}需要注意:ArticleDaoImp類中的所有成員函數(shù)都是以靜態(tài)函數(shù)(static)的方式被封裝在類中,這意味著系統(tǒng)的其他模塊在使用ArticleDaoImp類時不需要進行類的實例化,而是直接通過“類”來訪問這些函數(shù)。
12.3.1MVC簡介
在軟件工程中,經常以“低耦合,高內聚”作為面向對象軟件設計的目標。低耦合即降低各個模塊之間的邏輯聯(lián)系,使各模塊邏輯分工明確,模塊之間以適當?shù)慕涌谶M行數(shù)據交互,接口也盡可能少而簡單;12.3MVC框架模式應用高內聚即單一模塊內的邏輯明確,由相關性很強的代碼所組成,每一模塊只負責單一的任務,使得該目標設計的軟件邏輯明確,代碼可讀性強,易于維護和變更,便于移植和重用。
為了達到上述軟件設計的目標,常常會對軟件進行功能結構上的劃分,而劃分的方法有許多,在這里我們將對網站開發(fā)中較為常用的MVC設計結構做一介紹。
MVC設計結構,即模型-視圖-控制器(Model-View-Controller)結構,是一種重要的面向對象設計結構,它可以有效地使系統(tǒng)中的數(shù)據輸入、處理和輸出功能在模塊設計的基礎上建立規(guī)范的接口。
MVC設計結構把軟件系統(tǒng)分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。使用MVC結構的益處是可以完全降低業(yè)務層和應用表示層的相互影響。Model和View代碼分離可使相同的數(shù)據(或對象)以多種形式表現(xiàn)。MVC在項目中提供對象和層之間的相對獨立,將使系統(tǒng)的維護變得更簡單,同時可以提升代碼的重用性。它強制性地使應用程序的輸入、處理和輸出分開,三個核心部件各自處理自己的任務。MVC結構的各個部分的功能和聯(lián)系如圖12-6所示。
圖12-6MVC結構圖視圖(View)代表用戶交互界面。一個應用可以有很多不同的視圖。視圖負責采集模型數(shù)據,即解釋模型以某種格式顯示給用戶,同時傳遞用戶請求和輸入數(shù)據給控制器和模型,完成用戶交互。當視圖相關的模型數(shù)據變化時,視圖將自動更新。對視圖來講,它只是作為一種輸出數(shù)據并允許用戶操縱的方式。模型(Model)就是業(yè)務流程/狀態(tài)的處理以及業(yè)務規(guī)則的制定。業(yè)務流程的處理過程對其他層來說是黑箱操作。模型中包含業(yè)務規(guī)則的制定,是MVC的核心。模型封裝應用程序狀態(tài),并接受視圖的狀態(tài)查詢,返回最終的處理結果。同時,模型可根據控制器指令修改狀態(tài)。一個模型能為多個視圖提供數(shù)據。模型代碼只需寫一次就可以被多個視圖重用,所以增強了代碼重用,為后期維護提供方便??刂破?Controller)可以理解為視圖和模型的紐帶。它先接收請求,然后將模型與視圖匹配在一起,共同完成用戶的請求??刂破骶褪且粋€分發(fā)器,決定選擇何種模型和視圖,可以完成什么樣的用戶請求??刂破鞑⒉蛔鋈魏蔚臄?shù)據處理,只是將信息傳遞給模型,而不是由視圖直接和模型關聯(lián)處理,從而將業(yè)務邏輯和表現(xiàn)層分離。例如,用戶點擊一個連接,控制器接受請求后,并不處理業(yè)務信息,它只把用戶的信息傳遞給模型,告訴模型做什么,選擇符合要求的視圖返回給用戶。因此,一個模型可能對應多個視圖,一個視圖可能對應多個模型。
模型、視圖與控制器的分離,使得一個模型可以具有多個顯示視圖。如果用戶通過某個視圖的控制器改變了模型的數(shù)據,所有其他依賴于這些數(shù)據的視圖都應反映這些變化。MVC的處理過程是:首先控制器接收用戶的請求,并決定應該調用哪個模型來進行處理,然后模型用業(yè)務邏輯來處理用戶的請求并返回數(shù)據,最后控制器用相應的視圖格式化模型返回的數(shù)據,并通過表示層呈現(xiàn)給用戶。
系統(tǒng)中的“視圖對象”是指系統(tǒng)與用戶交互的界面,它主要負責接受用戶的數(shù)據輸入和數(shù)據的輸出顯示;“模型對象”描述了系統(tǒng)的數(shù)據格式和邏輯規(guī)則,它主要負責系統(tǒng)的數(shù)據處理任務,如對數(shù)據庫的訪問和操作;“控制對象”負責協(xié)調系統(tǒng)的運行,按照用戶的輸入和一定的邏輯規(guī)則調用相應的模型和視圖。在MVC結構中可以有多個視圖對應模型和控制器,使用戶可以選擇不同的角度觀察同一組數(shù)據,也可以有多個模型對數(shù)據做多種的處理,而控制器可以根據情況選擇使用視圖和模型。12.3.2直網站MVC結構設計
MVC是一種結構良好,邏輯分明,便于拓展和管理的軟件設計思想,MVC結構已經廣泛應用于各類軟件工程,包括基于JSP和基于Java的Web應用開發(fā)。
在使用JSP進行Web應用開發(fā)的發(fā)展過程中,先后出現(xiàn)了Model1和Model2設計結構。Model1模型的特點就是幾乎所有的邏輯與顯示工作都由JSP頁面完成,只用少量的JavaBean來完成對數(shù)據庫的操作。以MVC結構的角度來看,視圖與控制器的實現(xiàn)混雜在一起,分工不明確,實現(xiàn)模型功能的JavaBean功能有限,且與數(shù)據庫和JSP的耦合度高。Model1各模塊的交互過程如圖12-7所示。由上述可知,Model1架構的實現(xiàn)較為簡單,各模塊之間的耦合度高,管理難度大。
隨著網站開發(fā)技術的成熟,Web應用的開發(fā)出現(xiàn)了Model2設計結構。Model2由Sun公司提出,并引入了MVC結構的特點,明確各個模塊之間的職責和功能。在Model2中,業(yè)務邏輯主要由Servlet文件構成,實現(xiàn)了控制器的功能;JSP則主要用作于視圖的展現(xiàn);JavaBean則封裝對數(shù)據庫的操作,實現(xiàn)了模型的功能。相對于Model1,Model2更加適合大型Web應用程序的開發(fā),但同時也加大了開發(fā)的復雜度。Model2各模塊的交互過程如圖12-8所示。
圖12-7Model各模塊交互過程
圖12-8Model2各模塊交互過程新聞站的設計主要采用了Model2結構,符合MVC設計結構的思想?;贛VC結構的網站設計中各個模塊的組成和說明如下:
(1)模型(Model)。模型主要封裝的是對數(shù)據庫進行操作的功能,調用者使用模型提供的接口且無需了解數(shù)據庫和數(shù)據訪問等方面的實現(xiàn)細節(jié),從而達到其他模塊與數(shù)據庫解耦目的。需要說明的是,模型的部分可以由前面描述的ORM方式來實現(xiàn)。
(2)視圖(View)。視圖主要由JSP頁面部分構成,由控制器操縱,對用戶的請求做出處理,并生成HTML頁面來作為響應信息的載體反饋給用戶。
(3)控制器(Controller)??刂破髦饕蒘ervlet構成,接受用戶請求,選擇相應的視圖做出響應。
使用MVC設計結構對網站系統(tǒng)進行分層,使得系統(tǒng)結構清晰,邏輯明確。MVC設計雖然加大了前期的開發(fā)難度,但卻大大減少了后期維護的工作量。12.3.3MVC實現(xiàn)解析
本小節(jié)將以新聞管理模塊作為典型的例子來分析MVC結構的實現(xiàn)。需要說明的是:MVC結構的實現(xiàn)不僅需要進行代碼編寫,還需要配合Web服務器所提供的運行機制。這里,我們主要以Model、View和Controller的順序討論部分代碼。
(1)Model模塊。Model模塊封裝了對數(shù)據庫的操作,其實現(xiàn)內容即前面ORM設計所實現(xiàn)的數(shù)據訪問功能,故對其實現(xiàn)代碼不再贅述。
(2)View模塊。View模塊是直接與用戶進行交互的部分,包含的頁面也較多,主要包括新聞管理界面、添加新聞界面、修改新聞界面和評論留言界面等。下面將舉例說明View模塊如何調用Model模塊進行數(shù)據的展示。網站用一個JSP頁面(Item.jsp)來列出某個欄目中的新聞標題以及簡述,如圖12-9所示。
圖12-9Item.jsp效果圖
Item.jsp文件的部分代碼如下:
<%@pagelanguage="java"import="java.util.*,bean.article.*"pageEncoding="UTF-8"%>
<%
String_columnid=request.getParameter("id");
intcolumnid=Integer.parseInt(_columnid);
%>//獲取欄目的對應id
…
<%
List<Article>list_1=ArticleDAOImp.select(columnid,7);
for(inti=0;i<list_1.size();i++)//循環(huán)列出新聞標題與簡略
{
%>
<DIVstyle="TEXT-ALIGN:left;WIDTH:100%"id=MasterContent1_Panel1><BR>
<DIVclass=frame>
<h4><Ahref="<%=list_1.get(i).getArthtmlurl()%>">
<%=list_1.get(i).getArtitle()%></A>//這里分別獲取了新聞鏈接與新聞標題
</h4><spanstyle="font-size:12px;FLOAT:right"class=titltTime><%
=list_1.get(i).平共處getArtupdateTime()%></span>
<DIVclass="list">
<P><SPANstyle="FONT-SIZE:medium">
<%=list_1.get(i).getContent()%></SPAN></P>
…
<Ahref="<%=list_1.get(i).getArthtmlurl()%>">[詳情]</A></P></DIV>
<P><SPAN>作者<%=list_1.get(i).getArtauthor()%>:</SPAN>|<SPAN>關鍵詞:</SPAN>
</P></DIV></DIV>
<%}%>從上述代碼可以看到,在代碼首行需要引入Model模塊包含的java文件,即bean.article包,如圖12-10所示。bean.article包含了Article.java以及ArticleDAOImp.java兩個java文件。這里引入的JavaBean就是Model所封裝的與數(shù)據庫操作相關的ORM類。
圖12-10bean.article結構說明代碼中直接調用Model封裝的ArticleDAOImp中的select方法,根據具體的需要獲取數(shù)據庫中的數(shù)據。使用Article類作為數(shù)據的載體,生成實例集List<Article>,使數(shù)據之后可以方便地在系統(tǒng)中進行調用。
除了查詢數(shù)據,網站系統(tǒng)通常還需要對數(shù)據進行增、刪、改的操作。下面以添加新聞的頁面addArticle.jsp為例作解析。添加新聞頁面效果如圖12-11所示。
圖12-11添加新聞頁面
addArticle.jsp文件的完整代碼如下:
<%@pagecontentType="text/html;charset=UTF-8"language="java"import="java.sql.*,java.util.*,
net.fckeditor.*,bean.column.*"errorPage=""%>
//相關文件的包含
<%@tagliburi=""prefix="FCK"%>
//使用FCK文本編輯器
<fieldset>
<legend>添加新聞</legend>
<br/>
<formaction="addArticleAction"name="frm"method="post">
<tablewidth="880"border="0">
<tr>
<tdwidth="85">標題:</td>
<td><label>
<inputname="title"type="text"id="title"size="40"/>
</label></td>
</tr>
<tr>
<td>欄目名稱:</td>
<td><selectname="column"id="column">
<optionvalue="0">請選擇</option>
<%
String_columnid=request.getParameter("id");
intcolumnid=Integer.parseInt(_columnid);
List<Column>colist=ColumnDAOImp.select();
for(inti=0;i<colist.size();i++)
{
if(colist.get(i).getColumnid()==columnid)
{
%>
<optionvalue="<%=colist.get(i).getColumnid()%>"selected="selected"><%
=colist.get(i).getColumntitle()%></option>
<%
continue;
}
%>
<optionvalue="<%=colist.get(i).getColumnid()%>"><%=colist.get(i).getColumntitle()%>
</option>
<%}%>
</select></td>
</tr>
<tr>
<td>作者:</td>
<td><label>
<inputtype="text"name="author"id="author"size="40"/>
</label></td>
</tr>
<tr>
<td> </td>
<td>
<inputtype="checkbox"name="istop"value="1"/>是否置頂 <inputtype="checkbox"name="comment"value="1"/>是否評論
</td>
</tr>
<tr>
<td>文章內容:</td>
<td><%
FCKeditorfckEditor=newFCKeditor(request,"EditorDefault");
fckEditor.setHeight("400");
fckEditor.setWidth("790");
out.println(fckEditor);
%></td>
</tr>
<tr>
<tdcolspan="2"align="right">
<inputtype="submit"name="Submit"value="添加新聞"onclick="returnchecknews();"/>
</td>
</tr>
</table>
</form>
</fieldset>
從上述代碼中可以看出,表單提交后將以POST方式交由addArticleAction處理。在網站URL與servlet映射關系中,addArticleAction與AddArticleAction相關聯(lián),故請求轉由AddArticleAction這一servlet進行控制處理,這一過程將在Controller模塊的實現(xiàn)解析中進行闡述。該頁面同樣使用了直接在頁面中調用Model模塊進行查詢操作的方法:通過調用Model模塊ColumnDAOImp(欄目部分的ORM實現(xiàn))的select方法來訪問數(shù)據庫獲取所需數(shù)據,并保存至Column實例中,通過一個for循環(huán)列出了列表中的選項,并選取對應的欄目名作為默認值。為了使新聞的文字編輯工作變得簡便,提高用戶體驗,應設計一個功能完善的網頁文本編輯器,這需要通過大量的JavaScript程序設計和與其相關聯(lián)的Java代碼來完成。為了快速地實現(xiàn)這一功能,新聞站選擇了一個開源的、功能齊備且便于植入的網頁文本編輯器——FCKedit。在文件首部引入FCKedit的相關路徑,便可在代碼中使用它了。
由此可以看出,在網站View模塊中可以調用Model模塊進行數(shù)據庫的CRUD操作,無需使用繁復的SQL語句。調用Model模塊提供的方法便可輕松快捷地完成任務,使代碼更為簡潔精煉。此外,由于將數(shù)據庫的操作都封裝集中在Model模塊中,因此以后對其進行變更或維護也變得更為簡單。設想一下,若是沒有Model模塊,則網站代碼中會存在重復的數(shù)據庫連接和大量的SQL語句,對這樣的系統(tǒng)進行修改和維護將是一項令人頭疼的工作。
(3)Controller模塊。Controller模塊主要以Servlet實現(xiàn),負責接收控制請求,包含主要的邏輯控制功能。在系統(tǒng)中,對新聞信息進行增、刪、改的Controller模塊主要由三個Servlet構成:AddArticleAction.java、DeleteArticle.java和EditArticle.java,分別負責控制文章的添加、刪除和修改操作。雖然對于數(shù)據的查詢也可以采用Servlet來完成,但考慮到系統(tǒng)的規(guī)模和代碼的簡潔性,這個新聞系統(tǒng)對于查詢操作的實現(xiàn)最好直接采用JSP頁面來實現(xiàn)。下面以控制新聞添加的Servlet為例對Controller模塊進行解析。
AddArticleAction.java負責對添加新聞界面填入的數(shù)據進行控制,其完整代碼如下:
packagebean.servlet.article;
importjava.io.IOException;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importbean.article.ArticleDAOImp;
publicclassAddArticleActionextendsHttpServlet
{
publicvoiddestroy(){
super.destroy();//Justputs"destroy"stringinlog
}
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
doPost(request,response);
}
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{
request.setCharacterEncoding("UTF-8");
Stringtitle=request.getParameter("title");
Stringcolumn_id=request.getParameter("column");
intcolumnid=Integer.parseInt(column_id);
Stringauthor=request.getParameter("author");
Stringis_top=request.getParameter("istop");
String_comment=request.getParameter("comment");
booleanistop=is_top==null?false:true;
booleancomment=_comment==null?false:true;
Stringcontent=request.getParameter("EditorDefault");
intArticleid=ArticleDAOImp.insert(title,columnid,author,content,istop,comment);
if(Articleid>0)
{
response.sendRedirect("/news/article/editNews.jsp?id="+Articleid);
}
else{
response.sendRedirect("/news/common/failure.jsp");
}
}
publicvoidinit()throwsServletException{}
}
在上述代碼中,servlet進行控制的過程比較簡單:程序首先獲取了界面所傳入的各項數(shù)據信息,之后調用了Model模塊的ArticleDAOImp類的insert方法將獲取的數(shù)據持久化到數(shù)據庫中,根據寫入數(shù)據庫的操作是否成功來選擇不同的JSP頁面進行跳轉。
(4)總結。MVC三個模塊的交互過程可以總結為:View模塊調用Model模塊進行數(shù)據庫查詢操作,將獲取的數(shù)據與頁面元素進行交互,并展現(xiàn)給用戶。此外,View模塊還負責將用戶請求轉交至Controller模塊,由Controller模塊進行控制,并調用相應的View模塊做出響應;在Controller模塊的控制過程中,還會調用Model模塊進行數(shù)據庫的CRUD操作。新聞站MVC模塊的交互過程如圖12-12所示。
圖12-12新聞站MVC模塊交互示意圖可以看出,Controller模塊是整個MVC結構的中樞環(huán)節(jié),可對系統(tǒng)其他模塊進行控制和調用。Controller模塊從前臺的View模塊中獲取用戶的請求數(shù)據,進行相應的邏輯業(yè)務處理,并調用Model模塊進行數(shù)據的持久化操作,選擇相應的View模塊將響應的結果返回用戶。
JSP頁面也被稱為動態(tài)頁面,這是相對于HTML文件而言的,當然JSP頁面的執(zhí)行結果也是HTML。正是因為JSP頁面實際上是可執(zhí)行的Java程序,因此在響應用戶頁面請求時需要一定的執(zhí)行時間。12.4實現(xiàn)網頁靜態(tài)化如果網站的用戶較少,通常JSP的執(zhí)行時間不會特別明顯,但當網站有大量的用戶訪問時,服務器執(zhí)行大量JSP所帶來的延遲響應就會直接影響用戶的體驗。因此,在網站開發(fā)中需要考慮將那些不斷重復執(zhí)行,并且對于不同的用戶執(zhí)行結果相同的JSP頁面進行優(yōu)化。
“動態(tài)網頁靜態(tài)化”簡單說就是:讓系統(tǒng)一次性執(zhí)行JSP頁面,將生成的HTML結果保存為靜態(tài)的HTML文件;生成或修改相應的超鏈接,讓用戶對動態(tài)頁面的訪問轉變?yōu)閷o態(tài)頁面的訪問,從而提高網站的響應速度。一般認為,生成HTML靜態(tài)網頁的好處有:
●減輕服務器負擔。
●有利于搜索引擎優(yōu)化(SEO)。Baidu、Google等搜索引擎都會優(yōu)先收錄靜態(tài)頁面。
●加快頁面打開速度。靜態(tài)頁面無需連接數(shù)據庫,因此打開速度較動態(tài)頁面有明顯的提高。
●HTML頁面不會受系統(tǒng)相關漏洞的影響。靜態(tài)頁面可以減少被攻擊的漏洞,防止SQL注入。同時,數(shù)據庫出錯時,也不影響網站正常訪問。靜態(tài)化的核心環(huán)節(jié)包括兩個步驟:第一步,模仿瀏覽器訪問相關動態(tài)頁面;第二步,將獲取的HTML文本(也就是動態(tài)頁面的執(zhí)行結果)保存為服務器中的HTML文件。
本系統(tǒng)使用了文件系統(tǒng)對象(FSO)的生成方法并保存文件以實現(xiàn)靜態(tài)化,主要由MakeHtml.java以及MakeArticle.java來完成。需要靜態(tài)化的的頁面為arti
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024版土地使用權轉讓合同(商業(yè)用地)2篇
- 2025年度餐飲企業(yè)品牌形象設計與宣傳推廣合同6篇
- 2024租賃期間廠房轉租管理的委托出租合同
- 2024年皮革原料購銷合同范本
- 2025年度旅游度假精美合同協(xié)議范本(休閑度假版)3篇
- 2024年能源結構調整-充電樁施工建設及管理協(xié)議3篇
- 2024年蘋果手機消費者維權服務合同范本3篇
- 2024年項目評估合作協(xié)議
- 2024年度倒插門女婿離婚后財產保全與執(zhí)行協(xié)議3篇
- 2025年度網絡安全防護解決方案調研委托合同集錦3篇
- (完整版)鋼筋加工棚驗算
- 安徽省合肥市廬陽區(qū)2023-2024學年三年級上學期期末數(shù)學試卷
- 概念方案模板
- 西南交大畢業(yè)設計-地鐵車站主體結構設計
- 2024年山東傳媒職業(yè)學院高職單招(英語/數(shù)學/語文)筆試歷年參考題庫含答案解析
- 江蘇省南通市崇川區(qū)2023-2024學年三年級上學期期末語文試卷
- 華電行測題庫及答案2024
- crtd植入術護理查房
- 掃雪鏟冰安全教育培訓
- 人教版三年級下冊必讀書目《中國古代寓言故事》
- 涉密內網分級保護設計方案
評論
0/150
提交評論