PlayFramework 已全面向 Google Guice 演进, 由于我们的项目是从 Play 2.0 开始搭建, 现虽已升级到了 2.4.6, 但对 Guice 的还没抱得那么的紧, 有点积重而慢步前挪. Google Guice 也是一个依赖注入(DI) 容器, 据说它比 Spring 快了很多很多, 是轻量级项目或框架首选, Google 出品俱是精品.
既然它是轻量级的 DI, 那么必须呈上一个最轻量级的入门, 官方的 Get Started 文档其实啰嗦了点, 对于 BillingService 依赖的 CreditCardProcess, TransactionLog, 和 Receipt 都未有交待, 算不上一个完整项目. 所以我这里尽量简化, 又不偏离一个最简单的项目模型. 那就是下面那个例子, 它实现了在不同时间发出不同的问候.
项目需要依赖 Guice 及它的子依赖, 这得看你是用什么来管理依赖的, 依照 http://mvnrepository.com/artifact/com.google.inject/guice/4.0 根据实际情况加上依赖配置, 前面这个链接默认为 sbt 的, 我还真偏爱了 sbt, 若是 Maven 就配置 Maven 的 pom.xml 吧. Guice 的引入带上以下几个依赖包
+-com.google.inject:guice:4.0
+-aopalliance:aopalliance:1.0
+-com.google.guava:guava:16.0.1
+-javax.inject:javax.inject:1Guice 目前是 4.0. Read More
- Java 7 首次引入了 fork/join 框架,但一直未曾直接尝试. 而且基本上也很少在实际项目中直接写 fork-join 的代码,在我们使用第三方组件时倒是间接会接触到 fork/join 框架。譬如 Akka 的 fork-join-executor, sbt 执行测试用例时也是默认 fork/join 并发执行。fork-join 可以帮助我们把计算任务粒度细化,并更有效的利用多 CPU 内核。
fork-join 与 map-reduce 有些相妨,在 Java 7 时代我其实是忽视了它的存在。目今正在了解 Java 8 的 parallelStream 时,因为它的底层实现也是 fork/join, 所以有兴致去稍加体验一下。fork/join 的算法简单来讲就是递归对半去细化计算任务,及到不能细化时由多内核(线程)去计算被拆分的任务,最后反方向把结果汇总。
下面是从 《Java 8 IN ACTION》中截的一个说明 fork/join 的处理过程 Read More - 前面一篇 Java 8 的 groupingBy 能否产生空的 Map 分组 是提出来的思考,本篇就是上一篇的答案。
由于在 Java 8 中用 Collectors.groupingBy 对 List 进行分组时每个组里都必须存在元素,也就是Stream<Person> stream = Stream.of(new Person("Tom", "male"), new Person("Jerry", "male"));
只能得到结果
System.out.println(stream.collect(Collectors.groupingBy(person -> person.gender)));{male=[Tom, Jerry]}
而无法表示存在其他 gender 的可能性,并且 female=[] 的情况,即想要结果{male=[Tom, Jerry], female=[]}
如果想得到以上的结果该当如何呢? stream.collect() 接受一个 Collector, Collectors 中只是定义了许多常用的 Collector 实现,如果不够用的话我们可以实现自己的 Collector. 下面就来定义一个 GroupingWithKeys, 它需要实现 java.util.stream.Collector 接口,有五个接口方法. 事成之后我们写 Read More - 在 Java 8 之前如果我们要找到集合中第一个匹配元素,要使用外部循环,如下面方法 findFirstMatch() 如果找到一个大于 3 的数字立即返回它,否则返回 null
public Integer findFirstMatch() {
因为在 for 循环中找到第一个大于 3 的数字是 4, 并且立即返回,所以不管集合 integers 再大,也不会遍历整个集合。
List<Integer> integers = Arrays.asList(1, 4, 2, 5, 6, 3);
for(int i: integers) {
if(i > 3) return i;
}
return null;
}
注:不要纠结于上面示例方法的实际用途,实际上集体和匹配条件都该通过参数传入方法的,这里只作演示循环。
那么我们来到 Java 8 之后用 Stream API 该如何实现,翻遍了 Stream API, 能过滤元素的操作也就是 filter 方法,于是尝试这样的写法 Read More - 由于 Java 对集合的函数式操作并非原生态的,必须对得到的 stream() 进行过滤等操作,之后还是一个 stream(),一般我们最后返回给调用者需还原为相应的集合。这无法与 Scala 的
for ... yield操作相比。例如下面在使用 Stream API 过滤获得所有大于 3 的数字之后,方法的返回值还应该还原为 List<Integer>, 这个需求非常自然1List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); 2List<Integer> greaterThan3(list) { 3 Stream<Integer> streamOfInteger = list.stream().filter( i -> i > 3); 4 return streamOfInteger.ToIntegerList......; 5}
我们这儿的问题就是如何把上面的 streamOfInteger 转换为 List<Integer>, 有以下几种办法 Read More - Java 在运用面向方向编程时,依照 AspectJ 的语法自己书写 *.aj 文件可以得到尽可能大的控制能力。如果是一个 sbt 的项目,有一个 sbt-aspectj-plugin 插件可以帮上我们的忙。那么如何应用这个插件呢? 该插件首页面告诉我们要在
project/plugins.sbt中加上下面这句话其他就是参考例子 runnable sample projects,然而这几个例子并非那么直白。所以还是自己做一个最简单的例子来体验 sbt 项目如何使用 AspectJ. Read More1addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.10.4") - Jackson 是 Playfrmework 2 中默认的 JSON 处理框架,先前是 GSON,JSON 是 Playframework 中的第一等公民,可见 Jackson 在 Playframewok 中的重要地位。Jackson 提供了一系列的注解可用,像 @JsonIgnore, @JsonProperty, @JsonUnwrapped, @JsonFilter 等。人的需求总是很难得到满足,所以免不了还是要定义自己的注解。比如有这样一个需求,JavaBean 中被 @MaskField(这个即将成为我第一个自定义的注解) 标记的属性或 getter 方法,总是输出为
******, 无此标记的属性或方法输出原始值。我尝试过 @JsonFilter 或是单纯的自定义 JsonSerializer, 并不怎么如意。本人最终的实现方式涉及到- @JacksonAnnotationsInside -- 用来创建自己的 @MaskField 注解
- JsonSerializer -- 被 @MaskField 标记的字段采用自定义的 JsonSerializer 来序列化
- JacksonAnnotationIntrospector -- 禁用某一特定的注解,这样可以在做任意时候启用或禁用 @MaskField Read More
初学 Java 的人很不经意间就会把常量定义在接口中,大概唯一的理由是接口不能实例化,而使用接口中定义的常量也是不用附着在实例上的。 这主要还是 JDK 本身给我们做了很多这样的榜样, 如 java.io.ObjectStreamConstans, 多是出现在 Enum 类型到来之前。
其实 Java 的接口常量是一种反模式,理由如下:
1. 接口是不能阻止被实现或继承的, 也就是说子接口或实现中是能够覆盖掉常量的定义(重名),这样通过父,子接口(或实现) 去引用常量是可能不一致的
2. 同样的,由于被实现或继承,造成在继承树中可以用大量的接口, 类 或实例去引用 同一个常量,从而造成接口中定义的常量污染了命名空间。(Java 编译器竟然允许使用实例去引用类变量)
3. 接口暗含的意思是:它是需被实现的,代表着一种类型,它的公有成员是要被暴露的 API。而在接口中定义的常量说不上是 API4. 这点有些重复,Java 允许通过子类去引用父类中定义的常量,各级对像实例去引用父类的常量,所以这会造成相当的混乱不堪。定义的常量不能保证单一的引用方式。
参见: Effective java 第 19 条: 接口只用于定义类型
既然接口中不适于定义常量,那么该在何处为常量安家呢?接口为 实现/继承 而生,如果放在类中,并且这个类是 final,且封闭掉构造方法就行。 于是我们先前的接口常量定义 Read More
枚举的声明很简单, 像
enum Gender { Male, Female }, 其余事情就是 Java 编译器帮我们干的了,所以 enum 也就是一块语法糖。有了枚举确实是很方便,避免了传统常量的无范围性。那么编译器到底在后面做了什么呢?以及理解了这个之后我们可以怎么去使用 Java 的枚举, 下面就从这个例子说起:public enum Gender {
Male,
Female
}把上面的编译成 Gender.class, 然后用 javap -c Gender 反编译出来就是 Read More
- 使用过 Java 反射的大多都知道, 想要修改某个类或对象的私有变量的值的话, 在调用 set 设置新值之前执行一下 setAccessible(true) 即可。这样利用的 Java 的反射就能绕过 private 的限制 ,不再有 IllegalAccessException 异常了。这是一个 trick, 调用 Java 的私有方法也能这么做,有些人或许或这样来测试 Java 私有方法。
提前说一句:在修改 final 型值时,要特别留意它的常量值本身是否被编译器优化内联到某处,否则你会看到虽然没什么异常,但取出的还是原来的值。后面会稍为深入的讲到。
例如下面是一段完整的代码, 由于调用了 setAccessiable(true), 所以能成功把 OneCity 的私有属性 name 的值改为 "Shenzhen": Read More