dubbo-http-thrift随笔

前几天应两个哥们的需求,需要一个http协议的thrift服务,具体的需求2点:
1.请求方式是http,然后采用thrift序列化。
2.能够传递隐藏参数在http的header里。

这个周末,2个下午,在公司整出了一个beta版本,具体还有很多地方需要优化,下面会列出具体的优化点。代码地址:https://github.com/yankai913/dubbo-http-thrift,感兴趣的哥们可以去看下。

先说说thrift,如果Getting Started,直接官网http://thrift.apache.org/即可,这里说一下目前最新的版本是0.9.1的新特性,地址:https://issues.apache.org/jira/browse/THRIFT-563,简单点说就是,以前的版本一个端口只能暴漏一个服务,原因取决于TMessage的只传了methodName,而最新的版本methodName里包含了serviceName和methodName,这样,就算方法名相同,但是还有serviceName可以区分,这样一个端口就可以暴露多个服务了。

再说说dubbo,如果Getting Started,直接官方https://github.com/alibaba/dubbo,这里说下关于http的,dubbo暴露http服务时,httpServer有2种,一种是内嵌jetty,在jetty里开个用来处理请求的线程池,这种是默认的,另一种是servlet,配置里是server=servlet,但是它依赖于servlet容器启动来暴露服务,例如tomcat,处理线程交给tomcat管理,这种是推荐的。使用http时,采用了2种序列化,hessian和java自身的序列化,这里加了一种thrift的序列化。

接着说说优化点:
1.代码里写着需要优化或者TODO的地方都要修复。
2.注意类的加载避免内存溢出,减少字节数组的复制。
3.thrift里的序列化方式,代码里hardCode了是二进制,其他的方式应该还有json什么的,需要做个开关,自由选择。
4.序列化和反序列化的优化。
5.暴漏扩展handler。
6.异常容错的处理和response响应里的异常处理。

发表在 编程语言 | 标签为 , , , | Comments Off on dubbo-http-thrift随笔

hbase学习(一)hbase单机部署和java客户端连接单机hbase

最近看了些hbase的东西,打算写点什么,谁知鼓捣的过程中步步是坑,最终呕心沥血,憋出了这篇文章,实属不易。

hbase和hive总是成对出现的,简单说,hbase是数据库,hive是mapReduce作业。

先从hbase单机部署说起,尽量说的简洁点,说多了都是眼泪。

1.给服务器起个机器名,iptables关掉,同时本地绑定host。

起个机器名

vi /etc/hosts

127.0.0.1 localhost
211.155.225.210 love-kaige

vi /etc/sysconfig/network

NETWORKING=yes
HOSTNAME=love-kaige

关闭iptables

service iptables stop
chkconfig iptables off

本地绑定host
C:\Windows\System32\drivers\etc\hosts
211.155.225.210 love-kaige

然后reboot重启机子,执行
hostname显示love-kaige,service iptables status 显示 iptables: Firewall is not running. 即OK。

2.下载安装jdk和hbase。

jdk应该关系不大,我安装的是jdk-7u51-linux-x64.rpm,环境变量配好即可,此处略过。

hbase下载的是稳定版,地址:http://mirrors.aliyun.com/apache/hbase/stable/hbase-0.94.18.tar.gz。阿里云对apache下的项目和linux不同的发行版都做了镜像,方便了广大的码农,给个赞。

解压hbase,然后对hbase-site.xml进行修改,修改如下:

<configuration>
        <property>
                <name>hbase.rootdir</name>
                <value>file:/root/hbase</value>
        </property>
</configuration>

然后去hbase的bin目录,./start-hbase.sh起起来。

3.编写java代码。

添加依赖:

 <dependency>
	   <groupId> org.apache.hadoop</groupId >
	   <artifactId> hadoop-core </artifactId>
	   <version> 1.0.4</version >
 </dependency>
 <dependency>
	   <groupId> org.apache.hbase</groupId >
	   <artifactId> hbase</artifactId >
	   <version> 0.94.18</version >
 </dependency>

服务端和客户端的版本最好一致,现在都是0.94.18,免得出现莫名奇妙的问题。hadoop的版本和hbase的版本也有对应关系,把官网的hbase和hadoop的版本匹配表搬过来,

Table 2.1. Hadoop version support matrix

HBase-0.92.x HBase-0.94.x HBase-0.96
Hadoop-0.20.205 S X X
Hadoop-0.22.x S X X
Hadoop-1.0.x S S S
Hadoop-1.1.x NT S S
Hadoop-0.23.x X S NT
Hadoop-2.x X S S

S = supported and tested,支持
X = not supported,不支持
NT = not tested enough.可以运行但测试不充分

由于 HBase 依赖 Hadoop,它配套发布了一个Hadoop jar 文件在它的 lib 下。该套装jar仅用于独立模式。在分布式模式下,Hadoop版本必须和HBase下的版本一致。用你运行的分布式Hadoop版本jar文件替换HBase lib目录下的Hadoop jar文件,以避免版本不匹配问题。确认替换了集群中所有HBase下的jar文件。Hadoop版本不匹配问题有不同表现,但看起来都像挂掉了。

贴代码


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;


/**
 * 
 * @author yankai913@gmail.com
 * @date 2014-4-28
 */
public class SimpleClient {

    static final String rowKey = "row1";
    static HBaseAdmin hBaseAdmin;
    static Configuration conf;

    static {
        conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum", "love-kaige");
        try {
            hBaseAdmin = new HBaseAdmin(conf);
        }
        catch (MasterNotRunningException e) {
            e.printStackTrace();
        }
        catch (ZooKeeperConnectionException e) {
            e.printStackTrace();
        }
    }


    public static void createTable(String tableName, String[] columns) throws Exception {
        dropTable(tableName);
        HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
        for (String columnName : columns) {
            HColumnDescriptor column = new HColumnDescriptor(columnName);
            hTableDescriptor.addFamily(column);
        }
        hBaseAdmin.createTable(hTableDescriptor);
        System.out.println("create table successed");
    }


    public static void dropTable(String tableName) throws Exception {
        if (hBaseAdmin.tableExists(tableName)) {
            hBaseAdmin.disableTable(tableName);
            hBaseAdmin.deleteTable(tableName);
        }
        System.out.println("drop table successed");
    }


    public static HTable getHTable(String tableName) throws Exception {
        return new HTable(conf, tableName);
    }


    public static void insert(String tableName, Map<String, String> map) throws Exception {
        HTable hTable = getHTable(tableName);
        byte[] row1 = Bytes.toBytes(rowKey);
        Put p1 = new Put(row1);
        for (String columnName : map.keySet()) {
            byte[] value = Bytes.toBytes(map.get(columnName));
            String[] str = columnName.split(":");
            byte[] family = Bytes.toBytes(str[0]);
            byte[] qualifier = null;
            if (str.length > 1) {
                qualifier = Bytes.toBytes(str[1]);
            }
            p1.add(family, qualifier, value);
        }
        hTable.put(p1);
        Get g1 = new Get(row1);
        Result result = hTable.get(g1);
        System.out.println("Get: " + result);
        System.out.println("insert successed");
    }


    public static void delete(String tableName, String rowKey) throws Exception {
        HTable hTable = getHTable(tableName);
        List<Delete> list = new ArrayList<Delete>();
        Delete d1 = new Delete(Bytes.toBytes(rowKey));
        list.add(d1);
        hTable.delete(list);
        Get g1 = new Get(Bytes.toBytes(rowKey));
        Result result = hTable.get(g1);
        System.out.println("Get: " + result);
        System.out.println("delete successed");
    }


    public static void selectOne(String tableName, String rowKey) throws Exception {
        HTable hTable = getHTable(tableName);
        Get g1 = new Get(Bytes.toBytes(rowKey));
        Result result = hTable.get(g1);
        foreach(result);
        System.out.println("selectOne end");
    }


    private static void foreach(Result result) throws Exception {
        for (KeyValue keyValue : result.raw()) {
            StringBuilder sb = new StringBuilder();
            sb.append(Bytes.toString(keyValue.getRow())).append("\t");
            sb.append(Bytes.toString(keyValue.getFamily())).append("\t");
            sb.append(Bytes.toString(keyValue.getQualifier())).append("\t");
            sb.append(keyValue.getTimestamp()).append("\t");
            sb.append(Bytes.toString(keyValue.getValue())).append("\t");
            System.out.println(sb.toString());
        }
    }


    public static void selectAll(String tableName) throws Exception {
        HTable hTable = getHTable(tableName);
        Scan scan = new Scan();
        ResultScanner resultScanner = null;
        try {
            resultScanner = hTable.getScanner(scan);
            for (Result result : resultScanner) {
                foreach(result);
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (resultScanner != null) {
                resultScanner.close();
            }
        }
        System.out.println("selectAll end");
    }


    public static void main(String[] args) throws Exception {
        String tableName = "tableTest";
        String[] columns = new String[] { "column_A", "column_B" };
        createTable(tableName, columns);
        Map<String, String> map = new HashMap<String, String>();
        map.put("column_A", "AAA");
        map.put("column_B:1", "b1");
        map.put("column_B:2", "b2");
        insert(tableName, map);
        selectOne(tableName, rowKey);
        selectAll(tableName);
        delete(tableName, rowKey);
        dropTable(tableName);
    }

练习代码看这里

发表在 数据库, 编程语言 | 标签为 , | Comments Off on hbase学习(一)hbase单机部署和java客户端连接单机hbase

记oracle里continue关键字的陷阱

这几天在写pl/sql,中间用到continue关键字,一不小心就掉进坑里。
oracle版本是11g,贴sql:

造数据:

create table address(
addr varchar2(10)
);

insert into address values('a');
insert into address values('b');
insert into address values('c');
insert into address values('d');
commit;

遍历:

declare 
begin
     for i in 1..5 loop
       dbms_output.put_line(i);
     end loop;
end ;

打印结果:
1
2
3
4
5

declare
begin
    for i in (select * from address)  loop
        dbms_output.put_line(i.addr);
    end loop;
end; 

打印结果:
a
b
c
d

使用continue:

declare 
begin
    for i in 1..5 loop
        if i = 3 then
            continue;
        end if ;
        dbms_output.put_line(i);
    end loop;
end ;

打印结果:
1
2
4
5

惯性:

declare
begin
  for i in (select * from address)  loop
    if i.addr = 'c'then
       continue;
    end if;
    dbms_output.put_line(i.addr);
  end loop;
end; 

打印结果:
a
b
这里像break。

正确解法:

declare
begin
  for i in (select * from address)  loop
    if i.addr = 'c'then
        goto next_tag;
    end if;
    dbms_output.put_line(i.addr);
    <<next_tag>> null;
  end loop;
end; 

打印结果:
a
b
d
打印出continue的效果,收工。

发表在 数据库 | Comments Off on 记oracle里continue关键字的陷阱

二月份都干了些什么

访问自己的博客突然发现,二月份一篇博客都没写,不禁用手捏了捏额头,二月份都干了些什么。
进入公司以来,做过2个小需求,其他的时间都是在熟悉项目,包括代码和业务。但是过程中,消息队列引起了我的兴趣,其中有一个叫做rocketmq。rpc做过,但是消息队列没接触过,然后看到一个web控制台项目还是空的,就fork了一下,用了3个星期的业余时间完善并提交了代码,现在已经合并到原始仓库,叫做rocketmq-console,感兴趣的哥们可以上去看下。

发表在 生活笔记 | 2条评论

回顾2013,展望2014

2013年,在这12个月里只工作了6个月,上半年找工作用了差不多2个月,中间9月离职,一直到2013年12月30号,也就是前天才入职新公司上班,花了接近4个月找工作。

总结今年一年,
不足:
1.没有写出闪亮的东西。
2.没有完整的看完一本书。
3.沉迷于游戏。
4.生活作息无规律。
5.身体锻炼的不勤快。
6.驾照考到一半。
7.Github上新建的项目都是烂尾楼。

完成项:
1.搭建了自己的博客。
2.博客坚持在更新。

2014年期望:
1.调整好心态,去除浮躁,沉下心来做技术(看书,写博,写代码)。ps:把Github上的烂尾楼完成。
2.健康生活,坚持锻炼。
3.快乐工作,知识充电。

这一切都仅仅只是开始。

发表在 生活笔记 | Comments Off on 回顾2013,展望2014

阿里巴巴面试经历后记

继上次去滨江的阿里巴巴园区面试之后,在接下来的一个星期一下午一点多,接到阿里hr的电话,说面试通过了,我当时听了过后是热泪夺眶,心情久久不能平静,我锁了门去小区外的广场散步,想着自己这近3个月来找工作的经历,这算是苦尽甘来么,我在觉得幸运的时候,心里也一直默念着,福兮祸之所倚,祸兮福之所伏。
接下来就是体检,隔了一天就是电子offer,然后准备材料入职。至此,我去阿里共上了2天班。
在此谢谢各位亲朋好友的关心和祝福,我会再接再厉。2014,走你!

发表在 生活笔记 | Comments Off on 阿里巴巴面试经历后记

阿里巴巴面试经历前记

昨天去了滨江的阿里巴巴面试,晚上回来后就准备把面试的经过记录一下的,接着后来和以前的朋友玩游戏就给耽搁了。今天下午起床吃过饭后,趁着还有些印象,记录一下。

其实,找工作和找媳妇儿都是一样的,都是看缘分的。我去阿里面试的心情是好奇大于希望的,我会尽力去面试,结果怎么样我不是太看重,如果人家要我了,我只能说喜出望外,如果人家不要我,那这也算是一种人生经历。

我去滨江总部面试之前,已经电话面试2次了,也不完全说是电话面试,第一次是相互的了解一下,第二次是技术面试,昨天下午又经历2轮面试,到目前应该算是4面了,大公司就是不一样啊,虽然到目前还不知道结果如何。

面试约的时间是2013年12月18号下午1点半,我提前了半个小时,到了之后我没有急着进阿里巴巴的园区,阿里巴巴是一家伟大的公司,也是我非常向往的公司,再加上我是第一次来,所以我就围着阿里巴巴的园区周围转了转,好好的看了下,到点了才进去。

来到园区门口的保安室,我说我是来面试的,给他们看了面试的短信,他们也打了电话确认后,就给了我一个贴纸,贴在身上,印着阿里logo的贴纸上面写着我的姓名和面试两个字,然后跟我说去6号楼一楼前台拿表,接着我就进入园区了。

来到6号楼进去后,我发现前台不是妹子,是个帅哥,很有礼貌,我拿到表后,就跟我说,去对面的七号楼填表,进门左转,填完了就在那里等,面试官会主动找你的。

我拿着表,进了七号楼,一进门就闻到了一股咖啡香,感觉像个星巴克,随便找个座位坐下开始填表,我发现也有几个人跟我一样在填表,我填完过后,就在那里刷微博,过了一会,我听见有人叫我的名字,我回头应了下,发现2个面试官,2个男的,都是搞技术的,其中一个人带着电脑,寒暄过后就在一楼找了个房间进去后就开始聊了起来。
继续阅读

发表在 生活笔记 | 8条评论

Guice系列之用户指南(十二)

原文地址:https://code.google.com/p/google-guice/wiki/InjectingProviders

InjectingProviders(注入的提供者):通过注入提供者来产生对象支持依赖注入。

和普通的依赖注入一样,每一种类型都能够确切地获取它所依赖的实例。RealBillingService包含CreditCardProcessor和TransactionLog,当灵活性是必须地时,Guice会绑定一个提供者,当调用get()方法时,提供者会产生一个对象的值。

public interface Provider<T> {
  T get();
}

这个提供者的类型是通过泛型参数来区分的,在Provider和Provider之间。只要是注入值的地方都可以通过注入提供者来代替。

public class RealBillingService implements BillingService {
  private final Provider<CreditCardProcessor> processorProvider;
  private final Provider<TransactionLog> transactionLogProvider;

  @Inject
  public RealBillingService(Provider<CreditCardProcessor> processorProvider,
      Provider<TransactionLog> transactionLogProvider) {
    this.processorProvider = processorProvider;
    this.transactionLogProvider = transactionLogProvider;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    CreditCardProcessor processor = processorProvider.get();
    TransactionLog transactionLog = transactionLogProvider.get();

    /* use the processor and transaction log here */
  }
}

对每一种绑定或者是注解,注入器都有内建绑定的相应的提供者。

Providers for multiple instances(不同实例的提供者):顾名思义。

在使用提供者的过程中,你可能需要一种类型的不同实例。设想当一个披萨支付失败时,你的应用程序正在保存一个概括条目和一个详细订单。用各种提供者,你就能随时获取一个新的条目:

public class LogFileTransactionLog implements TransactionLog {

  private final Provider<LogFileEntry> logFileProvider;

  @Inject
  public LogFileTransactionLog(Provider<LogFileEntry> logFileProvider) {
    this.logFileProvider = logFileProvider;
  }

  public void logChargeResult(ChargeResult result) {
    LogFileEntry summaryEntry = logFileProvider.get();
    summaryEntry.setText("Charge " + (result.wasSuccessful() ? "success" : "failure"));
    summaryEntry.save();

    if (!result.wasSuccessful()) {
      LogFileEntry detailEntry = logFileProvider.get();
      detailEntry.setText("Failure result: " + result);
      detailEntry.save();
    }
  }

Providers for lazy loading(延时加载的提供者):在提供者里做部分逻辑延迟产生大对象。
当你在获取关于一个类型的依赖,同时这个依赖的创建开销又特别的大时,你能够用提供者来推迟这项工作。当你不是非常需要这个依赖时,这是很有用的:

public class DatabaseTransactionLog implements TransactionLog {
  
  private final Provider<Connection> connectionProvider;

  @Inject
  public DatabaseTransactionLog(Provider<Connection> connectionProvider) {
    this.connectionProvider = connectionProvider;
  }

  public void logChargeResult(ChargeResult result) {
    /* only write failed charges to the database */
    if (!result.wasSuccessful()) {
      Connection connection = connectionProvider.get();
    }
  }

Providers for Mixing Scopes(混合作用域的提供者):提供者产生不同作用域的对象。
依赖一个很窄的作用域的对象是一个错误,设想你使用了单例的事务日志,但是这个事务日志对当前用户需要用的是请求作用域。如果你直接注入用户就会出错,因为用户是随着请求的改变而改变的。自从提供者能够按需的产生对象值,它们使你安全的混合多个作用域成为可能:

@Singleton
public class ConsoleTransactionLog implements TransactionLog {
  
  private final AtomicInteger failureCount = new AtomicInteger();
  private final Provider<User> userProvider;

  @Inject
  public ConsoleTransactionLog(Provider<User> userProvider) {
    this.userProvider = userProvider;
  }

  public void logConnectException(UnreachableException e) {
    failureCount.incrementAndGet();
    User user = userProvider.get();
    System.out.println("Connection failed for " + user + ": " + e.getMessage());
    System.out.println("Failure count: " + failureCount.incrementAndGet());
  }
发表在 编程语言 | 标签为 , | Comments Off on Guice系列之用户指南(十二)

Guice系列之用户指南(十一)

原文地址:https://code.google.com/p/google-guice/wiki/Injections

Injections(各种注入):常见的有构造函数注入,set方法注入,成员变量注入,前面几篇提到过,就说说没有提到的。

Optional Injections(可选择的注入):就是在注入时可以选择使用注入,通过@Inject(optional=true)实现。

public class PayPalCreditCardProcessor implements CreditCardProcessor {
  private static final String SANDBOX_API_KEY = "development-use-only";

  private String apiKey = SANDBOX_API_KEY;

  @Inject(optional=true)
  public void setApiKey(@Named("PayPal API key") String apiKey) {
    this.apiKey = apiKey;
  }

或者是在成员变量上:

@Inject(optional=true) Date launchDate;

On-demand Injection(按需注入):方法注入或者变量注入可能用在初始化一个已经存在的实例。你可以用Injector.injectMembers API:

  public static void main(String[] args) {
    Injector injector = Guice.createInjector(...);
    
    CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();
    injector.injectMembers(creditCardProcessor);

Static Injections(静态注入):不推荐,依赖关系不透明和受类的全局状态影响。

@Override public void configure() {
    requestStaticInjection(ProcessorFactory.class);
    ...
  }

class ProcessorFactory {
  @Inject static Provider<Processor> processorProvider;

  /**
   * @deprecated prefer to inject your processor instead.
   */
  @Deprecated
  public static Processor getInstance() {
    return processorProvider.get();
  }
}

Automatic Injection(自动注入):自动注入
Guice自动注入有如下情况:
在绑定代码中,实例被传递到toInstance()
在绑定代码中,provider实例被传递到toProvider()

发表在 编程语言 | 标签为 , | Comments Off on Guice系列之用户指南(十一)

Guice系列之用户指南(十)

原文地址:https://code.google.com/p/google-guice/wiki/Scopes

Scopes:作用域。

默认情况下,Guice每次在调用时都会返回一个新的实例,这种行为是可以通过作用域配置的,作用域允许复用对象实例。在一个应用服务的生命周期中,对象可能是单例的(@Singleton),也可能是一个回话的(@SessionScoped),也可能是一个请求的(@RequestScoped)。Guice在web应用中也包含一个servlet扩展的作用域。自定义作用域可以在不同类型的应用中使用。

Applying Scopes
作用域使用有不用的方式,例如注解,

@Singleton
public class InMemoryTransactionLog implements TransactionLog {
  /* everything here should be threadsafe! */
}

也可以配置在代码里,

  bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);

也可以注解在@Provides方法处,

@Provides @Singleton
  TransactionLog provideTransactionLog() {
    ...
  }

如果有些冲突的作用域同时在一个类型上或者代码里的bind()方法上配置,那么bind()的配置生效。如果一个类型你不想给它设置要作用域,那么就绑定Scopes.NO_SCOPE。

像链接绑定那样,作用域应用于绑定的父类型,而不是绑定目标实现类。设想我们有一个同时实现Bar接口和Grill接口的实现类Applebees,这就要求需要同时绑定的这两种类型,一种是类型Bar,另一种是Grill:

  bind(Bar.class).to(Applebees.class).in(Singleton.class);
  bind(Grill.class).to(Applebees.class).in(Singleton.class);

这是因为作用域应用于这个绑定的类型(Bar,Grill),而不是满足这个类型的实现类Applebees,为了允许只有一个实例,用一个注解@Singleton声明在父类型上,或者在代码里绑定。

 bind(Applebees.class).in(Singleton.class);

这种绑定使得以上的其他两种.in(Singleton.class)语句不必要。这种in()语句还接受像RequestScoped.class或者是ServletScopes.REQUEST的注解:

bind(UserPreferences.class)
      .toProvider(UserPreferencesProvider.class)
      .in(ServletScopes.REQUEST);

这种注解是推荐优先的,因为它允许这个模块在不同的类型应用中复用,打个比方,一个被@RequestScoped注解的对象即能够在web应用的http请求中被使用,也可以在一个API服务器的rpc中被使用。

Eager Singletons
Eager Singletons(各种饥渴的单例):不是延迟的,是饿汉式的单例。
Guice有特殊的语法定义把单例为饿汉式的:

bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();

Guice有特殊的语法定义把单例为饥渴的:

饿汉式的单例可以很快揭示初始化问题,并确保最终用户获得一致的,直观的体验。懒汉式的单例保证了一个快速的编辑-完成-启动的开发周期,用这个Stage的枚举可以区分那种策略被使用:

PRODUCTION DEVELOPMENT
.asEagerSingleton() eager eager
.in(Singleton.class) eager lazy
.in(Scopes.SINGLETON) eager lazy
@Singleton eager* lazy

Guice会在已知类型的情况下创建饿汉式单例,这些类型都是在自己的模块,并加上这些类型递归依赖提到的类型。
继续阅读

发表在 编程语言 | 标签为 , | Comments Off on Guice系列之用户指南(十)