而在其它的編輯器中,默認保存的內容都是GB2312或者GBK(NOTEPAD中對應ANSI).而根據前面所說的UTF-8和GBK,GB2312等的編碼值是不同的這一點,可以知道,如果文件使用了UTF-8,那么字符編碼就必須使用UTF-8,否則編碼值的不同就可能造成亂碼。而這也就是為什么那么多的人使用了UTF-8編碼后還會產生亂碼的根本原因。(JS和JSP都是這個道理)
3、JSP,STRUTS等的中文亂碼解決方案
?????其實解決的方法只有一個:
???方法只有一種,但處理方式就多種多樣了,初學者會在JSP頁面上直接使用,而有經驗的程序員會使用過濾器。而現在所要說的方法也是過濾器。這里以統一使用UTF-8作為編碼作為例子說明。具體過程就不多說了,網上有很多教程。偷懶一點的,到TOMCAT中復制就行了。在TOMCAT的目錄下的\webapps\jsp-examples\WEB-INF\classes\filters\找到SetCharacterEncodingFilter.java 這個類,放到你的程序中并配置好映射路徑。配置好后基本上你的亂碼問題就解決了。但要映射路徑中需要注意的就是不能使用 '*'
像上面這樣配置的話(可能也是網上大多教程的做法,想當年也是害苦了我),可能你只有JSP的亂碼解決了,要解決STRUTS的亂碼需要映射 *.do 或者 servletActionName。然后在初始化參數中設置encoding的值就行了。
當然,最重要的是要記得根據前面所說的方法,改變你所使用的編輯器保存文件的編碼要與使用的字符編碼一致。
而在JSP內容中,還是使用如網上教程所說的那種技倆,在所有頁面的頁首加入:
?contextLoader的源碼
?public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
?? throws BeansException {
? long startTime = System.currentTimeMillis();
? if (logger.isInfoEnabled()) {
?? logger.info("Root WebApplicationContext: initialization started");
? }
? servletContext.log("Loading Spring root WebApplicationContext");
? try {
?? // Determine parent for root web application context, if any.
?? ApplicationContext parent = loadParentContext(servletContext);
?? WebApplicationContext wac = createWebApplicationContext(servletContext, parent);
?? servletContext.setAttribute(
???? WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
?? if (logger.isInfoEnabled()) {
??? logger.info("Using context class [" + wac.getClass().getName() +
????? "] for root WebApplicationContext");
?? }
?? if (logger.isDebugEnabled()) {
??? logger.debug("Published root WebApplicationContext [" + wac +
????? "] as ServletContext attribute with name [" +
????? WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
?? }
?? if (logger.isInfoEnabled()) {
??? long elapsedTime = System.currentTimeMillis() - startTime;
??? logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
?? }
?? return wac;
? }
? catch (RuntimeException ex) {
?? logger.error("Context initialization failed", ex);
?? servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
?? throw ex;
? }
? catch (Error err) {
?? logger.error("Context initialization failed", err);
?? servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
?? throw err;
? }
?}
?注意WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,這里面放了WebApplicationContext,需要使用時從ServletContext取出
?可以使用WebApplicationContextUtils得到WebApplicationContext
?public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
? Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
? if (attr == null) {
?? return null;
? }
? if (attr instanceof RuntimeException) {
?? throw (RuntimeException) attr;
? }
? if (attr instanceof Error) {
?? throw (Error) attr;
? }
? if (!(attr instanceof WebApplicationContext)) {
?? throw new IllegalStateException("Root context attribute is not of type WebApplicationContext: " + attr);
? }
? return (WebApplicationContext) attr;
?}
?關鍵的問題在于struts如何啟動的spring的,ContextLoaderPlugIn的源碼
?
?// Publish the context as a servlet context attribute.
? String attrName = getServletContextAttributeName();
? getServletContext().setAttribute(attrName, wac);
?
?public String getServletContextAttributeName() {
? return SERVLET_CONTEXT_PREFIX + getModulePrefix();
?}
?不同加載的Key竟然不同,原因就是WebApplicationContext放在那里的問題,可spring調用的時候會根據WebApplicationContext里面定義的那個名字去找的,問題出在這里
?在struts-config.xml中配置
??? <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
????? <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" />
??? </plug-in>
??? <controller>
??????? <set-property property="processorClass" value="org.springframework.web.struts.DelegatingRequestProcessor" />
??? </controller>
?原理是這樣的,Struts雖然只能有一個ActionServlet實例,但是對于不同的子應用分別能有自己的RequestProcessor實例每個RequestProcessor實例分別對應不同的struts配置文件。
?? 子應用的ProcessorClass類必須重寫一般就是繼承RequestProcessor類,然后再其配置文件的controller元素中的<processorClass>屬性中作出修改。那么當
? getRequestProcessor(getModuleConfig(request)).process(request,response);就能根據request選擇相應的moduleconfig,再根據其<processorClass>屬性選擇相應的RequestProcessor子類來處理相應的請求了。
?
ServletContext 被 Servlet 程序用來與 Web 容器通信。例如寫日志,轉發請求。每一個 Web 應用程序含有一個Context,被Web應用內的各個程序共享。因為Context可以用來保存資源并且共享,所以我所知道的 ServletContext 的最大應用是Web緩存----把不經常更改的內容讀入內存,所以服務器響應請求的時候就不需要進行慢速的磁盤I/O了。
ServletContext 被 Servlet 程序用來與 Web 容器通信。例如寫日志,轉發請求。每一個 Web 應用程序含有一個Context,被Web應用內的各個程序共享。因為Context可以用來保存資源并且共享,所以我所知道的 ServletContext 的最大應用是Web緩存----把不經常更改的內容讀入內存,所以服務器響應請求的時候就不需要進行慢速的磁盤I/O了。
ServletContextListener 是 ServletContext 的監聽者,如果 ServletContext 發生變化,如服務器啟動時 ServletContext 被創建,服務器關閉時 ServletContext 將要被銷毀。
在JSP文件中,application 是 ServletContext 的實例,由JSP容器默認創建。Servlet 中調用 getServletContext()方法得到 ServletContext 的實例。
我們使用緩存的思路大概是:
服務器啟動時,ServletContextListener 的 contextInitialized()方法被調用,所以在里面創建好緩存。可以從文件中或者從數據庫中讀取取緩存內容生成類,用 ervletContext.setAttribute()方法將緩存類保存在 ServletContext 的實例中。
程序使用 ServletContext.getAttribute()讀取緩存。如果是 JSP,使用a pplication.getAttribute()。如果是 Servlet,使用 getServletContext().getAttribute()。如果緩存發生變化(如訪問計數),你可以同時更改緩存和文件/數據庫。或者你等 變化積累到一定程序再保存,也可以在下一步保存。
服務器將要關閉時,ServletContextListener 的 contextDestroyed()方法被調用,所以在里面保存緩存的更改。將更改后的緩存保存回文件或者數據庫,更新原來的內容。
import User; //my own class
import DatabaseManager; // my own class
import javax.servlet.ServletContext;
import javax.servlet.ServletContextListener;
public class MyContextListener
implements ServletContextListener {
private ServletContext context = null;
public void contextInitialized(ServletContextEvent event) {
context = event.getServletContext();
User user = DatabaseManager.getUserById(1);
context.setAttribute("user1", user);
}
public void contextDestroyed(ServletContextEvent event) {
User user = (User)context.getAttribute("user1");
DatabaseManager.updateUserData(user);
this.context = null;
}
}
你實現(implements)了 ServletContextListener 編譯后,把它放在正確的WEB-INF/classes目錄下,更改WEB-INF目錄下的 web.xml文件,在web-app節點里添加
<listener>?來自:
<listener-class>MyServletContextListener</listener-class>
</listener>
圖三 deploytool的啟動畫面
圖四 新建一個Application
圖五 將.class文件添加到SecurityJar中
圖六 將ClientCode目錄下的文件添加到SecurityJar中
圖七 SecurityApp的JNDI Names標簽頁的設置
圖八 SecurityApp的Web Context標簽頁的設置
圖九 發布完成后的Deployment Progress
圖十 運行效果第一個頁面(加密/解密請求窗)
圖十一 運行效果第二個頁面(加密/解密結果顯示頁)
[概述]
Taglib是JSP比較高級的技術。做為JSP開發人員來講,不了解taglib是可以接受的。因為JSP的風格或者JAVA的風格就是一種簡潔的美。如果技術太過復雜或者繁瑣,就會在技術的汪洋中失去自我。
但是,當我們的項目變得越來越大,或者團隊有了一些技術積累之后,很自然就會有將我們的開發工作提高一個層次的需要。當我們面對一些非常類似的界面或者程序單元的時候,我們會想到把這樣的工作成果直接用于下一個項目。這樣的問題可以由taglib來解決。
到此還沒有真正說明一下taglib是什么。只要你從事過JSP的開發工作,你就已經使用過taglib了。只不過是在不知不覺中使用的。你一定不會忘記 <jsp:include />標記。實際上這就是一個taglib。Taglib直譯做標記庫,是JSP定義給開發人員可以使用自行定義的標記體系。也就是說,開發人員可以在JSP中使用自己定義的特殊標記。而該標記可以用作特定的用途。比如顯示一個每個頁都需要的公司版權信息。就可以不用復制粘貼相同的代碼到每個頁去了。
但是taglib可以完成的工作遠遠不止這些。由于每個自定義標記一定是一個完全的JAVA類,我們可以定義非常豐富的一組行為,并且可以通過自定義的attribute來控制它。
[實例]
我習慣用實例來說明問題。大家也許都對用戶會話狀態的檢查不陌生。當用戶登錄到系統后,我們希望自動保持用戶的登錄狀態。而這個過程在每個需要認證用戶才能訪問的程序單元都需要實現。通常我們需要訪問預定義的session服務器變量,當這個變量不滿足某值時即判定改用戶為非法訪問或者會話狀態丟失。
我們來看一下使用taglib如何實現。我們需要編寫一個僅處理該邏輯的標記。實現的最簡單的邏輯:檢查用戶狀態session值,不滿足某值時即將用戶轉向一個預先設置的報錯頁。
以下是源碼(CheckSessionTag.java):
(省略了細節)
[分析]
在以上源碼中,我們在使tag自動檢查用戶session變量中的"member_id"值,如果該值為空,則立即判定用戶沒有訪問權限,則立即將流程導向一個預先設定的報錯頁:/home/check_session_fail.jsp.
類CheckSessionTag派生自:TagSupport(javax.servlet.jsp.tagext.TagSupport). 是一個從JAVA 1.3就開始支持的類庫,位于servlet.jar包。java文檔給出的描述是:
A base class for defining new tag handlers implementing Tag. The TagSupport class is a utility class intended to be used as the base class for new tag handlers. The TagSupport class implements the Tag and IterationTag interfaces and adds additional convenience methods including getter methods for the properties in Tag. TagSupport has one static method that is included to facilitate coordination among cooperating tags. Many tag handlers will extend TagSupport and only redefine a few methods.
(該類為所有taglib的基類。該類定義了實現標記的一系列接口。)
在實例CheckSessionTag類中,我們僅僅重寫了doEndTag方法。沒有向其容器:jsp頁輸出任何東西。但是該類在實際應用中是切實可行的。
以下是在一個成品項目中的某jsp中截取的片斷:
<logic:checkSession />
當我們直接使用輸入url的方式訪問本頁時,我們被立即帶到了報告:用戶會話狀態丟失或者未經登錄的頁。
這就省卻了我們習以為常的一項工作:以前我們必須復制相同的scriplet到每個jsp頁。(實際上,也可以使用類似intercept filter的模式來處理此需求)。這使我們的開發工作變得reusable, flexiable, 和extendable。可以想象,如果我們想改變檢查session的邏輯,則可以僅僅通過改變CheckSessionTag內部的邏輯就可以通盤改變。并且我們可以通過給logic:checkSession標記加上類似target=admin的attribute來限定只有管理員才可以訪問的區域。這就是形成組件化開發的基本過程。
如何實施呢?
[實施過程]
我們需要做一系列工作來將taglib引入我們的工程。
web.xml
為了使jsp解析器可以識別我們的taglib必須將其配置在web.xml內。web.xml位于/WEB-INF目錄內。
使用特定語法來配置我們的taglib:
web.xml(片斷)
該語法告訴容器到什么地方去尋找所有logic:開頭的tag.
tld是taglib defination的縮寫。即taglib定義。該文件定義了我們使用的標記,Java類如何加載,并且該標記如何工作。讓我們來看一段實際的tld:
logic.tld(片斷)
我們注意到taglib標記內.
1.?short-name: 標記的prefix名。
2.?uri: 識別該taglib的名稱。
tag標記:
1.?name: 標記名(prefix:之后)
2.?tag-class:類名(包含包名)
3.?body-content: 標記內容模式,如果該標記沒有內容則為empty.
將logic.tld放置在/WEB-INF下,此時確保編譯好的CheckSessionTag類可以被容器訪問到。即可完成配置。必須注意的是,不同的jsp容器的配置可能有差別。本文的配置是基于Resin 2.1.11
[應用]
建立一個jsp頁。在源碼的開頭加入如下的預編譯指令:(page)
<%@ taglib prefix="logic" uri="/WEB-INF/logic.tld" %>
此指令告訴編譯器到哪里去尋找所有以logic:開頭的標記的定義。
接下來在任意位置加入標記:<logic:checkSession />即可以工作了。當標記被實例化后,即自動執行doEndTag過程,檢查session服務器變量值,之后將頁過程跳轉。
[深入一些:attribute]
有時候我們希望可以對標記進行一些定制。依舊拿checkSession做例:現在我們要限制兩類用戶訪問系統:某
些部分僅允許具有管理員權限的用戶訪問。這樣我們設想可以在<logic:checkSession target=admin />進行進一步定義。這需要在CheckSessionTag類中增加一些額外的邏輯。檢查的過程很簡單,取決于你的安全系統的分析,但是,我們寫了target attribute如何是類可以得到該數據,這是一個關鍵的問題。
為了實現對tag增加可用的attribute, 需要做如下工作:
1.?為類增加相應的成員及相應讀寫器:
CheckSessionTag.java(片斷):?
2.?更改logic.tld:
為tag標記增加arrtibute節點,語法如下:
name: attribute名
required: 是否為必須的attribute
幸運的是,jsp內置地將attribute解釋為java類的成員,我們無需顯式地獲取該值,即可直接使用。也就是說,我們只要在tag內指定了target=admin, 那么,CheckSessionTag在活著的時候就自動獲取該值,可以在邏輯中直接使用。
這樣,我們就可以給tag增加任意的attribute.
[入門以后]
到此為止,我么就可以寫一些類似的簡單的tag了。根據不同的需求,完成不同的邏輯。當我們開發一個新的tag的時候,我們在logic.tld里增加一個tag子標記。設置好相應的類型。如果需要,我們可以把我們已經寫好的tag們完全的移植到第二個項目中使用,僅僅做很少的更改。而更改也僅僅限于對java源代碼。
在我和我的團隊的實際經驗中,taglib最多的應用還是在設計一系列特殊的UI. 例如類似于選擇省市多級行政區域的選擇器。為了放置在HTML中嵌入過多scriplet和腳本,我們的做法通常是將其寫在組裝好的tag里;為了形成整個應用一致的外觀,我們設計了一套用于組裝頁構圖的框架;為了向jsp輸出集合數據,我們設計了可以綁定數據的呈現器。經過很長一個時期的工作,我們發現我們的項目中jsp頁內僅有很少的html, 而完全是有taglib組成的。最終,我們的taglib按照layout, logic, element, form分類,已經形成了比較大的規模。我們甚至完成了apache的struts框架完成的一部分工作。甚至做得更靈活。所有這些工作中積累的思想甚至影響到我在.NET平臺下的技術思路。
為了不使篇幅太長,我不準備在本文再做深入的探索。僅僅介紹taglib入門級的一些東西。有時間的話,可能會再次寫一些高級一些的taglib的設計方法。
[與ASP.NET Web Custom Control]
不能說taglib與ASP.NET Web Custom Control有什么可比性。但是我習慣將二者放到一起看。因為因為在某些實際項目中,我確實從二者之間找到一些共性。甚至有時候創作一些組件的時候實現是完全相同的。
1.?二者都定義了當標記輸出開始標記和結束標記時的過程。
2.?二者都可以跨應用使用。
3.?可以使用相同的HTML和腳本。
但比較起來,ASP.NET Web Custom Control更高級一些。可以在容器中直接以對象的形式訪問。并且可以定義方法,事件。意即可以完全控制一個控件。
而taglib同樣有優點,例如可以嵌套使用,開發成本低等。最關鍵的是,taglib具有做為java產品的精細,小巧和活潑。他們分別是有著不同風格的優秀技術。
參考文檔
taglib最佳實踐(IBM developerWorks中文站)
http://www-900.ibm.com/developerWorks/cn/java/j-jsp07233/
肖菁,軟件工程師,IBM developerWorks/Bea dev2dev/sun 技術開發者撰稿人,主要研究J2EE、web services以及他們在websphere、weblogic平臺上的實現,擁有IBM的 Developing With Websphere Studio證書。您可以通過guilaida@163.com和作者取得聯系,或者查看作者的主頁獲取更多信息。
Cache是一種用于提高系統響應速度、改善系統運行性能的技術。尤其是在Web應用中,通過緩存頁面的輸出結果,可以很顯著的改善系統運行性能。本文中作者給大家介紹一個實現J2EE框架中Web應用層緩存功能的開放源代碼項目----OSCache。通過應用OSCache,我們不但可以實現通常的Cache功能,還能夠改善系統的穩定性。
在信息系統建設過程中我們通常會遇到這樣的問題:
1. 基礎數據的變更問題
信息系統中需要處理的基礎數據的內容短時間內是不會發生變化的,但是在一個相對長一些的時間里,它卻可能是動態增加或者減少的。
舉個例子:電子商務中關于送貨區域的定義,可能短時間內不會發生變化,但是隨著電子商務企業業務的擴大,系統中需要處理的送貨區域就可能增加。所以我們的系統中不得不在每次向客戶展示送貨區域信息的時候都和數據庫(假設送貨區域信息保存在數據庫中,這也是通常采用的處理方法)進行交互。
2. 統計報表(不僅限于統計報表)的問題
一般來說,統計報表是一個周期性的工作,可能是半個月、一個月或者更長的時間才會需要更新一次,然而統計報表通常是圖形顯示或者是生成pdf、word、excel等格式的文件,這些圖形內容、文件的生成通常需要消耗很多的系統資源,給系統運行造成很大的負擔。
通過比較分析,不難發現這兩類問題有一些共同點:
1、被處理的內容短時間不變,所以短時間內可以作為靜態內容進行處理
2、在一個不太長的時間內,被處理的內容可能或者必定產生變化,所以必須將他們作為動態內容進行處理
3、在合理的時間區段內可以忽略被處理內容變化后帶來的影響
4、對這些內容的處理動作比較消耗系統性能,影響系統響應時間
緩存技術可以幫助我們很好的解決這個問題:
1、緩存信息
當上述的基礎數據或者統計報表第一次被訪問時,被處理的內容被當作動態信息,基礎數庫從數據庫中獲得,統計報表也會被生成符合要求的圖形、文件,然后這些信息都會被放入緩存信息中。
2、響應信息由緩存提供
當上述的基礎數據或者統計報表繼續被訪問時,系統將會首先檢查緩存信息中是否有對應的內容和我們設定的緩存規則,如果符合緩存信息存在而且符合緩存規則,給出的響應將來自于緩存信息,如果沒有或者緩存信息已經不符合設定的要求,系統將重復上一步的動作。
很顯然,上面的步驟2中,多數情況下,當用戶請求到達時,被處理的內容將來自于緩存,所以大大的減少了與數據庫的交互,或者不再需要為每個請求都生成一次報表圖形或者文件,這部分工作的減少對于降低系統性能消耗、提高系統穩定性和并發處理能力是非常有益的。
OSCache是OpenSymphony組織提供的一個J2EE架構中Web應用層的緩存技術實現組件,它的出現解決了我們面臨的問題。 OSCache目前最新的穩定版本是2.0,本文中的例子都是基于這個版本的,如果大家運行例子的過程中發生問題,請首先確認是否采用了正確的軟件版本。
1. 兼容多種支持JSP的web服務器
已經通過兼容測試的web服務器包括OrionServer (1.4.0或者以上版本) 、Macromedia JRun (3.0或者以上版本) 、BEA Weblogic (7.x或者以上版本) 、IBM Websphere (5.0版本)、Silverstream (3.7.4版本)、Caucho Resin (1.2.3或者以上版本)、Tomcat (4.0或者以上版本) ,其他支持servlet2.3、jsp1.2的web服務器應該都是完全兼容OSCache的。
2. 可選的緩存區
你可以使用內存、硬盤空間、同時使用內存和硬盤或者提供自己的其他資源(需要自己提供適配器)作為緩存區。
3. 靈活的緩存系統
OSCache支持對部分頁面內容或者對頁面級的響應內容進行緩存,編程者可以根據不同的需求、不同的環境選擇不同的緩存級別。
4. 容錯
在一般的web應用中,如果某個頁面需要和數據庫打交道,而當客戶請求到達時,web應用和數據庫之間無法進行交互,那么將返回給用戶"系統出錯"或者類似的提示信息,如果使用了OSCache的話,你可以使用緩存提供給用戶,給自己贏得維護系統或者采取其他補救的時間。
其它特性還包括對集群的支持、緩存主動刷新等特性,大家可以參考OpenSymphony網站上的其他資源獲取更多的信息。
OSCache是一個基于web應用的組件,他的安裝工作主要是對web應用進行配置,大概的步驟如下:
1. 下載、解壓縮OSCache
請到OSCache的主頁http://www.opensymphony.com/oscache/download.html下載Oscache的最新版本,作者下載的是OSCache的最新穩定版本2.0。
將下載后的。Zip文件解壓縮到c:\oscache(后面的章節中將使用%OSCache_Home%來表示這個目錄)目錄下
2. 新建立一個web應用
3. 將主要組件%OSCache_Home%\oscache.jar放入WEB-INF\lib目錄
4. commons-logging.jar、commons-collections.jar的處理
5. 將oscache.properties、oscache.tld放入WEB-INF\class目錄
6. 修改web.xml文件
在web.xml文件中增加下面的內容,增加對OSCache提供的taglib的支持:
<taglib> <taglib-uri>oscache</taglib-uri> <taglib-location>/WEB-INF/classes/ oscache.tld</taglib-location> </taglib> |
OSCache中按照緩存范圍的不同分為兩種不同的方式:一種是緩存JSP頁面中部分或者全部內容,一種是基于整個頁面文件的緩存。
這是OSCache提供的標簽庫中最重要的一個標簽,包括在標簽中的內容將應用緩存機制進行處理,處理的方式將取決于編程者對cache標簽屬性的設置。
第一次請求到達時,標簽中的內容被處理并且緩存起來,當下一個請求到達時,緩存系統會檢查這部分內容的緩存是否已經失效,主要是以下幾項:
如果符合上面四項中的任何一項,被緩存的內容視為已經失效,這時被緩存的內容將被重新處理并且返回處理過后的信息,如果被緩存的內容沒有失效,那么返回給用戶的將是緩存中的信息。
cache標簽的屬性說明:
key - 標識緩存內容的關鍵詞。在指定的作用范圍內必須是唯一的。默認的key是被訪問頁面的URI和后面的請求字符串。
你可以在同一個頁面中使用很多cache標簽而不指定他的key屬性,這種情況下系統使用該頁面的URI和后面的請求字符串,另外再自動給這些key增加一個索引值來區分這些緩存內容。但是不推薦采用這樣的方式。
scope - 緩存發生作用的范圍,可以是application或者session
time - 緩存內容的時間段,單位是秒,默認是3600秒,也就是一個小時,如果設定一個負值,那么這部分被緩存的內容將永遠不過期。
duration - 指定緩存內容失效的時間,是相對time的另一個選擇,可以使用簡單日期格式或者符合USO-8601的日期格式。如:duration='PT5M' duration='5s'等
refresh - false 或者true。
如果refresh屬性設置為true,不管其他的屬性是否符合條件,這部分被緩存的內容都將被更新,這給編程者一種選擇,決定什么時候必須刷新。
mode - 如果編程者不希望被緩存的內容增加到給用戶的響應中,可以設置mode屬性為"silent"
其它可用的屬性還包括:cron 、groups、language、refreshpolicyclass、refreshpolicyparam。
上面的這些屬性可以單獨使用,也可以根據需要組合使用,下面的例子將講解這些常用屬性的使用方式。
1. 最簡單的cache標簽用法
使用默認的關鍵字來標識cache內容,超時時間是默認的3600秒
<cache:cache> <% //自己的JSP代碼內容 %> </cache:cache> |
2. 用自己指定的字符串標識緩存內容,并且設定作用范圍為session。
<cache:cache key="foobar" scope="session"> <% //自己的JSP代碼內容 %> </cache:cache> |
3.動態設定key值,使用自己指定的time屬性設定緩存內容的超時時間,使用動態refresh值決定是否強制內容刷新。
因為OSCache使用key值來標識緩存內容,使用相同的key值將會被認為使用相同的的緩存內容,所以使用動態的key值可以自由的根據不同的角色、不同的要求決定使用不同的緩存內容。
<cache:cache key="<%= product.getId() %>" time="1800" refresh="<%= needRefresh %>"> <% //自己的JSP代碼內容 %> </cache:cache> |
4. 設置time屬性為負數使緩存內容永不過期
<cache:cache time="-1"> <% //自己的JSP代碼內容 %> |
5. 使用duration屬性設置超期時間
<cache:cache duration='PT5M'> <% //自己的JSP代碼內容 %> |
6. 使用mode屬性使被緩存的內容不加入給客戶的響應中
<cache:cache mode='silent'> <% //自己的JSP代碼內容 %> |
在OSCache組件中提供了一個CacheFilter用于實現頁面級的緩存,主要用于對web應用中的某些動態頁面進行緩存,尤其是那些需要生成pdf格式文件/報表、圖片文件等的頁面,不僅減少了數據庫的交互、減少數據庫服務器的壓力,而且對于減少web服務器的性能消耗有很顯著的效果。
這種功能的實現是通過在web.xml中進行配置來決定緩存哪一個或者一組頁面,而且還可以設置緩存的相關屬性,這種基于配置文件的實現方式對于J2EE來說應該是一種標準的實現方式了。
[注] 只有客戶訪問時返回http頭信息中代碼為200(也就是訪問已經成功)的頁面信息才能夠被緩存
1. 緩存單個文件
修改web.xml,增加如下內容,確定對/testContent.jsp頁面進行緩存。
<filter> <filter-name>CacheFilter</filter-name> <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class> </filter> <filter-mapping> <filter-name>CacheFilter</filter-name> <!-對/testContent.jsp頁面內容進行緩存--> <url-pattern>/testContent.jsp</url-pattern> </filter-mapping> |
2. 緩存URL pattern
修改web.xml,增加如下內容,確定對*.jsp頁面進行緩存。
<filter> <filter-name>CacheFilter</filter-name> <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class> </filter> <filter-mapping> <filter-name>CacheFilter</filter-name> <!-對所有jsp頁面內容進行緩存--> <url-pattern>*.jsp</url-pattern> </filter-mapping> |
3. 自己設定緩存屬性
在頁面級緩存的情況下,可以通過設置CacheFilter的初始屬性來決定緩存的一些特性:time屬性設置緩存的時間段,默認為3600秒,可以根據自己的需要只有的設置,而scope屬性設置,默認為application,可選項包括application、session
<filter> <filter-name>CacheFilter</filter-name> <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class> <init-param> <param-name>time</param-name> <param-value>600</param-value> </init-param> <init-param> <param-name>scope</param-name> <param-value>session</param-value> </init-param> </filter> <filter-mapping> <filter-name>CacheFilter</filter-name> <!-對所有jsp頁面內容進行緩存--> <url-pattern>*.jsp</url-pattern> </filter-mapping> |
系統平臺:windows 2000 高級服務器/ P3 800 /512M內存
web服務器:websphere 5.0
數據庫服務器:mysql 4.0.18-nt
性能測試用工具:apache Jmeter
這次性能測試對比方為使用緩存和不使用緩存兩種,他們的訪問代碼都是一樣的:通過數據源從本地mysql數據庫中獲取person表的所有記錄,然后顯示在頁面上。
測試中將模仿10個用戶,每個用戶發起5次請求,然后統計所有訪問花費的時間。
使用緩存后的測試結果 不使用緩存時的測試結果
所有請求花費的總時間(毫秒) 20569 22870
性能測試的詳細結果請大家查看下載內容中的《不使用cache時的系統性能測試結果.txt》和《使用cache后系統性能測試結果.txt》
在J2EE系統中,我們經常需要處理一些特殊的動態內容,這些內容在一個時間段內的變更非常有限,但是又不得不將他們確定為動態內容進行輸出,而且非常消耗數據庫系統資源或者web服務器的資源,這時我們就可以采用Cache----一種用于提高系統響應速度、改善系統運行性能的技術----來優化我們的系統。尤其是在Web應用中,這種處理可以很顯著的改善系統運行性能。
本文中作者給大家介紹一個實現J2EE框架中Web應用層緩存功能的開放源代碼項目----OSCache。它提供了在J2EE系統中實現緩存需要的豐富的功能。通過應用OSCache,我們不但可以實現通常的Cache功能、自由的設定cache的相關特性比如緩存時間段/緩存內容等,提升系統性能,而且還能有效的改善系統的穩定性。除此之外,OSCache組件還提供了更多的特性比如集群、容錯、靈活的緩存區選擇等。
作者根據自己的使用經驗給大家提供了一些簡單的例子,他們部分演示了如何使用OSCache組件提供的豐富特性,OSCache提供的特性遠不止這些,需要大家在今后的時間里深入的研究,同時也希望大家通過E-mail和作者貢獻研究成果。
○用二分鐘配置:
將下載的oscache-2.1.1-full.zip解壓,你會看到oscache-2.1.1.jar 及一些目錄 docs、etc、lib、src。
現在將 etc目錄下oscache.properties?、oscache.tld 及 oscache-2.1.1.jar 文件復制到你的應用服務器目錄下
使用tomcat,jdk1.4 配置例:
???????? D:\Tomcat 5.0\webapps\ROOT\WEB-INF\lib\oscache-2.1.1.jar
???????? D:\Tomcat 5.0\webapps\ROOT\WEB-INF\classes\oscache.properties
???????? D:\Tomcat 5.0\webapps\ROOT\WEB-INF\classes\oscache.tld
oscache.properties 文件配置參數說明:
cache.memory
值為true 或 false ,默認為在內存中作緩存,
如設置為false,那cache只能緩存到數據庫或硬盤中,那cache還有什么意義:)
cache.capacity
緩存元素個數
cache.persistence.class
持久化緩存類,如此類打開,則必須設置cache.path信息
cache.cluster 相關
為集群設置信息。如
cache.cluster.multicast.ip為廣播IP地址
cache.cluster.properties為集群屬性
再將 D:\Tomcat 5.0\webapps\ROOT\WEB-INF\web.xml 文件中添加如下代碼
<taglib>
<taglib-uri>oscache</taglib-uri>
<taglib-location>/WEB-INF/classes/oscache.tld</taglib-location>
</taglib>
就是這么簡單,OSCache就配置好了。
○再用二分鐘測試一下:
編寫jsp文件
======================cache1.jsp 內容如下=================
<%@ page contentType="text/html;charset=GBK"%>
<%@ page import="java.util.*" %>
<%@ taglib uri="oscache" prefix="cache" %>
<html>
<body>
沒有緩存的日期: <%= new Date() %><p>
<!--自動刷新-->
<cache:cache time="30">
每30秒刷新緩存一次的日期: <%= new Date() %> <p>
</cache:cache>
<!--手動刷新-->
<cache:cache key="testcache">
手動刷新緩存的日期: <%= new Date() %> <p>
</cache:cache>
<a /></body>
</html>
=========================================================
====================cache2.jsp 執行手動刷新頁面如下===========
<%@ page contentType="text/html;charset=GBK"%>
<%@ taglib uri="oscache" prefix="cache" %>
<html>
<body>緩存已刷新...<p>
<cache:flush key="testcache" scope="application"/>
<a /></body>
</html>
==========================================================???????
其他: 緩存過濾器 CacheFilter
可以讓你所有.jsp頁面自動緩存。
你可以在web.xml中定義緩存過濾器,定義特定資源的緩存。
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
<init-param>
<param-name>time</param-name>
<param-value>60</param-value>
</init-param>
<init-param>
<param-name>scope</param-name>
<param-value>session</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
上面定義將緩存所有.jsp頁面,緩存刷新時間為60秒,緩存作用域為Session
注意,CacheFilter只捕獲Http頭為200的頁面請求,即只對無錯誤請求作緩存,
而不對其他請求(如500,404,400)作緩存處理
此文完。。謝謝觀看。
1、POJO 緩存
這種方式的緩存直接調用OSCache的API進行,主要用于處理頁面內容會根據參數動態改變,可以將參數設置為key值來保存數據:
首先,聲明成員變量:
?// OSCache Adminitrator instance
?private static GeneralCacheAdministrator cacheAdmin = null;
其次,進行初始化:
?public RingArtistAction() {
? cacheAdmin = new GeneralCacheAdministrator();
?}
將POJO進行緩存:
? // Cache data key and refresh period
? String key = sex + ":" + place;
? int refreshPeriod = Constants.getIntegerValue(Constants.OSCACHE_REFRESH_PERIOD).intValue();
? try {
????? // Get from the cache
?? artists = (Map) cacheAdmin.getFromCache(key, refreshPeriod);
? } catch (NeedsRefreshException nre) {
????? try {
????????? // Get the value (probably from the database)
??? int count = getArtistCount(sex, place, errors);
??? artists = getArtistData(sex, place, count, errors);
????????? // Store in the cache
??? cacheAdmin.putInCache(key, artists);
????? } catch (Exception ex) {
????????? // We have the current content if we want fail-over.
??? artists = (Map) nre.getCacheContent();
????????? // It is essential that cancelUpdate is called if the
????????? // cached content is not rebuilt
??? cacheAdmin.cancelUpdate(key);
??? ex.printStackTrace();
????? }
? }
?
2、HTTP Response 緩存
這種方式的緩存用來處理整個頁面的內容固定,不會根據參數動態改變:
首先在web.xml中配置CacheFilter:
?<filter>
? <filter-name>CacheFilter</filter-name>
? <filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class>
? <init-param>
?? <param-name>time</param-name>
?? <param-value>86400</param-value>
? </init-param>
? <init-param>
?? <param-name>scope</param-name>
?? <param-value>application</param-value>
? </init-param>
?</filter>
將所有需要緩存的頁面加入filter-mapping:
?<filter-mapping>
? <filter-name>Set Character Encoding</filter-name>
? <url-pattern>/*</url-pattern>
?</filter-mapping>
注意,只有返回狀態為200(HttpServletResponse.SC_OK)的內容才會被緩存
3、JSP Tag 緩存
JSP Tag緩存主要用于緩存JSP頁面的局部內容:
? <cache:cache key="especialcategory" cron="* 5 * * *">
? <jsp:include page="/ringcategory.do" flush="true" >
??? <jsp:param name="ringType" value="1"/>
? </jsp:include>
? </cache:cache>
4、O/R Data Access 緩存
請閱讀參考資料的內容獲取詳情。
參考資料:
Taking the load off: OSCache helps databases cope:http://www.theserverside.com/articles/article.tss?l=OSCacheHelpsDatabases
?
|
現在JDK1.4里有了自己的正則表達式API包,JAVA程序員可以免去找第三方提供的正則表達式庫的周折了,我們現在就馬上來了解一下這個SUN提供的遲來恩物- -對我來說確實如此。
1.簡介:
java.util.regex是一個用正則表達式所訂制的模式來對字符串進行匹配工作的類庫包。
它包括兩個類:Pattern和Matcher Pattern 一個Pattern是一個正則表達式經編譯后的表現模式。
Matcher 一個Matcher對象是一個狀態機器,它依據Pattern對象做為匹配模式對字符串展開匹配檢查。 首先一個Pattern實例訂制了一個所用語法與PERL的類似的正則表達式經編譯后的模式,然后一個Matcher實例在這個給定的Pattern實例的模式控制下進行字符串的匹配工作。
以下我們就分別來看看這兩個類:
2.Pattern類:
Pattern的方法如下: static Pattern compile(String regex)
將給定的正則表達式編譯并賦予給Pattern類
static Pattern compile(String regex, int flags)
同上,但增加flag參數的指定,可選的flag參數包括:CASE INSENSITIVE,MULTILINE,DOTALL,UNICODE CASE, CANON EQ
int flags()
返回當前Pattern的匹配flag參數.
Matcher matcher(CharSequence input)
生成一個給定命名的Matcher對象
static boolean matches(String regex, CharSequence input)
編譯給定的正則表達式并且對輸入的字串以該正則表達式為模開展匹配,該方法適合于該正則表達式只會使用一次的情況,也就是只進行一次匹配工作,因為這種情況下并不需要生成一個Matcher實例。
String pattern()
返回該Patter對象所編譯的正則表達式。
String[] split(CharSequence input)
將目標字符串按照Pattern里所包含的正則表達式為模進行分割。
String[] split(CharSequence input, int limit)
作用同上,增加參數limit目的在于要指定分割的段數,如將limi設為2,那么目標字符串將根據正則表達式分為割為兩段。
一個正則表達式,也就是一串有特定意義的字符,必須首先要編譯成為一個Pattern類的實例,這個Pattern對象將會使用matcher()方法來生成一個Matcher實例,接著便可以使用該 Matcher實例以編譯的正則表達式為基礎對目標字符串進行匹配工作,多個Matcher是可以共用一個Pattern對象的。
現在我們先來看一個簡單的例子,再通過分析它來了解怎樣生成一個Pattern對象并且編譯一個正則表達式,最后根據這個正則表達式將目標字符串進行分割:
import java.util.regex.*;
public class Replacement{
public static void main(String[] args) throws Exception {
// 生成一個Pattern,同時編譯一個正則表達式
Pattern p = Pattern.compile("[/]+");
//用Pattern的split()方法把字符串按"/"分割
String[] result = p.split(
"Kevin has seen《LEON》seveal times,because it is a good film."
+"/ 凱文已經看過《這個殺手不太冷》幾次了,因為它是一部"
+"好電影。/名詞:凱文。");
for (int i=0; i<result.length; i++)
System.out.println(result[i]);
}
}
輸出結果為:
Kevin has seen《LEON》seveal times,because it is a good film.
凱文已經看過《這個殺手不太冷》幾次了,因為它是一部好電影。
名詞:凱文。
很明顯,該程序將字符串按"/"進行了分段,我們以下再使用 split(CharSequence input, int limit)方法來指定分段的段數,程序改動為:
tring[] result = p.split("Kevin has seen《LEON》seveal times,because it is a good film./ 凱文已經看過《這個殺手不太冷》幾次了,因為它是一部好電影。/名詞:凱文。",2);
這里面的參數"2"表明將目標語句分為兩段。
輸出結果則為:
Kevin has seen《LEON》seveal times,because it is a good film.
凱文已經看過《這個殺手不太冷》幾次了,因為它是一部好電影。/名詞:凱文。
由上面的例子,我們可以比較出java.util.regex包在構造Pattern對象以及編譯指定的正則表達式的實現手法與我們在上一篇中所介紹的Jakarta-ORO 包在完成同樣工作時的差別,Jakarta-ORO 包要先構造一個PatternCompiler類對象接著生成一個Pattern對象,再將正則表達式用該PatternCompiler類的compile()方法來將所需的正則表達式編譯賦予Pattern類:
PatternCompiler orocom=new Perl5Compiler();
Pattern pattern=orocom.compile("REGULAR EXPRESSIONS");
PatternMatcher matcher=new Perl5Matcher();
但是在java.util.regex包里,我們僅需生成一個Pattern類,直接使用它的compile()方法就可以達到同樣的效果:
Pattern p = Pattern.compile("[/]+");
因此似乎java.util.regex的構造法比Jakarta-ORO更為簡潔并容易理解。
3.Matcher類:
Matcher方法如下: Matcher appendReplacement(StringBuffer sb, String replacement)
將當前匹配子串替換為指定字符串,并且將替換后的子串以及其之前到上次匹配子串之后的字符串段添加到一個StringBuffer對象里。
StringBuffer appendTail(StringBuffer sb)
將最后一次匹配工作后剩余的字符串添加到一個StringBuffer對象里。
int end()
返回當前匹配的子串的最后一個字符在原目標字符串中的索引位置 。
int end(int group)
返回與匹配模式里指定的組相匹配的子串最后一個字符的位置。
boolean find()
嘗試在目標字符串里查找下一個匹配子串。
boolean find(int start)
重設Matcher對象,并且嘗試在目標字符串里從指定的位置開始查找下一個匹配的子串。
String group()
返回當前查找而獲得的與組匹配的所有子串內容
String group(int group)
返回當前查找而獲得的與指定的組匹配的子串內容
int groupCount()
返回當前查找所獲得的匹配組的數量。
boolean lookingAt()
檢測目標字符串是否以匹配的子串起始。
boolean matches()
嘗試對整個目標字符展開匹配檢測,也就是只有整個目標字符串完全匹配時才返回真值。
Pattern pattern()
返回該Matcher對象的現有匹配模式,也就是對應的Pattern 對象。
String replaceAll(String replacement)
將目標字符串里與既有模式相匹配的子串全部替換為指定的字符串。
String replaceFirst(String replacement)
將目標字符串里第一個與既有模式相匹配的子串替換為指定的字符串。
Matcher reset()
重設該Matcher對象。
Matcher reset(CharSequence input)
重設該Matcher對象并且指定一個新的目標字符串。
int start()
返回當前查找所獲子串的開始字符在原目標字符串中的位置。
int start(int group)
返回當前查找所獲得的和指定組匹配的子串的第一個字符在原目標字符串中的位置。
(光看方法的解釋是不是很不好理解?不要急,待會結合例子就比較容易明白了)
一個Matcher實例是被用來對目標字符串進行基于既有模式(也就是一個給定的Pattern所編譯的正則表達式)進行匹配查找的,所有往Matcher的輸入都是通過CharSequence接口提供的,這樣做的目的在于可以支持對從多元化的數據源所提供的數據進行匹配工作。
我們分別來看看各方法的使用:
★matches()/lookingAt ()/find():
一個Matcher對象是由一個Pattern對象調用其matcher()方法而生成的,一旦該Matcher對象生成,它就可以進行三種不同的匹配查找操作:
matches()方法嘗試對整個目標字符展開匹配檢測,也就是只有整個目標字符串完全匹配時才返回真值。
lookingAt ()方法將檢測目標字符串是否以匹配的子串起始。
find()方法嘗試在目標字符串里查找下一個匹配子串。
以上三個方法都將返回一個布爾值來表明成功與否。
★replaceAll ()/appendReplacement()/appendTail():
Matcher類同時提供了四個將匹配子串替換成指定字符串的方法:
replaceAll()
replaceFirst()
appendReplacement()
appendTail()
replaceAll()與replaceFirst()的用法都比較簡單,請看上面方法的解釋。我們主要重點了解一下appendReplacement()和appendTail()方法。
appendReplacement(StringBuffer sb, String replacement) 將當前匹配子串替換為指定字符串,并且將替換后的子串以及其之前到上次匹配子串之后的字符串段添加到一個StringBuffer對象里,而appendTail(StringBuffer sb) 方法則將最后一次匹配工作后剩余的字符串添加到一個StringBuffer對象里。
例如,有字符串fatcatfatcatfat,假設既有正則表達式模式為"cat",第一次匹配后調用appendReplacement(sb,"dog"),那么這時StringBuffer sb的內容為fatdog,也就是fatcat中的cat被替換為dog并且與匹配子串前的內容加到sb里,而第二次匹配后調用appendReplacement(sb,"dog"),那么sb的內容就變為fatdogfatdog,如果最后再調用一次appendTail(sb),那么sb最終的內容將是fatdogfatdogfat。
還是有點模糊?那么我們來看個簡單的程序:
//該例將把句子里的"Kelvin"改為"Kevin"
import java.util.regex.*;
public class MatcherTest{
public static void main(String[] args)
throws Exception {
//生成Pattern對象并且編譯一個簡單的正則表達式"Kelvin"
Pattern p = Pattern.compile("Kevin");
//用Pattern類的matcher()方法生成一個Matcher對象
Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in Kelvin Chen's KelvinSoftShop company");
StringBuffer sb = new StringBuffer();
int i=0;
//使用find()方法查找第一個匹配的對象
boolean result = m.find();
//使用循環將句子里所有的kelvin找出并替換再將內容加到sb里
while(result) {
i++;
m.appendReplacement(sb, "Kevin");
System.out.println("第"+i+"次匹配后sb的內容是:"+sb);
//繼續查找下一個匹配對象
result = m.find();
}
//最后調用appendTail()方法將最后一次匹配后的剩余字符串加到sb里;
m.appendTail(sb);
System.out.println("調用m.appendTail(sb)后sb的最終內容是:"+ sb.toString());
}
}
最終輸出結果為:
第1次匹配后sb的內容是:Kevin
第2次匹配后sb的內容是:Kevin Li and Kevin
第3次匹配后sb的內容是:Kevin Li and Kevin Chan are both working in Kevin
第4次匹配后sb的內容是:Kevin Li and Kevin Chan are both working in Kevin Chen's Kevin
調用m.appendTail(sb)后sb的最終內容是:Kevin Li and Kevin Chan are both working in Kevin Chen's KevinSoftShop company.
看了上面這個例程是否對appendReplacement(),appendTail()兩個方法的使用更清楚呢,如果還是不太肯定最好自己動手寫幾行代碼測試一下。
★group()/group(int group)/groupCount():
該系列方法與我們在上篇介紹的Jakarta-ORO中的MatchResult .group()方法類似(有關Jakarta-ORO請參考上篇的內容),都是要返回與組匹配的子串內容,下面代碼將很好解釋其用法:
import java.util.regex.*;
public class GroupTest{
public static void main(String[] args)
throws Exception {
Pattern p = Pattern.compile("(ca)(t)");
Matcher m = p.matcher("one cat,two cats in the yard");
StringBuffer sb = new StringBuffer();
boolean result = m.find();
System.out.println("該次查找獲得匹配組的數量為:"+m.groupCount());
for(int i=1;i<=m.groupCount();i++){
System.out.println("第"+i+"組的子串內容為: "+m.group(i));
}
}
}
輸出為:
該次查找獲得匹配組的數量為:2
第1組的子串內容為:ca
第2組的子串內容為:t
Matcher對象的其他方法因比較好理解且由于篇幅有限,請讀者自己編程驗證。
4.一個檢驗Email地址的小程序:
最后我們來看一個檢驗Email地址的例程,該程序是用來檢驗一個輸入的EMAIL地址里所包含的字符是否合法,雖然這不是一個完整的EMAIL地址檢驗程序,它不能檢驗所有可能出現的情況,但在必要時您可以在其基礎上增加所需功能。
import java.util.regex.*;
public class Email {
public static void main(String[] args) throws Exception {
String input = args[0];
//檢測輸入的EMAIL地址是否以 非法符號"."或"@"作為起始字符
Pattern p = Pattern.compile("^\\.|^\\@");
Matcher m = p.matcher(input);
if (m.find()){
System.err.println("EMAIL地址不能以'.'或'@'作為起始字符");
}
//檢測是否以"www."為起始
p = Pattern.compile("^www\\.");
m = p.matcher(input);
if (m.find()) {
System.out.println("EMAIL地址不能以'www.'起始");
}
//檢測是否包含非法字符
p = Pattern.compile("[^A-Za-z0-9\\.\\@_\\-~#]+");
m = p.matcher(input);
StringBuffer sb = new StringBuffer();
boolean result = m.find();
boolean deletedIllegalChars = false;
while(result) {
//如果找到了非法字符那么就設下標記
deletedIllegalChars = true;
//如果里面包含非法字符如冒號雙引號等,那么就把他們消去,加到SB里面
m.appendReplacement(sb, "");
result = m.find();
}
m.appendTail(sb);
input = sb.toString();
if (deletedIllegalChars) {
System.out.println("輸入的EMAIL地址里包含有冒號、逗號等非法字符,請修改");
System.out.println("您現在的輸入為: "+args[0]);
System.out.println("修改后合法的地址應類似: "+input);
}
}
}
例如,我們在命令行輸入:java Email www.kevin@163.net
那么輸出結果將會是:EMAIL地址不能以'www.'起始
如果輸入的EMAIL為@kevin@163.net
則輸出為:EMAIL地址不能以'.'或'@'作為起始字符
當輸入為:cgjmail#$%@163.net
那么輸出就是:
輸入的EMAIL地址里包含有冒號、逗號等非法字符,請修改
您現在的輸入為: cgjmail#$%@163.net
修改后合法的地址應類似: cgjmail@163.net
5.總結:
本文介紹了jdk1.4.0-beta3里正則表達式庫--java.util.regex中的類以及其方法,如果結合與上一篇中所介紹的Jakarta-ORO API作比較,讀者會更容易掌握該API的使用,當然該庫的性能將在未來的日子里不斷擴展,希望獲得最新信息的讀者最好到及時到SUN的網站去了解。
6.結束語:
本來計劃再多寫一篇介紹一下需付費的正則表達式庫中較具代表性的作品,但覺得既然有了免費且優秀的正則表達式庫可以使用,何必還要去找需付費的呢,相信很多讀者也是這么想的:,所以有興趣了解更多其他的第三方正則表達式庫的朋友可以自己到網上查找或者到我在參考資料里提供的網址去看看。
作者信息:liqian??liqianbnu@126.com?? http://202.112.88.39/liqian/
文章原始出處:http://202.112.88.39/liqian/000271.html
自從接觸Java和JSP以來,就不斷與Java的中文亂碼問題打交道,現在終于得到了徹底的解決,現將我們的解決心得與大家共享。
一、Java中文問題的由來
Java的內核和class文件是基于unicode的,這使Java程序具有良好的跨平臺性,但也帶來了一些中文亂碼問題的麻煩。原因主要有兩方面,Java和JSP文件本身編譯時產生的亂碼問題和Java程序于其他媒介交互產生的亂碼問題。
首先Java(包括JSP)源文件中很可能包含有中文,而Java和JSP源文件的保存方式是基于字節流的,如果Java和JSP編譯成class文件過程中,使用的編碼方式與源文件的編碼不一致,就會出現亂碼。基于這種亂碼,建議在Java文件中盡量不要寫中文(注釋部分不參與編譯,寫中文沒關系),如果必須寫的話,盡量手動帶參數-ecoding GBK或-ecoding gb2312編譯;對于JSP,在文件頭加上<%@ page contentType="text/html;charset=GBK"%>或<%@ page contentType="text/html;charset=gb2312"%>基本上就能解決這類亂碼問題。
本文要重點討論的是第二類亂碼,即Java程序與其他存儲媒介交互時產生的亂碼。很多存儲媒介,如數據庫,文件,流等的存儲方式都是基于字節流的,Java程序與這些媒介交互時就會發生字符(char)與字節(byte)之間的轉換,具體情況如下:
從頁面form提交數據到java程序 byte->char
從java程序到頁面顯示 char—>byte
從數據庫到java程序 byte—>char
從java程序到數據庫 char—>byte
從文件到java程序 byte->char
從java程序到文件 char->byte
從流到java程序 byte->char
從java程序到流 char->byte
如果在以上轉換過程中使用的編碼方式與字節原有的編碼不一致,很可能就會出現亂碼。
二、解決方法
前面已經提到了Java程序與其他媒介交互時字符和字節的轉換過程,如果這些轉換過程中容易產生亂碼。解決這些亂碼問題的關鍵在于確保轉換時使用的編碼方式與字節原有的編碼方式保持一致,下面分別論述(Java或JSP自身產生的亂碼請參看第一部分)。
1、JSP與頁面參數之間的亂碼
JSP獲取頁面參數時一般采用系統默認的編碼方式,如果頁面參數的編碼類型和系統默認的編碼類型不一致,很可能就會出現亂碼。解決這類亂碼問題的基本方法是在頁面獲取參數之前,強制指定request獲取參數的編碼方式:request.setCharacterEncoding("GBK")或request.setCharacterEncoding("gb2312")。
如果在JSP將變量輸出到頁面時出現了亂碼,可以通過設置response.setContentType("text/html;charset=GBK")或response.setContentType("text/html;charset=gb2312")解決。
如果不想在每個文件里都寫這樣兩句話,更簡潔的辦法是使用Servlet規范中的過慮器指定編碼,過濾器的在web.xml中的典型配置和主要代碼如下:
web.xml:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>net.vschool.web.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
CharacterEncodingFilter.java:
public class CharacterEncodingFilter implements Filter
{
protected String encoding = null;
public void init(FilterConfig filterConfig) throws ServletException
{
this.encoding = filterConfig.getInitParameter("encoding");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
chain.doFilter(request, response);
}
}
2、Java與數據庫之間的亂碼
大部分數據庫都支持以unicode編碼方式,所以解決Java與數據庫之間的亂碼問題比較明智的方式是直接使用unicode編碼與數據庫交互。很多數據庫驅動自動支持unicode,如Microsoft的SQLServer驅動。其他大部分數據庫驅動,可以在驅動的url參數中指定,如如mm的mysql驅動:jdbc:mysql://localhost/WEBCLDB?useUnicode=true&characterEncoding=GBK。
3、Java與文件/流之間的亂碼
Java讀寫文件最常用的類是FileInputStream/FileOutputStream和FileReader/FileWriter。其中FileInputStream和FileOutputStream是基于字節流的,常用于讀寫二進制文件。讀寫字符文件建議使用基于字符的FileReader和FileWriter,省去了字節與字符之間的轉換。但這兩個類的構造函數默認使用系統的編碼方式,如果文件內容與系統編碼方式不一致,可能會出現亂碼。在這種情況下,建議使用FileReader和FileWriter的父類:InputStreamReader/OutputStreamWriter,它們也是基于字符的,但在構造函數中可以指定編碼類型:InputStreamReader(InputStream in, Charset cs) 和OutputStreamWriter(OutputStream out, Charset cs)。
4、其他
上面提到的方法應該能解決大部分亂碼問題,如果在其他地方還出現亂碼,可能需要手動修改代碼。解決Java亂碼問題的關鍵在于在字節與字符的轉換過程中,你必須知道原來字節或轉換后的字節的編碼方式,轉換時采用的編碼必須與這個編碼方式保持一致。我們以前使用Resin服務器,使用smartUpload組件上傳文件,上傳文件同時傳遞的中文參數獲取沒有亂碼問題。當在Linux中把Resin設置成服務后,上傳文件同時的中文參數獲取出現了亂碼。這個問題困擾了我們很久,后來我們分析smartUpload組件的源文件,因為文件上傳采用的是字節流的方式,里面包含的參數名稱和值也是字節流的方式傳遞的。smartUpload組件讀取字節流后再將參數名稱和值從字節流中解析出來,問題就出現在smartUpload將字節流轉換成字符串時采用了系統默認的編碼,而將Resin設置成服務后,系統默認的編碼可能發生了改變,因此出現了亂碼。后來,我們更改了smartUpload的源文件,增加了一個屬性charset和setCharset(String)方法,將upload()方法中提取參數語句:
String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1 );
改成了
String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1, charset );
終于解決了這個亂碼問題。
三、后記
接觸Java和JSP已經有一年多了,這一年來最大的收獲是越來越喜歡上了Java,開始把問題當作樂事去研究,沒有了以前的恐懼心理,我相信我會繼續堅持下去。這一年來,從網上學習了很多同行的寶貴經驗,在此表示感謝。這是我第一篇自己總結的Java學習心得,由于水平有限,本文中偏頗和錯誤之處,歡迎指正。如果對你有些價值,在保留作者信息和文章原始出處的前提下可以隨處轉載。
撰寫該文之前已參考了很多關于Java中文問題的文章,其中影響比較大的有owen1944在“Java研究組織”中發表的《這是我們公司總結的一些關于中文亂碼問題的一些解決方案和經驗和大家分享!》等。本文談到的解決方法已應用到“基于網絡的協作學習系統-WebCL”等項目中,并通過資源綁定的方式實現了該平臺中文文兩個版本的即時切換。Google根據瀏覽器自動選擇語言,一個頁面同時顯示多種語言的國際化應用和車東的《Java中文處理學習筆記——Hello Unicode》一文引起了我極大的興趣,日后想將繼續探討Java的國際化問題,歡迎大家一起討論。
1、
獲取字符串的長度:length()
2、判斷字符串的前綴或后綴與已知字符串是否相同
前綴 startsWith(String s)、后綴 endsWith(String s)
3、比較兩個字符串:equals(String s)
4、把字符串轉化為相應的數值
int型 Integer.parseInt(字符串)、long型 Long.parseLong(字符串)
float型 Folat.valueOf(字符串).floatValue()
double型 Double.valueOf(字符串).doubleValue()
5、
將數值轉化為字符串:valueOf(數值)
6、
字符串檢索
indexOf(Srting s) 從頭開始檢索
indexOf(String s ,int startpoint) 從startpoint處開始檢索
如果沒有檢索到,將返回-1
7、
得到字符串的子字符串
substring(int startpoint) 從startpoint處開始獲取
substring(int start,int end) 從start到end中間的字符
8、
替換字符串中的字符,去掉字符串前后空格
replace(char old,char new) 用new替換old
9、
分析字符串
StringTokenizer(String s) 構造一個分析器,使用默認分隔字符(空格,換行,回車,Tab,進紙符)
StringTokenizer(String s,String delim) delim是自己定義的分隔符
nextToken() 逐個獲取字符串中的語言符號
boolean hasMoreTokens() 只要字符串還有語言符號將返回true,否則返回false
countTokens() 得到一共有多少個語言符號
1、文本框
TextField() 構造文本框,一個字符長、TextField(int x) 構造文本框,x個字符長
TextField(String s) 構造文本框,顯示s、setText(String s) 設置文本為s
getText() 獲取文本、setEchoChar(char c) 設置顯示字符為c
setEditable(boolean) 設置文本框是否可以被修改、addActionListener() 添加監視器
removeActionListener() 移去監視器
2、文本區
TextArea() 構造文本區;TextArea(String s) 構造文本區,顯示s
TextArea(String s,int x,int y) 構造文本區,x行,y列,顯示s
TextArea(int x,int y) 構造文本區,x行,y列
TextArea(String s,int x,ing y,int scrollbar)
scrollbar的值是:
TextArea.SCROLLBARS_BOTH;TextArea.SCROLLBARS_VERTICAL_ONLY
TextArea.SCROLLBARS_HORIZONTAL_ONLY;TextArea.SCROLLBARS_NONE
setText(String s) 設置文本為s;getText() 獲取文本
addTextListener() 添加監視器;removeTextListener() 移去監視器
insert(String s,int x) 在x處插入文本s
replaceRange(String s,int x,int y) 用s替換從x到y處的文本
append(String s) 在文本的最后追加文本s
Int getCaretPosition(int n) 獲取文本區中光標的位置
第三章 按鈕
1、按鈕
Button() 構造按鈕;Button(String s) 構造按鈕,標簽是s
setLabel(String s) 設置按鈕標簽是s;getLabel() 獲取按鈕標簽
addActionListener() 添加監視器;removeActionListener() 移去監視器
第四章 標簽
1、標簽
Label() 構造標簽;Label(String s) 構造標簽,顯示s
Label(String s,int x)x是對齊方式,取值:Label.LEFT;Label.RIGHT;Label.CENTER
setText(String s) 設置文本s;getText() 獲取文本
setBackground(Color c) 設置標簽背景顏色;setForeground(Color c) 設置字體顏色
1、選擇框
Checkbox() 構造選擇框;Checkbox(String s) 構造選擇框,給定標題s
Checkbox(String s,boolean b) b設定初始狀態
Checkbox(String s,boolean b,CheckboxGroup g) g設定了所屬的組(有了組就成為單選框)
addItemListener() 添加監視器;removeItemListener() 移去監視器
getState() 返回選擇框的是否選中狀態;setState(boolean b) 設置選擇框的狀態
getLabel() 獲取選擇框的標題;setLabel(String s) 設置選擇框的標題為s
第六章 選擇控件和滾動列表
1、選擇控件
Choice() 構造選擇控件;add(String s) 向選擇控件增加一個選項
addItemListener() 添加監視器;removeItemListener() 移去監視器
getSelectedIndex() 返回當前選項的索引;getSelectedItem() 返回當前選項的字符串代表
insert(String s,int n) 在n處插入選項s
remove(int n);removeAll()
2、滾動列表
List() 構造滾動列表;List(int n) 參數n是可見行數
List(int n,boolean b) 參數b是設置是否可以多項選擇
add(String s) 向列表的結尾增加一個選項;add(String s,int n) 在n處增加一個選項
AddActionListener() 滾動列表添加監視器
addItemListener() 滾動列表上的選項添加監視器
remove(int n) 刪除n初的選項;remnoveAll() 刪除全部選項
getSelectedIndex() 返回當前選項的索引;getSelectedItem() 返回當前選項的字符串代表
3、組件類的一些常用方法
void setBackground(Color c) 設置組件背景顏色
void setForeground(Color c) 設置組件前景顏色
void setFonts(Font f) 設置組件字體
void setBounds(int x,int y,int w,int h) 設置坐標,x,y表示在容器中坐標,w,h表示寬和高
void setLocation(int x,int y) 移動到x,y 處;void setSize(int w,int h) 設置寬和高
void setVisible(boolean b) 設置組建是否可見
int getBounds().wigth 獲取寬;int getBounds().height 獲取高
int getBounds().x 獲取x 坐標;int getBounds().y 獲取y 坐標
Toolkit getToolkit() 獲取工具包對
void setEnabled(boolean b) 設置是否可以使用(默認可以)
1、窗口
Frame() 構造窗口;Frame(String s) 窗口標題是s
setBounds(int x,int y,int w,int h) 窗口位置x,y,寬w,高y
setSize(int w,int h) 設置窗口位置(單位是像素)
setBackground(Color c) 設置背景顏色;setVisible(boolean b) 設置窗口是否可見
pack() 窗口出現時緊湊;setTitle(String s) 設置標題為s
getTitle() 獲取標題;setResizable(boolean b) 設置窗口大小是否可以調整
2、菜單條
Menubar() 構造菜單條;setMenubar() 窗口添加菜單條
3、菜單
Menu() 構造菜單;Menu(String s) 構造菜單,標題s
add(MenuItem item) 菜單增加菜單選項item;add(String s) 向菜單增加選項s
getItem(int n) 獲取n處的選項;getItemCount() 獲取選項數目
insert(MenuItem item,int n) 在n處插入菜單選項item
insert(String s,int n) 在n處插入菜單選項
remove(int n) 刪除菜單的n處的菜單選項;removeAll() 刪除全部
4、菜單項
MenuItem() 構造菜單項;MenuItem(String s) 構造標題是s的菜單項
setEnabled(boolean b) 設置是否可以被選擇;getLabel() 得到菜單選項名
addActionListener() 添加監視器
5、有關菜單的技巧
addSeparator() 增加菜單分割線;CheckboxMenuItem() 復選框菜單項
setShortcut(MenuShortcut k) 設置快捷鍵(k取值KeyEvent.VK_A----KeyEvent.VK_Z)
1、Dialog類
Dialog(Frame f,String s) 構造對話框,初始不可見,s是標題,f是對話框所依賴的窗口
Dialog(Frame f,String s,boolean b) b設置初始是否可見
getTitle() 獲取對話框標題;setTitle(String s) 設置對話框標題
setModal(boolean b) 設置對話框模式;setSize(int w,int h) 設置對話框大小
setVisible(boolean b) 顯示或隱藏對話框
2、FileDialog類
Filedialog(Frame f,String s,int mode) mode的值是fileDialog.LOAD或者fileDialog.SAVE
public String getDirectory() 獲取當前文件對話框中顯示的文件所屬目錄
public String getFile() 獲取當前文件對話框中文件的字符串表示,不存在返回null
1、使用MouseListener借口處理鼠標事件
鼠標事件有5種:按下鼠標鍵,釋放鼠標鍵,點擊鼠標鍵,鼠標進入和鼠標退出
鼠標事件類型是MouseEvent,主要方法有:
getX(),getY() 獲取鼠標位置;getModifiers() 獲取鼠標左鍵或者右鍵
getClickCount() 獲取鼠標被點擊的次數;getSource() 獲取鼠標發生的事件源
事件源獲得監視器的方法addMouseListener(),移去監視器的方法removeMouseListener()
處理事件源發生的時間的事件的接口是MouseListener 接口中有如下的方法
mousePressed(MouseEvent) 負責處理鼠標按下事件
mouseReleased(MouseEvent) 負責處理鼠標釋放事件
mouseEntered(MouseEvent) 負責處理鼠標進入容器事件
mouseExited(MouseEvent) 負責處理鼠標離開事件
mouseClicked(MouseEvent) 負責處理點擊事件
2、使用MouseMotionListener接口處理鼠標事件
事件源發生的鼠標事件有2種:拖動鼠標和鼠標移動;鼠標事件的類型是MouseEvent
事件源獲得監視器的方法是addMouseMotionListener()
處理事件源發生的事件的接口是MouseMotionListener 接口中有如下的方法
mouseDragged() 負責處理鼠標拖動事件;mouseMoved() 負責處理鼠標移動事件
3、控制鼠標的指針形狀
setCursor(Cursor.getPreddfinedCursor(Cursor.鼠標形狀定義)) 鼠標形狀定義見(書 P 210)
4、鍵盤事件
鍵盤事件源使用addKeyListener 方法獲得監視器
鍵盤事件的接口是KeyListener 接口中有3個方法
public void keyPressed(KeyEvent e) 按下鍵盤按鍵
public void keyReleased(KeyEvent e) 釋放鍵盤按鍵
public void keyTypde(KeyEvent e) 按下又釋放鍵盤按鍵
1、Java的線程類與Runnable接口
Thread類
public Thread() 創建線程對象;? public Thread(Runnable target)
target稱為被創建線程的目標對象,負責實現Runnable
線程優先級
Thread類有三個有關線程優先級的靜態常量:MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY
新建線程將繼承創建它的副線程的優先級。
用戶可以調用Thread類的setPriority(int a)來修改a的取值:
Thread.MIN_PRIORITY,Thread.MAX_PRIORITY,Thread.NORM_PRIORITY
主要方法
啟動線程 start();定義線程操作 run()
使線程休眠 sleep():sleep(int millsecond) 以毫秒為單位的休眠時間
sleep(int millsecond,int nanosecond) 以納秒為單位的休眠時間
currentThread() 判斷誰在占用CPU的線程
1、FileInputStream類
FileInputStream(String name) 使用給定的文件名name創建一個FileInputStream對象
FileInputStream(File file) 使用File對象創建FileInpuStream對象
File類有兩個常用方法:
File(String s) s確定文件名字;File(String directory,String s) directory是文件目錄
處理I/O異常
當出現I/O錯誤的時候,Java生成一個IOException(I/O異常)對象來表示這個錯誤的信號。
程序必須使用一個catch檢測這個異常
從輸入流中讀取字節
int read() 返回0~255之間一個整數,如果到輸入流末尾,則返回-1
int read(byte b[]) 讀取字節數組
int read(byte b[],int off,int len):
off指定把數據存放在b中什么地方,len指定讀取的最大字節數
關閉流
close()
2、FileOutputStream類
FileOutputStream(String name) 使用指定的文件名name創建FileOutputStream對象
FileOutputStream(File file) 使用file對象創建FileOutputStream對象
FileOutputStream(FileDescriptor fdobj) 使用FileDescriptor對象創建FileOutputStream對象
3、FileReader類和FileWriter類
FileReader(String filename);FileWriter(String filename)
處理時需要FileNotFoundException異常
4、RandomAccessFile類
RandomAccessFile不同于FileInputStream和FileOutputStream,不是他們的子類
當我們想對一個文件進行讀寫操作的時候,創建一個指向該文件的RandomAccessFile流就行了
RandomAccessFile類有兩個構造方法:
RandomAccessFile(String name, String mode) name是文件名,mode取r(只讀)或rw(讀寫)
RandomAccessFile(File file,String mode) file給出創建流的源
seek(long a) 移動RandomAccessFile流指向文件的指針,a確定指針距文件開頭的位置
getFilePointer() 獲取當前文件的指針位置;close() 關閉文件
getFD() 獲取文件的FileDescriptor;length() 獲取文件長度
read() 讀取一個字節數據;readBoolean() 讀取一個布爾值;readByte() 讀取一個字節
readChar();readFloat();readFully(byte b[]);readInt();readLine();readLong()
readUnsignedShort();readUTF() 讀取一個UTF字符串
setLength(long newLength) 設置文件長度;skipByte(int n) 在文件中跳過給定數量的字節
write(byte b[]) 寫b.length個字節到文件;writeBoolean(bolean b)
writeByte(int v);writeChar(char c);writeChars(String s);writeDouble(double d)
writeFloat(float v);writeInt(int i);writeLong(long l);writeShort(int i)
writeUTF(String s)
5、管道流
PipedInputStream類;PipedInputStream() 創建一個管道輸入流
PipedInputStream(PipedOutputStream a) 連接到輸出流a的輸入流
read() 從輸入流中讀取一個字節
read(byte b[],int off,int len) off是在b中的開始位置,len是字節長度
PipedOutputStream類;PipedOutputStream() 創建一個輸出流
PipedOutputStream(PipedInputStream a) 連接到輸入流a的輸出流
write(int b);write(byte b[],int off,int len)
counnect() 連接輸入輸出流;close() 關閉流;在使用的時候要捕獲IOException異常。
6、數據流
DataInputStream類(數據輸入流)
DataInputStream(InputStream in) 將數據輸入流指向一個由in指定的輸入流
DataOutputStream類(數據輸出流)
DataOutputStream(OutputStream out) 將數據輸出流指向一個由out指定的輸出流
主要方法:
close();read() 讀取一個字節數據;readBoolean() 讀取一個布爾值
readByte() 讀取一個字節;readChar();readFloat();readFully(byte b[])
readInt();readLine();readLong();readUnsignedShort()
readUTF() 讀取一個UTF字符串;skipByte(int n) 在文件中跳過給定數量的字節
write(byte b[]) 寫b.length個字節到文件;writeBoolean(bolean b)
writeByte(int v);writeChar(char c);writeChars(String s);
writeDouble(double d);writeFloat(float v);writeInt(int i)
writeLong(long l);writeShort(int i);writeUTF(String s)
7、對象流
ObjectInputStream類和ObjectOutputStream類分別是DataInputStream類和DataOutputStream類的子類
8、回壓輸入流
PushbackInputStream類;PushbackInputStream(InputStream in)
PushbackReader類;PushbackReader(Reader in)
unread(char c) 回壓一個字符;unread(char c[]) 回壓數組c中全部字符
unread(char c[],offset,int n) 回壓c中從offset開始的n個字符
1、使用URL(統一資源定位)
例如:
try
{
url=new URL("}
catch(MalformedURLException e)
{
System.out.println("Bad );
}
在Applet 中鏈接向另外的Web頁面,使用代碼:
getAppletContext().showDocument(url);
2、套接字
客戶建立到服務器的套接字(Socket)
Socket(String host,int port) host是服務器的IP地址,port是端口號
建立了套接字后可以使用getInputStream()獲得輸入流
還可以使用getOutputStream()獲得一個輸出流
服務器建立接受客戶套接字的服務器套接字(ServerSocket)
ServerSocket(int port) port是端口號
建立了套接字連接后可以使用accept()接收客戶的套接字
可以使用getOutputStream()獲得一個輸出流
還可以使用getInputStream()獲得一個輸入流
3、InetAddress類
getByName(String s) 獲取Internet上主機的地址
getHostName() 獲取InetAddress對象所包含的域名
getHostAddress() 獲取InetAddress對象所包含的IP地址
getLocalHost() 獲取本地地址
4、UDP數據報
發送數據包,即使用DatagramPacket類將數據打包,有兩種構造方法
DatagramPacket(byte data[],int length,InetAddress address,int port)
?含有data數組的數據
?該數據包將發送到地質是address,端口是port的主機上
DatagramPacket(byte data[],int offset,int length,InetAddress address,int port)
?含有data數組的從offset開始,length長度的數據
?該數據包將發送到地質是address,端口是port的主機上
接收數據包,即使用DatagramSocket(int port)創建一個對象,port必須和待接收的數據包的端口相同
例如:
如果發送方的數據包端口是5566
DatagramSocket mail=new DatagramSocket(5566);
然后對象mail可以使用方法receive(Datagrampacket pack)接收數據包
在使用參數pack 接收數據包前,必須創建pack
byte data[]=new byte[100];
int length=90;
DatagramPacket pack=new DatagramPataet(data,length);
mail.receive(pack);
該數據包pack將接收長度是length的數據放入data,注意數據長度不要超過8192KB
pack還可以調用方法getPort()獲取所接受數據包是從什么端口發出的
調用方法InetAddress getAddress()可以獲知數據包來自哪個主機
1、java支持的圖像類型:GIF,JPEG,BMP
2、Image類
首先申請一個Image對象
Image img =getImage(URL url,String name) url是圖像地址,name是圖片名稱
通常使用:
Image img =getImage(getCodBase(),String name) getCodBase()獲取當前小應用程序的URL,也就是在同一目錄下
圖像被加載后,就可以在paint()中繪制了
drawImage(Image img,int x,int y,ImageObserver observer)
img是上面獲取的圖像, x,y是指定圖像左上角的位置,observer是加載圖像時的圖像觀察器
Applet類已經實現了ImageObserver接口,所以可以直接使用this作為最后一個參數
drawImage(Image img,int x,int y,int width,int height,ImageObserver observer)
width和height是要繪制的圖像的寬和高
可以使用img.getHeight(this)和img.getWidth(this)來獲取被加載的圖像的寬和高
3、設置Java窗口圖標
Frame對象可以使用setIconImage(Image img)方法設置左上角圖標,默認圖標是咖啡杯
1、JDBC-ODBC橋接器
建立JDBC-ODBC橋接器
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver";
}
catch(ClassNotFoundException e){}
連接到數據庫
try
{
Connection con=DriverManager.getConnection("jdbc:jdbc:數據源名稱","數據源的login name",
"數據源的password"
}
catch(SQLException e){}
向數據庫發送SQL語句
try
{
Statement sql=con.createStatement();
}
catch(SQLException e){}
處理查詢結果
ResultSet rs=sql.executeQuery("SQL語句";
1、在小程序中播放聲音
java可以播放au,aiff,wav,midi,rfm格式的音頻
可以調用Applet的一個靜態方法:
newAudioClip(URL url,String name) url是地址,name是音頻名稱
也可以用Applet類的實例方法:
getAudioClip(URL url,String name)
根據url地址和聲音文件name,獲得一個用于播放的音頻對象,這對象可以使用下面的方法來處理聲音:
play() 播放聲音文件name;loop() 循環播放name;stop() 停止播放name
2、Java媒體框架(JMF)
創建播放器
try
{
URL url=new URL(getDocumenBase(),視頻文件名稱);
player player=Manager.createPlayer(url);
}
catch(IOException e){}
向播放器注冊控制監視器
player.addControllerListener(監視器);
創建監視器必須使用接口ControllerListener ,該接口中的方法是
public void controllerUpdate(ControllerEvent e)
讓播放器對媒體進行預提取:player.prefetch()
啟動播放器:player.start();
停止播放器:player.stop();
停止播放器后必須釋放內存中的資源:player.deallocate();
1、Jcomponent類
Jcomponent類 是所有輕量組件的父類,主要的子類有:
JButton 創建按鈕對象,而且可以創建在圖標的按鈕;
JTree 創建樹對象
JComboBox 創建組合框對象,和Choice相似;JCheckBox 創建復選框對象
JFileChooser 創建文件選擇器;JInternalFrame 創建內部窗體
JLabel 創建標簽;JMenu 創建菜單對象;JMenuBar 創建菜單條對象
JMenuItem 創建菜單項對象;JPanel 創建面板對象;JPasswordField 創建口令文本對象
JPopupMenu 創建彈出式菜單;JProgressBar 創建進程條;JRadioButton 創建單選按鈕
JScrollBar 創建滾動條;JScrollPane 創建滾動窗格;JSlider 創建滾動條
JSplitPane 創建拆分窗格;JTable 創建表格;JTextArea 創建文本區
JTexPane 創建文本窗格;JToolBar 創建工具條;JToolTip 創建工具提示對象
2、JFrame類
JFrame類及其子類創建的對象是窗體
(1)JFrame類及其子類創建的窗體是swing窗體
(2)不可以把組件直接加到swing窗體中,應該把組件加到內容面板中
(3)不能為swing窗體設置布局,而應當為內容面板設置布局
(4)swing窗體通過調用getContentPane()方法得到它的內容面板
3、JApplet類
(1)不可以把組件直接添加到小程序容器中,也應該添加到內容面板中
(2)不能為小程序設置布局
(3)小程序容器通過調用getContenPane()方法得到內容面板
4、JDialog類
(1)不可以把組件直接添加到對話框容器中,也應該添加到內容面板中
(2)不能為對話框設置布局
(3)對話框容器通過調用getContenPane()方法得到內容面板
5、JPanel面板:JPanel();JPanel(布局對象)
6、滾動窗口JScrollPane:JScrollPane();JScrollPane(component c)
7、拆分窗口JSplitPane
JSplitPane(int a,Component b,Component c)
a的取值是HORIZONTAL_SPLIT或者VERTICAL_SPLIT決定水平拆分還是垂直拆分
JSplitPane(int a,boolean b ,Component b,Component c) b的取值決定拆分線移動的時候組件是否連續變化
8、內部窗體JInternalFrame
JInternalFrame(String title,boolean resizable,booleam closable,boolean max,boolean min)
參數的意義分別是窗口名稱,是否能調整大小,是否有關閉按鈕,最大化按鈕,最小化按鈕
(1)不能把組件直接加到窗體中,而是加到內容面板中
(2)必須先把內部窗體加到一個容器中(JDesktopPane),該容器是專門為內部窗體服務的
9、按鈕(JButton)
JButton() 創建按鈕;JButton(String s) s是按鈕的名字
JButton(Icon icon) icon是按鈕上的圖標;JButton(String s,Icon icon)
getText() 獲取按鈕名字;getIcon() 獲取按鈕圖標
setIcon(Icon icon) 設置按鈕圖標
setHorizontalTextposition(int a) a確定按鈕上圖標的位置,取值:
AbstractButton_CENTR,AbstractButton_LEFT,AbstractButton_RIGHT
setVerticalTextposition(int a) a確定按鈕上名字相對圖標的位置,取值:
AbstractButton.TOP,AbstractButton.BOTTOM,AbstractButton.CENTR
setMnemonic(char c) 設置按鈕的鍵盤操作方式是字符c(Alt+c)
setEnabled(boolean b) b決定按鈕是否可以被單擊
## 使用SimpleLog實現
#org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
后面的值是你想要的實現日志接口的類.apache的commons-logging.jar本身帶了兩個實現,SimpleLog
是其中的一個,如果 你的要使用Log4J,還必須把Log4J大相關jar放到相應的lib下.
2.如果使用SimpleLog,那么在WEB-INF\classes下要有一個simplelog.properties文件,內容:
//---------------------------------------------
org.apache.commons.logging.simplelog.defaultlog = info
主要是定義SimpleLog日志輸出級別.
3.使用Log4J進行日志輸出不僅可以定義輸出的級別,還可以定義輸出的地方和輸出的格式.而simplelog只有System.err.???????? Log4J主要是定義三個Logger,appender 和layout,appender 是輸出的地方,layout就是布局,也就是輸出的格式.
//-----------------------------------------------
## LOGGERS ##
#定義一個logger
log4j.rootLogger=INFO,console,file
## APPENDERS ##
#定義一個appender,后面的紅色字體就是他的名字,可以任意定義.其值是相關的輸出的類.可取值如
#ConsoleAppender
#RollingFileAppender
#DailyRollingFileAppender
..............................................
# log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=log.txt
#如果使用FileAppender那么后面還跟很有多屬性如MaxFileSize,MaxBackupIndex等.
## LAYOUTS ##
# 定義格式
#格式主要有好幾中常用的就是SimpleLayout,HTMLLayout,PatternLayout,使用PatternLayout時后面還要有一個定義格式的ConversionPattern.
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
# PatternLayout
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%t %p - %m%n
#以下是使用的方法:
1.在action,servlet中
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
???????? Log log=LogFactory.getLog("helloapplog");
???????? log.trace("This is a trace message");
???????? log.debug("This is a debug message");
???????? log.info("This is an info message");
???????? log.warn("This is a warn message");
???????? log.error("This is an error message");
???????? log.fatal("This is a fatal message");
2.在jsp中.
?<%@ page import="org.apache.commons.logging.Log" %>
<%@ page import="org.apache.commons.logging.LogFactory" %>
<% Log logger = LogFactory.getLog( this.getClass(? ) ); %>
? <% logger.warn( "This is a warn message from a jsp" ); %>
? <% logger.error( "This is an error message from a jsp" ); %>
(一)Query
?1.TermQuey//查詢特定的某個Field
?//過程:
?Term t = new Term("subject", "ant");
?//Term構造函數的參數:param1是Field名,param2是用戶查詢的關鍵字
?Query query = new TermQuery(t);
?Hits hits = searcher.Search(query);
?2.QueryParse//簡化查詢語句
?//過程:
?Query query = QueryParser.Parse("+JUNIT +ANT -MOCK","contents",new SimpleAnalyzer());
?Hits hits = searcher.Search(query);
?//注意:TermQuery和QueryParse只要在QueryParse的Parse方法中只有一個word,就會自動轉換成TermQuery.
?3.RangeQuery//范圍查詢,用于查詢范圍,通常用于時間
?//過程:
?RangeQuery query = new RangeQuery(begin, end, true);
?Hits hits = searcher.Search(query);
?//RangeQuery的第三個參數用于表示是否包含該起止日期.Lucene用[] 和{}分別表示包含和不包含.RangeQuery到QueryParse的轉化
?//Query query = QueryParser.Parse("pubmonth:[200004 TO 200206]", "subject", new SimpleAnalyzer());
?//IndexSearcher searcher = new IndexSearcher(directory);
?//Hits hits = searcher.Search(query);
?4.PrefixQuery//搜索是否包含某個特定前綴
?PrefixQuery query = new PrefixQuery(new Term("category", "/Computers"));
?Hits hits = searcher.Search(query);
?5.BooleanQuery//用于測試滿足多個條件
?TermQuery searchingBooks =new TermQuery(new Term("subject", "junit"));
?RangeQuery currentBooks =new RangeQuery(new Term("pubmonth", "200301"),new Term("pubmonth", "200312"),true);
?BooleanQuery currentSearchingBooks = new BooleanQuery();
?currentSearchingBooks.Add(searchingBooks, true, false);
?currentSearchingBooks.Add(currentBooks, true, false);
?IndexSearcher searcher = new IndexSearcher(directory);
?Hits hits = searcher.Search(currentSearchingBooks);
?//什么時候是與什么時候又是或? 關鍵在于BooleanQuery對象的Add方法的參數.
?//參數一是待添加的查詢條件.
?//參數二Required表示這個條件必須滿足嗎? True表示必須滿足, False表示可以不滿足該條件.
?//參數三Prohibited表示這個條件必須拒絕嗎? True表示這么滿足這個條件的結果要排除, False表示可以滿足該條件.
?//BooleanQuery和QueryParse
?[Test]
???????? public void TestQueryParser()
???????? {
????????????? Query query = QueryParser.Parse("pubmonth:[200301 TO 200312] AND junit", "subject", new SimpleAnalyzer());
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length());
????????????? query = QueryParser.Parse("/Computers/JUnit OR /Computers/Ant", "category", new WhitespaceAnalyzer());
????????????? hits = searcher.Search(query);
????????????? Assert.AreEqual(2, hits.Length());
???????? }
??//注意AND和OR的大小 如果想要A與非B 就用 A AND –B 表示, +A –B也可以.
??//默認的情況下QueryParser會把空格認為是或關系,就象google一樣.但是你可以通過QueryParser對象修改這一屬性.
[Test]
???????? public void TestQueryParserDefaultAND()
???????? {
????????????? QueryParser qp = new QueryParser("subject", new SimpleAnalyzer());
????????????? qp.SetOperator(QueryParser.DEFAULT_OPERATOR_AND );
????????????? Query query = qp.Parse("pubmonth:[200301 TO 200312] junit");
????????????? IndexSearcher searcher = new IndexSearcher(directory);
????????????? Hits hits = searcher.Search(query);
????????????? Assert.AreEqual(1, hits.Length());
???????? }
?6.PhraseQuery//短語查詢
?7.WildcardQuery//通配符搜索
?8.FuzzyQuery//模糊查詢
(二)Analyzer
?WhitespaceAnalyzer:僅僅是去除空格,對字符沒有lowcase化,不支持中文
?SimpleAnalyzer:功能強于WhitespaceAnalyzer,將除去letter之外的符號全部過濾掉,并且將所有的字符lowcase化,
?不支持中文
?StopAnalyzer:StopAnalyzer的功能超越了SimpleAnalyzer,在SimpleAnalyzer的基礎上
??? 增加了去除StopWords的功能,不支持中文
?StandardAnalyzer:英文的處理能力同于StopAnalyzer.支持中文采用的方法為單字切分.
?ChineseAnalyzer:來自于Lucene的sand box.性能類似于StandardAnalyzer,缺點是不支持中英文混和分詞.
?CJKAnalyzer:chedong寫的CJKAnalyzer的功能在英文處理上的功能和StandardAnalyzer相同
??? 但是在漢語的分詞上,不能過濾掉標點符號,即使用二元切分
?最強大的分析程序——StandardAnalyzer類。
?StandardAnalyzer類會將文本的所有內容變成小寫的,并去掉一些常用的停頓詞(stop word)。
?停頓詞是像“a”、“the”和“in”這樣的詞,它們都是內容里非常常見的詞,但是對搜索卻一點用處都沒有。
?分析程序也會分析搜索查詢,這就意味著查詢會找到匹配的部分。例如,
?這段文本“The dog is a golden retriever(這條狗是一只金毛獵犬)”,
?就會被處理為“dog golden retriever”作為索引。當用戶搜索“a Golden Dog”的時候,
?分析程序會處理這個查詢,并將其轉變為“golden dog”,這就符合我們的內容了。
(三)性能問題
?1.切詞,索引,存屬
?域存儲字段規則
?方法 切詞 索引 存儲 用途
?Field.Text(String name, String value) 切分詞索引并存儲,比如:標題,內容字段
?Field.Text(String name, Reader value)? 切分詞索引不存儲,比如:META信息,
?不用于返回顯示,但需要進行檢索內容
?Field.Keyword(String name, String value)? 不切分索引并存儲,比如:日期字段
?Field.UnIndexed(String name, String value)? 不索引,只存儲,比如:文件路徑
?Field.UnStored(String name, String value)? 只全文索引,不存儲
-----------------------------------------------------------------------------------------------
?Keywork
?該類型的數據將不被分析,而會被索引并保存保存在索引中.
?UnIndexed
?該類型的數據不會被分析也不會被索引,但是會保存在索引.
?UnStored
?和UnIndexed剛好相反,被分析被索引,但是不被保存.
?Text
?和UnStrored類似.如果值的類型為string還會被保存.如果值的類型Reader就不會被保存和UnStored一樣.
?
?2.優化索引生成文件
??1.索引的權重SetBoost
???if (GetSenderDomain().EndsWith(COMPANY_DOMAIN))
???//如果是公司郵件,提高權重,默認權重是1.0
?????????? doc.SetBoost(1.5);??????????????????????
???else
???//如果是私人郵件,降低權重.
?????????? doc.SetBoost(0.1);
??不僅如此你還可以對Field也設置權重.比如你對郵件的主題更感興趣.就可以提高它的權重.?
??Field senderNameField = Field.Text("senderName", senderName);
??Field subjectField = Field.Text("subject", subject);
??subjectField.SetBoost(1.2);
??2.利用IndexWriter 屬性對建立索引進行高級管理
???在建立索引的時候對性能影響最大的地方就是在將索引寫入文件的時候所以在具體應用的時候就需要對此加以控制
???IndexWriter屬性??
???描述?? 默認值???備注
???MergeFactory 10????控制segment合并的頻率和大小
???MaxMergeDocs Int32.MaxValue 限制每個segment中包含的文檔數
???MinMergeDocs 10????當內存中的文檔達到多少的時候再寫入segment
???Lucene默認情況是每加入10份文檔就從內存往index文件寫入并生成一個segement,然后每10個segment就合并成一個segment.通過MergeFactory這個變量就可以對此進行控制.
???MaxMergeDocs用于控制一個segment文件中最多包含的Document數.比如限制為100的話,即使當前有10個segment也不會合并,因為合并后的segmnet將包含1000個文檔,超過了限制.
???MinMergeDocs用于確定一個當內存中文檔達到多少的時候才寫入文件,該項對segment的數量和大小不會有什么影響,它僅僅影響內存的使用,進一步影響寫索引的效率.
??3.利用RAMDirectory充分發揮內存的優勢
???在實際應用中RAMDirectory和FSDirectory協作可以更好的利用內存來優化建立索引的時間.
???具體方法如下:
???1.建立一個使用FSDirectory的IndexWriter
???2.建立一個使用RAMDirectory的IndexWriter
???3. 把Document添加到RAMDirectory中
???4. 當達到某種條件將RAMDirectory 中的Document寫入FSDirectory.
???5. 重復第三步
???示意代碼:
????? private FSDirectory fsDir = FSDirectory.GetDirectory("index",true);?
?????? private RAMDirectory ramDir = new RAMDirectory();
?????? private IndexWriter fsWriter = IndexWriter(fsDir,new SimpleAnalyzer(), true);
?????? private IndexWriter ramWriter = new IndexWriter(ramDir,new SimpleAnalyzer(), true);
?????? while (there are documents to index)
????? {
???????? ramWriter.addDocument(doc);
???????? if (condition for flushing memory to disk has been met)
???????? {
?????????? fsWriter.AddIndexes(Directory[]{ramDir}) ;
?????????? ramWriter.Close();????????? //why not support flush?
?????????? ramWriter =new IndexWriter(ramDir,new SimpleAnalyzer(),true);
???????? }
???? }
???這里的條件完全由用戶控制,而不是FSDirectory采用對Document計數的方式控制何時寫入文件.
???相比之下有更大的自由性,更能提升性能.
??4.控制索引內容的長度
???Lucene對一份文本建立索引時默認的索引長度是10,000. 你可以通過IndexWriter 的MaxFieldLength屬性對此加以修改.還是用一個例子說明問題.
??5.Optimize 優化
=------------------------jackstudio-----------------