Java Web 開發(fā)發(fā)展簡介

遠古期 - 靜態(tài)頁面時代

講Java Web開發(fā)的歷史進程牌芋,不得不提Web開發(fā)的歷史進程乎完。
在互聯(lián)網(wǎng)剛發(fā)展的時候,那時候的網(wǎng)站功能是很簡單的亡容。那時候的網(wǎng)站還都是靜態(tài)的嗤疯。這里所說的靜態(tài)是指,請求訪問的網(wǎng)頁都是事先編輯好的萍倡,不能改變的身弊。

這里先講下當時一個請求是如何返回結(jié)果的。

比如列敲,你想訪問新浪上的一張圖片阱佛,會在瀏覽器鍵入這個圖片的地址:

image.png

瀏覽器會根據(jù)地址像新浪服務器發(fā)送HTTP請求。新浪服務器上的HTTP Server接收到請求后戴而,會根據(jù)路徑地址/img/12345.jpg查找的這個文件凑术,然后read文件,再把圖片數(shù)據(jù)發(fā)送給客戶端所意,客戶端的瀏覽器就能正確展示圖片了淮逊。

image.png

也就是說,這里的URL對服務器來說就是查找文件的地址扶踊,而文件必須實實在在存在于服務器中的特定目錄下的泄鹏。

缺點
很明顯,訪問的資源必須事先已經(jīng)存在秧耗,否則訪問不到备籽。而動態(tài)展示也是沒法實現(xiàn)的。比如:某人剛發(fā)布了一篇文章分井,想在首頁立即看到是不可能的车猬。只能重新手動編輯首頁,把文章鏈接加進去

混沌期 - CGI時代

然而尺锚,如果頁面一直是靜態(tài)的額珠闰,也就不會有現(xiàn)在紛繁復雜的網(wǎng)站了。那么動態(tài)展示頁面的解決方案是什么呢瘫辩?是CGI伏嗜!
CGI全稱是通用網(wǎng)關(guān)接口(Common Gateway Interface)。那么它的作用是啥呢伐厌?

CGI是啥

首先阅仔,要清楚CGI是啥?
CGI是一個可執(zhí)行的程序或者可運行的腳本弧械。幾乎所有語言都能寫CGI八酒,像python,C刃唐,甚至shell羞迷。

舉個例子界轩。下面一段C代碼,經(jīng)過編譯成可執(zhí)行程序后衔瓮,就是一個CGI浊猾。

int _tmain(int argc, _TCHAR* argv[])
{
    printf("Content-type:text/html\n\n");
    printf("%s",getenv("QUERY_STRING")); //打印get獲取的信息
    return 0;
}

再或者,下面一個python腳本热鞍,也是一個CGI

#!/usr/bin/python
# -*- coding: UTF-8 -*-

print "Content-type:text/html"
print                               # 空行葫慎,告訴服務器結(jié)束頭部
print '<html>'
print '<head>'
print '<meta charset="utf-8">'
print '<title>Hello Word - 我的第一個 CGI 程序!</title>'
print '</head>'
print '<body>'
print '<h2>Hello Word! 我是來自菜鳥教程的第一CGI程序</h2>'
print '</body>'
print '</html>'

OK薇宠,知道了CGI是可執(zhí)行的程序或腳本偷办,但是怎么工作的呢?

CGI怎么用

image.png

如上圖澄港,當瀏覽器發(fā)送一個CGI請求后椒涯,服務器會啟動一個進程運行CGI程序或腳本,由CGI來處理數(shù)據(jù)回梧,并將結(jié)果返回給服務器废岂,服務器再將結(jié)果返回給瀏覽器。

舉個表單提交的例子:

<form id="form" name="form" method="post" action="http://localhost/cgi-bin/test/cgi_test.cgi">
  <p>輸入內(nèi)容:
    <input type="text" name="user" id="user" />
  </p>
  <p>
    <input type="submit" name="submit" id="submit" value="提交" />
  </p>
</form>

上面是一個表單提交的html代碼狱意,展示的效果是下面這個樣子:

image.png

細心的你會發(fā)現(xiàn)湖苞,action的值是http://localhost/cgi-bin/test/cgi_test.cgi。這里详囤,cgi_test.cgi就是一個cgi程序财骨。
還記得上面那段C++代碼嗎?

int _tmain(int argc, _TCHAR* argv[])
{
    printf("Content-type:text/html\n\n");
    printf("%s",getenv("QUERY_STRING")); //打印get獲取的信息
    return 0;
}

cgi_test.cgi就是這段代碼編譯出來的可執(zhí)行程序纬纪。
這段代碼的作用是什么呢蚓再?
作用是將表單提交的信息直接打印出來滑肉。
如何做到的包各?
只有兩行代碼,第二行代碼是關(guān)鍵靶庙。getenv()是C函數(shù)庫中的函數(shù)问畅,getenv("QUERY_STRING")的意思是讀取環(huán)境變量QUERY_STRING的值。而QUERY_STRING的值就是表單提交的信息六荒。
OK护姆,這個CGI的功能就清晰了。表單提交后展示下面的結(jié)果也就不奇怪了:

image.png

我們再通過一個圖梳理下上述流程:

image.png

綜上掏击,CGI工作模式示意圖如下:

image.png

CGI的特點

  • 由Http Server喚起卵皂。常見的Http Server如Apache,Lighttpd砚亭,nginx都支持CGI
  • CGI單獨啟動進程灯变,并且每次調(diào)用都會重新啟動進程
  • 可以用任何語言編寫殴玛,只要該語言支持標準輸入、輸出和環(huán)境變量

CGI的缺點

  • 消耗資源多:每個請求都會啟動一個CGI進行添祸,進程消耗資源15M內(nèi)存的話滚粟,同時到達100個請求的話,就會占用1.5G內(nèi)存刃泌。如果請求更多凡壤,資源消耗是不可想象的弦赖。
  • :啟動進程本身就慢疗涉。每次啟動進程都需要重新初始化數(shù)據(jù)結(jié)構(gòu)等,會變得更慢厅瞎。

引申

為了解決CGI重復啟動進程和初始化的問題林艘,后來出現(xiàn)了FastCGI

開荒期 - Servlet時代

在CGI繁榮發(fā)展的時代盖奈,Java還沒有發(fā)展起來。當Java開始參與歷史狐援,引領(lǐng)潮流的時候钢坦,也必然會借鑒和改進之前的技術(shù)和思想。

鑒于CGI的一些缺點啥酱,Java Web在開始設(shè)計的時候就想出了一種解決方案 -- Servlet
同樣爹凹,第一個問題,Servlet是啥镶殷?

Servlet是啥禾酱?

舉個例子,網(wǎng)站一般都有注冊功能绘趋。當用戶填寫好注冊信息颤陶,點擊“注冊”按鈕時,誰來處理這個請求陷遮?用戶名是否重復誰來校驗滓走?用戶名和密碼需要寫入數(shù)據(jù)庫,誰來寫入帽馋?是Servlet搅方!

Servlet是實現(xiàn)javax.servlet.Servlet接口的類。一般處理Web請求的Servlet還需要繼承javax.servlet.http.HttpServlet

abstract class HttpServlet implements Servlet{
    void doGet();
    void doPost();
}

doGet()方法處理GET請求
doPost()方法處理POST請求

瀏覽器發(fā)來的請求是怎么被Servlet處理的呢绽族?還是舉表單提交的例子姨涡。
我們假設(shè)表單樣式如下,只是簡單提交兩個數(shù)據(jù):網(wǎng)址名和網(wǎng)址吧慢。并假設(shè)處理URL為http://localhost:8080/TomcatTest/HelloForm

image.png

瀏覽器工作
當表單使用GET方式提交時涛漂,瀏覽器會把表單數(shù)據(jù)組裝成這樣的URL:http://localhost:8080/TomcatTest/HelloForm?name=菜鳥教程&url=www.runoob.com

好,現(xiàn)在瀏覽器的任務暫時告一段落检诗,開始Java Web服務工作了匈仗。

Java Web服務
首先底哗,我們得指定http://localhost:8080/TomcatTest/HelloForm這個URL由誰來處理。這個映射關(guān)系需要在web.xml中配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
  <servlet>
    <servlet-name>HelloForm</servlet-name>
    <servlet-class>com.runoob.test.HelloForm</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloForm</servlet-name>
    <url-pattern>/TomcatTest/HelloForm</url-pattern>
  </servlet-mapping>
</web-app>

web.xml中配置的意思是:當URI為/TomcatTest/HelloForm時锚沸,交給com.runoob.test.HelloForm處理跋选。而HelloForm正是個Servlet。

因此哗蜈,我們需要編寫HelloForm這樣一個Servlet:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class HelloForm
 */
@WebServlet("/HelloForm")
public class HelloForm extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public HelloForm() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 設(shè)置響應內(nèi)容類型
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();
        String title = "使用 GET 方法讀取表單數(shù)據(jù)";
        // 處理中文
        String name =new String(request.getParameter("name").getBytes("ISO8859-1"),"UTF-8");
        String docType = "<!DOCTYPE html> \n";
        out.println(docType +
            "<html>\n" +
            "<head><title>" + title + "</title></head>\n" +
            "<body bgcolor=\"#f0f0f0\">\n" +
            "<h1 align=\"center\">" + title + "</h1>\n" +
            "<ul>\n" +
            "  <li><b>站點名</b>:"
            + name + "\n" +
            "  <li><b>網(wǎng)址</b>:"
            + request.getParameter("url") + "\n" +
            "</ul>\n" +
            "</body></html>");
    }
    
    // 處理 POST 方法請求的方法
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

由于請求方式是GET前标,因此需要doGet()方法來處理。仔細閱讀doGet()方法的代碼距潘,發(fā)現(xiàn)處理邏輯只是把表單數(shù)據(jù)放入到了一段html代碼中炼列。這段html代碼會被傳輸給瀏覽器,然后瀏覽器渲染出結(jié)果音比,如下圖所示:

image.png

Servlet的特點

Servlet相對于CGI有了很大的改進俭尖,效率更高,功能更強大洞翩,更容易移植稽犁。主要表現(xiàn)在一下幾個方面:

  • CGI每個請求啟動一個進程,而Servlet是更輕量的線程骚亿。線程和進程的對比和優(yōu)劣請自行Google已亥。
  • CGI每個進程都需要初始化,Servlet只初始化一次實例就行
  • Servlet依托于Java語言来屠,具有很好的跨平臺型虑椎。CGI根據(jù)語言的不同,跨平臺型不同
  • CGI與數(shù)據(jù)庫連接需要重連俱笛,Servlet可以使用數(shù)據(jù)庫連接池捆姜。
  • Java有豐富的、各種各樣的庫函數(shù)

Servlet的缺點

看上面的代碼迎膜,會發(fā)現(xiàn)泥技,html代碼是寫在Java代碼中的。對于前端人員來說星虹,這種形式非常非常難以開發(fā)和修改零抬。

Servlet的升級 -- JSP

Servlet是在Java代碼中寫HTML代碼镊讼。與之對應的就是在HTML代碼中寫Java代碼宽涌,這就是JSP。

JSP是啥蝶棋?

JSP:JavaServer Pages
簡單點說卸亮,就是可以在html中寫Java代碼。

還是先從例子中大概了解下JSP:

還是上面表單處理的例子玩裙。表單的html代碼就不展示了兼贸,我們直接模擬GET請求段直,即在瀏覽器中輸入地址:http://localhost:8080/testjsp/main.jsp?name=菜鳥教程&url=http://www.runoob.com

很明顯,這個URL的關(guān)鍵是main.jsp溶诞。這個文件的內(nèi)容是啥呢鸯檬?
main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鳥教程(runoob.com)</title>
</head>
<body>
<h1>使用 GET 方法讀取數(shù)據(jù)</h1>
<ul>
<li><p><b>站點名:</b>
   <%= request.getParameter("name")%>
</p></li>
<li><p><b>網(wǎng)址:</b>
   <%= request.getParameter("url")%>
</p></li>
</ul>
</body>
</html>

這就是JSP,在html代碼中插入Java代碼螺垢。java代碼被<% %>所包圍喧务。
<%= request.getParameter("name")%>表示獲取請求參數(shù)name的值,<%= request.getParameter("url")%>表示獲取請求參數(shù)url的值枉圃。最終展示結(jié)果是怎樣的呢功茴?看下圖:

image.png

JSP是如何工作的?

為啥html代碼中可以寫Java代碼呢孽亲?看下圖:

image.png

其實原理是這樣的:

就像其他普通的網(wǎng)頁一樣坎穿,您的瀏覽器發(fā)送一個HTTP請求給服務器。

Web服務器識別出這是一個對JSP網(wǎng)頁的請求返劲,并且將該請求傳遞給JSP引擎玲昧。通過使用URL或者.jsp文件來完成。

JSP引擎從磁盤中載入JSP文件篮绿,然后將它們轉(zhuǎn)化為servlet酌呆。這種轉(zhuǎn)化只是簡單地將所有模板文本改用println()語句,并且將所有的JSP元素轉(zhuǎn)化成Java代碼搔耕。

JSP引擎將servlet編譯成可執(zhí)行類隙袁,并且將原始請求傳遞給servlet引擎。

Web服務器的某組件將會調(diào)用servlet引擎弃榨,然后載入并執(zhí)行servlet類菩收。在執(zhí)行過程中,servlet產(chǎn)生HTML格式的輸出并將其內(nèi)嵌于HTTP response中上交給Web服務器鲸睛。

Web服務器以靜態(tài)HTML網(wǎng)頁的形式將HTTP response返回到您的瀏覽器中娜饵。

最終,Web瀏覽器處理HTTP response中動態(tài)產(chǎn)生的HTML網(wǎng)頁官辈,就好像在處理靜態(tài)網(wǎng)頁一樣箱舞。

用一句話來講:每個JSP都最終會變成對應的Servlet執(zhí)行

JSP的缺點

在HTML代碼中寫Java代碼,方便了前端人員拳亿,但是苦了后端人員晴股。因此,單純使用JSP肺魁,開發(fā)效率依舊不高电湘。

后來,有牛人發(fā)現(xiàn),Servlet天生非常適合邏輯處理(因為主要是Java代碼)寂呛,而JSP非常適合頁面展示(因為主要是html代碼)怎诫,那么在結(jié)合Servlet和JSP各自的優(yōu)缺點后,誕生了Web開發(fā)中最常用和最重要的架構(gòu)設(shè)計模式:MVC

發(fā)展期 - MVC時代

MVC模式(Model-View-Controller)是軟件工程中的一種軟件架構(gòu)模式贷痪,把軟件系統(tǒng)分為三個基本部分:模型(Model)幻妓、視圖(View)和控制器(Controller):

  • Controller——負責轉(zhuǎn)發(fā)請求,對請求進行處理
  • View——負責界面顯示
  • Model——業(yè)務功能編寫(例如算法實現(xiàn))劫拢、數(shù)據(jù)庫設(shè)計以及數(shù)據(jù)存取操作實現(xiàn)
image.png

簡而言之涌哲,請求發(fā)來后,會首先經(jīng)過Controller層處理尚镰,需要返回的結(jié)果封裝成對象傳遞給JSP阀圾,然后JSP負責取出數(shù)據(jù)展示就夠了。這樣狗唉,后端開發(fā)人員只負責編寫Servlet初烘,前端人員負責JSP,極大提升了開發(fā)效率分俯。

@WebServlet("/userPosts")
public class UserPostController extends HttpServlet {

    private static final long serialVersionUID = -4208401453412759851L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        User user = Data.getByUsername(username);
        List<Post> posts = Data.getPostByUser(user);

        req.setAttribute("posts", posts);
        req.setAttribute("user", user);
        RequestDispatcher dispatcher = req.getRequestDispatcher("/templates/userPost.jsp");
        dispatcher.forward(req, resp);
    }
}

像上面這段代碼肾筐,UserPostController就是一個Servlet,負責邏輯處理缸剪。需要返回的數(shù)據(jù)封裝到HttpServletRequest對象中吗铐,傳遞給jsp頁面。而負責展示的就是/templates/userPost.jsp這個jsp文件杏节。

繁盛期 - 框架時代

有了Servlet和JSP唬渗,相當于有了武器。有了MVC奋渔,相當于有了戰(zhàn)術(shù)镊逝。但是武器和戰(zhàn)術(shù)之間還缺少一層,就是具體實施者嫉鲸。

實踐證明撑蒜,單純使用Servlet、JSP和MVC開發(fā)玄渗,依然會面臨諸多的問題座菠。而程序員普遍存在一種特質(zhì),就是懶藤树。因為懶浴滴,所以才想著能有更簡單的解決辦法。因為懶也榄,針對一些通用問題巡莹,才會想出通用解決方法√鹱希可以說降宅,因為懶,科技才有了進步囚霸。腰根。。這時候拓型,為了解放勞動力额嘿,一些開源框架營運而出。這些框架的目的只有一個:讓開發(fā)簡單劣挫,簡單册养,更簡單

提到Java Web框架,就不得不提幾乎所有開發(fā)者都知道的三大框架:SSH

SSH

關(guān)于三大框架压固,這里不做介紹了球拦,網(wǎng)上的文章鋪天蓋地。想要說的是:無論什么框架帐我,都是對常見問題的抽象和封裝坎炼,再出什么新的框架,也萬變不離其宗拦键,脫離不了Servlet這個根基谣光。學習的時候千萬不能跟著框架走,框架讓做什么就做什么芬为,而是要想為什么這樣做萄金。否則,今天學會了一個框架媚朦,明天又出了新的框架捡絮,又會抓瞎了。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瑞侮,一起剝皮案震驚了整個濱河市的圆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌半火,老刑警劉巖越妈,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钮糖,居然都是意外死亡梅掠,警方通過查閱死者的電腦和手機酌住,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阎抒,“玉大人酪我,你說我怎么就攤上這事∏胰” “怎么了都哭?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長逞带。 經(jīng)常有香客問我欺矫,道長,這世上最難降的妖魔是什么展氓? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任穆趴,我火速辦了婚禮,結(jié)果婚禮上遇汞,老公的妹妹穿的比我還像新娘毡代。我一直安慰自己,他們只是感情好勺疼,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布教寂。 她就那樣靜靜地躺著,像睡著了一般执庐。 火紅的嫁衣襯著肌膚如雪酪耕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天轨淌,我揣著相機與錄音迂烁,去河邊找鬼。 笑死递鹉,一個胖子當著我的面吹牛盟步,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躏结,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼却盘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了媳拴?” 一聲冷哼從身側(cè)響起黄橘,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屈溉,沒想到半個月后塞关,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡子巾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年帆赢,在試婚紗的時候發(fā)現(xiàn)自己被綠了小压。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡椰于,死狀恐怖怠益,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情廉羔,我是刑警寧澤溉痢,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布僻造,位于F島的核電站憋他,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏髓削。R本人自食惡果不足惜竹挡,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望立膛。 院中可真熱鬧揪罕,春花似錦、人聲如沸宝泵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儿奶。三九已至框往,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闯捎,已是汗流浹背椰弊。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓤鼻,地道東北人秉版。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像茬祷,于是被迫代替她去往敵國和親清焕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容