What is the crazy

James Gosling在他May 08,2004的Blog的帖子:《The world needs more crazy people》谈到了一些做事的原则,可以作为思考的引子;更要警醒、反省、鼓励自己。

“Most real innovation is done by crazy people doing crazy things. The keys are:

* Learn all you can before you go adventuring.
* Don't be afraid to make mistakes.
* Only make new mistakes.
* Keep your eyes open.
* Don't just look straight ahead: develop your peripheral vision.
* It's the things that go in unexpected directions are the most important."

如何评审(Review)

最近在搞一个培训的文档的评审,步骤大约是:
1.有一个Session Topic;
2.讲师先要围绕这个Topic写一个大纲,主要是大约是将Topic拆分若干细节分支;
3.评审PPT等文档;
如何对这样的文档进行评审,因为文档只是第一步,也是讲师能否讲这个Topic讲好的关键,所以就开始想如何保证讲师的培训质量,如何保证评审的质量,如何尽量能够促使讲师讲某一个Topic的广度和深度展现给audient呢。
我的思路大约是:
从行业角度看,大家一般讨论这个Topic的角度;
从audient看,我们希望他们能接收到什么信息,他们自身提高中需要什么信息;
从讲师来看,能够给Topic带了什么special的东西;
在我读一个讲稿的时候,我希望我的第一感觉,感觉到一个明显的主线,知道这个讲稿在究竟要讲些什么;能够有一个完整的概念模型,简单易懂,这样便于大家理解;然后能够看到细节性的分析和讲解,就是深入的内容。觉得既要有葡萄蜜饯,也要有果酱颗粒,葡萄蜜饯不至于太突兀,通用的信息让大家理解起来容易,有一种背靠着行业的味道;对于我而言,我更加希望能够看到更多的个人理解的东西,更多的果酱颗粒,会带来思考的地方,带来讨论的地方,往往也是启迪人的地方。希望看多一些specail的地方,希望看到许多兴奋点。
附:果酱和葡萄蜜饯来源于《The Secrets of Consulting》
“只要还有果酱,草莓酱就永远都不会被涂抹得过薄。”
“永远不会有人抱怨葡萄蜜饯。”

Google技术栈

和一个朋友讨论技术的学习,都有什么技术需要一个技术人员储备的,无意间朋友告诉我,Google关心的技术都有:
• algorithms
• artificial intelligence
• compiler optimization
• computer architecture
• computer graphics
• data compression
• data mining
• file system design
• genetic algorithms
• information retrieval
• machine learning
• natural language processing
• operating systems
• profiling
• robotics
• text processing
• user interface design
• web information retrieval
• and more!
参见 http://labs.google.com/
细节 http://labs.google.com/papers.html
中文 http://labs.google.cn/ 并没有这个。

Annotation实战

现在annotation正在改变着java developer的一些开发生活,各种框架都在充分应用annotation来简化开发,我们来看看annotation到底是怎么一回事?
为什么引入annotation?
由于在Java的一些实际开发中,已经有了一些annotation出现的雏形.比方说webservice使用一些tag自动生成一些接口和实现,EJB的deployment descriptor对象,transient修饰符代表不能被serialization,@deprecated等。那么他们的共用特性是什么呢?尽管这些数据不会直接对Java的语义产生影响,但是是通过工具、库或者运行时程序的语义来散发影响力的。以这种方式出现的新功能称之为annotation机制。所以Java是在增强Java语言支持易于开发的背景下提出annotation机制的,annotation对于已有的Java机制的影响力主要在于Java source files、Java class files和运行时的reflection;主要关联java classes,interfaces,methods和fields。(,或者能够能够被javac compiler或者其他的工具读取、或者作为配置项能够被存贮在class文件中、或者在运行时用Java reflection API检测的到。)
虽然在第一天提出annotation这个特性,就一直在强调:annotion易于使用,但是应用开发者无须自己定义annotaion类型。但是不论是更好的使用现有的annotation类型,还是便于有朝一日自己确实需要扩展annotion类型,或者更好的理解开发模型,更好的理解程序的开发运行,都非常有必要了解一些annotation的。

我们看一下Meta-annotations,看到底如何自己定义一个annnotation类型。Meta-annotations主要包括四个部分:

1
2
3
4
Target
Retention
Documented
Inherited

Target里面的ElementType是用来指定Annotation类型可以用在哪一些元素上的.

1
2
3
4
5
6
7
Target(ElementType.TYPE)-can be applied to any element of a class
Target(ElementType.FIELD)-can be applied to a field or property
Target(ElementType.METHOD)-can be applied to a method level annotation
Target(ElementType.PARAMETER)-can be applied to the parameters of a method
Target(ElementType.CONSTRUCTOR)-can be applied to constructors
Target(ElementType.LOCAL_VARIABLE)-can be applied to local variables
Target(ElementType.ANNOTATION_TYPE)-indicates that the declared type itself is an annotation typ

Retention用来指定annotation类型保留在哪儿和多长时间。

1
2
3
RetentionPolicy.SOURCE-Annotations with this type will be by retained only at the source level and will be ignored by the compiler
RetentionPolicy.CLASS-Annotations with this type will be by retained by the compiler at compile time, but will be ignored by the VM
RetentionPolicy.RUNTIME-Annotations with this type will be retained by the VM so they can be read only at run-time

Documented用来指定这个annotation类型应当被javadoc工具文档化使用。
Inherited用来指定使用这个类型的类是个继承来的。
举一个简单的例子:
测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AnnotationTestableTeseCase {

@Test
public void go() throws SecurityException, ClassNotFoundException {
int passed = 0, failed = 0;
for (Method m : Class.forName(Foo.class.getName()).getMethods()) {
if (m.isAnnotationPresent(Testable.class)) {
try {
m.invoke(null);
passed++;
} catch (Throwable ex) {

failed++;
}
}
}
Assert.assertEquals(2, passed);
Assert.assertEquals(2, failed);
}
}

Annotation类型定义类:

1
2
3
4
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Testable {
}

使用Testable类型的客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Foo {
@Testable
public static void m1() {
}

public static void m2() {
}

@Testable
public static void m3() {
throw new RuntimeException("Boom");
}

public static void m4() {
}

@Testable
public static void m5() {
}

public static void m6() {
}

@Testable
public static void m7() {
throw new RuntimeException("Crash");
}

public static void m8() {
}
}

Okey!
参考:
http://java.sun.com/developer/technicalArticles/releases/j2se15/
http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html
http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html
http://www.developer.com/java/other/article.php/10936_3556176_3

Ruby实战

先从一段Ruby程序开始,即欧几里德最大公约数的算法实现:
测试用例当然要先,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require 'test/unit'
require './one/Apple'

class TestApple < Test::Unit::TestCase

def test_cal
ys = Apple.new
assert_equal(5,ys.gcd(35,25))
assert_equal(50,ys.gcd(100,50))
assert_equal(9,ys.gcd(27,18))
assert_equal(5,ys.gcd(5,10))
assert_equal(5,ys.gcd(5,0))
assert_equal(-5,ys.gcd(-5,0))
assert_raise(ArgumentError){ys.gcd(0,0)}
assert_equal(23,ys.gcd(0,23))
end
end

然后就是算法的实现代码了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Apple

def gcd(arg1=0, arg2=0)
if((arg1==0)&&(arg2==0))
raise ArgumentError, "arg1 and arg2 are 0.", caller
else
gcdImpl(arg1,arg2)
end
end

def gcdImpl(arg1=0, arg2=0)
if(arg2 == 0)
return arg1
elsif
temp=arg1%arg2
if(temp == 0)
return arg2
else
gcdImpl(arg2,temp)
end
end
end

end

从一个算法的描述来实现一个算法,仅仅是编写一段程序而已。而对于算法这种问题的程序化解决方案而言,需要如何一回事情呢?比方说,最大公约数到底是怎么一回事,5和0的最大公约数怎么可能是5呢?所以要学习算法到底是怎么一回事,需要探讨更多的问题?
对于问题的理解、性能、选择、数据结构、如何设计、算法的描述、正确性证明、分析、代码实现等等一系列单元。当程序实现仅仅是第一反应的时候,问题理解、分析、设计、验证就变得异常重要了。
算法所拥有的通用问题类型:排序、查找、字符串处理、图问题、组合问题、几何问题、数值问题;都是一些已经相当成熟的问题类型。很多使我们常常听说或者涉及的,也是算法设计和分析频繁的地方,算是很多的“肩膀”吧,算法都是都是在这里站立起来的。

算法学习第一季-读《算法设计与分析基础》

大学修的计算机专业,毕业之时,《数据结构》《计算方法》《操作系统》《编译原理》等如同课本一样,永远的被打包起来。工作以来,一直在做Java的开发,Web开发和服务端应用,总是零星的涉及到些许数组、链表、树等数据结构,也主要以线性数据结构为主;算法基本上就是“经典”的数据结构书上所讨论的几种算法,JDK都有实现,在应用中早已无需我们思考太多。重要的是我们要把应用按着需求实现了,重要的是我们一步一步把应用带向我们认为简单务实美的方向前进。闲暇思索,对于虚拟机的看法不过是面试的话语;对于开发的认识同样如此,多是面试的谈资;对于一个更有经验的Java应用开发者,能够在开发提供一套解决方案,组合解决问题的模式积攒起来一段可预期时间内解决一系列问题,也许有人将这归之为架构师,谁又能说这不是一条Java应用架构师之路呢?有一天,恍然大悟,其实Java应用开发者比拼的是各自的开发经验、领域知识、开阔的思路、强悍的执著;事情总没有唯一的,以前和dreamhead讨论过业务,当然也有无数的把应用开发者的需求作为业务的Java开发“仁人志士”。而对于一个从事了几年Java应用开发的人来说,那么我们的激情下一个积攒点在哪儿呢?虽然刚开始接触计算机,就知道一句名言“程序=算法+数据结构”,现实中这几年算法对于我,如同鲁迅先生笔中的故乡,“愈远了”,“渐渐远离了我”,“模糊了”。
人生有多少个十年,人生有几多青春,对于一个个体,又有几多沉淀?
一如我刚毕业的青春年少无知,对于技术这个词汇激情澎湃,几乎认为是自己生命的全部。对于人来说,最重要的不是你能否学技术,学了多少技术,掌握了多少技术功底;而是一个人前进的方向是什么,你的人生想要的是什么,你的人生在计算机这个舞台你如何定位思考、如何的目标的?想好了再前行,前行了就不再犹豫。
算法是否总给人一种严肃的科学的概念,我相信,算法并不严肃,可以嬉笑怒骂,可以轻轻松松的和我们很多一线开发人员在一起。
非常感谢作者Anay Levitin著书《Introduction to the Design and Analysis of Algorithms》,感谢译者潘彦翻译这么好的书籍《算法设计与分析基础》,特别感谢dreamhead五一来沈阳一聚并送这本书给我。
虽然序言前言部分往往都有精髓的思想语句,然后对于算法无知的我决定直切正题,从绪论开始阅读。
为什么要学习算法?
由于“微积分”“算法”被并成为科学殿堂两大宝石的论断,很为大家熟知,至于算法是如何的英明神武,在现实中也许需要细细体会,我并未有多少深深体会悍然汗然。
作者直接扔出了为什么学习算法这个话题,一个是计算机专业人士必备,另外一个是能开发人们的分析能力。如果能有选择,我宁愿选择第二个,我想必备意味着用这个东西现在或者未来要解决切实的问题,我现在并不是先有棘手的问题—没有棘手的问题而去学习本身未必不是一个棘手的问题;如果籍此能够在具备计算机人士必须具备的基础之外,成就一下自己分析能力的增强,真是不错。
什么是算法?
该书中,对于算法的定义是“一系列解决问题的清晰指令,即对于符合一定规范的输入,能够在有限时间内获得所要求的输出。”可以看到算法,有输入,有输出,有指令,当然有执行这些指令的环境,也许第一个想到是计算机,对,计算机,但是也许是从事数学计算的人。定义等同于算法是解决问题的办法。
所以既然是解决问题,当然有问题域/值域;算法本身如何表达描述,算法自己的优劣之分;等等。
第一个例子是关于“计算两个整数的最大公约数”问题,记得在SICP一书中也阐述了这个例子。解决这个问题首先寻找的是“最大公约数的定义”,并仔细描述了欧几里得算法、连续整数检测算法、“艾拉托色尼筛”算法;并采用了结构化描述和伪码两种方式描述算法细节。
该书以如此简洁的言语和例子为我们打开了算法的道路。在接下来的一章节,会首先看到“算法是问题的程序化解决方案”,是的,我们就是要去寻找问题的程序化解决方案的。

RubyOnRails实战

简单开始
下载并且安装

Ruby 1.8.4;
RubyGems;

通过命令行安装所有的Rails和其依赖:

gem install rails --include-dependencies

在用户工作目录运行命令行,即创建第一个rails的模板:

rails depot
cd depot
ruby script/server

在浏览器打开 http://localhost:3000 即可发现第一个rails的web应用已经开始run起来了。

然后我参考书籍《Agile Web Development with Rails》2nd Edition的第一个例子一步一步的动手写了第一个ROR程序。(具体的过程不详细记录了.)
从数据库准备、数据库的配置、创建表、migrate、创建controller和maintenance、add missing column、添加validation功能、使用scaffold、定制html、装填unit test数据。整个例子的思路比较清晰,从数据库、数据访问、业务、controller、展示;给我留下深刻的影响的是:自动生成功能、内嵌web server、支持数据库配置,支持应用的模板、支持mvc模板等,所以让人似乎感觉到一些DSL的色彩。

做一个复杂一些的例子
数据库准备
支持数据库Mysql,从 http://dev.mysql.com/downloads/mysql/5.0.html#downloads 下载数据库Mysql安装,并且下载Mysql Administrator作为数据库管理工具。
创建Schema为:

depot_dev

在文件./config/database.yml中对数据库进行配置:

development:
  adapter: mysql
  database: depot_dev
  username: depot
  password: depot
  host: localhost

测试配置:

rake db:migrate

如果配置不正确在控制台会出现错误的日志。
创建Products的Model和Table如下:

D:\IDE\rails-exampe\depot>ruby script/generate model product

输出:

exists  app/models/
exists  test/unit/
exists  test/fixtures/
create  app/models/product.rb
create  test/unit/product_test.rb
create  test/fixtures/products.yml
create  db/migrate
create  db/migrate/001_create_products.rb

在./db/migrate/001_create_products.rb用类似DDL的形式表达表的结构:

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.column :title,       :string
      t.column :description, :text
      t.column :image_url,   :string
    end
  end

  def self.down
    drop_table :products
  end
end

Okey,那就Migrate吧:

D:\IDE\rails-exampe\depot>rake db:migrate

输出:

D:0:Warning: require_gem is obsolete.  Use gem instead.
(in D:/IDE/rails-exampe/depot)
== CreateProducts: migrating ==================================================
-- create_table(:products)
   -> 0.0780s
== CreateProducts: migrated (0.0780s) =========================================

数据库一切搞定,下一步开始应用层了,首先创建Controller:

D:\IDE\rails-exampe\depot>ruby script/generate controller admin

输出:

exists  app/controllers/
exists  app/helpers/
create  app/views/admin
exists  test/functional/
create  app/controllers/admin_controller.rb
create  test/functional/admin_controller_test.rb
create  app/helpers/admin_helper.rb

创建Maintenance应用:
编辑app/controller/admin_controller.rb

class AdminController < ApplicationController
  scaffold :product
end

启动ruby script/server,通过浏览器就可以浏览这个Rail scaffolds的应用了。
迭代2:Add a missing column

D:\IDE\rails-exampe\depot>ruby script/generate migration add_price
      exists  db/migrate
      create  db/migrate/002_add_price.rb

编辑db/migrate/002_add_price.rb

class AddPrice < ActiveRecord::Migration
  def self.up
    add_column :products, :price, :decimal, :precision => 8, :scale => 2, :default => 0
  end

  def self.down
    remove_column :products, :price
  end
end

执行命令:

D:\IDE\rails-exampe\depot>rake db:migrate

输出:

D:0:Warning: require_gem is obsolete.  Use gem instead.
(in D:/IDE/rails-exampe/depot)
== AddPrice: migrating ========================================================
-- add_column(:products, :price, :decimal, {:default=>0, :precision=>8, :scale=>2})
-> 0.2650s
== AddPrice: migrated (0.2650s) ===============================================

一切Okey,第二个迭代也已经结束:)
第三个迭代:Validate!
编辑app/model/product.rb文件:

class Product < ActiveRecord::Base
  validates_presence_of :title, :description, :image_url
  validates_numericality_of :price
  validates_uniqueness_of :title
  validates_format_of :image_url, 
                      :with    => %r{\.(gif|jpg|png)$}i,
                      :message => "must be a URL for a GIF, JPG, or PNG image"

  protected
  def validate
    errors.add(:price, "should be at least 0.01") if price.nil? ||  price < 0.01
  end
end

第四个迭代:Prettier Listings
创建静态的scaffold如下:

D:\IDE\rails-exampe\depot>ruby script/generate scaffold product admin
      exists  app/controllers/
      exists  app/helpers/
      exists  app/views/admin
      exists  app/views/layouts/
      exists  test/functional/
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
        skip    app/models/product.rb
   identical    test/unit/product_test.rb
   identical    test/fixtures/products.yml
      create  app/views/admin/_form.rhtml
      create  app/views/admin/list.rhtml
      create  app/views/admin/show.rhtml
      create  app/views/admin/new.rhtml
      create  app/views/admin/edit.rhtml
overwrite app/controllers/admin_controller.rb? [Ynaqd] y
       force  app/controllers/admin_controller.rb
overwrite test/functional/admin_controller_test.rb? [Ynaqd] y
       force  test/functional/admin_controller_test.rb
   identical  app/helpers/admin_helper.rb
overwrite app/views/layouts/admin.rhtml? [Ynaqd] y
       force  app/views/layouts/admin.rhtml
      create  public/stylesheets/scaffold.css

定制list.rhtml:

<div id="product-list">
  <h1>Product Listing</h1>

  <table cellpadding="5" cellspacing="0">
  <% for product in @products %>
    <tr valign="top" class="<%= cycle('list-line-odd', 'list-line-even') %>">

      <td>
        <img class="list-image" src="<%= product.image_url %>"/>
      </td>

      <td width="60%">
        <span class="list-title"><%= h(product.title) %></span><br />
        <%= h(truncate(product.description, 80)) %>
      </td>

      <td class="list-actions">
        <%= link_to 'Show', :action => 'show', :id => product %><br/>
        <%= link_to 'Edit', :action => 'edit', :id => product %><br/>
        <%= link_to 'Destroy', { :action  => 'destroy', :id => product },
                                 :confirm => "Are you sure?",
                                 :method  => :post %>
      </td>
    </tr>
  <% end %>
  </table>
</div>

<%=  if @product_pages.current.previous 
       link_to("Previous page", { :page => @product_pages.current.previous })
     end
%>
<%= if @product_pages.current.next 
      link_to("Next page", { :page => @product_pages.current.next })
    end
%>

<br />

<%= link_to 'New product', :action => 'new' %>

增加测试数据:

D:\IDE\rails-exampe\depot>ruby script/generate migration add_test_data
      exists  db/migrate
      create  db/migrate/003_add_test_data.rb

编辑db/migrate/003_add_test_data.rb:

class AddTestData < ActiveRecord::Migration
  def self.up
    Product.delete_all
    Product.create(:title => 'Pragmatic Version Control',
      :description =>
      %{<p>
        This book is a recipe-based approach to using Subversion that will
        get you up and running quickly--and correctly. All projects need
    version control: it's a foundational piece of any project's
    infrastructure. Yet half of all project teams in the U.S. don't use
    any version control at all. Many others don't use it well, and end
    up experiencing time-consuming problems.
      </p>},
    :image_url => '/images/svn.jpg',
    :price => 28.50)
  end

  def self.down
    Product.delete_all
  end
end

安装测试数据:

D:\IDE\rails-exampe\depot>rake db:migrate

输出:

D:0:Warning: require_gem is obsolete.  Use gem instead.
(in D:/IDE/rails-exampe/depot)
== AddTestData: migrating =====================================================
== AddTestData: migrated (0.2030s) ============================================

编辑Layouts:app/views/layouts/admin.rhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <title>Admin: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'scaffold', 'depot' %>
</head>
<body>
<p style="color: green"><%= flash[:notice] %></p>
<%= yield :layout %>
</body>
</html>

编辑:app/views/admin/list.rhtml

<div id="product-list">
  <h1>Product Listing</h1>

  <table cellpadding="5" cellspacing="0">
  <% for product in @products %>
    <tr valign="top" class="<%= cycle('list-line-odd', 'list-line-even') %>">

      <td>
        <img class="list-image" src="<%= product.image_url %>"/>
      </td>

      <td width="60%">
        <span class="list-title"><%= h(product.title) %></span><br />
        <%= h(truncate(product.description, 80)) %>
      </td>

      <td class="list-actions">
        <%= link_to 'Show', :action => 'show', :id => product %><br/>
        <%= link_to 'Edit', :action => 'edit', :id => product %><br/>
        <%= link_to 'Destroy', { :action  => 'destroy', :id => product },
                                 :confirm => "Are you sure?",
                                 :method  => :post %>
      </td>
    </tr>
  <% end %>
  </table>
</div>

<%=  if @product_pages.current.previous 
       link_to("Previous page", { :page => @product_pages.current.previous })
     end
%>
<%= if @product_pages.current.next 
      link_to("Next page", { :page => @product_pages.current.next })
    end
%>

<br />

<%= link_to 'New product', :action => 'new' %>

参考:
http://www.rubyonrails.org/down
书籍《Agile Web Development with Rails》2nd Edition

另,JSR 315: Java Servlet 3.0 Specification under review at JCP:http://jcp.org/en/jsr/detail?id=315可以看到Servlet 3.0的规范中要涉及到的一些东西。

Google Guice实战

Guice(pronounced “juice”)发布了1.0版本.
首先看看Guice的定位:an ultra-lightweight, next-generation dependency injection container for Java 5 and later.
这是一个基于Java 5及后续版本的轻量级依赖注入容器。Martin Fowler关于依赖注入的精彩讨论参见:http://www.martinfowler.com/articles/injection.html#InversionOfControl
在这篇讨论中涉及到几种注入方式:Constructor Injection、Setter Injection、Interface Injection;
实际应用开发中依赖的注入问题自始至终都伴随着我们,当我们走过了最简单直接的手动注入、运用模式进行注入,到依赖于框架,当前成熟的框架众多,例如:Spring IOC、PicoContainer、Hivemind等;那么Guice作为一个依赖注入的framework,又是怎么定位自己的呢?其special的地方又在哪儿呢?我们首先看一个非常简单的例子:
对于注入描述的类如下:

public class MyModule implements Module {
    public void configure(Binder binder) {
        binder.bind(Service.class).to(ServiceImpl.class).in(Scopes.SINGLETON);
    }
}

依赖注入发生的地方:

public class Client {

    private final Service service;

    @Inject
    public Client(Service service) {
        this.service = service;
    }

    public void go() {
        service.go();
    }
}

从这个例子我们可以清晰看出,Guice的依赖注入已经不同于以往的三种注入模式了,Okey,你已经看到了Guice充分使用了Java的新特性annotation来进行依赖注入的,已经完全跨越了Constructor Injection、Setter Injection、Interface Injection的概念了。这样,依赖注入只是需要一个或者多个annotation而已,一切搞定。
下一步我看看Guice这个Framework是要如何进行依赖注入的?
我们首先看看Guice的Architecture.

public class MyModule implements Module {
    public void configure(Binder binder) {
        // Bind Foo to FooImpl. Guice will create a new
        // instance of FooImpl for every injection.
        binder.bind(Foo.class).to(FooImpl.class);

        // Bind Bar to an instance of Bar.
        Bar bar = new Bar();
        binder.bind(Bar.class).toInstance(bar);
    }
}

该部分代码的sequence diagram如下:

运行时Guice的依赖注入的绑定器structure如下:

很明显的看到,一个注入的绑定关联主键Key、Scope、Provider;其中Key包含了依赖的Type和Annotation.
看到了Guice的简单绑定,一定会有一个疑问:就是一个类型的多个绑定怎么实现?Guice是运行Annotation binding来解决这个问题的。
例如绑定代码如下:

public class BindingModule implements Module {

    public void configure(Binder binder) {
        binder.bind(IService.class).annotatedWith(Blue.class).to(BlueService.class);

    }

}
注入代码如下:

public class BindingClient {
    // @Inject
    // @Blue
    // private IService service;

    private IService service;

    @Inject
    public void injectService(@Blue IService service) {
        this.service = service;
    }

    public void go() {
        this.service.go();
    }
}
annotation部分代码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Blue {

}

如果再复杂一些,Guice还支持有属性的annotation:
绑定代码

public class NamedModule implements Module {

/*
* (non-Javadoc)
* 
* @see com.google.inject.Module#configure(com.google.inject.Binder)
*/
public void configure(Binder binder) {
    binder.bind(IPerson.class).annotatedWith(new NamedAnnotation("Bob")).to(Bob.class);
}

}
注入代码:
public class NamedClient {

    @Inject
    @Named("Bob")
    private IPerson person;

    public void say(){
        this.person.say();
    }
}
annotation代码:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Named {
    String value();
}

public class NamedAnnotation implements Named {

    private String value;

    public NamedAnnotation(String value){
        this.value = value;
    }
    /*
    * (non-Javadoc)
    * 
    * @see com.neusoft.anno.Named#value()
    */
    public String value() {
        return value;
    }

    public int hashCode() {
        return 127 * "value".hashCode() ^ value.hashCode();
    }

    public boolean equals(Object o) {
    if (!(o instanceof Named))
        return false;
        Named other = (Named) o;
        return value.equals(other.value());
    }

    public String toString() {
        return "@" + Named.class.getName() + "(value=" + value + ")";
    }

    /*
    * (non-Javadoc)
    * 
    * @see java.lang.annotation.Annotation#annotationType()
    */
    public Class annotationType() {
        return Named.class;
    }

}

通过这几个例子,可以清晰看到,Guice应用annotation进行注入的关联的,用module将依赖关系组装起来,提供给客户端代码进行使用;而且annotation是可以自由定制的,支持灵活扩展的。

Guice本身也支持实现类的直接绑定:

public class MixerModule implements Module {

    /*
    * (non-Javadoc)
    * 
    * @see com.google.inject.Module#configure(com.google.inject.Binder)
    */
    public void configure(Binder binder) {
        binder.bind(Concrete.class);

    }

}

Guice有一种隐式绑定,其方法是这样,针对一个接口,如果缺少显式绑定,Guice 会寻找一个指向具体实现的@ImplementedBy标注。

@ImplementedBy(GoodNightImpl.class)
    public interface IGoodNight {

    void goodNight();
    }

    @Singleton
    public class GoodNightImpl implements IGoodNight {

    /*
    * (non-Javadoc)
    * 
    * @see com.neusoft.nobinding.IGoodNight#goodNight()
    */
    public void goodNight() {
        System.out.println("Good night!");
    }

}

那么,Provider是怎么回事呢?“有时对于每次注入,客户代码需要某个依赖的多个实例。其它时候,客户可能不想在一开始就真地获取对象,而是等到注入后的某个时候再获取。对于任意绑定类型 T,你可以不直接注入 T 的实例,而是注入一个 Provider,然后在需要的时候调用 Provider.get()”因此,可以使用Provider去真正的来创建依赖的对象实例,直到需要的时候才创建客户代码需要的实例。

Provider等绑定代码:

public class WidgetModule implements Module {

    /*
    * (non-Javadoc)
    * 
    * @see com.google.inject.Module#configure(com.google.inject.Binder)
    */
    public void configure(Binder binder) {
        binder.bind(IService.class).to(WidgetService.class).in(Scopes.SINGLETON);
        binder.bind(Widget.class).toProvider(WidgetProvider.class);
    }

}
Provider的注入:

public class Widget {
    private IService service;

    @Inject Provider provider;

    public Widget(IService service){
        this.service = service;
    }

    public void go(){
        this.service.go();
    }
}
Provider的实现:

public class WidgetProvider implements Provider {

    final IService service;

    @Inject
    WidgetProvider(IService service) {
        this.service = service;
    }

    public Widget get() {
        return new Widget(service);
    }

}

Service的接口和实现:

public interface IService {

    void go();

}
public class WidgetService implements IService {

    /* (non-Javadoc)
    * @see com.neusoft.service.IService#go()
    */
    public void go() {
        System.out.println("Widget Service Hello.");
    }

}

客户代码:

public class WidgetApplication {

    /**
    * @param args
    */
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new WidgetModule());
        Widget widget = injector.getInstance(Widget.class);
        widget.go();
    }

}

这个例子完全表达出了Provider的价值所在。
另外,Guice也提供了Constant Values以及Converting Stings的支持:
Guice会针对Primitive types、primitive wrapper types、Strings、Enums、Classes类型进行类型的自动判断绑定机制。对于Primitive types、primitive wrapper types,如果 Guice 仍然无法找到一个的显式绑定,它会去找一个拥有相同绑定标注的常量 String 绑定,并试图将字符串转换到相应的值。
这就是一个Guice如何进行依赖注入的一个基本的思路,可以看到Guice的代码量十分少,是一个不错的依赖注入的方案,也将会推动依赖注入的进步。
参考:
Guice项目:http://code.google.com/p/google-guice/
Guice中文文档:http://docs.google.com/View?docid=dqp53rg_3hjf3ch

走向Miami(原名:重返Miami)

为什么重返Miami,在自己内心深处,不知道什么是出路的时候,这不是一种逃避,是一种面对,但不是“重新”,寻找到内心很多寂寥,若有若无的种种境地。
在飞机在北太平洋上空,在强气流,在阳光所洒在的云层中穿梭的时候,我多么强烈的意识到一个人是必须要面对人的一生必须要面对的许许多多事情的。如同人最后一定要离开一个世界一样,无所谓失败,无所谓失去,无所谓面对。我想我是太执著于在自己过去的阴影之下,太过于执著于一种心情。
“许多人生活在‘其实我很希望。。。只是很可惜。。。’的模式中,找借口完全接触不到你的生命,使你不能经历成长的喜悦。只有真刀真枪地面对自己,才了解生命和幸福的深度。”当我看到一个朋友的签名的时候,不禁也想起另外一个朋友的话,“如果你要找借口,一百个你都能找到。”
(翻开我的blog和自己的心情,就如同一个朋友曾经一语道破为如同“失恋”的心态。悲观的心态可能有积极的人生吗?)“先知,先觉,先行”,王阳明先生“知行合一”。无论多少心情,多少道理,都不如行动来的猛烈。
有梦想,有目标,究竟每一天能有多少东西能够沉淀下来?为了梦想,为了目标。““无产阶级失去的只是枷锁”,如果人的一生有枷锁,就是一个人的心;最难改变的是性格中的不肯改变。准备好了吗,改变一切不肯改变的?
重返Miami,在于能够释放自己,释放自己平和稳定的心境,放下、放松;真刀真枪的面对自己;改变不肯改变的自己;在自己的内心寻找到自己。

Java 5.0的Instrumentation特性

Java 5发布有一段时间了,Instrumentation这个feature是Java 5新提供的。其方式是通过修改字节码的方式使得Java开发人员能够操作类。官方文档说主要是给工具提供修改应用的状态、行为使用的:)
先来个简单的例子看看到底什么是Instrumentation:
在JSE 1.5.0的Javadoc的看到java.lang.instrument仅有两个接口ClassFileTransformer和Instrumentation。我们就看看着两个接口的用法:

public class Greeting implements ClassFileTransformer {

    //字节码转换在这个方法中进行。
    public byte[] transform(ClassLoader arg0, String classname, Class arg2, ProtectionDomain arg3, byte[] arg4)
    throws IllegalClassFormatException {
        System.out.printf("hello:" + classname);
        return new byte[]{};
    }

    //options是通过命令行传递给虚拟机的参数。
    public static void premain(String options, Instrumentation ins) {
        if (options != null) {
            System.out.printf(" I've been called with options: \"%s\"\n", options);
        } else
            System.out.println(" I've been called with no options.");
            ins.addTransformer(new Greeting());
        }

    }

    public class Sample {

    /**
    * @param args
    */
    public static void main(String[] args) {
        (new Sample()).hello();

    }

    public void hello() {
        for (int i = 0; i < 10000; i++) {
        int index =0;
        index++;
        }
    }

}

使用命令行参数的命令行:

java -javaagent:Greeting.jar="Hello, Sample" Sample

因此下一步是需要打个jar包,jar包中包含META-INF/MANIFEST.MF和Class文件。
其中META-INF/MANIFEST.MF的内容如下:
Manifest-Version: 1.0
Premain-Class: Timing

包含的类文件有:Greeting.class和Sample.class
打包:

jar cvfM greeting.jar *

输出:
adding: Greeting.class(in = 1774) (out= 869)(deflated 51%)
adding: META-INF/(in = 0) (out= 0)(stored 0%)
adding: META-INF/MANIFEST.MF(in = 44) (out= 46)(deflated -4%)
adding: Sample.class(in = 556) (out= 371)(deflated 33%)

运行命令行:

java -javaagent:greeting.jar="Hello,Sample" Greeting

控制台输出:
I’ve been called with options: “Hello,Sample”

运行命令行:

java -javaagent:greeting.jar="Hello,Sample" Sample

控制台输出:
I’ve been called with options: “Hello,Sample”
hello:Sample

通过这个例子估计Instrutment API使用的方法已经基本上有了个理解了。
下面就是举一个用apache bcel构造bytecode的Instrutment的实际的例子:

使用 instrumentation ,使用Apache 开源项目 BCEL修改bytecode,实现用于计算一个方法运行时间的功能。这种方式,用于性能测量的语句与业务逻辑完全分离,同时也可以用于测量任意类的任意方法的 运行时间,提高了代码的重用性。

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;

public class Timing implements ClassFileTransformer {

    private String methodName;

    private Timing(String methodName) {
        this.methodName = methodName;
        System.out.println(methodName);
    }

    public byte[] transform(ClassLoader loader, String className, Class cBR, java.security.ProtectionDomain pD, byte[] classfileBuffer) throws IllegalClassFormatException {
        try {
            ClassParser cp = new ClassParser(new java.io.ByteArrayInputStream(classfileBuffer), className + ".java");
            JavaClass jclas = cp.parse();
            ClassGen cgen = new ClassGen(jclas);
            Method[] methods = jclas.getMethods();
            int index;
            for (index = 0; index < methods.length; index++) {
                if (methods[index].getName().equals(methodName)) {
                    break;
                }
            }

            if (index < methods.length) {
                addTimer(cgen, methods[index]);
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                cgen.getJavaClass().dump(bos);
                return bos.toByteArray();
            }
            System.err.println("Method " + methodName + " not found in " + className);
            System.exit(0);

        } catch (IOException e) {
            System.err.println(e);
            System.exit(0);
        }
        return null; // No transformation required
    }

    private static void addTimer(ClassGen cgen, Method method) {

        // set up the construction tools
        InstructionFactory ifact = new InstructionFactory(cgen);
        InstructionList ilist = new InstructionList();
        ConstantPoolGen pgen = cgen.getConstantPool();
        String cname = cgen.getClassName();
        MethodGen wrapgen = new MethodGen(method, cname, pgen);
        wrapgen.setInstructionList(ilist);

        // rename a copy of the original method
        MethodGen methgen = new MethodGen(method, cname, pgen);
        cgen.removeMethod(method);
        String iname = methgen.getName() + "_timing";
        methgen.setName(iname);
        cgen.addMethod(methgen.getMethod());
        Type result = methgen.getReturnType();

        // compute the size of the calling parameters
        Type[] parameters = methgen.getArgumentTypes();
        int stackIndex = methgen.isStatic() ? 0 : 1;
        for (int i = 0; i < parameters.length; i++) {
            stackIndex += parameters[i].getSize();
        }

        // save time prior to invocation
        ilist.append(ifact.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC));
        ilist.append(InstructionFactory.createStore(Type.LONG, stackIndex));

        // call the wrapped method
        int offset = 0;
        short invoke = Constants.INVOKESTATIC;
        if (!methgen.isStatic()) {
            ilist.append(InstructionFactory.createLoad(Type.OBJECT, 0));
            offset = 1;
            invoke = Constants.INVOKEVIRTUAL;
        }
        for (int i = 0; i < parameters.length; i++) {
            Type type = parameters[i];
            ilist.append(InstructionFactory.createLoad(type, offset));
            offset += type.getSize();
        }
        ilist.append(ifact.createInvoke(cname, iname, result, parameters, invoke));

        // store result for return later
        if (result != Type.VOID) {
            ilist .append(InstructionFactory.createStore(result, stackIndex + 2));
        }

        // print time required for method call
        ilist.append(ifact.createFieldAccess("java.lang.System", "out", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC));
        ilist.append(InstructionConstants.DUP);
        ilist.append(InstructionConstants.DUP);
        String text = "Call to method " + methgen.getName() + " took ";
        ilist.append(new PUSH(pgen, text));
        ilist.append(ifact.createInvoke("java.io.PrintStream", "print", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
        ilist.append(ifact.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC));
        ilist.append(InstructionFactory.createLoad(Type.LONG, stackIndex));
        ilist.append(InstructionConstants.LSUB);
        ilist.append(ifact.createInvoke("java.io.PrintStream", "print",
        Type.VOID, new Type[] { Type.LONG }, Constants.INVOKEVIRTUAL));
        ilist.append(new PUSH(pgen, " ms."));
        ilist.append(ifact.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));

        // return result from wrapped method call
        if (result != Type.VOID) {
            ilist.append(InstructionFactory.createLoad(result, stackIndex + 2));
        }
        ilist.append(InstructionFactory.createReturn(result));

        // finalize the constructed method
        wrapgen.stripAttributes(true);
        wrapgen.setMaxStack();
        wrapgen.setMaxLocals();
        cgen.addMethod(wrapgen.getMethod());
        ilist.dispose();
    }

    public static void premain(String options, Instrumentation ins) {
        if (options != null) {
            ins.addTransformer(new Timing(options));
        } else {
            System.out.println("Usage: java -javaagent:Timing.jar=\"class:method\"");
            System.exit(0);
        }

    }
}

打jar包:

$ jar cvfM timing.jar *

输出:
adding: META-INF/(in = 0) (out= 0)(stored 0%)
adding: META-INF/MANIFEST.MF(in = 44) (out= 46)(deflated -4%)
adding: Sample.class(in = 556) (out= 371)(deflated 33%)
adding: Timing.class(in = 7372) (out= 3442)(deflated 53%)

运行命令行:

$ java -classpath bcel-5.2.jar -javaagent:timing.jar="hello" Sample

输出:
hello
Call to method hello_timing took 2047 ms.

运行命令:

$ java -classpath bcel-5.2.jar -javaagent:timing.jar="main" Sample

输出:
main
Call to method main_timing took 2469 ms.

通过这段代码,基本能够了解Instrument的用处之一了:)

参考:http://www.ibm.com/developerworks/cn/java/j-lo-instrumentation/