abstract:分別列舉了Java,Python,Ruby,Node.js的一些熱門框架,初學(xué)編程,發(fā)現(xiàn)這個(gè)世界里有各種各樣的編程語(yǔ)言和五花八門的框架技術(shù)歸納題主的問(wèn)題:這個(gè)世界上有各種各樣的框架,設(shè)計(jì)這些五花八門框架的初衷到底是什么?我們?cè)摬辉搶W(xué)習(xí)框架,該如何學(xué)習(xí)使用這些框架?回答題主的問(wèn)題:一、首先,到底什么是框架?想要回答這個(gè)問(wèn)題,我們要慢慢來(lái)。①首先從DRY原則開(kāi)始說(shuō)起Don't Repeat Y
分別列舉了Java,Python,Ruby,Node.js的一些熱門框架,初學(xué)編程,發(fā)現(xiàn)這個(gè)世界里有各種各樣的編程語(yǔ)言和五花八門的框架技術(shù)
歸納題主的問(wèn)題:
這個(gè)世界上有各種各樣的框架,設(shè)計(jì)這些五花八門框架的初衷到底是什么?我們?cè)摬辉搶W(xué)習(xí)框架,該如何學(xué)習(xí)使用這些框架?
回答題主的問(wèn)題:
一、首先,到底什么是框架?
想要回答這個(gè)問(wèn)題,我們要慢慢來(lái)。
①
首先從DRY原則開(kāi)始說(shuō)起
Don't Repeat Yourself,不要重復(fù)你的代碼。
DRY原則的重要性怎么提都不過(guò)分,很多人說(shuō)編程是種機(jī)械性的工作,而有很多程序員也自嘲為碼農(nóng),意為編程成了一種沒(méi)有技術(shù)含量的體力性工作。如果不想淪為這個(gè)境界,首先需要的就是將DRY原則融入你的血液,在今后的編碼工作中加以運(yùn)用。
1)最初級(jí)的DRY:語(yǔ)法級(jí)別
System.out.println(1); System.out.println(2); …… System.out.println(10);
我想只要學(xué)過(guò)基礎(chǔ)語(yǔ)法,都會(huì)采用下面的形式。
for (int i = 1; i <= 10; i++) { System.out.println(i); }
如果發(fā)現(xiàn)有任何人采用上面一種形式的編碼形式,那么不用懷疑,他對(duì)于編程絕對(duì)還沒(méi)有入門。
我們當(dāng)然會(huì)選擇省力的做法,這種做法不但省力,還會(huì)有利于我們后續(xù)修改或擴(kuò)展這組代碼,如:
for (int i = 1; i <= 10; i++) { System.out.println(i * 2 + 1); }
我們進(jìn)行這樣的修改,只需要修改一處,而上面的形式卻需要修改10處,當(dāng)然會(huì)更麻煩且更容易出錯(cuò),所以請(qǐng)記住能不重復(fù)就不重復(fù)。
2)進(jìn)階的DRY原則:方法級(jí)別
當(dāng)我們經(jīng)常寫一些重復(fù)性代碼時(shí),我們就要注意看能否將其抽取出來(lái)成為一個(gè)方法,如:
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
讓我們將其抽取到一個(gè)方法 threadSleep() 中,這樣我們只需要調(diào)用 threadSleep() 就可以實(shí)現(xiàn)原來(lái)的功能,不但所需敲擊的代碼更少,而且代碼看起來(lái)更加清楚明白。而為了增加這個(gè)方法的復(fù)用性,我們還可以將其中固定的數(shù)字抽取成為參數(shù),如:
private static void threadSleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } }
這樣我們就可以利用這個(gè)方法實(shí)現(xiàn)不同時(shí)間的sleep了。要注意提高代碼的復(fù)用性也是實(shí)踐DRY原則的一個(gè)重要方法,在后面我們也可以看到框架為了提高所謂的靈活性進(jìn)行的一些設(shè)計(jì),如在適當(dāng)?shù)奈恢迷黾訑U(kuò)展點(diǎn)。
3)繼續(xù)進(jìn)階的DRY原則:類型級(jí)別
現(xiàn)在我們看一個(gè)類
public class Person { private String name; private int age; // Setter & Getter ... }
我們新建一些Person類實(shí)例,并進(jìn)行一些操作:
Person person = new Person(); person.setName("jack"); person.setAge(18); Person person2 = new Person(); person2.setName("rose"); person2.setAge(17); ..... System.out.printf("Name: %s, Age:%d\n", person.getName(), person.getAge()); System.out.printf("Name: %s, Age:%d\n", person2.getName(), person2.getAge()); .....
觀察這些代碼,其實(shí)有很大的DRY改造空間,首先可以添加一個(gè)構(gòu)造方法
public Person(String name, int age) { this.name = name; this.age = age; }
其次,可以添加一個(gè)toString()方法
public String toString() { return String.format("Name: %s, Age: %d", name, age); }
這樣的話,上面的代碼就可以改成下面的形式。
Person person = new Person("jack", 18); Person person2 = new Person("rose", 17); ...... System.out.println(person.toString()); System.out.println(person2.toString()); ......
4)繼續(xù)繼續(xù)進(jìn)階的DRY原則:多個(gè)類組合級(jí)別
上面的代碼我們其實(shí)還是有改善空間,就是利用容器類
List<Person> list = new ArrayList<>(); list.add(new Person("jack", 18)); list.add(new Person("rose", 17)); ...... list.forEach(p -> System.out.println(p));
這里利用JDK8的Stream API以及Lambda表達(dá)式輸出,其實(shí)可以進(jìn)一步簡(jiǎn)化為
list.forEach(System.out::println);
這里我們可以看到,基本上我們寫代碼只寫有變化的代碼,而盡量不寫機(jī)械性重復(fù)性的代碼,其實(shí)后面我們就會(huì)知道,這就叫專注于業(yè)務(wù)邏輯,所謂業(yè)務(wù)邏輯就是你這個(gè)項(xiàng)目中,與別的項(xiàng)目都不一樣的地方,必須由你親自去編寫實(shí)現(xiàn)的部分。
其實(shí)容器類很大程度上也是為了幫助我們編寫代碼而被設(shè)計(jì)出來(lái)的,首先讓我們不必為每一個(gè)對(duì)象起名字(省去了person,person2,...等變量),然后又為批量操作提供了可能性。像是這樣一系列有用的類組合起來(lái)可以稱之為類庫(kù)。常用的類庫(kù)有Commons-Lang包等,為我們提供了一大批實(shí)用方法,我之所以提到類庫(kù),也是因?yàn)榭蚣芷鋵?shí)也是一種特殊的類庫(kù),但是卻與一般的類庫(kù)有著本質(zhì)的不同。
②
設(shè)計(jì)模式,更高層級(jí)的DRY應(yīng)用
上面我講到了DRY原則的幾個(gè)層次,一般情況下大家也早就這樣使用了,屬于入門之后很容易自己就想到得一些層次。但是設(shè)計(jì)模式不一樣,設(shè)計(jì)模式是經(jīng)過(guò)長(zhǎng)時(shí)間編碼之后,經(jīng)過(guò)系統(tǒng)性的總結(jié)所提出的針對(duì)某一類問(wèn)題的最佳解決方案,又稱之為最佳實(shí)踐。
而在小規(guī)模的編碼工作中,其實(shí)并不需要什么設(shè)計(jì)模式,只有大型程序才有設(shè)計(jì)模式發(fā)揮的空間,所以我們需要借助一些特定領(lǐng)域有足夠規(guī)模的問(wèn)題來(lái)了解一下設(shè)計(jì)模式存在的必要性。
1)連接數(shù)據(jù)庫(kù),進(jìn)行一些操作,并安全釋放數(shù)據(jù)庫(kù)連接。
public static boolean updatePassword(String username, String password, String newpassword) { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; boolean success = false; try { conn = beginTransaction(); stmt = conn.prepareStatement("select id, password from user where username = ?"); stmt.setString(1, username); rs = stmt.executeQuery(); if (rs.next()) { if (rs.getString("password").equals(password)) { PreparedStatement stmt2 = null; try { stmt2 = conn.prepareStatement("update user set password = ? where id = ?"); stmt2.setString(1, newpassword); stmt2.setLong(2, rs.getLong("id")); success = stmt2.executeUpdate() > 0; } finally { safeClose(stmt2); } } } commitTransaction(conn); return success; } catch (SQLException e) { rollbackTransaction(conn); throw new RuntimeException(e); } finally { safeClose(rs); safeClose(stmt); safeClose(conn); } }
上面是一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)事務(wù),雖然只有一個(gè)查詢和一個(gè)更新,但是想要將其繼續(xù)簡(jiǎn)化卻并不容易,雖然其中有關(guān)于業(yè)務(wù)邏輯的部分只是少量幾行代碼,但是初始化,異常,提交,回滾操作讓我們很難抽取出一個(gè)合適的方法來(lái)。雖然我們已經(jīng)抽取出了 begin,commit,rollback,safeClose等方法,但是仍嫌繁瑣。
我們發(fā)現(xiàn)之所以我們難以抽取方法,主要是因?yàn)榱鞒?,因?yàn)槔锩鏍砍兜搅鞒炭刂?,而流程控制一般是由我們程序員來(lái)控制的,所以也就必然需要我們手動(dòng)編碼來(lái)完成。難道真的就不能繼續(xù)簡(jiǎn)化了嗎?這就是需要設(shè)計(jì)模式的時(shí)候了。
2)應(yīng)用設(shè)計(jì)模式「模板方法模式」
public static boolean updatePassword(String username, String password, String newpassword) { return connection(conn -> statement(conn, "select id, password from user where username = ?", stmt -> { stmt.setString(1, username); return resultSet(stmt, rs -> { if (rs.next()) { if (rs.getString("password").equals(password)) { long id = rs.getLong("id"); return statement(conn, "update user set password = ? where id = ?", stmt2 -> { stmt2.setString(1, newpassword); stmt2.setLong(2, id); return stmt2.executeUpdate() == 1; }); } } return false; }); })); }
可以看到,所有的conn,stmt,rs的開(kāi)啟和關(guān)閉,事務(wù)的提交和回滾都不用自己手動(dòng)編寫代碼進(jìn)行操作了,之所以可以達(dá)到這個(gè)效果,就是因?yàn)槭褂昧四0宸椒ㄔO(shè)計(jì)模式,核心就是通過(guò)回調(diào)方法傳遞想對(duì)資源進(jìn)行的操作,然后將控制權(quán)交給另一個(gè)方法,讓這個(gè)方法掌握流程控制,然后適當(dāng)?shù)臅r(shí)候回調(diào)我們的代碼(也就是我們自己寫的業(yè)務(wù)邏輯相關(guān)的代碼)。
這是需要額外寫的幾個(gè)方法
public interface ConnectionCallback<T> { T doConnection(Connection conn) throws SQLException; } public interface StatementCallback<T> { T doStatement(PreparedStatement stmt) throws SQLException; } public interface ResultSetCallback<T> { T doResultSet(ResultSet rs) throws SQLException; } public static <T> T connection(ConnectionCallback<T> callback) { Connection conn = null; T result = null; try { conn = beginTransaction(); result = callback.doConnection(conn); commitTransaction(conn); } catch (SQLException e) { rollbackTransaction(conn); throw new RuntimeException(e); } finally { safeClose(conn); } return result; } public static <T> T statement(Connection conn, String sql, StatementCallback<T> callback) throws SQLException { PreparedStatement stmt = null; T result = null; try { stmt = conn.prepareStatement(sql); result = callback.doStatement(stmt); } finally { safeClose(stmt); } return result; } public static <T> T resultSet(PreparedStatement stmt, ResultSetCallback<T> callback) throws SQLException { ResultSet rs = null; T result = null; try { rs = stmt.executeQuery(); result = callback.doResultSet(rs); } finally { safeClose(rs); } return result; }
你們可能會(huì)疑惑,這些代碼加上我們寫的業(yè)務(wù)邏輯的代碼,比原來(lái)的代碼還要長(zhǎng),有什么必要使用這個(gè)設(shè)計(jì)模式。這正是我前面已經(jīng)指出的一個(gè)問(wèn)題,那就是要你的程序規(guī)模足夠大才有必要應(yīng)用設(shè)計(jì)模式,試想如果你有上百個(gè)乃至上千個(gè)數(shù)據(jù)庫(kù)操作方法需要寫,那么是不是寫這幾個(gè)額外的方法,就不算什么了呢。
其實(shí)這正是DRY原則在更高層次上的應(yīng)用,即結(jié)合設(shè)計(jì)模式來(lái)達(dá)到更高層次的代碼復(fù)用效果,進(jìn)而應(yīng)用DRY原則。而想要在這個(gè)層次繼續(xù)向上攀升,那就必須是結(jié)合眾多設(shè)計(jì)模式以及一些高層架構(gòu)設(shè)計(jì),能夠幫助我們實(shí)現(xiàn)這一目的的就是框架。
3)框架,是設(shè)計(jì)模式的集大成者,是DRY原則的最高應(yīng)用
先讓我們來(lái)看一下,使用框架會(huì)是什么樣的一種體驗(yàn)?
這里以Hibernate + Spring聲明式事務(wù)為例
@Transactional public boolean updatePassword(String username, String password, String newpassword) { User user = (User) session().createQuery("from User where username = :username") .setString("username", username) .uniqueResult(); if (user != null && user.getPassword().equals(password)) { user.setPassword(newpassword); return true; } return false; }
可以發(fā)現(xiàn)令人驚訝的簡(jiǎn)潔,而且代碼邏輯異常清晰,完全不需要考慮conn,stmt,rs等資源的釋放,以及事務(wù)的提交和回滾,但是這些事情其實(shí)框架已經(jīng)默默的幫我們做到了。這才叫真正的專注于業(yè)務(wù)邏輯,盡最大可能的只寫與業(yè)務(wù)邏輯有關(guān)的代碼。
當(dāng)然這些框架的效果雖然神奇,其實(shí)只要細(xì)細(xì)探究其內(nèi)部原理,是完全可以理解并掌握的。
二、那么問(wèn)題就來(lái)了,框架到底是什么?要不要學(xué),怎么學(xué)?
上面我說(shuō)過(guò)了,框架其實(shí)就是一個(gè)或一組特殊的類庫(kù),特殊在什么地方?特殊在控制權(quán)轉(zhuǎn)移!
框架與一般類庫(kù)不同的地方是,我們調(diào)用類庫(kù),而框架調(diào)用我們。也就是說(shuō)框架掌握整個(gè)程序的控制權(quán),我們必須一定程度上把程序流程的控制權(quán)交給框架,這樣框架才能更好的幫助我們。
下面以JavaWeb開(kāi)發(fā)為例再進(jìn)行一些說(shuō)明,并順便簡(jiǎn)單介紹一下JavaWeb的一些脈絡(luò)。
①
靜態(tài)網(wǎng)頁(yè)時(shí)代
本來(lái)網(wǎng)站都是一個(gè)個(gè)靜態(tài)HTML組成的,或許這些網(wǎng)頁(yè)還是用Dreamweaver寫的,但是這樣的靜態(tài)頁(yè)面顯然不能滿足我們,很快我們就迎來(lái)了動(dòng)態(tài)網(wǎng)頁(yè)的時(shí)代。
②
Servlet時(shí)代
如果熟悉HTTP協(xié)議的話,我們就知道其實(shí)訪問(wèn)網(wǎng)頁(yè)的過(guò)程不過(guò)是一次TCP連接罷了。瀏覽器發(fā)起TCP連接到服務(wù)器,服務(wù)器接受請(qǐng)求,然后返回HTML代碼作為響應(yīng)。那么我們完全可以等到接受到請(qǐng)求之后,再動(dòng)態(tài)生成HTML代碼返回給客戶端。
Servlet就是這么做的,其主要代碼不過(guò)是利用out.write()一點(diǎn)一點(diǎn)的輸出HTML代碼罷了。當(dāng)然我們可以在其中摻雜一點(diǎn)動(dòng)態(tài)的東西,如返回當(dāng)前的時(shí)間。
out.write("<!DOCTYPE html>\r\n"); out.write("<html>\r\n"); out.write("<head>\r\n"); out.write("<title>Index Page</title>\r\n"); out.write("</head>\r\n"); out.write("<body>\r\n"); out.write("Hello, " + new Date() + "\r\n"); out.write("</body>\r\n"); out.write("</html>\r\n");
③ JSP包打天下的時(shí)代
純粹的Servlet很是丑陋,給前端程序員理解和修改這樣的代碼帶來(lái)了很多困難。因此JSP技術(shù)被發(fā)明了出來(lái),原理也不復(fù)雜,就是不直接寫Servlet,而是先寫好JSP文件,再由服務(wù)器將JSP文件編譯成Servlet。而JSP中是以常見(jiàn)的HTML標(biāo)簽為主,這樣前端程序員就能方便的修改這些代碼了。
<!DOCTYPE html> <html> <head> <title>Index Page</title> </head> <body> Hello, <%=new Date()%> </body> </html>
PS:由只使用 Servlet到使用JSP,雖然是一個(gè)簡(jiǎn)單的變化,但這迎合了前后端專業(yè)分工的大趨勢(shì),讓前段人員只需要懂得HTML/CSS/JavaScrip代碼就可以開(kāi)始工作,而不需要學(xué)習(xí)Servlet那枯燥無(wú)味的用法,因此借著JSP技術(shù)的東風(fēng),JavaWeb技術(shù)迅速的擴(kuò)展開(kāi)來(lái)了。
④ Servlet + JSP 時(shí)代
隨著JSP技術(shù)的發(fā)展,用它寫成的網(wǎng)站也越來(lái)越大,業(yè)務(wù)邏輯也越來(lái)越復(fù)雜。開(kāi)發(fā)人員漸漸發(fā)現(xiàn)整個(gè)網(wǎng)站漸漸的再次變成了一團(tuán)亂麻,不僅僅是JSP中夾雜了大量的Java代碼,頁(yè)面之間的耦合關(guān)系也越來(lái)越緊密。
即便是要修改一個(gè)簡(jiǎn)單的按鈕文本,或者是引入一段靜態(tài)的內(nèi)容,也需要打開(kāi)越來(lái)越龐大的JSP頁(yè)面,艱難到找到需要修改的部分,有時(shí)還不僅僅是一處,這種修改是有很大的風(fēng)險(xiǎn)的,完全有可能引入新的錯(cuò)誤。
這時(shí)候開(kāi)發(fā)者漸漸意識(shí)到,僅僅使用JSP是不行的,JSP承擔(dān)了太多的責(zé)任。這時(shí)人們又想起了Servlet,Servlet中主要使用Java代碼,處理業(yè)務(wù)邏輯非常輕松。如果JSP只使用HTML代碼,而將業(yè)務(wù)邏輯的代碼轉(zhuǎn)移到Servlet中,就可以大大的減輕JSP的負(fù)擔(dān),并且讓前后端分工更加明確。
⑤
MVC模式時(shí)代
在
Servlet + JSP模式的基礎(chǔ)上,Java陣營(yíng)進(jìn)一步發(fā)展出了一種適合JavaWeb應(yīng)用的設(shè)計(jì)模式,MVC設(shè)計(jì)模式,即將程序分為顯示層(Viewer),控制層(Controller),模型層(Model)。如下圖所示:
一次典型的訪問(wèn)是這樣的流程:
1. 用戶輸入網(wǎng)址或點(diǎn)擊鏈接或提交表單,瀏覽器發(fā)起請(qǐng)求
2. --> 通過(guò)互聯(lián)網(wǎng),通過(guò)HTTP協(xié)議 -->
3. Tomcat接受到HTTP請(qǐng)求,生成HttpServletRequest對(duì)象,根據(jù)Web.xml的配置,調(diào)用開(kāi)發(fā)者編寫的HttpServlet,HttpServlet根據(jù)請(qǐng)求內(nèi)容,調(diào)用JavaBean獲取數(shù)據(jù),JavaBean從數(shù)據(jù)庫(kù)獲取數(shù)據(jù),返回HttpServlet,HttpServlet將數(shù)據(jù)轉(zhuǎn)發(fā)給JSP,JSP負(fù)責(zé)將數(shù)據(jù)渲染為HTML,由Tomcat負(fù)責(zé)將HTML轉(zhuǎn)化為HTTP響應(yīng),返回客戶端。
4. --> 通過(guò)互聯(lián)網(wǎng),通過(guò)HTTP協(xié)議 -->
5. 客戶端瀏覽器接收到HTTP響應(yīng),瀏覽器將HTML渲染為頁(yè)面,并運(yùn)行其中可能存在的JavaScript進(jìn)一步調(diào)整界面。
整個(gè)流程必須由開(kāi)發(fā)者精確設(shè)計(jì)才能運(yùn)作流暢,其中客戶端HTML和JavaScript屬于前端設(shè)計(jì),服務(wù)器運(yùn)行的其他內(nèi)容屬于后端設(shè)計(jì)。雖然符合J2EE規(guī)范的Tomcat等應(yīng)用服務(wù)器已經(jīng)幫我們實(shí)現(xiàn)了最復(fù)雜的一塊,即HTTP協(xié)議部分,還給我們提供了JSP這個(gè)模板引擎,以及自定義標(biāo)簽等手段。但是在控制層,在模型層,J2EE能給我們的幫助少之甚少。
就拿用戶提交一個(gè)表單為例,而我們?cè)赟ervlet中獲取參數(shù)為例,雖然不用我們解析HTTP報(bào)文,應(yīng)該已經(jīng)是要謝天謝地了,但是我們要做的事情仍然很多,分析一下:
1. 客戶端傳過(guò)來(lái)的數(shù)據(jù)全是文本,而我們需要的是Java對(duì)象。
2. 凡是文本就有編碼問(wèn)題,而這需要前后端配合解決。
3. 客戶端的輸入是不可信的,我們必須校驗(yàn)參數(shù)的合法性。
4. 我們還必須將校驗(yàn)結(jié)果反饋給客戶,并且最好不要讓客戶全部重新輸入。
5. 我們往往不是只有一個(gè)參數(shù)需要,而是有幾個(gè)甚至更多參數(shù),要妥善的處理各種情況組合。
這些事情幾乎全部都需要我們手動(dòng)編碼來(lái)完成,幾乎每一個(gè) Servlet 都充斥著這樣的代碼,設(shè)置編碼,獲取參數(shù),校驗(yàn)參數(shù),校驗(yàn)通不過(guò)返回錯(cuò)誤信息,校驗(yàn)通過(guò)則進(jìn)行業(yè)務(wù)處理。而更重要的是,獲取參數(shù)僅僅是整個(gè)流程中的一小步,我們的Servlet中存在著大量的重復(fù)性,機(jī)械性代碼,而處理業(yè)務(wù)邏輯的代碼可能只有一兩行。
⑥
JavaWeb框架
既然存在著大量的重復(fù),我們當(dāng)然不能忍,必須請(qǐng)出DRY大法。顯然JavaWeb應(yīng)用是一個(gè)規(guī)模龐大,流程復(fù)雜的應(yīng)用,我們正需要JavaWeb框架的幫助。以Struts2框架為例,他能給我們什么幫助呢?
1. 在控制層,由Struts2的核心控制器接管控制權(quán),將本來(lái)在Web.xml進(jìn)行配置的一些工作,轉(zhuǎn)移到自定義的struts.xml文件中,這個(gè)文件的配置形式更友好。
2. Struts2封裝了Serlvet Api,使用POJO對(duì)象作為控制器(Action),大量使用反射,不要求繼承特定類,有利于復(fù)用及單元測(cè)試。提供ActionSupport類,結(jié)合struts2標(biāo)簽,能很方面實(shí)現(xiàn)的校驗(yàn)信息的收集及反饋。
3. 提供國(guó)際化支持,在顯示層有國(guó)際化相關(guān)的標(biāo)簽,在控制層由國(guó)際化相關(guān)的API。提供基于配置的校驗(yàn)及JS生成技術(shù)。智能化的參數(shù)類型轉(zhuǎn)換,支持自定義轉(zhuǎn)換器。提供Action攔截器,方便實(shí)現(xiàn)AOP模式。
4. 提供了基于OGNL表達(dá)式的數(shù)據(jù)共享模式,前后端數(shù)據(jù)交流更簡(jiǎn)單,提供了Struts2標(biāo)簽庫(kù),簡(jiǎn)單好用,支持多種模板,如FreeMarker,支持各種插件,如JSON,支持整合多種框架,如Spring??傊痪湓?,能在各方各面給我們強(qiáng)大的幫助。
⑦
所以當(dāng)然要學(xué)框架,要用框架,那么要怎么學(xué)?
1. 用框架要知其然,還要知其所以然,要大體明白框架實(shí)現(xiàn)一個(gè)功能特性的原理,不能只是會(huì)用,只是覺(jué)得很神奇就可以了。就拿前面的Hibernate + Spring聲明式事務(wù)為例,要弄明白框架這部分是怎么實(shí)現(xiàn)的。
2. 首先要夯實(shí)你的語(yǔ)言基礎(chǔ),如JavaSE基礎(chǔ),語(yǔ)法掌握,用法掌握,有些同學(xué)語(yǔ)法還不熟練就開(kāi)始學(xué)框架,等于地基沒(méi)打就起高樓,你可能會(huì)快一步,但是遲早要遇到瓶頸,甚至摔跟頭。
3. 那么何時(shí)開(kāi)始學(xué)習(xí)框架?我不建議新手一開(kāi)始就直接使用框架。
就好像一開(kāi)始學(xué)習(xí)編程語(yǔ)言,大家都不推薦直接使用IDE,一定要用命令行自己編譯運(yùn)行幾個(gè)文件之后,了解清楚了之后才可以使用IDE,要不然對(duì)于底層原理不了解,遇到問(wèn)題沒(méi)法自己手動(dòng)排查。
4. 使用框架也是一樣,如果不是自己寫多了重復(fù)性的代碼,就很難理解框架為什么要這么設(shè)計(jì)。如果不嘗試幾種不同的實(shí)現(xiàn),就很難理解框架為了靈活性而做出的設(shè)計(jì)和擴(kuò)展點(diǎn)。如果不寫幾十個(gè)權(quán)限檢查語(yǔ)句,就很難理解AOP到底有什么好處。
5. 框架這么好,我該全部使用框架嗎?首先只有在規(guī)模以上的程序中,才有應(yīng)用框架的必要,一個(gè)簡(jiǎn)單的程序沒(méi)必要使用框架,當(dāng)然如果你很熟練,使用也無(wú)所謂。
6. 要學(xué)習(xí)一下框架的核心源代碼,要為擴(kuò)展框架做好準(zhǔn)備,因?yàn)殡m然框架基本上還算靈活,但是面對(duì)錯(cuò)綜復(fù)雜的業(yè)務(wù)需求,永遠(yuǎn)不可能面面俱到,而你不了解框架的話,可能會(huì)給你實(shí)現(xiàn)業(yè)務(wù)需求造成麻煩。這也是有些人堅(jiān)持使用Servlet+JSP原生開(kāi)發(fā),而不是用框架的理由。
7. 只要程序大了,歸根究底還是要使用框架的,不是用別人寫好的,就是自己寫一套。這里我不建議自己寫,不要重復(fù)造輪子,總有專業(yè)造輪子的。你草草寫就的往往不如別人已經(jīng)千錘百煉的代碼。除非你是為了學(xué)習(xí)與研究的目的,自己寫,那就是一件很好的事情。