netty BlockingOperationException

对netty线程模型了解不深,使用Future的sync()或await()方法不当会产生BlockingOperationException。stackoverflow上有个回答很好的解答了这个问题:stackoverflow

1
BlockingOperationException will be throw by netty if you call sync*or await* on a Future in the same thread that the EventExecutor is using and to which the Future is tied to. This is usually the EventLoop that is used by the Channel itself.

源码解析

Future代表一个异步任务的结果,netty对java.util.concurrent.Future进行了增强:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 扩展了JDK的Future接口
*/

public interface Future<V> extends java.util.concurrent.Future<V> {

//异步操作完成且正常终止
boolean isSuccess();

//异步操作是否可以取消
boolean isCancellable();

//异步操作失败的原因
Throwable cause();

//添加一个监听者,异步操作完成时回调,类比javascript的回调函数
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);

//添加多个监听者
Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);

//移除一个监听者
Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);

//移除一个监听者
Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);

//等待任务结束,如果任务产生异常或被中断则抛出异常,否则返回Future自身
Future<V> sync() throws InterruptedException;

//等待任务结束,任务本身不可中断,如果产生异常则抛出异常,否则返回Future自身
Future<V> syncUninterruptibly();

//等待任务结束,如果任务被中断则抛出中断异常,与sync不同的是只抛出中断异常,不抛出任务产生的异常
Future<V> await() throws InterruptedException;

//阻塞直到异步操作完成
Future<V> awaitUninterruptibly();

//同await,加了时间限制
boolean await(long timeout, TimeUnit unit) throws InterruptedException;

//同await,加了时间限制
boolean await(long timeoutMillis) throws InterruptedException;

//同awaitUninterruptibly,加了时间限制
boolean awaitUninterruptibly(long timeout, TimeUnit unit);

//同awaitUninterruptibly,加了时间限制
boolean awaitUninterruptibly(long timeoutMillis);

//非阻塞地返回异步结果,如果尚未完成返回null
V getNow();

//取消任务
@Override
boolean cancel(boolean mayInterruptIfRunning);
}

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
32
33
34
35
36
DefaultPromise.java

@Override
public Promise<V> await() throws InterruptedException {
//如果任务已经完成则直接返回
if (isDone()) {
return this;
}

if (Thread.interrupted()) {
throw new InterruptedException(toString());
}

//检查是否产生死锁
checkDeadLock();

synchronized (this) {
while (!isDone()) {
incWaiters();
try {
//等待被唤醒
wait();
} finally {
decWaiters();
}
}
}
return this;
}

protected void checkDeadLock() {
EventExecutor e = executor();
if (e != null && e.inEventLoop()) {
throw new BlockingOperationException(toString());
}
}

判断执行任务线程和current thread是否时同一个线程,如果是就检测为死锁抛出异常BlockingOperationException

01-guava Joiner和Splitter

Joiner

1
2
3
4
5
List<String> list = Lists.newArrayList("a", "b", "c", "d", "e");

String joinStr = Joiner.on("||").skipNulls().join(list);

//joinStr:a||b||c||d||e
1
2
3
4
5
6
7
Map<String, String> testMap = Maps.newLinkedHashMap();
testMap.put("广东", "粤语");
testMap.put("广西", "壮语");
testMap.put("北京", "普通话");
testMap.put("四川", "四川话");
String returnStr = Joiner.on("#").withKeyValueSeparator("=").join(testMap);
//returnStr:广东=粤语#广西=壮语#北京=普通话#四川=四川话

Splitter

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
Splitter splitter = Splitter.on('|');
Iterable<String> parts = splitter.split("1 |2|3|||");
parts.forEach(d -> System.out.println(d + "=="));

//Iterable<String> parts = splitter.split("1 |2|3|||");
1 ==
2==
3==
==
==
==

//Iterable<String> parts = splitter.trimResults().split("1 |2|3|||");
1==
2==
3==
==
==
==

//Iterable<String> parts = splitter.omitEmptyStrings().split("1 |2|3|||");
1 ==
2==
3==

//Iterable<String> parts = splitter.omitEmptyStrings().trimResults().split("1 |2|3|||");
1==
2==
3==
1
2
3
4
5
6
7
8
9
10
11
12
String startString = "广东=粤语#广西=壮语#北京=普通话#四川=四川话";
Map<String,String> testMap = Maps.newLinkedHashMap();
testMap.put("广东","粤语");
testMap.put("广西","壮语");
testMap.put("北京","普通话");
testMap.put("四川","四川话");
Splitter.MapSplitter mapSplitter =
Splitter.on("#").withKeyValueSeparator("=");
Map<String,String> splitMap =
mapSplitter.split(startString);
System.out.println("splitMap:" + splitMap);
assertThat(testMap, is(splitMap));

来源:Getting Started with Google Guava

03-Matchers

Matchers是JUnit框架的外部补充。Matchers在Junit内部被称为Hamcrest框架。JUnit 4.8.2内置Hamcrest,所以你不必下载它,并自己添加。

Matcher与org.junit.Assert.assertThat()方法一起使用,如下所示:

1
2
3
public void assertThat(Object o, Matcher matcher){
...
}

您可以实现自己的Matcher,但JUnit(Hamcrest)附带了一些可以使用的内置匹配器。 这里有几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.junit.Test;

public class MyMatcherTest {

@Test
public void testWithMatchers() {
assertThat("this string", is("this string"));
assertThat(123, is(123));
}
}

assertThat()方法接受一个对象和一个Matcher实现。Matcher确定测试是否通过或失败。assertThat()方法只是关注“plumming” - 意思是使用给定的对象来调用Matcher。JUnit(Hamcrest)附带了可以使用的内置Matcher的集合。在上面的示例中,org.hamcrest.CoreMatchers.is()方法用于创建Matcher。如果比较的两个值相等,则返回的匹配器is()返回true,否则返回false。

如果Matcher返回false,则assertThat方法会抛出异常。如果Matcher返回true,则assertThat()方法返回正常。Matcher如何返回true或false,我将在本文后面回到。 现在只是想象它可以。

Chaining Matchers

您可以链接一些Matcher进行使用,例如:

1
2
3
4
5
6
@Test
public void testWithMatchers() {

assertThat(123, not( is(345) ) );

}

注意链接的呼叫不是(is(345))。 这真的是两个匹配的组合。 is()方法返回一个Matcher,而not()方法返回另一个Matchers。 由not()返回的Matcher将否定作为输入给出的Matcher的Matcher输出。 在这种情况下,它是由is()方法返回的匹配器的输出,被否定。

Core Matchers

在开始实现自己的Matcher之前,您应该看看JUnit附带的核心Matcher。 这是一个Matcher方法的列表:

Core
    any()     匹配一切
    is()     检查给定对象是否相等的Matcher 
    describedAs()     向Matcher添加说明

Logical
    allOf() 获取一组Matcher,并且所有Matcher必须与目标对象匹配
    anyOf()     获取一组Matcher,并且至少有一个Matcher必须报告它与目标对象匹配。
    not()     取消先前匹配器的输出
 Object
    equalTo() 检查给定对象是否相等的Matcher
    instanceOf() 检查给定的对象是X类型还是与X类型兼容

    notNullValue() + nullValue() 测试给定对象是否不是null,是否是null

静态方法matches()创建一个新的Matcher并返回它。

该Matcher是BaseMatcher类的匿名子类。JUnit文档指出,您应该始终扩展BasreMatcher,而不是自己实现Matcher。因此,如果将来将新方法添加到Matcher中,BaseMatcher可以实现它们。你的子类也会自动得到这些方法。这将避免破坏您的代码。

下面是如何使用这个自定义Matcher:

1
2
3
4
5
6
7
@Test
public void testThat() {
MyUnit myUnit = new MyUnit();

assertThat(myUnit.getTheSameObject(), matches("constant string"));

}

02-Assert Methods

正如您从上一篇文章看到的,实现JUnit单元测试的大部分秘密是使用org.junit.Assert类中的assert方法。在本文中,我将详细介绍该类中可用的assert方法。

以下是断言方法的列表:

  • assertArrayEquals()
  • assertEquals()
  • assertTrue() + assertFalse()
  • assertNull() + assertNotNull()
  • assertSame() and assertNotSame()
  • assertThat()

在本文的其余部分中,我将解释这些assert方法的工作原理,并向您展示如何使用它们的示例。这些例子将测试一个名为MyUnit的类。这个类的代码没有显示出来,但是你不需要代码来了解如何测试它。

assertArrayEquals()

assertArrayEquals()方法将测试两个数组是否相等。换句话说,如果两个数组包含相同数量的元素,并且数组中的所有元素都相等。要检查元素相等性,使用其equals()方法比较数组中的元素。更具体地说,使用它们的equals()方法逐个比较每个数组的元素。这意味着,这两个数组包含相同的元素是不够的。他们也必须按照同样的顺序出现。

下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.junit.Test;
import static org.junit.Assert.*;

public class MyUnitTest {

@Test
public void testGetTheStringArray() {
MyUnit myUnit = new MyUnit();

String[] expectedArray = {"one", "two", "three"};

String[] resultArray = myUnit.getTheStringArray();

assertArrayEquals(expectedArray, resultArray);
}
}

首先创建预期的数组。第二步myUnit.getTheStringArray()方法被调用,这是我们要测试的方法。第三步,将myUnit.getTheStringArray()方法调用的结果与预期的数组进行比较。

如果数组相等,则assertArrayEquals()将继续执行而不会出现错误。如果数组不相等,将抛出异常,并且测试中止。assertArrayEquals()之后的任何测试代码都不会被执行。

assertEquals()

assertEquals()方法使用equals()方法比较两个对象的相等性。下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.junit.Test;
import static org.junit.Assert.*;

public class MyUnitTest {

@Test
public void testConcatenate() {
MyUnit myUnit = new MyUnit();

String result = myUnit.concatenate("one", "two");

assertEquals("onetwo", result);
}
}

首先调用myUnit.concatenate()方法,结果存储在变量result中。其次,使用assertEquals()方法将结果值与期望值“onetwo”进行比较。如果两个对象根据其equals()方法的实现相等,则assertEquals()方法将正常返回。否则,assertEquals()方法将抛出异常,并且测试将在那里停止。

此示例与String对象进行了比较,但assertEquals()方法可以将任何两个对象进行比较。 assertEquals()方法也可以使用像int和float这样的基本类型彼此进行比较。

assertTrue() + assertFalse()

assertTrue()和assertFalse()方法测试单个变量以查看其值是true还是false。 下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.junit.Test;
import static org.junit.Assert.*;

public class MyUnitTest {

@Test
public void testGetTheBoolean() {
MyUnit myUnit = new MyUnit();

assertTrue (myUnit.getTheBoolean());

assertFalse(myUnit.getTheBoolean());
}
}

如果getTheBoolean()方法返回true,则assertTrue()方法将正常返回。 否则会抛出异常,测试将停止。

如果getTheBoolean()方法返回false,则assertFalse()方法将正常返回。 否则会抛出异常,测试将停止。

当然,如果getTheBoolean()方法在两个调用中都返回相同的值,则上述测试将在assertTrue()或assertFalse()调用中失败。

assertNull() + assertNotNull()

assertNull()和assertNotNull()方法测试单个变量以查看它是否为null或不为null。下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.junit.Test;
import static org.junit.Assert.*;

public class MyUnitTest {

@Test
public void testGetTheObject() {
MyUnit myUnit = new MyUnit();

assertNull(myUnit.getTheObject());

assertNotNull(myUnit.getTheObject());
}
}

如果myUnit.getTheObject()返回null,则assertNull()方法将正常返回。 如果返回非空值,则assertNull()方法将抛出异常,并在此处中止测试。

assertNotNull()方法与assertNull()方法相反,如果将null值传递给它,则抛出异常,如果非空值传递给它,则返回正常。

assertSame() and assertNotSame()

assertSame()和assertNotSame()方法测试两个对象引用是否指向相同的对象。指向的两个对象根据其equals()方法是相等的还不够。它必须是完全相同的对象指向。下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.junit.Test;
import static org.junit.Assert.*;

public class MyUnitTest {

@Test
public void testGetTheSameObject() {
MyUnit myUnit = new MyUnit();

assertSame (myUnit.getTheSameObject(),
myUnit.getTheSameObject());

assertNotSame(myUnit.getTheSameObject(),
myUnit.getTheSameObject());
}
}

如果两个引用指向同一个对象,则assertSame()方法将正常返回。 否则将抛出异常,测试将在此停止。

assertNotSame()方法与assertSame()方法相反。 如果两个对象没有指向同一个对象,则assertNotSame()方法将正常返回。 其他人抛出异常,测试停在这里。

assertThat()

assertThat()方法将对象与org.hamcrest.Matcher进行比较,以查看给定对象是否匹配Matcher要求匹配的任何对象。

Matcher需要更长的时间来解释,我们将在下一篇文章中对其进行解释。

01-A Simple Unit Test

在本文中,我将向您展示使用JUnit 4.8.1实现的简单单元测试。

下面是要测试的类的代码:

1
2
3
4
5
6
    public class MyUnit {

public String concatenate(String one, String two){
return one + two;
}
}

我会保持这个类使其简单,让我们更容易理解发生了什么。

单元测试

要测试这个类,我需要一个单元测试来测试每个公共方法。该类只有一个公共方法,concatenate(),所以我需要测试的是这种方法。

单元测试被实现为具有测试方法的类。每个测试方法通常测试目标类的单一方法。有时,测试方法可以测试目标类中的多个方法,有时,如果要测试的方法很大,则将测试拆分为多个测试方法。这是JUnit单元测试,测试concatenate()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.junit.Test;
import static org.junit.Assert.*;

public class MyUnitTest {

@Test
public void testConcatenate() {
MyUnit myUnit = new MyUnit();

String result = myUnit.concatenate("one", "two");

assertEquals("onetwo", result);

}
}

单元测试类是一个普通类,有一个方法testConcatenate()。注意这个方法如何用JUnit注释@Test进行注释。这是为了向单元测试运行者发信号,这个方法代表d单元测试,应该执行。没有使用@Test注释的方法不会被测试运行器执行。在testConcatenate()方法的内部创建一个MyUnit的实例。 它将使用两个字符串值 作为参数调用oncatenate()方法。

最后,assertEquals()方法被调用,这种方法是做实际测试的。在这种方法中,我们将被调用的方法(concatenate())的输出与预期输出进行比较。换句话说,我们将“onetwo”(预期输出)与concatenate()方法返回的值进行比较,该值保存在变量result中。

如果两个值相等,则不会发生任何反应。assertEquals()方法返回正常。如果两个值不相等,则会抛出异常,并在此处停止执行测试方法。

assertEquals()方法是静态导入的方法,通常位于org.junit.Assert类中。注意这个类导入的地方在MyUnitTest顶部。使用方法的静态导入比写Assert.assertEquals()要短。

您可以根据需要在单元测试类中拥有尽可能多的测试方法。 测试执行器会发下他们,并执行他们。 在这个例子中,我只写了一个测试方法。

这是使用Junit4.8.2进行单元测试的简单例子。

Quartz与Guice集成 Job如何自动注入Guice容器托管的对象

  • 添加自定义类实现JobFactory接口(参考了org.springframework.scheduling.quartz.AdaptableJobFactory类)

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51

    import extensions.InjectorInstance;
    import org.quartz.Job;
    import org.quartz.Scheduler;
    import org.quartz.SchedulerException;
    import org.quartz.spi.JobFactory;
    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.util.ReflectionUtils;

    import java.lang.reflect.Method;

    /**
    * @author longhuashen
    * @since 2017/9/20
    */

    public class GuiceQuartzAdaptableJobFactory implements JobFactory {

    @Override
    public Job newJob(TriggerFiredBundle triggerFiredBundle, Scheduler scheduler) throws SchedulerException {
    try {
    Object jobObject = createJobInstance(triggerFiredBundle);
    return adaptJob(jobObject);
    }
    catch (Exception ex) {
    throw new SchedulerException("Job instantiation failed", ex);
    }
    }

    protected Job adaptJob(Object jobObject) throws Exception {
    if (jobObject instanceof Job) {
    return (Job) jobObject;
    }
    else {
    throw new IllegalArgumentException("Unable to execute job class [" + jobObject.getClass().getName() +
    "]: only [org.quartz.Job] and [java.lang.Runnable] supported.");
    }
    }

    protected Object createJobInstance(TriggerFiredBundle triggerFiredBundle) throws Exception {
    //调用父类的方法
    Method getJobDetail = triggerFiredBundle.getClass().getMethod("getJobDetail");
    Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, triggerFiredBundle);
    Method getJobClass = jobDetail.getClass().getMethod("getJobClass");
    Class jobClass = (Class) ReflectionUtils.invokeMethod(getJobClass, jobDetail);
    Object jobInstance = jobClass.newInstance();

    //InjectorInstance.injector是获取到Guice Injector对象
    InjectorInstance.injector.injectMembers(jobInstance);
    return jobInstance;
    }
    }
  • 注入自定义Factory

1
2
3
4
5
6
7
8
9
@Override
public Module getModule(IApplication arg0) {
return new AbstractModule() {
@Override
protected void configure() {
bind(GuiceQuartzAdaptableJobFactory.class);
}
};
}
  • 在自定义Job里注入Guice里的bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    import com.google.inject.Inject;

    /**
    * @author longhuashen
    * @since 2017/9/20
    */

    public class InventoryWarnJob implements Job {

    @Inject
    private InventoryWarnConfigMapper inventoryWarnConfigMapper;
    }

【译】Guice-BindingAnnotations

Binding Annotations

偶尔你会想要一个相同类型的多个绑定。例如,您可能需要一个PayPal信用卡处理器和Google Checkout处理器。要启用此功能,需要添加一个可选绑定的注释。注解和类型一起唯一标识绑定。这一对叫做“键”(key)。

定义绑定注解需要两行代码加上几个导入。 将其放在自己的.java文件或其注释的类型中。

1
2
3
4
5
6
7
8
9
10
11
12
package example.pizza;

import com.google.inject.BindingAnnotation;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface PayPal {}

您不需要了解所有这些元注释,但如果您好奇:

  • @BindingAnnotation告诉Guice这是一个绑定注解。 如果多个绑定注释适用于同一个成员,Guice将会产生错误。
  • @Target({FIELD,PARAMETER,METHOD})是向使用者说明其作用域。 它可以防止@PayPal意外地被应用到不起作用的地方。
  • @Retention(RUNTIME)表明注释在运行期间起作用。

要依赖注解的绑定,请将注解应用于注入的参数:

1
2
3
4
5
6
7
public class RealBillingService implements BillingService {

@Inject
public RealBillingService(@PayPal CreditCardProcessor processor,
TransactionLog transactionLog)
{

...
}

最后,我们创建一个使用注释的绑定,在bind()语句中使用可选的annotatedWith子句:

1
2
3
bind(CreditCardProcessor.class)
.annotatedWith(PayPal.class)
.to(PayPalCreditCardProcessor.class);

@Named

Guice带有一个内置的绑定注释@Named,它需要传入一个字符串值:

1
2
3
4
5
6
7
public class RealBillingService implements BillingService {

@Inject
public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
TransactionLog transactionLog) {

...
}

由于编译器无法检查字符串,我们建议少用@Named注解。 定义您自己的专用注解提供了更好的类型安全性。

Binding Annotations with Attributes

Guice支持具有属性值的绑定注解(如@Named)。在极少数情况下,您需要这样的注解(并且不能使用@Provides方法),我们建议您从Auto / Value项目中使用@AutoAnnotation,因为正确实现注解是容易出错的。如果您决定手动创建自定义实现,请务必正确实现Javadoc中详细说明的equals()和hashCode()规范 Annotation Javadoc。将此类的实例传递给annotatedWith()绑定子句。

【译】Guice-LinkedBindings

Linked Bindings

Linked Bindings将类型映射到其实现。下面这个例子将接口TransactionLog映射到其实现DatabaseTransactionLog:

1
2
3
4
5
6
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
}
}

当您调用inject.getInstance(TransactionLog.class)时,或者当注入器遇到对TransactionLog的依赖时,它将使用DatabaseTransactionLog。某类型绑定到其任何子类型,例如实现类或扩展类。甚至可以将具体的DatabaseTransactionLog类绑定到一个子类:

1
bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);

写法:

1
2
3
4
5
6
7
    public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
}
}

在这种情况下,当使用TransactionLog时,注入器将返回MySqlDatabaseTransactionLog实例。

【译】Guice-Getting Started

使用依赖注入,对象在其构造函数中接受依赖关系。要构建一个对象,你首先构建它的依赖。但是要构建每个依赖关系,您需要其相关依赖,依此类推。所以当你建立一个对象时,你真的需要建立一个对象关系图。

手工构建对象关系图是劳动密集型,容易出错的,使测试变得困难。Guice可以帮你构建对象图。但首先,Guice需要配置为完全按照需要构建对象关系图。

为了说明,我们将启动BillingService类,它在其构造函数中接受它的依赖接口CreditCardProcessor和TransactionLog。为了明确指出Guice调用BillingService构造函数,我们添加了@Inject注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class BillingService {
private final CreditCardProcessor processor;
private final TransactionLog transactionLog;

@Inject
BillingService(CreditCardProcessor processor,
TransactionLog transactionLog) {
this.processor = processor;
this.transactionLog = transactionLog;
}

public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
...
}
}

我们要使用PaypalCreditCardProcessor和DatabaseTransactionLog构建一个BillingService。 Guice使用绑定来将类型映射到它们的实现。 一个模块是使用链式的方法调用指定的绑定集合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BillingModule extends AbstractModule {
@Override
protected void configure() {

/*
* This tells Guice that whenever it sees a dependency on a TransactionLog,
* it should satisfy the dependency using a DatabaseTransactionLog.
*/

bind(TransactionLog.class).to(DatabaseTransactionLog.class);

/*
* Similarly, this binding tells Guice that when CreditCardProcessor is used in
* a dependency, that should be satisfied with a PaypalCreditCardProcessor.
*/

bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}
}

模块是注入器的构建块,它是Guice的对象图构建器。 首先我们创建注入器,然后我们可以使用它来构建BillingService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  public static void main(String[] args) {
/*
* Guice.createInjector() takes your Modules, and returns a new Injector
* instance. Most applications will call this method exactly once, in their
* main() method.
*/

Injector injector = Guice.createInjector(new BillingModule());

/*
* Now that we've got the injector, we can build objects.
*/

BillingService billingService = injector.getInstance(BillingService.class);
...
}

通过构建billingService,我们使用Guice构建了一个小对象图。 该图包含计费服务及其相关的信用卡处理器和事务日志。

google公司的java编程风格指南

介绍

本文档是Google针对java平台的编码标准的完整定义。 Java源文件被描述为Google样式,当且仅当它遵守这里的规则时。

像其他编程风格指南一样,所涉及的问题不仅包括格式化的审美问题,而且还涉及其他类型的约定或编码标准。然而,本文件主要侧重于我们普遍遵循的快速规则,避免提供不能明确强制执行的建议(无论是人力还是工具)。

术语注释

在本文件中,除非另有说明:

  • 术语类被用于包括“普通”类,枚举类,接口或注释类型(@interface)。
  • 术语成员(一个类)用于包含嵌套类,字段,方法或构造函数; 也就是除了初始化和注释之外,所有类的顶级内容。
  • 术语评论总是指实施意见。 我们不使用短语“文档评论”,而是使用通用术语“Javadoc”。

其他“术语注释”将在本文档中偶尔出现。

源文件说明

  • 文件名

源文件名由它包含的顶级类(其中只有一个)的区分大小写的名称以及.java扩展名组成。

  • 文件编码

使用UTF-8编码

特殊字符

  • 空格字符

除了行终止符序列,ASCII水平空格字符(0x20)是唯一出现在源文件中任何地方的空格字符。 这意味着:

  1. 字符串和字符文字中的所有其他空白字符都将被转义
  2. 不用Tal来缩进
  • 其它特殊字符

对于具有特殊转义序列(\ b,\ t,\ n,\ f,\ r,\“,\”和\)的任何字符,使用该序列而不是相应的八进制(例如\ 012)或 Unicode(例如\ u000a)转义。

  • 非ASCII字符

对于其余的非ASCII字符,使用实际的Unicode字符(例如∞)或等效的Unicode转义(例如\ u221e)。 选择只取决于使代码更容易阅读和理解,尽管Unicode转义外部字符串文字和注释是非常不鼓励的。

源文件结构

源文件按顺序组成:

  1. 许可或版权信息(如果存在)
  2. 包语句
  3. 导入语句
  4. 完全的一个顶级类

使用一个完全的空白行分隔存在的每个部分。

  • 添加许可或版权信息(如果存在)

如果许可或版权信息属于文件,则标明。

  • 包语句

  • import语句

    不使用通配符导入

  • 导入语句排序

    1. 所有静态导入单个块。
    2. 所有非静态导入的单个块。

如果存在静态和非静态导入,则单个空行将两个块分开。 导入语句之间没有其他空行。

在每个块内,导入的名称以ASCII排序顺序显示。 (注意:这不同于导入语句是ASCII排序顺序,因为’。’在’;’之前排序。)