① 如何更好地使用java 8的Optional
我們知道 Java 8 增加了一些很有用的 API, 其中一個就是 Optional. 如果對它不稍假探索, 只是輕描淡寫的認為它可以優雅的解決 NullPointException 的問題, 於是代碼就開始這么寫了
Optional<User> user = ......
if (user.isPresent()) {
return user.getOrders();
} else {
return Collections.emptyList();
}
那麼不得不說我們的思維仍然是在原地踏步, 只是本能的認為它不過是 User 實例的包裝, 這與我們之前寫成
User user = .....
if (user != null) {
return user.getOrders();
} else {
return Collections.emptyList();
}
實質上是沒有任何分別. 這就是我們將要講到的使用好 java 8 Optional 類型的正確姿勢.
在里約奧運之時, 新聞一再提起五星紅旗有問題, 可是我怎麼看都看不出來有什麼問題, 後來才道是小星星膜拜中央的姿勢不對. 因此我們千萬也別對自己習以為常的事情覺得理所當然, 絲毫不會覺得有何不妥, 換句話說也就是當我們切換到 Java 8 的 Optional 時, 不能繼承性的對待過往 null 時的那種思維, 應該掌握好新的, 正確的使用 Java 8 Optional 的正確姿勢.
直白的講, 當我們還在以如下幾種方式使用 Optional 時, 就得開始檢視自己了
調用 isPresent() 方法時
調用 get() 方法時
Optional 類型作為類/實例屬性時
Optional 類型作為方法參數時
isPresent() 與 obj != null 無任何分別, 我們的生活依然在步步驚心. 而沒有 isPresent() 作鋪墊的 get() 調用在 IntelliJ IDEA 中會收到告警
Reports calls to java.util.Optional.get() without first checking with a isPresent() call if a value is available. If the Optional does not contain a value, get() will throw an exception. (調用 Optional.get() 前不事先用 isPresent() 檢查值是否可用. 假如 Optional 不包含一個值, get() 將會拋出一個異常)
把 Optional 類型用作屬性或是方法參數在 IntelliJ IDEA 中更是強力不推薦的
Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type for a field or a parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent 「no result」. Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, which java.util.Optional is not. (使用任何像 Optional 的類型作為欄位或方法參數都是不可取的. Optional 只設計為類庫方法的, 可明確表示可能無值情況下的返回類型. Optional 類型不可被序列化, 用作欄位類型會出問題的)
所以 Optional 中我們真正可依賴的應該是除了 isPresent() 和 get() 的其他方法:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
public void ifPresent(Consumer<? super T> consumer)
public Optional<T> filter(Predicate<? super T> predicate)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
我略有自信的按照它們大概使用頻度對上面的方法排了一下序.
先又不得不提一下 Optional 的三種構造方式: Optional.of(obj), Optional.ofNullable(obj) 和明確的 Optional.empty()
Optional.of(obj): 它要求傳入的 obj 不能是 null 值的, 否則還沒開始進入角色就倒在了 NullPointerException 異常上了.
Optional.ofNullable(obj): 它以一種智能的, 寬容的方式來構造一個 Optional 實例. 來者不拒, 傳 null 進到就得到 Optional.empty(), 非 null 就調用 Optional.of(obj).
那是不是我們只要用 Optional.ofNullable(obj) 一勞永逸, 以不變應二變的方式來構造 Optional 實例就行了呢? 那也未必, 否則 Optional.of(obj) 何必如此暴露呢, 私有則可?
我本人的觀點是: 1. 當我們非常非常的明確將要傳給 Optional.of(obj) 的 obj 參數不可能為 null 時, 比如它是一個剛 new 出來的對象(Optional.of(new User(...))), 或者是一個非 null 常量時; 2. 當想為 obj 斷言不為 null 時, 即我們想在萬一 obj 為 null 立即報告 NullPointException 異常, 立即修改, 而不是隱藏空指針異常時, 我們就應該果斷的用 Optional.of(obj) 來構造 Optional 實例, 而不讓任何不可預計的 null 值有可乘之機隱身於 Optional 中.
現在才開始怎麼去使用一個已有的 Optional 實例, 假定我們有一個實例 Optional<User> user, 下面是幾個普遍的, 應避免 if(user.isPresent()) { ... } else { ... } 幾中應用方式.
存在即返回, 無則提供默認值
return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null;
return user.orElse(UNKNOWN_USER);
存在即返回, 無則由函數來產生
return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
存在才對它做點什麼
user.ifPresent(System.out::println);
//而不要下邊那樣
if (user.isPresent()) {
System.out.println(user.get());
}
map 函數隆重登場
當 user.isPresent() 為真, 獲得它關聯的 orders, 為假則返回一個空集合時, 我們用上面的 orElse, orElseGet 方法都乏力時, 那原本就是 map 函數的責任, 我們可以這樣一行
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
//上面避免了我們類似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}
map 是可能無限級聯的, 比如再深一層, 獲得用戶名的大寫形式
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
這要擱在以前, 每一級調用的展開都需要放一個 null 值的判斷
User user = .....
if(user != null) {
String name = user.getUsername();
if(name != null) {
return name.toUpperCase();
} else {
return null;
}
} else {
return null;
}
針對這方面 Groovy 提供了一種安全的屬性/方法訪問操作符 ?.
user?.getUsername()?.toUpperCase();
Swift 也有類似的語法, 只作用在 Optional 的類型上.
用了 isPresent() 處理 NullPointerException 不叫優雅, 有了 orElse, orElseGet 等, 特別是 map 方法才叫優雅.
其他幾個, filter() 把不符合條件的值變為 empty(), flatMap() 總是與 map() 方法成對的, orElseThrow() 在有值時直接返回, 無值時拋出想要的異常.
一句話小結: 使用 Optional 時盡量不直接調用 Optional.get() 方法, Optional.isPresent() 更應該被視為一個私有方法, 應依賴於其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等這樣的方法.
最後, 最好的理解 Java 8 Optional 的方法莫過於看它的源代碼 java.util.Optional, 閱讀了源代碼才能真真正正的讓你解釋起來最有底氣, Optional 的方法中基本都是內部調用 isPresent() 判斷, 真時處理值, 假時什麼也不做.
② servlet使用filter找不到filter class
自己去classes文件下看看就有沒有cn/chenghui/bbs.utils/CharacterEncodingFilter.class文件,一般是沒有成功編譯這個文件,導致不能正常載入。你可以把項目中的classes文件夾給刪了,重新編譯一下。
③ java中的空指針異常怎麼解決
原文:https://www.hu.com/question
你這個問題的解決
問題定位:
在堆棧異常信息的第一行就可以定位到是哪裡出了空指針,倘若這里不是你寫的類,可以往下翻一下,找到你寫的類,就是這里出現的空指針。
問題解決:
對一個空對象調用裡面的方法或者屬性的時候會報空指針,檢查這個對象為什麼是空即可。
Java 空指針異常的若干解決方案
Java 中任何對象都有可能為空,當我們調用空對象的方法時就會拋出 NullPointerException 空指針異常,這是一種非常常見的錯誤類型。我們可以使用若干種方法來避免產生這類異常,使得我們的代碼更為健壯。本文將列舉這些解決方案,包括傳統的空值檢測、編程規范、以及使用現代 Java 語言引入的各類工具來作為輔助。
運行時檢測
最顯而易見的方法就是使用 if (obj == null) 來對所有需要用到的對象來進行檢測,包括函數參數、返回值、以及類實例的成員變數。當你檢測到 null 值時,可以選擇拋出更具針對性的異常類型,如 IllegalArgumentException,並添加消息內容。我們可以使用一些庫函數來簡化代碼,如 Java 7 開始提供的 Objects#requireNonNull 方法:
public void testObjects(Object arg) {
Object checked = Objects.requireNonNull(arg, "arg must not be null");
checked.toString();}
Guava 的 Preconditions 類中也提供了一系列用於檢測參數合法性的工具函數,其中就包含空值檢測:
public void testGuava(Object arg) {
Object checked = Preconditions.checkNotNull(arg, "%s must not be null", "arg");
checked.toString();
}
我們還可以使用 Lombok 來生成空值檢測代碼,並拋出帶有提示信息的空指針異常:
public void testLombok(@NonNull Object arg) {
arg.toString();
生成的代碼如下:
public void testLombokGenerated(Object arg) {
if (arg == null) {
throw new NullPointerException("arg is marked @NonNull but is null");
}
arg.toString();
}
這個註解還可以用在類實例的成員變數上,所有的賦值操作會自動進行空值檢測。
編程規范
·通過遵守某些編程規范,也可以從一定程度上減少空指針異常的發生。
使用那些已經對 null 值做過判斷的方法,如 String#equals、String#valueOf、以及三方庫中用來判斷字元串和集合是否為空的函數:
if (str != null && str.equals("text")) {}
if ("text".equals(str)) {}
if (obj != null) { obj.toString(); }
String.valueOf(obj); // "null"
// from spring-core
StringUtils.isEmpty(str);
CollectionUtils.isEmpty(col);
// from guava
Strings.isNullOrEmpty(str);
// from commons-collections4
CollectionUtils.isEmpty(col);
·如果函數的某個參數可以接收 null 值,考慮改寫成兩個函數,使用不同的函數簽名,這樣就可以強制要求每個參數都不為空了:
public void methodA(Object arg1) {
methodB(arg1, new Object[0]);
}
public void methodB(Object arg1, Object[] arg2) {
for (Object obj : arg2) {} // no null check
}
·如果函數的返回值是集合類型,當結果為空時,不要返回 null 值,而是返回一個空的集合;如果返回值類型是對象,則可以選擇拋出異常。Spring JdbcTemplate 正是使用了這種處理方式:
// 當查詢結果為空時,返回 new ArrayList<>()
jdbcTemplate.queryForList("SELECT * FROM person");
// 若找不到該條記錄,則拋出
jdbcTemplate.queryForObject("SELECT age FROM person WHERE id = 1", Integer.class);
// 支持泛型集合
public <T> List<T> testReturnCollection() {
return Collections.emptyList();
}
靜態代碼分析
Java 語言有許多靜態代碼分析工具,如 Eclipse IDE、SpotBugs、Checker Framework 等,它們可以幫助程序員檢測出編譯期的錯誤。結合 @Nullable 和 @Nonnull 等註解,我們就可以在程序運行之前發現可能拋出空指針異常的代碼。
但是,空值檢測註解還沒有得到標准化。雖然 2006 年 9 月社區提出了 JSR 305 規范,但它長期處於擱置狀態。很多第三方庫提供了類似的註解,且得到了不同工具的支持,其中使用較多的有:
javax.annotation.Nonnull:由 JSR 305 提出,其參考實現為 com.google.code.findbugs.jsr305;
org.eclipse.jdt.annotation.NonNull:Eclipse IDE 原生支持的空值檢測註解;
e.umd.cs.findbugs.annotations.NonNull:SpotBugs 使用的註解,基於 findbugs.jsr305;
org.springframework.lang.NonNull:Spring Framework 5.0 開始提供;
org.checkerframework.checker.nullness.qual.NonNull:Checker Framework 使用;
android.support.annotation.NonNull:集成在安卓開發工具中;
我建議使用一種跨 IDE 的解決方案,如 SpotBugs 或 Checker Framework,它們都能和 Maven 結合得很好。
SpotBugs 與 @NonNull、@CheckForNull
SpotBugs 是 FindBugs 的後繼者。通過在方法的參數和返回值上添加 @NonNull 和 @CheckForNull 註解,SpotBugs 可以幫助我們進行編譯期的空值檢測。需要注意的是,SpotBugs 不支持 @Nullable 註解,必須用 @CheckForNull 代替。如官方文檔中所說,僅當需要覆蓋 @ParametersAreNonnullByDefault 時才會用到 @Nullable。
官方文檔 中說明了如何將 SpotBugs 應用到 Maven 和 Eclipse 中去。我們還需要將 spotbugs-annotations 加入到項目依賴中,以便使用對應的註解。
以下是對不同使用場景的說明:
對於 Eclipse 用戶,還可以使用 IDE 內置的空值檢測工具,只需將默認的註解 org.eclipse.jdt.annotation.Nullable 替換為 SpotBugs 的註解即可:
Checker Framework 與 @NonNull、@Nullable
Checker Framework 能夠作為 javac 編譯器的插件運行,對代碼中的數據類型進行檢測,預防各類問題。我們可以參照 官方文檔,將 Checker Framework 與 maven-compiler-plugin 結合,之後每次執行 mvn compile 時就會進行檢查。Checker Framework 的空值檢測程序支持幾乎所有的註解,包括 JSR 305、Eclipse、甚至 lombok.NonNull。
Checker Framework 默認會將 @NonNull 應用到所有的函數參數和返回值上,因此,即使不添加這個註解,以下程序也是無法編譯通過的:
Checker Framework 對使用 Spring Framework 5.0 以上的用戶非常有用,因為 Spring 提供了內置的空值檢測註解,且能夠被 Checker Framework 支持。一方面我們無需再引入額外的 Jar 包,更重要的是 Spring Framework 代碼本身就使用了這些註解,這樣我們在調用它的 API 時就能有效地處理空值了。舉例來說,StringUtils 類里可以傳入空值的函數、以及會返回空值的函數都添加了 @Nullable 註解,而未添加的方法則繼承了整個框架的 @NonNull 註解,因此,下列代碼中的空指針異常就可以被 Checker Framework 檢測到了:
Optional 類型
Java 8 引入了 Optional<T> 類型,我們可以用它來對函數的返回值進行包裝。這種方式的優點是可以明確定義該方法是有可能返回空值的,因此調用方必須做好相應處理,這樣也就不會引發空指針異常。但是,也不可避免地需要編寫更多代碼,而且會產生很多垃圾對象,增加 GC 的壓力,因此在使用時需要酌情考慮。
方法的鏈式調用很容易引發空指針異常,但如果返回值都用 Optional 包裝起來,就可以用 flatMap 方法來實現安全的鏈式調用了:
Java 8 Stream API 同樣使用了 Optional 作為返回類型:
此外,Java 8 還針對基礎類型提供了單獨的 Optional 類,如 OptionalInt、OptionalDouble 等,在性能要求比較高的場景下很適用。
其它 JVM 語言中的空指針異常
Scala 語言中的 Option 類可以對標 Java 8 的 Optional。它有兩個子類型,Some 表示有值,None 表示空。
除了使用 Option#isEmpty 判斷,還可以使用 Scala 的模式匹配:
Scala 的集合處理函數庫非常強大,Option 則可直接作為集合進行操作,如 filer、map、以及列表解析(for-comprehension):
Kotlin 使用了另一種方式,用戶在定義變數時就需要明確區分 可空和不可空類型。當可空類型被使用時,就必須進行空值檢測。
Kotlin 的特性之一是與 Java 的可互操作性,但 Kotlin 編譯器無法知曉 Java 類型是否為空,這就需要在 Java 代碼中使用註解了,而 Kotlin 支持的 註解 也非常廣泛。Spring Framework 5.0 起原生支持 Kotlin,其空值檢測也是通過註解進行的,使得 Kotlin 可以安全地調用 Spring Framework 的所有 API。
結論
在以上這些方案中,我比較推薦使用註解來預防空指針異常,因為這種方式十分有效,對代碼的侵入性也較小。所有的公共 API 都應該使用 @Nullable 和 @NonNull 進行註解,這樣就能強制調用方對空指針異常進行預防,讓我們的程序更為健壯。
④ java方法問題
異常的類別 異常的分類有不同方式。這里,我們將討論從 EJB 的角度如何對異常進行分類。EJB 規范將異常大致分成三類:JVM 異常:這種類型的異常由 JVM 拋出。OutOfMemoryError 就是 JVM 異常的一個常見示例。對 JVM 異常您無能為力。它們表明一種致命的情況。唯一得體的退出辦法是停止應用程序伺服器(可能要增加硬體資源),然後重新啟動系統。應用程序異常:應用程序異常是一種定製異常,由應用程序或第三方的庫拋出。這些本質上是受查異常(checked exception);它們預示了業務邏輯中的某個條件尚未滿足。在這樣的情況下,EJB 方法的調用者可以得體地處理這種局面並採用另一條備用途徑。系統異常:在大多數情況下,系統異常由 JVM 作為 RuntimeException 的子類拋出。例如,NullPointerException 或 ArrayOutOfBoundsException 將因代碼中的錯誤而被拋出。另一種類型的系統異常在系統碰到配置不當的資源(例如,拼寫錯誤的 JNDI 查找(JNDI lookup))時發生。在這種情況下,系統就將拋出一個受查異常。捕獲這些受查系統異常並將它們作為非受查異常(unchecked exception)拋出頗有意義。最重要的規則是,如果您對某個異常無能為力,那麼它就是一個系統異常並且應當作為非受查異常拋出。 受查異常是一個作為 java.lang.Exception 的子類的 Java 類。通過從 java.lang.Exception 派生子類,就強制您在編譯時捕獲這個異常。相反地,非受查異常則是一個作為 java.lang.RuntimeException 的子類的 Java 類。從 java.lang.RuntimeException 派生子類確保了編譯器不會強制您捕獲這個異常。清單 1. 三種常見的異常處理做法100 try {101 OrderHome homeObj = EJBHomeFactory.getInstance().getOrderHome();102 Collection orderCollection = homeObj.findByCustomerId(id);103 iterator orderItter = orderCollection.iterator();104 while (orderIter.hasNext()) {105 Order orderRemote = (OrderRemote) orderIter.getNext();106 OrderValue orderVal = orderRemote.getValue();107 if (orderVal.getDate() < "mm/dd/yyyy") {108 OrderItemHome itemHome =EJBHomeFactory.getInstance().getItemHome();109 Collection itemCol = itemHome.findByOrderId(orderId)110 Iterator itemIter = itemCol.iterator();111 while (itemIter.hasNext()) {112 OrderItem item = (OrderItem) itemIter.getNext();113 item.remove();114 }115 orderRemote.remove();116 }117 }118 } catch (NamingException ne) {119 throw new EJBException("Naming Exception occurred");120 } catch (FinderException fe) {121 fe.printStackTrace();122 throw new EJBException("Finder Exception occurred");123 } catch (RemoteException re) {124 re.printStackTrace();125 //Some code to log the message126 throw new EJBException(re);127 }EJB 異常處理探試法EJB 組件應拋出哪些異常?您應將它們記錄到系統中的什麼地方?這兩個問題盤根錯結、相互聯系,應該一起解決。解決辦法取決於以下因素:您的 EJB 系統設計:在良好的 EJB 設計中,客戶機絕不調用實體 EJB 組件上的方法。多數實體 EJB 方法調用發生在會話 EJB 組件中。如果您的設計遵循這些准則,則您應該用會話 EJB 組件來記錄異常。如果客戶機直接調用了實體 EJB 方法,則您還應該把消息記錄到實體 EJB 組件中。然而,存在一個難題:相同的實體 EJB 方法可能也會被會話 EJB 組件調用。在這種情形下,如何避免重復記錄呢?類似地,當一個會話 EJB 組件調用其它實體 EJB 方法時,您如何避免重復記錄呢?很快我們就將探討一種處理這兩種情況的通用解決方案。(請注意,EJB 1.1 並未從體系結構上阻止客戶機調用實體 EJB 組件上的方法。在 EJB 2.0 中,您可以通過為實體 EJB 組件定義本地介面規定這種限制。處理應用程序異常在這一部分及其後的幾個部分中,我們將更仔細地研究用 EJB 異常處理應用程序異常和系統異常,以及 Web 層設計。作為這個討論的一部分,我們將探討處理從會話和實體 EJB 組件拋出的異常的不同方式。實體 EJB 組件中的應用程序異常清單 2 顯示了實體 EJB 的一個 ejbCreate() 方法。這個方法的調用者傳入一個 OrderItemValue 並請求創建一個 OrderItem 實體。因為 OrderItemValue 沒有名稱,所以拋出了 CreateException。清單 2 顯示了 CreateException 的一個很典型的用法。類似地,如果方法的輸入參數的值不正確,則查找程序方法將拋出 FinderException。然而,如果您在使用容器管理的持久性(CMP),則開發者無法控制查找程序方法,從而 FinderException 永遠不會被 CMP 實現拋出。盡管如此,在 Home 介面的查找程序方法的 throws 子句中聲明 FinderException 還是要更好一些。RemoveException 是另一個應用程序異常,它在實體被刪除時被拋出。從實體 EJB 組件拋出的應用程序異常基本上限定為這三種類型(CreateException、FinderException 和 RemoveException)及它們的子類。多數應用程序異常都來源於會話 EJB 組件,因為那裡是作出智能決策的地方。實體 EJB 組件一般是啞類,它們的唯一職責就是創建和取回數據。會話 EJB 組件中的應用程序異常清單 3 顯示了來自會話 EJB 組件的一個方法。這個方法的調用者設法訂購 n 件某特定類型的某商品。SessionEJB() 方法計算出倉庫中的數量不夠,於是拋出 NotEnoughStockException。NotEnoughStockException 適用於特定於業務的場合;當拋出了這個異常時,調用者會得到採用另一個備用途徑的建議,讓他訂購更少數量的商品。清單 3. 會話 EJB 組件中的樣本容器回調方法public ItemValueObject[] placeOrder(int n, ItemType itemType) throwsNotEnoughStockException {//Check Inventory.Collection orders = ItemHome.findByItemType(itemType);if (orders.size() < n) {throw NotEnoughStockException("Insufficient stock for " + itemType);}}
處理系統異常
系統異常處理是比應用程序異常處理更為復雜的論題。由於會話 EJB 組件和實體 EJB 組件處理系統異常的方式相似,所以,對於本部分的所有示例,我們都將著重於實體 EJB 組件,不過請記住,其中的大部分示例也適用於處理會話 EJB 組件。當引用其它 EJB 遠程介面時,實體 EJB 組件會碰到 RemoteException,而查找其它 EJB 組件時,則會碰到 NamingException,如果使用 bean 管理的持久性(BMP),則會碰到 SQLException。與這些類似的受查系統異常應該被捕獲並作為 EJBException 或它的一個子類拋出。原始的異常應被包裝起來。清單 4 顯示了一種處理系統異常的辦法,這種辦法與處理系統異常的 EJB 容器的行為一致。通過包裝原始的異常並在實體 EJB 組件中將它重新拋出,您就確保了能夠在想記錄它的時候訪問該異常。清單 4. 處理系統異常的一種常見方式try {OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();Order order = orderHome.findByPrimaryKey(Integer id);} catch (NamingException ne) {throw new EJBException(ne);} catch (SQLException se) {throw new EJBException(se);} catch (RemoteException re) {throw new EJBException(re);}避免重復記錄通常,異常記錄發生在會話 EJB 組件中。但如果直接從 EJB 層外部訪問實體 EJB 組件,又會怎麼樣呢?要是這樣,您就不得不在實體 EJB 組件中記錄異常並拋出它。這里的問題是,調用者沒辦法知道異常是否已經被記錄,因而很可能再次記錄它,從而導致重復記錄。更重要的是,調用者沒辦法訪問初始記錄時所生成的唯一的標識。任何沒有交叉引用機制的記錄都是毫無用處的。請考慮這種最糟糕的情形:單機 Java 應用程序訪問了實體 EJB 組件中的一個方法 foo()。在一個名為 bar() 的會話 EJB 方法中也訪問了同一個方法。一個 Web 層客戶機調用會話 EJB 組件的方法 bar() 並也記錄了該異常。如果當從 Web 層調用會話 EJB 方法 bar() 時在實體 EJB 方法 foo() 中發生了一個異常,則該異常將被記錄到三個地方:先是在實體 EJB 組件,然後是在會話 EJB 組件,最後是在 Web 層。而且,沒有一個堆棧跟蹤可以被交叉引用!幸運的是,解決這些問題用常規辦法就可以很容易地做到。您所需要的只是一種機制,使調用者能夠:訪問唯一的標識查明異常是否已經被記錄了您可以派生 EJBException 的子類來存儲這樣的信息。清單 5 顯示了 LoggableEJBException 子類:清單 5. LoggableEJBException ? EJBException 的一個子類public class LoggableEJBException extends EJBException {protected boolean isLogged;protected String uniqueID;public LoggableEJBException(Exception exc) {super(exc);isLogged = false;uniqueID = ExceptionIDGenerator.getExceptionID();}....}類 LoggableEJBException 有一個指示符標志(isLogged),用於檢查異常是否已經被記錄了。每當捕獲一個 LoggableEJBException 時,看一下該異常是否已經被記錄了(isLogged == false)。如果 isLogged 為 false,則記錄該異常並把標志設置為 true。ExceptionIDGenerator 類用當前時間和機器的主機名為異常生成唯一的標識。如果您喜歡,也可以用有想像力的演算法來生成這個唯一的標識。如果您在實體 EJB 組件中記錄了異常,則這個異常將不會在別的地方被記錄。如果您沒有記錄就在實體 EJB 組件中拋出了 LoggableEJBException,則這個異常將被記錄到會話 EJB 組件中,但不記錄到 Web 層中。清單 6 顯示了使用這一技術重寫後的清單 4。您還可以繼承 LoggableException 以適合於您的需要(通過給異常指定錯誤代碼等)。清單 6. 使用 LoggableEJBException 的異常處理try {OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();Order order = orderHome.findByPrimaryKey(Integer id);} catch (NamingException ne) {throw new LoggableEJBException(ne);} catch (SQLException se) {throw new LoggableEJBException(se);} catch (RemoteException re) {Throwable t = re.detail;if (t != null && t instanceof Exception) {throw new LoggableEJBException((Exception) re.detail);} else {throw new LoggableEJBException(re);}}記錄 RemoteException從清單 6 中,您可以看到 naming 和 SQL 異常在被拋出前被包裝到了 LoggableEJBException 中。但 RemoteException 是以一種稍有不同 ? 而且要稍微花點氣力 ? 的方式處理的。 會話 EJB 組件中的系統異常如果您決定記錄會話 EJB 異常,請使用清單 7 所示的記錄代碼;否則,請拋出異常,如清單 6 所示。您應該注意到,會話 EJB 組件處理異常可有一種與實體 EJB 組件不同的方式:因為大多數 EJB 系統都只能從 Web 層訪問,而且會話 EJB 可以作為 EJB 層的虛包,所以,把會話 EJB 異常的記錄推遲到 Web 層實際上是有可能做到的。它之所以不同,是因為在 RemoteException 中,實際的異常將被存儲到一個稱為 detail(它是 Throwable 類型的)的公共屬性中。在大多數情況下,這個公共屬性保存有一個異常。如果您調用 RemoteException 的 printStackTrace,則除列印 detail 的堆棧跟蹤之外,它還會列印異常本身的堆棧跟蹤。您不需要像這樣的 RemoteException 的堆棧跟蹤。為了把您的應用程序代碼從錯綜復雜的代碼(例如 RemoteException 的代碼)中分離出來,這些行被重新構造成一個稱為 ExceptionLogUtil 的類。有了這個類,您所要做的只是每當需要創建 LoggableEJBException 時調用 ExceptionLogUtil.createLoggableEJBException(e)。請注意,在清單 6 中,實體 EJB 組件並沒有記錄異常;不過,即便您決定在實體 EJB 組件中記錄異常,這個解決方案仍然行得通。清單 7 顯示了實體 EJB 組件中的異常記錄:清單 7. 實體 EJB 組件中的異常記錄try {OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();Order order = orderHome.findByPrimaryKey(Integer id);} catch (RemoteException re) {LoggableEJBException le =ExceptionLogUtil.createLoggableEJBException(re);String traceStr = StackTraceUtil.getStackTrace(le);Category.getInstance(getClass().getName()).error(le.getUniqueID() +":" + traceStr);le.setLogged(true);throw le;}您在清單 7 中看到的是一個非常簡單明了的異常記錄機制。一旦捕獲受查系統異常就創建一個新的 LoggableEJBException。接著,使用類 StackTraceUtil 獲取 LoggableEJBException 的堆棧跟蹤,把它作為一個字元串。然後,使用 Log4J category 把該字元串作為一個錯誤加以記錄。
StackTraceUtil 類的工作原理
在清單 7 中,您看到了一個新的稱為 StackTraceUtil 的類。因為 Log4J 只能記錄 String 消息,所以這個類負責解決把堆棧跟蹤轉換成 String 的問題。清單 8 說明了 StackTraceUtil 類的工作原理:清單 8. StackTraceUtil 類public class StackTraceUtil {public static String getStackTrace(Exception e){StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);return sw.toString();}....}java.lang.Throwable 中預設的 printStackTrace() 方法把出錯消息記錄到 System.err。Throwable 還有一個重載的 printStackTrace() 方法,它把出錯消息記錄到 PrintWriter 或 PrintStream。上面的 StackTraceUtil 中的方法把 StringWriter 包裝到 PrintWriter 中。當 PrintWriter 包含有堆棧跟蹤時,它只是調用 StringWriter 的 toString(),以獲取該堆棧跟蹤的 String 表示。Web 層的 EJB 異常處理在 Web 層設計中,把異常記錄機制放到客戶機端往往更容易也更高效。要能做到這一點,Web 層就必須是 EJB 層的唯一客戶機。此外,Web 層必須建立在以下模式或框架之一的基礎上:模式:業務委派(Business Delegate)、FrontController 或攔截過濾器(Intercepting Filter)框架:Struts 或任何包含層次結構的類似於 MVC 框架的框架為什麼異常記錄應該在客戶機端上發生呢?嗯,首先,控制尚未傳到應用程序伺服器之外。所謂的客戶機層在 J2EE 應用程序伺服器本身上運行,它由 JSP 頁、servlet 或它們的助手類組成。其次,在設計良好的 Web 層中的類有一個層次結構(例如:在業務委派(Business Delegate)類、攔截過濾器(Intercepting Filter)類、http 請求處理程序(http request handler)類和 JSP 基類(JSP base class)中,或者在 Struts Action 類中),或者 FrontController servlet 形式的單點調用。這些層次結構的基類或者 Controller 類中的中央點可能包含有異常記錄代碼。對於基於會話 EJB 記錄的情況,EJB 組件中的每一個方法都必須具有記錄代碼。隨著業務邏輯的增加,會話 EJB 方法的數量也會增加,記錄代碼的數量也會增加。Web 層系統將需要更少的記錄代碼。如果您的 Web 層和 EJB 層在同一地方並且不需要支持任何其它類型的客戶機,那麼您應該考慮這一備用方案。不管怎樣,記錄機制不會改變;您可以使用與前面的部分所描述的相同技術。真實世界的復雜性到現在為止,您已經看到了簡單情形的會話和實體 EJB 組件的異常處理技術。然而,應用程序異常的某些組合可能會更令人費解,並且有多種解釋。清單 9 顯示了一個示例。OrderEJB 的 ejbCreate() 方法試圖獲取 CustomerEJB 的一個遠程引用,這會導致 FinderException。OrderEJB 和 CustomerEJB 都是實體 EJB 組件。您應該如何解釋 ejbCreate() 中的這個 FinderException 呢?是把它當作應用程序異常對待呢(因為 EJB 規范把它定義為標准應用程序異常),還是當作系統異常對待?清單 9. ejbCreate() 方法中的 FinderExceptionpublic Object ejbCreate(OrderValue val) throws CreateException {try {if (value.getItemName() == null) {throw new CreateException("Cannot create Order without a name");}String custId = val.getCustomerId();Customer cust = customerHome.fingByPrimaryKey(custId);this.customer = cust;} catch (FinderException ne) {//How do you handle this Exception ?} catch (RemoteException re) {//This is clearly a System Exceptionthrow ExceptionLogUtil.createLoggableEJBException(re);}return null;} 文字太多打不下了,具體你在看看類似的教程
⑤ 什麼是java過濾器! 它的功能和作用是什麼啊
Filter 技術是servlet 2.3 新增加的功能.servlet2.3是sun公司與2000年10月發布的,它的開發者包括許多個人和公司團體,充分體現了sun公司所倡導的代碼開放性原則.由於眾多的參與者的共同努力,servlet2.3比以往功能都強大了許多,而且性能也有了大幅提高.
它新增加的功能包括:
1. 應用程序生命周期事件控制;
2. 新的國際化;
3. 澄清了類的裝載規則;
4. 新的錯誤及安全屬性;
5. 不贊成使用HttpUtils 類;
6. 各種有用的方法;
7. 闡明並擴展了幾個servlet DTD;
8. filter功能.
其中最重要的就是filter功能.它使用戶可以改變一個request和修改一個 response. Filter 不是一個servlet,它不能產生一個response,它能夠在一個request到達servlet之前預處理request,也可以在離開 servlet時處理response.換種說法,filter其實是一個」servlet chaining」(servlet 鏈).一個filter 包括:
1. 在servlet被調用之前截獲;
2. 在servlet被調用之前檢查servlet request;
3. 根據需要修改request頭和request數據;
4. 根據需要修改response頭和response數據;
5. 在servlet被調用之後截獲.
你能夠配置一個filter 到一個或多個servlet;單個servlet或servlet組能夠被多個filter 使用.幾個實用的filter 包括:用戶辨認filter,日誌filter,審核filter,加密filter,符號filter,能改變xml內容的XSLT filter等.
一個filter必須實現javax.servlet.Filter介面並定義三個方法:
1.void setFilterConfig(FilterConfig config) //設置filter 的配置對象;
2. FilterConfig getFilterConfig() //返回filter的配置對象;
3. void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) //執行filter 的工作.
伺服器每次只調用setFilterConfig方法一次准備filter 的處理;調用doFilter方法多次以處理不同的請求.FilterConfig介面有方法可以找到filter名字及初始化參數信息.伺服器可以設置 FilterConfig為空來指明filter已經終結.
每一個filter從doFilter()方法中得到當前的request及 response.在這個方法里,可以進行任何的針對request及response的操作.(包括收集數據,包裝數據等).filter調用 chain.doFilter()方法把控制權交給下一個filter.一個filter在doFilter()方法中結束.如果一個filter想停止 request處理而獲得對response的完全的控制,那它可以不調用下一個filter.
一個filter可以包裝request 或response以改變幾個方法和提供用戶定製的屬性.Api2.3提供了HttpServletRequestWrapper 和HttpServletResponseWrapper來實現.它們能分派最初的request和response.如果要改變一個方法的特性,必須繼承wapper和重寫方法.下面是一段簡單的日誌filter用來記錄所有request的持續時間.
public class LogFilter implements Filter {
FilterConfig config;
public void setFilterConfig(FilterConfig config) {
this.config = config;
}
public FilterConfig getFilterConfig() {
return config;
}
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain) {
ServletContext context = getFilterConfig().getServletContext();
long bef = System.currentTimeMillis();
chain.doFilter(req, res); // no chain parameter needed here
long aft = System.currentTimeMillis();
context.log("Request to " + req.getRequestURI()
+ ": " + (aft-bef));
}
}
當server調用setFilterConfig(),filter保存config信息. 在doFilter()方法中通過config信息得到servletContext.如果要運行這個filter,必須去配置到web.xml中.以 tomcat4.01為例:
<filter>
<filter-name>
log //filter 名字
</filter-name>
<filter-class>
LogFilter //filter class(上例的servlet)
</filter-class>
</filter>
<filter-mapping>
<filter-name>log</filter-name>
<servletname>servletname</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>servletname</servletname>
<servletclass>servletclass</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletname</servlet-name>
<url-pattern>*</url-pattern>
</servlet-mapping>
把這個web.xml放到web-inf中(詳請參考tomcat幫助文檔).
當每次請求一個request時(如index.jsp),先到LogFilter中去並調用doFilter()方法,然後才到各自的servlet中去.如果是一個簡單的servlet(只是一個頁面,無任何輸出語句),那麼可能的輸出是:
Request to /index.jsp: 10
Filter是一個COM組件,由一個或多個Pin組成。Pin也是一個COM組件。 Filter文件的擴展名為.ax,但也可以是.dll。Filter根據其包含Input pin或Output pin的情況(或在Filter Graph的位置),大致可分為三類:Source Filter(僅有Output pin)、Transform Filter(同時具有Input pin和Output pin)和Renderer Filter(僅有Input pin)。
一般情況下,創建Filter使用一個普通的Win32 DLL項目。而且,一般Filter項目不使用MFC。這時,應用程序通過CoCreateInstance函數Filter實例;Filter與應用程序在二進制級別的協作。另外一種方法,也可以在MFC的應用程序項目中創建Filter。這種情況下,Filter不需注冊為COM組件,Filter與應用程序之間的協作是源代碼級別的;創建Filter實例,不再使用CoCreateInstance函數,而是直接new出一個Filter對象,如下:
m_pFilterObject = new CFilterClass();
// make the initial refcount 1 to match COM creation
m_pFilterObject ->AddRef();
因為Filter的基類實現了對象的引用計數,所以即使在第二種情況下,對創建後的Filter對象的操作也完全可以遵循COM標准。
Filter是一個獨立功能模塊,最好不要將Filter依賴於其他第三方的DLL。因為 Filter具有COM的位置透明性特點,Filter文件可以放在硬碟的任何位置,只要位置移動後重新注冊。但此時,如果Filter依賴其他DLL,則Filter對該DLL的定位就會出現問題。
Filter不能脫離Filter Graph單獨使用。所以,如果你想繞過Filter Graph直接使用Filter實現的模塊功能,請將你的Filter移植成DMO(DirectX Media Object)。對於DirectShow應用程序開發者來說,還有一點,請不要忘記使用OleInitialize進行初始化。
2. Filter的注冊
Filter是COM組件,所以在使用前一定要注冊。Filter的注冊程序為 regsvr32.exe。如果帶上命令行參數/u,表示注銷;如果帶上是/s,表示不彈出任何注冊/注銷成功與否的提示對話框。如果你想在Build Filter項目的時候進行自動注冊,請在VC的Project settings的Custom Build頁如下設置:
Description: Register filter
Commands: regsvr32 /s /c $(TargetPath)
echo regsvr32 exe.time > $(TargetDir)\$(TargetName).trg
Outputs: $(TargetDir)\$(TargetName).trg
Filter的注冊信息包括兩部分:基本的COM信息和Filter信息。注冊信息都存放在注冊表中。前者的位置為:HKEY_CLASSES_ROOT\CLSID\Filter Clsid\,後者的位置為:HKEY_CLASSES_ROOT\CLSID\Category\Instance\ Filter Clsid\。COM信息標示了Filter是一個標準的可以通過CoCreateInstance函數創建的COM組件,Filter信息標示了我們通過Graphedit看到的描述這個Filter的信息。如果你不想讓Graphedit看到(或者讓Filter枚舉器找到)你寫的Filter,你完全可以不注冊Filter信息。而且不用擔心,你這么做也完全不會影響Filter的功能。
屏蔽注冊Filter信息的方法也很簡單。因為CBaseFilter實現了IAMovieSetup介面的兩個函數:Register和Unregister。我們只需重載這兩個函數,直接return S_OK就行了。
Filter的Merit值。這個值是微軟的「智能連接」函數使用的。在Graphedit中,當我們加入一個Source Filter後,在它的pin上執行「Render」,會自動連上一些Filter。Merit的值參考如下:
MERIT_PREFERRED = 0x800000,
MERIT_NORMAL = 0x600000,
MERIT_UNLIKELY = 0x400000,
MERIT_DO_NOT_USE = 0x200000,
MERIT_SW_COMPRESSOR = 0x100000,
MERIT_HW_COMPRESSOR = 0x100050
Merit值只有大於MERIT_DO_NOT_USE的時候才有可能被「智能連接」使用;Merit的值越大,這個Filter的機會就越大。
3. Filter之間Pin的連接過程
Filter只有加入到Filter Graph中並且和其它Filter連接成完整的鏈路後,才會發揮作用。Filter之間的連接(也就是Pin之間的連接),實際上是連接雙方的一個 Media type的協商過程。連接的方向總是從Output pin指向Input pin。連接的大致過程為:如果調用連接函數時已經指定了完整的Media type,則用這個Media type進行連接,成功與否都結束連接過程;如果沒有指定或不完全指定了Media type,則進入下面的枚舉過程。枚舉欲連接的Input pin上所有的Media type,逐一用這些Media type與Output pin進行連接(如果連接函數提供了不完全Media type,則要先將每個枚舉出來的Media type與它進行匹配檢查),如果Output pin也接受這種Media type,則Pin之間的連接宣告成功;如果所有Input pin上枚舉的Media type,Output pin都不支持,則枚舉Output pin上的所有Media type,並逐一用這些Media type與Input pin進行連接。如果Input pin接受其中的一種Media type,則Pin之間的連接到此也宣告成功;如果Output pin上的所有Media type,Input pin都不支持,則這兩個Pin之間的連接過程宣告失敗。
每個Pin都可以實現GetMediaType函數來提供該Pin上支持的所有 Preferred Media type(但一般只在Output pin上實現,Input pin主要實現CheckMediaType看是否支持當前提供的Media type就行了)。連接過程中,Pin上枚舉得到的所有Media type就是這里提供的。
在CBasePin類中有一個protected的成員變數 m_bTryMyTypesFirst,默認值為false。在我們定製Filter的Output pin中改變這個變數的值為true,可以定製我們自己的連接過程(先枚舉Output pin上的Media type)。
當Pin之間的連接成功後,各自的pin上都會調用CompleteConnect函數。我們可以在這里取得一些連接上的Media type的信息,以及進行一些計算等。在Output pin的CompleteConnect實現中,還有一個重要的任務,就是協商Filter Graph運行起來後Sample傳輸使用的內存配置情況。這同樣是一個交互過程:首先要詢問一下Input pin上的配置要求,如果Input pin提供內存管理器(Allocator),則優先使用Input pin上的內存管理器;否則,使用Output pin自己生成的內存管理器。我們一般都要實現DecideBufferSize來決定存放Sample的內存大小。注意:這個過程協商完成之後,實際的內存並沒有分配,而要等到Output pin上的Active函數調用。
4. Filter Media type概述
Media type一般可以有兩種表示:AM_MEDIA_TYPE和CMediaType。前者是一個Struct,後者是從這個Struct繼承過來的類。
每個Media type有三部分組成:Major type、Subtype和Format type。這三個部分都使用GUID來唯一標示。Major type主要定性描述一種Media type,比如指定這是一個Video,或Audio或Stream等;Subtype進一步細化Media type,如果Video的話可以進一步指定是UYVY或YUY2或RGB24或RGB32等;Format type用一個Struct更進一步細化Media type。
如果Media type的三個部分都是指定了某個具體的GUID值,則稱這個Media type是完全指定的;如果Media type的三個部分中有任何一個值是GUID_NULL,則稱這個Media type 是不完全指定的。GUID_NULL具有通配符的作用。
常用的Major type:
MEDIATYPE_Video;
MEDIATYPE_Audio;
MEDIATYPE_AnalogVideo; // Analog capture
MEDIATYPE_AnalogAudio;
MEDIATYPE_Text;
MEDIATYPE_Midi;
MEDIATYPE_Stream;
MEDIATYPE_Interleaved; // DV camcorder
MEDIATYPE_MPEG1SystemStream;
MEDIATYPE_MPEG2_PACK;
MEDIATYPE_MPEG2_PES;
MEDIATYPE_DVD_ENCRYPTED_PACK;
MEDIATYPE_DVD_NAVIGATION;
常用的Subtype:
MEDIASUBTYPE_YUY2;
MEDIASUBTYPE_YVYU;
MEDIASUBTYPE_YUYV;
MEDIASUBTYPE_UYVY;
MEDIASUBTYPE_YVU9;
MEDIASUBTYPE_Y411;
MEDIASUBTYPE_RGB4;
MEDIASUBTYPE_RGB8;
MEDIASUBTYPE_RGB565;
MEDIASUBTYPE_RGB555;
MEDIASUBTYPE_RGB24;
MEDIASUBTYPE_RGB32;
MEDIASUBTYPE_ARGB32; // Contains alpha value
MEDIASUBTYPE_Overlay;
MEDIASUBTYPE_MPEG1Packet;
MEDIASUBTYPE_MPEG1Payload; // Video payload
MEDIASUBTYPE_MPEG1AudioPayload; // Audio payload
MEDIASUBTYPE_MPEG1System; // A/V payload
MEDIASUBTYPE_MPEG1VideoCD;
MEDIASUBTYPE_MPEG1Video;
MEDIASUBTYPE_MPEG1Audio;
MEDIASUBTYPE_Avi;
MEDIASUBTYPE_Asf;
MEDIASUBTYPE_QTMovie;
MEDIASUBTYPE_PCM;
MEDIASUBTYPE_WAVE;
MEDIASUBTYPE_dvsd; // DV
MEDIASUBTYPE_dvhd;
MEDIASUBTYPE_dvsl;
MEDIASUBTYPE_MPEG2_VIDEO;
MEDIASUBTYPE_MPEG2_PROGRAM;
MEDIASUBTYPE_MPEG2_TRANSPORT;
MEDIASUBTYPE_MPEG2_AUDIO;
MEDIASUBTYPE_DOLBY_AC3;
MEDIASUBTYPE_DVD_SUBPICTURE;
MEDIASUBTYPE_DVD_LPCM_AUDIO;
MEDIASUBTYPE_DVD_NAVIGATION_PCI;
MEDIASUBTYPE_DVD_NAVIGATION_DSI;
MEDIASUBTYPE_DVD_NAVIGATION_PROVIDER;
常用的Format type:
FORMAT_None
FORMAT_DvInfo DVINFO
FORMAT_MPEGVideo MPEG1VIDEOINFO
FORMAT_MPEG2Video MPEG2VIDEOINFO
FORMAT_VideoInfo VIDEOINFOHEADER
FORMAT_VideoInfo2 VIDEOINFOHEADER2
FORMAT_WaveFormatEx WAVEFORMATEX
5. Filter之間的數據傳送
Filter之間的數據是通過Sample來傳送的。Sample是一個COM組件,擁有自己的一段數據緩沖。Sample由Allocator統一管理。如下圖所示:
Filter之間數據傳送的方式有兩種:Push模式和Pull模式。
⑥ Java 開發中用到的幾種過濾器
在Java中有時會遇見亂碼的情況,這里提供了幾種轉換方法(一)Java中的編碼轉換 (二)可以在web.xml文件中配置的自己寫的過濾器 第一種方法最簡單也最方便,但是只能用在少量的地方或是偶爾一兩次轉碼,如果大面積使用就不方便了,也大大增加了編碼量,如果你的項目里沒有用Spring的框架開發,用web.xml配置自定義過濾器可以一勞永逸的解決 1.首先要編寫自己的過濾器類(實現了javax.servlet.Filter): package fck.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class CodeFilter implements Filter { public void destroy() { // TODO Auto-generated method stub } public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter) throws IOException, ServletException { // TODO Auto-generated method stub request.setCharacterEncoding("utf-8"); filter.doFilter(request, response); } public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } } 2.在web.xml文件中編寫過濾器配置: CodeFilter fck.filter.CodeFilter CodeFilter /* 之後不用再做什麼代碼的改變,過濾器就將所有的訪問都轉碼了 (三)使瀏覽器不緩存頁面的過濾器 import javax.servlet.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** *//** * 用於的使 Browser 不緩存頁面的過濾器 */ public class ForceNoCacheFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException{ ((HttpServletResponse) response).setHeader("Cache-Control","no-cache"); ((HttpServletResponse) response).setHeader("Pragma","no-cache"); ((HttpServletResponse) response).setDateHeader ("Expires", -1); filterChain.doFilter(request, response); } public void destroy() { } public void init(FilterConfig filterConfig) throws ServletException { } } (四)檢測用戶是否登陸的過濾器 import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.List; import java.util.ArrayList; import java.util.StringTokenizer; import java.io.IOException; /** */ /** * 用於檢測用戶是否登陸的過濾器,如果未登錄,則重定向到指的登錄頁面 * 配置參數 * checkSessionKey 需檢查的在 Session 中保存的關鍵字 * redirectURL 如果用戶未登錄,則重定向到指定的頁面,URL不包括 ContextPath * notCheckURLList 不做檢查的URL列表,以分號分開,並且 URL 中不包括 ContextPath */ public class T implements Filter { protected FilterConfig filterConfig = null; private String redirectURL = null; private List notCheckURLList = new ArrayList(); private String sessionKey = null; public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; HttpSession session = request.getSession(); if (sessionKey == null) { filterChain.doFilter(request, response); return; } if ((!(request)) && session.getAttribute(sessionKey) == null) { response.sendRedirect(request.getContextPath() + redirectURL); return; } filterChain.doFilter(servletRequest, servletResponse); } public void destroy() { notCheckURLList.clear(); } private boolean (HttpServletRequest request) { String uri = request.getServletPath() + (request.getPathInfo() == null ? "" : request.getPathInfo()); return notCheckURLList.contains(uri); } public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; redirectURL = filterConfig.getInitParameter("redirectURL"); sessionKey = filterConfig.getInitParameter("checkSessionKey"); String notCheckURLListStr = filterConfig.getInitParameter("notCheckURLList"); if (notCheckURLListStr != null) { StringTokenizer st = new StringTokenizer(notCheckURLListStr, ";"); notCheckURLList.clear(); while (st.hasMoreTokens()) { notCheckURLList.add(st.nextToken()); } } } } (五)資源保護過濾器 import javax.servlet.Filter; import javax.servlet.FilterConfig; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Iterator; import java.util.Set; import java.util.HashSet; // import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** */ /** * This Filter class handle the security of the application. * * It should be configured inside the web.xml. * * @author Derek Y. Shen */ public class SecurityFilter implements Filter { // the login page uri private static final String LOGIN_PAGE_URI = "login.jsf"; // the logger object private Log logger = LogFactory.getLog(this.getClass()); // a set of restricted resources private Set restrictedResources; /** */ /** * Initializes the Filter. */ public void init(FilterConfig filterConfig) throws ServletException { this.restrictedResources = new HashSet(); this.restrictedResources.add("/createProct.jsf"); this.restrictedResources.add("/editProct.jsf"); this.restrictedResources.add("/proctList.jsf"); } /** */ /** * Standard doFilter object. */ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { this.logger.debug("doFilter"); String contextPath = ((HttpServletRequest) req).getContextPath(); String requestUri = ((HttpServletRequest) req).getRequestURI(); this.logger.debug("contextPath = " + contextPath); this.logger.debug("requestUri = " + requestUri); if (this.contains(requestUri, contextPath) && !this.authorize((HttpServletRequest) req)) { this.logger.debug("authorization failed"); ((HttpServletRequest) req).getRequestDispatcher(LOGIN_PAGE_URI) .forward(req, res); } else { this.logger.debug("authorization succeeded"); chain.doFilter(req, res); } } public void destroy() { } private boolean contains(String value, String contextPath) { Iterator ite = this.restrictedResources.iterator(); while (ite.hasNext()) { String restrictedResource = (String) ite.next(); if ((contextPath + restrictedResource).equalsIgnoreCase(value)) { return true; } } return false; } private boolean authorize(HttpServletRequest req) { // 處理用戶登錄 /**//* * UserBean user = * (UserBean)req.getSession().getAttribute(BeanNames.USER_BEAN); * * if (user != null && user.getLoggedIn()) { //user logged in return * true; } else { return false; } */ } } (六) 利用Filter限制用戶瀏覽許可權 import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class T implements Filter { public void destroy() { } public void doFilter(ServletRequest sreq, ServletResponse sres, FilterChain arg2) throws IOException, ServletException { // 獲取uri地址 HttpServletRequest request = (HttpServletRequest) sreq; String uri = request.getRequestURI(); String ctx = request.getContextPath(); uri = uri.substring(ctx.length()); // 判斷admin級別網頁的瀏覽許可權 if (uri.startsWith("/admin")) { if (request.getSession().getAttribute("admin") == null) { request.setAttribute("message", "您沒有這個許可權"); request.getRequestDispatcher("/login.jsp").forward(sreq, sres); return; } } // 判斷manage級別網頁的瀏覽許可權 if (uri.startsWith("/manage")) { // 這里省去 } } // 下面還可以添加其他的用戶許可權,省去。
⑦ Java8新特性有哪些
【注意】本文節選自是 DZone 指南 Java 生態系統的專題文章,作者Trisha Gee是Java資深工程師和佈道者。在本文中,Trisha Gee闡述了Java 8的重要特性以及使用的原因,由OneAPM工程師翻譯。
一、要點速遞
1、在很多情況下,Java8 都能提升應用性能,而無需任何改變或性能調優。
2、Lambda 表達式、 Streams API 以及現有類的新方法都是提高生產力的重要工具。
3、Java8 新推出的 Optional 類型在處理 null 值時,能減少 NullPointerExceptions 的可能性,給開發者極大的靈活度。
二、其他特性:
速度更快
可以取悅老闆、滿足業務或運營人員的一大賣點是:Java8 運行應用時速度更快。通常,升級至 Java8 的應用都能得到速度上的提升,即便沒有做任何改變或調優。對於為了迎合特定 JVM 而做出調整的應用,這或許並不適用。但 Java8 性能更優的理由還有很多:
80%以上的高端企業級應用都使用JAVA平台(電信、銀行等)。JAVA是成熟的產品,已經有10年的歷史。如果你想在Java行業有所建樹,想要系統的進行java的學習,那麼你可以來這個群,前面是二三一,中間是三一四,後面是零二八。連起來就可以了。這里有很多互聯網大牛教你學習,還有免費的課程。不是想學習的就不要加了。
常見數據結構的性能提升:對廣受歡迎的 HashMap 進行的基準測試表明,它們在 Java8 中的性能更好。這種提升非常吸引人——你無需學習新的 Streams API 或 Lambda 語法,甚至不需要改變現有的代碼,就能提升應用的性能。
垃圾回收器提升:通常,Java 應用性能取決於垃圾回收的效率。的確,糟糕的垃圾回收會很大程度上影響應用性能。Java8 對垃圾回收做了很多改變,能有效提升性能並簡化調優。最為人熟知的改變是 PermGen 的移除與 Metaspace 的引入。
Fork/Join 速度提升:fork/join 框架是在 Java7 中首次引入的,目的是簡化使用 JVM 的並發程序。Java8 中投入了很多努力進一步提升該框架。現在,fork/join 在 Streams API 中用於並發操作。
此外,Java8 中還包含諸多改進以支持並發。Oracle 在 JDK 8 中總結了這些性能提升。
代碼行更少
Java 經常被人們詬病其樣本代碼太多。為此,Java8 新的 API 採用了更具功能性的方式,專注於實現什麼而不是如何實現。
Lambda 表達式
Java8 中的 Lambda 表達式不僅是 Java 已有的匿名內部類—— Java8 推出之前傳遞行為的方法之外的語法糖衣。Lambda 表達式採用了 Java 7 的內部改變,因此運用起來相當流暢。想了解如何使用 Lambda 表達式簡化代碼,請繼續閱讀。
集合新方法介紹
Lambda 表達式與 Streams 可能是 Java8 最大的兩個賣點,較少為人知的是 Java 現在允許開發者給現有類添加新的方法,而無需為了向後兼容性折中。這樣,新的方法,結合 Lambda 表達式,能在很大程序上簡化代碼。比如,我們常常需要判斷 Map 中的某個成員是否已經存在,如果不存在則創建之。在 Java8 之前,你可能會這么做:
privatefinalMap<CustomerId,Customer>customers=newHashMap<>();
(CustomerIdcustomerId){
Customercustomer=customers.get(customerId);
if(customer==null){
customer=newCustomer(customerId);
customers.put(customerId,customer);
}
customer.incrementOrders();
}
操作「檢查某個成員在 map 中是否存在,若不存在則添加之」是如此常用,Java 現在為 Map 添加了一個新方法 computeIfAbsent 來支持這個操作。該方法的第二個參數是一個 Lambda 表達式,該表達式定義了如何創建缺少的成員。
(CustomerIdcustomerId){
Customercustomer=customers.computeIfAbsent(customerId,
id->newCustomer(id));
customer.incrementOrders();
}
其實,Java8 還有一個新的特性,稱為方法引用(method references),它能使我們用更簡潔的代碼實現該功能:
(CustomerIdcustomerId){
Customercustomer=customers.computeIfAbsent(customerId,Customer::new);
customer.incrementOrders();
}
Java8 為 Map 與 List 都添加了新方法。你可以了解一下這些新方法,看它們能節省多少行代碼。
Streams API
Streams API 為查詢、操縱數據提供了更多靈活度。這是一個很強大的功能。閱讀這些文章能對 Streams API 有更全面的了解。在大數據時代建立流暢的數據查詢會非常有趣,而且也是常用的操作。比如,你有一列書,你希望按照字母表順序排列這些書的作者名,且不含重復。
publicList<Author>getAllAuthorsAlphabetically(List<Book>books){
List<Author>authors=newArrayList<>();
for(Bookbook:books){
Authorauthor=book.getAuthor();
if(!authors.contains(author)){
authors.add(author);
}
}
Collections.sort(authors,newComparator<Author>(){
publicintcompare(Authoro1,Authoro2){
returno1.getSurname().compareTo(o2.getSurname());
}
});
returnauthors;
}
在上面的代碼中,我們首先遍歷這列書,如果書的作者從未在作者列表出現,則添加之。之後,我們根據作者的姓氏按字母表順序對這些作者排序。這種排序操作正是 Streams 擅長解決的領域:
publicList<Author>getAllAuthorsAlphabetically(List<Book>books){
returnbooks.Streams()
.map(book->book.getAuthor())
.distinct()
.sorted((o1,o2)->o1.getSurname().compareTo(o2.getSurname()))
.collect(Collectors.toList());
}
上面的做法不僅代碼行更少,而且描述性更強——後來的開發者讀到這段代碼能夠輕易理解:1、代碼從書中獲取作者姓名。2、只在意從未出現過的作者。3、返回的列表按照作者姓氏排序。將 Streams API 與其他新特性——方法引用(method references)、比較器(Comparator)的新方法結合使用,可以得到更加簡潔的版本:
publicList<Author>getAllAuthorsAlphabetically(List<Book>books){
returnbooks.Streams()
.map(Book::getAuthor)
.distinct()
.sorted(Comparator.comparing(Author::getSurname))
.collect(Collectors.toList());
}
這里,排序方法按照作者姓氏排序,更加顯而易見了。
便於並行
此前我們淺聊過更利於開箱即用的性能,除了前面提到過的特性,Java8 能更好地利用 CPU 內核。將前例中的 Streams 方法替換為 parallelStreams,JVM 會將此運算分解為不同的任務,使用 fork/join 將這些任務運行在多個核上。然而,並行化並不是加速所有運算的魔法。並行化運算總是會帶來更多工作——分解運算,整合結果,因此無法總是減少時間。但是,對適合並行化的例子,這么做還是頗有效率的。
最大化減少 Null 指針
Java8 的另一個新特性是全新的 Optional 類型。該類型的含義是「我可能有值,也可能是 null。「這樣一來,API 就可以區分可能為 null 的返回值與絕對不會是 null 的返回值,從而最小化 NullPointerException 異常的發生幾率。
Optional 最贊的用處是處理 null。例如,假設我們要從一個列表中找一本特定的書,新創建的 findFirst() 方法會返回 Optional 類型的值,表明它無法確保是否找到特定的值。有了這個可選擇的值,我們接下來可以決定,如果是 null 值要如何處理。如果想要拋出一個自定義的異常,我們可以使用 orElseThrow:
publicBookfindBookByTitle(List<Book>books,Stringtitle){
Optional<Book>foundBook=books.Streams()
.filter(book->book.getTitle().equals(title))
.findFirst();
returnfoundBook.orElseThrow(()->newBookNotFoundException("Didnotfindbookwithtitle"+title));
}
或者,你可以返回其他書:
returnfoundBook.orElseGet(()->getRecommendedAlternativeBook(title));
或者,返回 Optional 類型,這樣,該方法的調用者可以自己決定書沒找到時要怎麼做。
總結:Java8 作為 Java 語言的一次重大發布,包含語法上的更改、新的方法與數據類型,以及一些能默默提升應用性能的隱性改善。Oracle 已經不再支持 Java 7,因此許多公司都被迫向 Java8 轉移。好消息是,Java8 對業務、現有的應用以及期望提高生產力的開發者都好好多。
⑧ java的垃圾回收處理搞不懂呀,請大俠幫忙指點。
String xinlu; 沒生成一個字元串內存就會生成一個String對象 for (int j = 0; j < 100; j++) {
if (j < 10) xinlu = lujing + "0" + j + "\\"; else 你這地方循環生成多少個字元串,就有多少個String的對象生成 嚴重占據內存
解決方法:將String xinlu;改為StringBuffer xinlu ; xinlu = lujing + "0" + j + "\\"; 改為:xinlu=xinlu.append("0").append(j) ; append()方法是增加字元串的方法
清理垃圾可以使用:System.gc() ;
⑨ Java8 使用filter對集合過濾的時候怎麼取到當前的索引即index
以下是indexOf的源代碼,可以看出, 是從0往後找,找到就返回
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}