session是通過(guò)在客戶(hù)端生成一個(gè)cookie,所有請(qǐng)求會(huì)帶上這個(gè)cookie。一個(gè)cookie的NAME、Domain和Path屬性值均相同,則會(huì)覆蓋,若未設(shè)置Domain域,則域?yàn)閕p(不包括端口),因此應(yīng)用A的session被應(yīng)用B的session覆蓋了。
經(jīng)測(cè)試:tomcat、weblogic、websphere的session默認(rèn)都是JSESSIONID 為key來(lái)識(shí)別的,因此在沒(méi)有特別設(shè)置下,同一個(gè)域下的多個(gè)應(yīng)用session會(huì)互相覆蓋。
解決辦法:
設(shè)置各個(gè)應(yīng)用使用不同的cookie-name,或者將JSESSIONID的path路徑設(shè)置為不同。
1)WebLogic的Cookie相關(guān)配置:weblogic.xml
屬性名 |
默認(rèn)值 | 值 |
cookie-name | JSESSIONID | 如未設(shè)置,默認(rèn)為“JSESSIONID” |
cookie-path | NULL | 如未設(shè)置,默認(rèn)為“/” |
cookie-domain | NULL | 如未設(shè)置,默認(rèn)為發(fā)放cookie的服務(wù)器的域 |
1. <session-descriptor> 2. <session-param> 3. <param-name>CookieName</param-name> 4. <param-value>HADFCookie</param-value> 5. </session-param> 6. </session-descriptor>
2)websphere的設(shè)置(設(shè)置不同JSESSIONID的path)
應(yīng)用程序->企業(yè)應(yīng)用程序-> [Application Server] ->
會(huì)話管理->1.覆蓋會(huì)話管理(需打鉤).
會(huì)話管理->2.啟用 cookie(需打鉤)->修改'Cookie路徑'
3)Tomcat的設(shè)置(設(shè)置不同JSESSIONID的path)
修改tomcat/conf/server.xml:
1.tomcat5修改方法
在啟動(dòng)項(xiàng)中增加org.apache.catalina.SESSION_COOKIE_NAME參數(shù)
linux
JAVA_OPTS=’-Dorg.apache.catalina.SESSION_COOKIE_NAME=yousessionname‘win
set JAVA_OPTS=”-Dorg.apache.catalina.SESSION_COOKIE_NAME=yousessionname“
2.tomcat6和tomcat7修改方法相同
在Context容器標(biāo)簽上增加sessionCookieName參數(shù)
<Context path=”/” docBase=”webapp” reloadable=”false” sessionCookieName=”yoursessionname”></Context>
還可以加上sessionCookiePath
<Context ... sessionCookiePath="/" > ... </Context>
延伸閱讀:tomcat修改jsessionid在cookie中的名稱(chēng) http://blog.shilimin.com/338.htm
公司名稱(chēng):北京豐帆佳宇運(yùn)輸有限公司
公司簡(jiǎn)介:渣土消納證辦理,大型支護(hù)土方深度開(kāi)挖,礦山暗挖,基坑開(kāi)挖,建筑垃圾清運(yùn),渣土清運(yùn),砂石料配送,工程機(jī)械租賃業(yè)務(wù)
公司網(wǎng)址:www.bjffjy.com
正如,圖片上的說(shuō)明:在IE6(沒(méi)有測(cè)試IE7或更高版本)li標(biāo)簽的第一條“聯(lián)系我們”,沒(méi)有前面的:點(diǎn)。看一下我的demo代碼啊吧~
要觸發(fā)這個(gè)BUG有不少“要點(diǎn)”??!第一 、.news-list ol{ padding:10px 10px 10px 10px;} 這條樣式必須要有,更確切的說(shuō)是第一個(gè)“10px”必須有,當(dāng)然你可以換成其他像素值,零除外!當(dāng)改成0之后這個(gè)bug就沒(méi)有了~,這也就是我為什么把這個(gè)分開(kāi)寫(xiě),沒(méi)直接寫(xiě)成:.news-list ol{ padding:10px ;},這樣的原因。
第二點(diǎn)、 .news-list li{ height:20px; list-style:disc inside;},這個(gè)里面也有一個(gè)必要的:高度。當(dāng)你把這個(gè)高度去掉的時(shí)候,你會(huì)發(fā)現(xiàn)這個(gè)bug也會(huì)消失。
有上面這兩個(gè)“苛刻”的條件,估計(jì)也就是為什么很少有人碰到的原因吧!知道了原因解決當(dāng)然不是問(wèn)題。從上面的兩點(diǎn)就可以很好的解決這bug了。
方法一、當(dāng)然就是準(zhǔn)對(duì)第一個(gè)條件的,如果可以去掉padding。
方法二、當(dāng)然也是在不影響布局的情況下:去掉 height
方法三、任然還是去掉:.news-list ol{ padding:10px 10px 10px 10px;} 這條樣式,同時(shí)在ol標(biāo)簽的父標(biāo)簽(這里的父標(biāo)簽就是<div class=”news-list”>了,當(dāng)然你還可以在這個(gè)中間加入一個(gè)div)中加入這條樣式。這樣既不會(huì)影響布局,又能很好的解決這個(gè)bug,下面是我的第三種解決方法代碼:
NDC和MDC是log4j用于存儲(chǔ)應(yīng)用程序的上下文信息(context infomation),從而便于在log中使用這些上下文信息。
NDC采用了一個(gè)類(lèi)似棧的機(jī)制來(lái)push存儲(chǔ)上下文信息,每一個(gè)線程都獨(dú)立地儲(chǔ)存上下文信息。比如說(shuō)一個(gè)servlet就可以針對(duì)每一個(gè)request創(chuàng)建對(duì)應(yīng)的NDC,儲(chǔ)存客戶(hù)端地址等等信息。相關(guān)的信息使用NDC.push(message);
在log的時(shí)候?qū)⑿畔⑤敵?。在相?yīng)的PatternLayout中使用”%x”來(lái)輸出存儲(chǔ)的上下文信息
例如:String remoteAddr = request.getRemoteAddr();
NDC.push(remoteAddr);
在log4j.properties文件中作如下的配置即可
log4j.appender.console.layout.ConversionPattern=%-d{yyyy/MM/dd HH:mm:ss,SSS} [%X] -[%c]-[%p] %m%n
MDC內(nèi)部使用了類(lèi)似map的機(jī)制來(lái)存儲(chǔ)信息,相對(duì)應(yīng)的方法,MDC.put(key,value);在配置PatternLayout的時(shí)候使用:%x{key}來(lái)輸出對(duì)應(yīng)的value
例如:String remoteAddr = request.getRemoteAddr();
MDC.put("ip", remoteAddr);
在log4j.properties文件中作如下的配置即可
log4j.appender.console.layout.ConversionPattern=%-d{yyyy/MM/dd HH:mm:ss,SSS} [%X{ip}] -[%c]-[%p] %m%n
總:如果在項(xiàng)目中有過(guò)濾器或者模板Action,你可以把獲取公共的屬性方法直接定義在里面,然后在配置文件中配置獲取顯示
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在請(qǐng)求應(yīng)用時(shí),struts2將會(huì)截獲所有請(qǐng)求,對(duì)于servlet請(qǐng)求將不能夠正常相應(yīng),是struts2把servlet當(dāng)成act
解決方法目前有四種:
方法1:統(tǒng)一在servlet后面加上.servlet(包括web.xml配置文件中和頁(yè)面上使用servlet的地方)
方法2:繼承StrutsPrepareAndExecuteF
public void init(FilterConfig filterConfig) throws
ServletException {
..............................
}
public void doFilter(ServletRequest request,
ServletResponse response,
IOException, ServletException {
if(url.contain("servlet")){
((HttpServletResponse) response).sendRedirect(redirectUrl);
}
super.doFilter(request, response, chain);
}
方法3:修改攔截頁(yè)面配置
原:
<filter>
/* </url-pattern>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.act
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
<filter-name>struts2</filter-name>
<url-pattern>/user/*</url-pattern>
servlet的請(qǐng)求路徑不必改變
方法4:在struts.xml文件中修改
……
1. 框架技術(shù)(Struts2.3.4+Srping3.2+Mybatis3.2)
i. 添加框架Struts2.3.4
Ø 清理lib
二、框架的使用
public class log4jlistener implements ServletContextListener {
public static final String log4jdirkey = "log4jdir";
public void contextDestroyed(ServletContextEvent servletcontextevent) {
System.getProperties().remove(log4jdirkey);
}
public void contextInitialized(ServletContextEvent servletcontextevent) {
String log4jdir = servletcontextevent.getServletContext().getRealPath("/");
//System.out.println("log4jdir:"+log4jdir);
System.setProperty(log4jdirkey, log4jdir);
}
}
web.xml配置:
安裝步驟:
1、下載aptana3.2 Eclipse Plugin插件.
下載地址:http://update1.aptana.org/studio/3.2/024747/index.html
2、解壓出features與plugins文件夾,COPY到
D:\Program Files\MyEclipse Blue Edition\MyPlugins\aptana3.2
3、在D:\Program Files\MyEclipse Blue Edition\MyEclipse Blue Edition 9.0 M2\dropins在新建文件aptana.link, 內(nèi)容是
path=D:\\Program Files\\MyEclipse Blue Edition\\MyPlugins\\aptana3.2 (注意斜線的方向,反了不行!Myeclipse會(huì)找不到路徑?。?/p>
4、刪除D:\Program Files\MyEclipse Blue Edition\MyEclipse Blue Edition 9.0 M2\configuration文件夾中org.eclipse.update文件夾
5、重啟 Myeclipse,看到了界面
2. 配置:
配置 Code Assist,對(duì)我來(lái)說(shuō)很重要,Javascript學(xué)的不咋地啊
配置 File Association,文件關(guān)聯(lián),主要有 htm,html,xml,javascript,css(這個(gè)沒(méi)找到) 【貌似不支持使用Aptana Editor 來(lái)打開(kāi)JSP文件】
再配置一些設(shè)置之后,重啟Myeclipse,試試看,打開(kāi)一個(gè) html,呵呵,js有代碼提示幫助,Enjoy it!
FORALL語(yǔ)句
FORALL語(yǔ)句的一個(gè)關(guān)鍵性改進(jìn),它可大大簡(jiǎn)化代碼,并且對(duì)于那些要在PL/SQL程序中更新很多行數(shù)據(jù)的程序來(lái)說(shuō),它可顯著提高其性能。
1:
用FORALL來(lái)增強(qiáng)DML的處理能力
Oracle為Oracle8i中的PL/SQL引入了兩個(gè)新的數(shù)據(jù)操縱語(yǔ)言(DML)語(yǔ)句:BULK COLLECT和FORALL。這兩個(gè)語(yǔ)句在PL/SQL內(nèi)部進(jìn)行一種數(shù)組處理
;BULK COLLECT提供對(duì)數(shù)據(jù)的高速檢索,FORALL可大大改進(jìn)INSERT、UPDATE和DELETE操作的性能。Oracle數(shù)據(jù)庫(kù)使用這些語(yǔ)句大大減少了
PL/SQL與SQL語(yǔ)句執(zhí)行引擎的環(huán)境切換次數(shù),從而使其性能有了顯著提高。
使用BULK COLLECT,你可以將多個(gè)行引入一個(gè)或多個(gè)集合中,而不是單獨(dú)變量或記錄中。下面這個(gè)BULK COLLECT的實(shí)例是將標(biāo)題中包含
有"PL/SQL"的所有書(shū)籍檢索出來(lái)并置于記錄的一個(gè)關(guān)聯(lián)數(shù)組中,它們都位于通向該數(shù)據(jù)庫(kù)的單一通道中。
DECLARE
TYPE books_aat
IS TABLE OF book%ROWTYPE
INDEX BY PLS_INTEGER;
books books_aat;
BEGIN
SELECT *
BULK COLLECT INTO book
FROM books
WHERE title LIKE '%PL/SQL%';
...
END;
類(lèi)似地,FORALL將數(shù)據(jù)從一個(gè)PL/SQL集合傳送給指定的使用集合的表。下面的代碼實(shí)例給出一個(gè)過(guò)程,即接收書(shū)籍信息的一個(gè)嵌套表,并將該
集合(綁定數(shù)組)的全部?jī)?nèi)容插入該書(shū)籍表中。注意,這個(gè)例子還利用了Oracle9i的FORALL的增強(qiáng)功能,可以將一條記錄直接插入到表中。
BULK COLLECT和FORALL都非常有用,它們不僅提高了性能,而且還簡(jiǎn)化了為PL/SQL中的SQL操作所編寫(xiě)的代碼。下面的多行FORALL INSERT相當(dāng)
清楚地說(shuō)明了為什么PL/SQL被認(rèn)為是Oracle數(shù)據(jù)庫(kù)的最佳編程語(yǔ)言。
CREATE TYPE books_nt
IS TABLE OF book%ROWTYPE;
/
CREATE OR REPLACE PROCEDURE add_books (
books_in IN books_nt)
IS
BEGIN
FORALL book_index
IN books_in.FIRST .. books_in.LAST
INSERT INTO book
VALUES books_in(book_index);
...
END;
不過(guò)在Oracle數(shù)據(jù)庫(kù)10g之前,以FORAll方式使用集合有一個(gè)重要的限制:該數(shù)據(jù)庫(kù)從IN范圍子句中的第一行到最后一行,依次讀取集合的內(nèi)容
。如果在該范圍內(nèi)遇到一個(gè)未定義的行,Oracle數(shù)據(jù)庫(kù)將引發(fā)ORA-22160異常事件:
ORA-22160: element at index [N] does not exist
對(duì)于FORALL的簡(jiǎn)單應(yīng)用,這一規(guī)則不會(huì)引起任何麻煩。但是,如果想盡可能地充分利用FORALL,那么要求任意FORALL驅(qū)動(dòng)數(shù)組都要依次填充可
能會(huì)增加程序的復(fù)雜性并降低性能。
在Oracle數(shù)據(jù)庫(kù)10g中,PL/SQL現(xiàn)在在FORALL語(yǔ)句中提供了兩個(gè)新子句:INDICES OF與VALUES OF,它們使你能夠仔細(xì)選擇驅(qū)動(dòng)數(shù)組中該由擴(kuò)展
DML語(yǔ)句來(lái)處理的行。
當(dāng)綁定數(shù)組為稀疏數(shù)組或者包含有間隙時(shí),INDICES OF會(huì)非常有用。該語(yǔ)句的語(yǔ)法結(jié)構(gòu)為:
FORALL indx IN INDICES
OF sparse_collection
INSERT INTO my_table
VALUES sparse_collection (indx);
VALUES OF用于一種不同的情況:綁定數(shù)組可以是稀疏數(shù)組,也可以不是,但我只想使用該數(shù)組中元素的一個(gè)子集。那么我就可以使用VALUES
OF來(lái)指向我希望在DML操作中使用的值。該語(yǔ)句的語(yǔ)法結(jié)構(gòu)為:
FORALL indx IN VALUES OF pointer_array
INSERT INTO my_table
VALUES binding_array (indx);
不用FOR循環(huán)而改用FORALL
假定我需要編寫(xiě)一個(gè)程序,對(duì)合格員工(由comp_analysis.is_eligible函數(shù)確定)加薪,編寫(xiě)關(guān)于不符合加薪條件的員工的報(bào)告并寫(xiě)入
employee_history表。我在一個(gè)非常大的公司工作;我們的員工非常非常多。
對(duì)于一位PL/SQL開(kāi)發(fā)人員來(lái)說(shuō),這并不是一項(xiàng)十分困難的工作。我甚至不需要使用BULK COLLECT或FORALL就可以完成這項(xiàng)工作,如清單 1所示
,我使用一個(gè)CURSOR FOR循環(huán)和單獨(dú)的INSERT及UPDATE語(yǔ)句。這樣的代碼簡(jiǎn)潔明了;不幸地是,我花了10分鐘來(lái)運(yùn)行此代碼,我的"老式"方法
要運(yùn)行30分鐘或更長(zhǎng)時(shí)間。
清單 1:
CREATE OR REPLACE PROCEDURE give_raises_in_department (
dept_in IN employee.department_id%TYPE
, newsal IN employee.salary%TYPE
)
IS
CURSOR emp_cur
IS
SELECT employee_id, salary, hire_date
FROM employee
WHERE department_id = dept_in;
BEGIN
FOR emp_rec IN emp_cur
LOOP
IF comp_analysis.is_eligible (emp_rec.employee_id)
THEN
UPDATE employee
SET salary = newsal
WHERE employee_id = emp_rec.employee_id;
ELSE
INSERT INTO employee_history
(employee_id, salary
, hire_date, activity
)
VALUES (emp_rec.employee_id, emp_rec.salary
, emp_rec.hire_date, 'RAISE DENIED'
);
END IF;
END LOOP;
END give_raises_in_department;
好在我公司的數(shù)據(jù)庫(kù)升級(jí)到了Oracle9i,而且更幸運(yùn)的是,在最近的Oracle研討會(huì)上(以及Oracle技術(shù)網(wǎng)站提供的非常不錯(cuò)的演示中)我了解
到了批量處理方法。所以我決定使用集合與批量處理方法重新編寫(xiě)程序。寫(xiě)好的程序如清單 2所示。
清單 2:
1 CREATE OR REPLACE PROCEDURE give_raises_in_department (
2 dept_in IN employee.department_id%TYPE
3 , newsal IN employee.salary%TYPE
4 )
5 IS
6 TYPE employee_aat IS TABLE OF employee.employee_id%TYPE
7 INDEX BY PLS_INTEGER;
8 TYPE salary_aat IS TABLE OF employee.salary%TYPE
9 INDEX BY PLS_INTEGER;
10 TYPE hire_date_aat IS TABLE OF employee.hire_date%TYPE
11 INDEX BY PLS_INTEGER;
12
13 employee_ids employee_aat;
14 salaries salary_aat;
15 hire_dates hire_date_aat;
16
17 approved_employee_ids employee_aat;
18
19 denied_employee_ids employee_aat;
20 denied_salaries salary_aat;
21 denied_hire_dates hire_date_aat;
22
23 PROCEDURE retrieve_employee_info
24 IS
25 BEGIN
26 SELECT employee_id, salary, hire_date
27 BULK COLLECT INTO employee_ids, salaries, hire_dates
28 FROM employee
29 WHERE department_id = dept_in;
30 END;
31
32 PROCEDURE partition_by_eligibility
33 IS
34 BEGIN
35 FOR indx IN employee_ids.FIRST .. employee_ids.LAST
36 LOOP
37 IF comp_analysis.is_eligible (employee_ids (indx))
38 THEN
39 approved_employee_ids (indx) := employee_ids (indx);
40 ELSE
41 denied_employee_ids (indx) := employee_ids (indx);
42 denied_salaries (indx) := salaries (indx);
43 denied_hire_dates (indx) := hire_dates (indx);
44 END IF;
45 END LOOP;
46 END;
47
48 PROCEDURE add_to_history
49 IS
50 BEGIN
51 FORALL indx IN denied_employee_ids.FIRST .. denied_employee_ids.LAST
52 INSERT INTO employee_history
53 (employee_id
54 , salary
55 , hire_date, activity
56 )
57 VALUES (denied_employee_ids (indx)
58 , denied_salaries (indx)
59 , denied_hire_dates (indx), 'RAISE DENIED'
60 );
61 END;
62
63 PROCEDURE give_the_raise
64 IS
65 BEGIN
66 FORALL indx IN approved_employee_ids.FIRST .. approved_employee_ids.LAST
67 UPDATE employee
68 SET salary = newsal
69 WHERE employee_id = approved_employee_ids (indx);
70 END;
71 BEGIN
72 retrieve_employee_info;
73 partition_by_eligibility;
74 add_to_history;
75 give_the_raise;
76 END give_raises_in_department;
掃一眼清單1 和清單2 就會(huì)清楚地認(rèn)識(shí)到:改用集合和批量處理方法將增加代碼量和復(fù)雜性。但是,如果你需要大幅度提升性能,這還是值得
的。下面,我們不看這些代碼,我們來(lái)看一看當(dāng)使用FORALL時(shí),用什么來(lái)處理CURSOR FOR循環(huán)內(nèi)的條件邏輯。
定義集合類(lèi)型與集合
在清單 2中,聲明段的第一部分(第6行至第11行)定義了幾種不同的集合類(lèi)型,與我將從員工表檢索出的列相對(duì)應(yīng)。我更喜歡基于employee%
ROWTYPE來(lái)聲明一個(gè)集合類(lèi)型,但是FORALL還不支持對(duì)某些記錄集合的操作,在這樣的記錄中,我將引用個(gè)別字段。所以,我還必須為員工ID、
薪金和雇用日期分別聲明其各自的集合。
接下來(lái)為每一列聲明所需的集合(第13行至第21行)。首先定義與所查詢(xún)列相對(duì)應(yīng)的集合(第13行至第15行):
employee_ids employee_aat;
salaries salary_aat;
hire_dates hire_date_aat;
然后我需要一個(gè)新的集合,用于存放已被批準(zhǔn)加薪的員工的ID(第17行):
approved_employee_ids employee_aat;
最后,我再為每一列聲明一個(gè)集合(第19行至第21行),用于記錄沒(méi)有加薪資格的員工:
denied_employee_ids employee_aat;
denied_salaries salary_aat;
denied_hire_dates hire_date_aat;
深入了解代碼
數(shù)據(jù)結(jié)構(gòu)確定后,我們現(xiàn)在跳過(guò)該程序的執(zhí)行部分(第72行至第75行),了解如何使用這些集合來(lái)加速進(jìn)程。
retrieve_employee_info;
partition_by_eligibility;
add_to_history;
give_the_raise;
我編寫(xiě)此程序使用了逐步細(xì)化法(也被稱(chēng)為"自頂向下設(shè)計(jì)")。所以執(zhí)行部分不是很長(zhǎng),也不難理解,只有四行,按名稱(chēng)對(duì)過(guò)程中的每一步進(jìn)
行了描述。首先檢索員工信息(指定部門(mén)的所有員工)。然后進(jìn)行劃分,將要加薪和不予加薪的員工區(qū)分出來(lái)。完成之后,我就可以將那些不
予加薪的員工添加至員工歷史表中,對(duì)其他員工進(jìn)行加薪。
以這種方式編寫(xiě)代碼使最終結(jié)果的可讀性大大增強(qiáng)。因而我可以深入到該程序中對(duì)我有意義的任何部分。
有了已聲明的集合,我現(xiàn)在就可以使用BULK COLLECT來(lái)檢索員工信息(第23行至第30行)。這一部分有效地替代了CURSOR FOR循環(huán)。至此,數(shù)
據(jù)被加載到集合中。
劃分邏輯(第32行至第46行)要求對(duì)剛剛填充的集合中的每一行進(jìn)行檢查,看其是否符合加薪條件。如果符合,我就將該員工ID從查詢(xún)填充的
集合復(fù)制到符合條件的員工的集合。如果不符合,則復(fù)制該員工ID、薪金和雇用日期,因?yàn)檫@些都需要插入到employee_history表中。
初始數(shù)據(jù)現(xiàn)在已被分為兩個(gè)集合,可以將其分別用作兩個(gè)不同的FORALL語(yǔ)句(分別從第51行和第66行開(kāi)始)的驅(qū)動(dòng)器。我將不合格員工的集合
中的數(shù)據(jù)批量插入到employee_history(add_to_history)表中,并通過(guò)give_the_raise過(guò)程,在employee表中批量更新合格員工的信息。
最后再仔細(xì)地看一看add_to_history(第48行至第61行),以此來(lái)結(jié)束對(duì)這個(gè)重新編寫(xiě)的程序的分析。FORALL語(yǔ)句(第51行)包含一個(gè)IN子句
,它指定了要用于批量INSERT的行號(hào)范圍。在對(duì)程序進(jìn)行第二次重寫(xiě)的說(shuō)明中,我將把用于定義范圍的集合稱(chēng)為"驅(qū)動(dòng)集合"。但在
add_to_history的這一版本中,我簡(jiǎn)單地假定: 使用在denied_employee_ids中定義的所有行。在INSERT自身內(nèi)部,關(guān)于不合格員工的三個(gè)集
合都會(huì)被用到;我將把這些集合稱(chēng)為"數(shù)據(jù)集合"??梢钥吹剑?qū)動(dòng)集合與數(shù)據(jù)集合無(wú)需匹配。在學(xué)習(xí)Oracle數(shù)據(jù)庫(kù)10g的新特性時(shí),這是一個(gè)關(guān)
鍵點(diǎn)。
結(jié)果,清單 2 的行數(shù)大約是清單 1行數(shù)的2倍,但是清單 2 中的代碼會(huì)在要求的時(shí)間內(nèi)運(yùn)行。在使用Oracle數(shù)據(jù)庫(kù)10g之前,在這種情況下,
我只會(huì)對(duì)能夠在這一時(shí)間內(nèi)運(yùn)行代碼并開(kāi)始下一個(gè)任務(wù)這一點(diǎn)感到高興。
不過(guò),有了Oracle數(shù)據(jù)庫(kù)10g中最新版的PL/SQL,現(xiàn)在我就可以在性能、可讀性和代碼量方面作出更多的改進(jìn)。
將VALUES OF用于此過(guò)程
在Oracle數(shù)據(jù)庫(kù)10g中,可以指定FORALL語(yǔ)句使用的驅(qū)動(dòng)集合中的行的子集??梢允褂靡韵聝煞N方法之一來(lái)定義該子集:
將數(shù)據(jù)集合中的行號(hào)與驅(qū)動(dòng)集合中的行號(hào)進(jìn)行匹配。你需要使用INDICES OF子句。
將數(shù)據(jù)集合中的行號(hào)與驅(qū)動(dòng)集合中所定義行中找到的值進(jìn)行匹配。這需要使用VALUES OF子句。
在對(duì)give_raises_in_department進(jìn)行第二次和最后一次改寫(xiě)中我將使用VALUES OF子句。清單 3 包含這個(gè)版本的全部代碼。我將略過(guò)這一程序
中與前一版本相同的部分。
從聲明集合開(kāi)始,請(qǐng)注意我不再另外定義集合來(lái)存放合格的和不合格的員工信息,而是在清單 3 (第17行至第21行)中聲明兩個(gè)"引導(dǎo)"集合:
一個(gè)用于符合加薪要求的員工,另一個(gè)用于不符合加薪要求的員工。這兩個(gè)集合的數(shù)據(jù)類(lèi)型都是布爾型;不久將會(huì)看到,這些集合的數(shù)據(jù)類(lèi)型
與FORALL語(yǔ)句毫無(wú)關(guān)系。FORALL語(yǔ)句只關(guān)心定義了哪些行。 在員工表中擁有50 000行信息的give_raises_in_department的三種執(zhí)行方法的占
用時(shí)間 執(zhí)行方法 用時(shí)
CURSOR FOR循環(huán) 00:00:38.01
Oracle數(shù)據(jù)庫(kù)10g之前的批量處理 00:00:06.09
Oracle數(shù)據(jù)庫(kù)10g的批量處理 00:00:02.06
在員工表中擁有100,000行數(shù)據(jù)的give_raises_in_department的三種執(zhí)行方法的占用時(shí)間 執(zhí)行方法 用時(shí)
CURSOR FOR循環(huán) 00:00:58.01
Oracle數(shù)據(jù)庫(kù)10g之前的批量處理 00:00:12.00
Oracle數(shù)據(jù)庫(kù)10g的批量處理 00:00:05.05
表1:處理50,000行和100,000行數(shù)據(jù)的用時(shí)測(cè)試結(jié)果
retrieve_employee_info子程序與前面的相同,但是對(duì)數(shù)據(jù)進(jìn)行劃分的方式完全不同(第32行至第44行)。我沒(méi)有將記錄從一個(gè)集合復(fù)制到另
一個(gè)集合(這個(gè)操作相對(duì)較慢),而只是確定與員工ID集合中的行號(hào)相匹配的相應(yīng)引導(dǎo)集合中的行(通過(guò)為其指定一個(gè)TRUE值)。
現(xiàn)在可以在兩個(gè)不同FORALL語(yǔ)句(由第49行和第65行開(kāi)始)中,將approved_list和denied_list集合用作驅(qū)動(dòng)集合。
為了插入到employee_history表中,我使用了如下語(yǔ)句:
FORALL indx IN VALUES OF denied_list
為了進(jìn)行更新(給員工進(jìn)行加薪),我使用這一格式:
FORALL indx IN VALUES OF approved_list
在這兩個(gè)DML語(yǔ)句中,數(shù)據(jù)集合是在BULK COLLECT 檢索步驟中填充的最初的集合;沒(méi)有進(jìn)行過(guò)復(fù)制。利用VALUES OF,Oracle數(shù)據(jù)庫(kù)在這些數(shù)據(jù)
集合的行中進(jìn)行篩選,僅使用行號(hào)與驅(qū)動(dòng)集合中行號(hào)相匹配的行
利用本程序中的VALUES OF,可以避免復(fù)制對(duì)全部記錄進(jìn)行復(fù)制,而是用行號(hào)的一個(gè)簡(jiǎn)單列表來(lái)替換它們。對(duì)于大型數(shù)組,進(jìn)行這些復(fù)制的開(kāi)銷(xiāo)
是非??捎^的。為了測(cè)試Oracle數(shù)據(jù)庫(kù)10g的優(yōu)越性,我裝入employee表并對(duì)50,000行和100,000行的數(shù)據(jù)運(yùn)行測(cè)試。為了模擬更多的現(xiàn)實(shí)情況
,我將Oracle數(shù)據(jù)庫(kù)10g之前的批量處理的執(zhí)行方法作了修改以進(jìn)行集合內(nèi)容的多次復(fù)制。然后我使用SQL*Plus SET TIMING ON來(lái)顯示運(yùn)行各個(gè)
不同的執(zhí)行方法所用的時(shí)間。表 1 給出了結(jié)果。
從這些時(shí)間測(cè)定得到的結(jié)論非常清楚:由單個(gè)DML語(yǔ)句變?yōu)榕刻幚韺⒋蠓s短耗用時(shí)間,數(shù)據(jù)為50,000行時(shí)的用時(shí)由38秒減為6秒,數(shù)據(jù)為
100,000行時(shí)的用時(shí)由58秒減為12秒。而且,通過(guò)使用VALUES OF來(lái)避免復(fù)制數(shù)據(jù),我可以將用時(shí)縮短一半左右。
即使沒(méi)有性能上的改進(jìn),VALUES OF及其同類(lèi)子句--INDICES OF也提高了PL/SQL語(yǔ)言的靈活性,使開(kāi)發(fā)人員能夠更輕松地編寫(xiě)出更直觀和更容易
維護(hù)的代碼。
在產(chǎn)品壽命這一點(diǎn)上,PL/SQL是一種成熟且功能強(qiáng)大的語(yǔ)言。因而,其很多新特性都是逐漸增加和改進(jìn)而成的。不過(guò),這些新特性還是使應(yīng)用
程序的性能和開(kāi)發(fā)人員的開(kāi)發(fā)效率有了重大改變。VALUES OF就是這種特性的一個(gè)很好的例子。
dba_開(kāi)頭.....
dba_users 數(shù)據(jù)庫(kù)用戶(hù)信息
dba_segments 表段信息
dba_extents 數(shù)據(jù)區(qū)信息
dba_objects 數(shù)據(jù)庫(kù)對(duì)象信息
dba_tablespaces 數(shù)據(jù)庫(kù)表空間信息
dba_data_files 數(shù)據(jù)文件設(shè)置信息
dba_temp_files 臨時(shí)數(shù)據(jù)文件信息
dba_rollback_segs 回滾段信息
dba_ts_quotas 用戶(hù)表空間配額信息
dba_free_space 數(shù)據(jù)庫(kù)空閑空間信息
dba_profiles 數(shù)據(jù)庫(kù)用戶(hù)資源限制信息
dba_sys_privs 用戶(hù)的系統(tǒng)權(quán)限信息
dba_tab_privs 用戶(hù)具有的對(duì)象權(quán)限信息
dba_col_privs 用戶(hù)具有的列對(duì)象權(quán)限信息
dba_role_privs 用戶(hù)具有的角色信息
dba_audit_trail 審計(jì)跟蹤記錄信息
dba_stmt_audit_opts 審計(jì)設(shè)置信息
dba_audit_object 對(duì)象審計(jì)結(jié)果信息
dba_audit_session 會(huì)話審計(jì)結(jié)果信息
dba_indexes 用戶(hù)模式的索引信息
user_開(kāi)頭
user_objects 用戶(hù)對(duì)象信息
user_source 數(shù)據(jù)庫(kù)用戶(hù)的所有資源對(duì)象信息
user_segments 用戶(hù)的表段信息
user_tables 用戶(hù)的表對(duì)象信息
user_tab_columns 用戶(hù)的表列信息
user_constraints 用戶(hù)的對(duì)象約束信息
user_sys_privs 當(dāng)前用戶(hù)的系統(tǒng)權(quán)限信息
user_tab_privs 當(dāng)前用戶(hù)的對(duì)象權(quán)限信息
user_col_privs 當(dāng)前用戶(hù)的表列權(quán)限信息
user_role_privs 當(dāng)前用戶(hù)的角色權(quán)限信息
user_indexes 用戶(hù)的索引信息
user_ind_columns 用戶(hù)的索引對(duì)應(yīng)的表列信息
user_cons_columns 用戶(hù)的約束對(duì)應(yīng)的表列信息
user_clusters 用戶(hù)的所有簇信息
user_clu_columns 用戶(hù)的簇所包含的內(nèi)容信息
user_cluster_hash_expressions 散列簇的信息
v$開(kāi)頭
v$database 數(shù)據(jù)庫(kù)信息
v$datafile 數(shù)據(jù)文件信息
v$controlfile 控制文件信息
v$logfile 重做日志信息
v$instance 數(shù)據(jù)庫(kù)實(shí)例信息
v$log 日志組信息
v$loghist 日志歷史信息
v$sga 數(shù)據(jù)庫(kù)SGA信息
v$parameter 初始化參數(shù)信息
v$process 數(shù)據(jù)庫(kù)服務(wù)器進(jìn)程信息
v$bgprocess 數(shù)據(jù)庫(kù)后臺(tái)進(jìn)程信息
v$controlfile_record_section 控制文件記載的各部分信息
v$thread 線程信息
v$datafile_header 數(shù)據(jù)文件頭所記載的信息
v$archived_log 歸檔日志信息
v$archive_dest 歸檔日志的設(shè)置信息
v$logmnr_contents 歸檔日志分析的DML DDL結(jié)果信息
v$logmnr_dictionary 日志分析的字典文件信息
v$logmnr_logs 日志分析的日志列表信息
v$tablespace 表空間信息
v$tempfile 臨時(shí)文件信息
v$filestat 數(shù)據(jù)文件的I/O統(tǒng)計(jì)信息
v$undostat Undo數(shù)據(jù)信息
v$rollname 在線回滾段信息
v$session 會(huì)話信息
v$transaction 事務(wù)信息
v$rollstat 回滾段統(tǒng)計(jì)信息
v$pwfile_users 特權(quán)用戶(hù)信息
v$sqlarea 當(dāng)前查詢(xún)過(guò)的sql語(yǔ)句訪問(wèn)過(guò)的資源及相關(guān)的信息
v$sql 與v$sqlarea基本相同的相關(guān)信息
v$sysstat 數(shù)據(jù)庫(kù)系統(tǒng)狀態(tài)信息
all_開(kāi)頭
all_users 數(shù)據(jù)庫(kù)所有用戶(hù)的信息
all_objects 數(shù)據(jù)庫(kù)所有的對(duì)象的信息
all_def_audit_opts 所有默認(rèn)的審計(jì)設(shè)置信息
all_tables 所有的表對(duì)象信息
all_indexes 所有的數(shù)據(jù)庫(kù)對(duì)象索引的信息
session_開(kāi)頭
session_roles 會(huì)話的角色信息
session_privs 會(huì)話的權(quán)限信息
index_開(kāi)頭
index_stats 索引的設(shè)置和存儲(chǔ)信息
偽表
dual 系統(tǒng)偽列表信息
8080端口被其他的應(yīng)用占用
第一步,命令提示符號(hào),執(zhí)行命令:netstat -ano
可見(jiàn),占用8080端口的進(jìn)程的PID是1476
第二步
方法一:
命令提示符號(hào),執(zhí)行命令:tasklist
xxx.exe 1476 Console 0 6,464 K
可見(jiàn),該占用8080端口的進(jìn)程是xxx.exe
通過(guò)任務(wù)管理器,終止進(jìn)程xxx.exe
方法二:寫(xiě)Web頁(yè)面就像我們建設(shè)房子一樣,地基牢固,房子才不會(huì)倒。同樣的,我們制作Web頁(yè)面也一樣,一個(gè)良好的HTML結(jié)構(gòu)是制作一個(gè)美麗的網(wǎng)站的開(kāi)始,同樣的,良好的CSS只存在同樣良好的HTML中,所以一個(gè)干凈的,語(yǔ)義的HTML的優(yōu)點(diǎn)很多,那么平時(shí)制作中,我們做到了這一點(diǎn)嗎?我們一起來(lái)看一張圖片:
上圖展示了兩段代碼,我想大家都只會(huì)喜歡第一種,我們先不說(shuō)其語(yǔ)義,至少他的結(jié)構(gòu)讓我們看上去清爽,而第二種呢?一看就是糟糕的代碼的代碼,讓人討厭的代碼。那么要怎么樣才能寫(xiě)出一個(gè)好的代碼,整潔的代碼呢?下面我們就從以下十二個(gè)方面一起來(lái)學(xué)習(xí),只要大家以后在寫(xiě)代碼的時(shí)候能堅(jiān)持下面的十二個(gè)原則,保準(zhǔn) 你的代碼質(zhì)量能上去,而且你寫(xiě)的代碼會(huì)人見(jiàn)人愛(ài)。
如果我們想做好一件事情,首先要知道我們有哪些權(quán)利去做,就如“DOCTYPE”的聲明,我們沒(méi)有必要去討論是否使用HTML4.01或者XHTML1.0或者說(shuō)現(xiàn)在的HTML5都提供了嚴(yán)格版本或者過(guò)渡版本,這些都能很好的支持我們寫(xiě)的代碼:
由于我們現(xiàn)在的布局不需要table布局也能做出很好的布局,那么我們就可以考慮不使用過(guò)渡型而使用嚴(yán)格型的“DOCTYPE”,為了向后兼容,我建議使用HTML5的聲明模式:
<!DOCTYPE HTML> <html lang="en-US">
如果想了解更多這方面的知識(shí),可以點(diǎn)擊:
在每個(gè)頁(yè)面的開(kāi)始中,我們都在<head>中設(shè)置了字符集,我們這里都是使用“UTF-8”
<meta charset="UTF-8" />
而且我們?cè)谄綍r(shí)寫(xiě)頁(yè)面中時(shí),時(shí)常會(huì)碰到"&"這樣的符號(hào),那么我們不應(yīng)該直接在頁(yè)面這樣寫(xiě)“&”:
我們應(yīng)該在代碼中使用字符編碼來(lái)實(shí)現(xiàn),比如說(shuō)“&”我們?cè)诖a中應(yīng)該使用“&”來(lái)代替他。
如果想了解更多這方面的知識(shí),可以點(diǎn)擊:
在頁(yè)面編輯中,代碼的縮進(jìn)有沒(méi)有正確,他不會(huì)影響你網(wǎng)站的任何功能,但要是你沒(méi)有一個(gè)規(guī)范的縮進(jìn)原則,讓讀你代碼的人是非常的生氣,所以說(shuō)正確的代碼縮進(jìn)可以增強(qiáng)你的代碼可讀性。標(biāo)準(zhǔn)程序的縮進(jìn)應(yīng)該是一個(gè)制表符(或幾個(gè)空格),形像一點(diǎn)的我們來(lái)看下文章開(kāi)頭那張圖,或者一起來(lái)看下面展示的這張圖,你看后就知道以后自己的代碼要怎么樣書(shū)寫(xiě)才讓人看了爽:
不用說(shuō),大家都喜歡下面的那種代碼吧。這只是一個(gè)人的習(xí)慣問(wèn)題,不過(guò)建議從開(kāi)始做好,利人利已。有關(guān)于這方面的介紹,大家還可以參考:Clean up your Web pages with HTML TIDY
頁(yè)面中寫(xiě)入CSS樣式有很多種方法,有些直接將樣式放入頁(yè)面的“<head>”中,這將是一個(gè)很不好的習(xí)慣,因?yàn)檫@樣不僅會(huì)搞亂我們的標(biāo)記,而且這些樣式只適合這一個(gè)HTML頁(yè)面。所以我們需要將CSS單獨(dú)提出,保存在外部,這樣后面的頁(yè)面也可以鏈接到這些樣式,如果你頁(yè)面需要修改,我們也只需要修改樣式文件就可以。正如下圖所示:
上面我們所說(shuō)的只是樣式,其實(shí)javascript腳本也和CSS樣式是同一樣的道理。圖文并說(shuō),我最終想表達(dá)的意思是“在制作web頁(yè)面中,盡量將你的CSS樣式和javascript腳本單獨(dú)放在一個(gè)文件中,然后通過(guò)鏈接的方式引用這些文件,這樣做的最大好處是,方便你的樣式和腳本的管理與修改。”
我們?cè)趯?xiě)HTML時(shí)總是需要標(biāo)簽的層級(jí)嵌套來(lái)幫我們完成HTML的書(shū)寫(xiě),但這些HTML的嵌套是有一定的規(guī)則的,如果要細(xì)說(shuō)的話,我們可能要用幾個(gè)章節(jié)來(lái)描述,那么我今天這里要說(shuō)的是,我們?cè)趯?xiě)HTML時(shí)不應(yīng)該犯以下這樣的超級(jí)錯(cuò)誤:
上圖的結(jié)構(gòu)我們是常見(jiàn)的,比如說(shuō)首頁(yè)的標(biāo)題,那么我們就應(yīng)該注意了,不能把“<h1>”放在“<a>”標(biāo)簽中,換句話說(shuō),就是不能么塊元素和在行內(nèi)元素中。上面只是一個(gè)例子,只是希望大家在平時(shí)的制作中不應(yīng)該犯這樣的超級(jí)錯(cuò)誤。
首先我們一起來(lái)看一個(gè)實(shí)例的截圖:
上圖明顯是一個(gè)導(dǎo)航菜單的制作,在上圖的實(shí)例中:有一個(gè)“div#topNav”包住了列表“ul#bigBarNavigation”,而“div”和“ul”列表都是塊元素,加上“div”此處用來(lái)包“ul”根本就沒(méi)有起到任何作用。雖然“div”的出現(xiàn)給我們制作web頁(yè)面帶來(lái)了極大的好處,但我們也沒(méi)有必要到處這樣的亂用,不知道大家平時(shí)有沒(méi)有注意這樣的細(xì)節(jié)呢?我是犯這樣的錯(cuò)誤,如果你也有過(guò)這樣的經(jīng)歷,那么從今天開(kāi)始,從現(xiàn)在開(kāi)始,我們一起努力來(lái)改正這樣的錯(cuò)誤。
有關(guān)于如何正確的使用標(biāo)簽,大家感興趣的話可以點(diǎn)擊:Divitis: what it is, and how to cure it.
這里所說(shuō)的命名就是給你的頁(yè)面中相關(guān)元素定義類(lèi)名或者是ID名,很多同學(xué)都有這栗的習(xí)慣,比如說(shuō)有一個(gè)元素字體是紅色的,給他加上“red”,甚至布局都寫(xiě)“left-sidebar”等,但是你有沒(méi)有想過(guò),如果這個(gè)元素定義了“red”后,過(guò)幾天客戶(hù)要求使用“藍(lán)色”呢?或者又說(shuō),那時(shí)的“left-sidebar”邊欄此時(shí)不想放在左邊了,而是想放在右邊,那么這樣一來(lái)我們前面的命名可以說(shuō)是一點(diǎn)意義都沒(méi)有了,正如下面的一個(gè)圖所示:
那么定義一個(gè)好的名就很得要了,不但自己能看懂你的代碼,而且別人也能輕松讀懂你的代碼,比如說(shuō)一個(gè)好的類(lèi)名和ID名“mainNav”、“subNav”、“footer”等,他能描述所包含的事情。不好的呢,比如前面所說(shuō)的。
如果想了解更多這方面的知識(shí),可以點(diǎn)擊:
我們?cè)谠O(shè)計(jì)菜單時(shí),有時(shí)要求所有菜單選項(xiàng)的文本全部大寫(xiě),大家平時(shí)是不是直接在HTML標(biāo)簽中就將他們?cè)O(shè)置成大寫(xiě)狀態(tài)呢?如果是的話,我覺(jué)得不好,如果為了將來(lái)具有更好的擴(kuò)展性,我們不應(yīng)該在HTML就將他們?cè)O(shè)置為全部大寫(xiě),更好的解決方法是通過(guò)CSS來(lái)實(shí)現(xiàn):
大家平時(shí)制作web頁(yè)面時(shí)不知道有沒(méi)有碰到這樣的問(wèn)題,就是整站下來(lái),使用了相同的布局和結(jié)構(gòu),換句話說(shuō),你在頁(yè)面的布局上使用了相同的結(jié)構(gòu),相同的類(lèi)名,但是突然你的上級(jí)主管說(shuō)應(yīng)客戶(hù)的需求,有一個(gè)頁(yè)面的布局需要邊欄和主內(nèi)容對(duì)換一下。此時(shí)你又不想為了改變一下布局而修改整個(gè)頁(yè)面的結(jié)構(gòu),此時(shí)有一個(gè)很好的解決辦法,就是在你的這個(gè)頁(yè)面中的“<body>”中定義一個(gè)特殊的類(lèi)名或ID名,這樣來(lái)你就可以輕松的達(dá)到你所要的需求。這樣的使用,不知道大家使用過(guò)沒(méi)有:
給“<body>”定義獨(dú)特的類(lèi)和ID名稱(chēng)是非常強(qiáng)大的,不僅僅是為了像上面一樣幫你改變布局,最主要的是有時(shí)他能幫你實(shí)現(xiàn)頁(yè)面中的某一部分達(dá)到特殊效果,而又不影響其它頁(yè)面的效果。為什么有這樣的功能,不用我說(shuō)我想大家都是知道的。因?yàn)槊總€(gè)頁(yè)面的內(nèi)容都是“<body>”的后代元素。
如果想了解更多這方面的知識(shí),可以點(diǎn)擊:
人不免會(huì)出錯(cuò),我們編寫(xiě)代碼的時(shí)候也是一樣的,你有時(shí)候總會(huì)小寫(xiě)或多寫(xiě),比如說(shuō)忘了關(guān)閉你的元素標(biāo)簽,不記得寫(xiě)上元素必須的屬性,雖然有一些錯(cuò)誤不會(huì)給你帶來(lái)什么災(zāi)難性的后果,但也不免會(huì)給你帶來(lái)你無(wú)法意估的錯(cuò)誤。所以建議您寫(xiě)完代碼的時(shí)候去驗(yàn)證你一下你的代碼。驗(yàn)證后的代碼總是比不驗(yàn)證的代碼強(qiáng):
為一有效的驗(yàn)證你的代碼,我們可以使用相關(guān)的工具或者瀏覽器的插件來(lái)幫助我們完成。如果你的代碼沒(méi)有任何錯(cuò)誤,W3C驗(yàn)證工具會(huì)在你們面前呈現(xiàn)綠色的文字,這樣讓你是無(wú)比的激動(dòng)人心,因?yàn)樵俅巫C明了你寫(xiě)的代碼經(jīng)得起W3C的標(biāo)準(zhǔn)。
如果想了解更多這方面的知識(shí),可以點(diǎn)擊:
這是一個(gè)很少見(jiàn)的錯(cuò)誤情況,因?yàn)槲蚁氪蠹覍?xiě)頁(yè)面都不會(huì)把邏輯順序打亂,換句話說(shuō),如果可能的話,讓你的網(wǎng)站具有一個(gè)先后邏輯順序是最好的,比如說(shuō)先寫(xiě)頁(yè)頭,在寫(xiě)頁(yè)體,最后寫(xiě)頁(yè)腳。當(dāng)然有時(shí)也會(huì)碰到特殊情況,如何頁(yè)腳部分在于我們代碼的邊欄以上,這可能是因?yàn)樗钸m合你的網(wǎng)站設(shè)計(jì)需求,這樣或許是可以理解的,但是如果你有別的方式實(shí)現(xiàn),我們都應(yīng)該把頁(yè)腳是放在一個(gè)頁(yè)面的最后,然后在通過(guò)特定的技術(shù)讓它達(dá)到你的設(shè)計(jì)需求:
上面我們一起討論了多個(gè)如何讓你開(kāi)始寫(xiě)一個(gè)整潔的HTML代碼。從一個(gè)項(xiàng)目的開(kāi)始,這一切都是非常容易的,但是如果需要你去修復(fù)一個(gè)現(xiàn)有的代碼,那多少都會(huì)有一定的難度。上面說(shuō)這么多主要是告訴您將要如何學(xué)習(xí)編寫(xiě)一個(gè)良好的、整潔的HTML代碼,并且一直堅(jiān)持這樣的編寫(xiě)。希望讀完這篇文章垢,在你的下一個(gè)項(xiàng)目中,你能從頭開(kāi)始,堅(jiān)持寫(xiě)一個(gè)整潔的HTML代碼。希望大家喜歡這樣的教程。最后在結(jié)束此教程之前,讓我們大家一起來(lái)感謝Chris Coyier給我們帶來(lái)這么好的經(jīng)驗(yàn)之談——《12 Principles For Keeping Your Code Clean》如果你有什么好的經(jīng)驗(yàn),希望與我們一起分享。