① 如何更好地使用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;
}