google guice hello world

Guice是一个轻量级的Java依赖注入(DI)框架。

使用依赖注入有很多优点,但是手动操作常常会导致编写大量样板代码。Guice是一个框架,用于编写使用依赖注入的代码,而不需要编写大量样板代码,有关动机的更多细节,请参阅本页面。

简单地说,Guice减少了对工厂的需求和Java代码中new的使用。把Guice的@Inject看作是新的。在某些情况下,您仍然需要编写工厂,但是您的代码不会直接依赖于它们。您的代码将更容易更改、单元测试和在其他上下文中重用。

Guice支持Java的类型安全特性,特别是涉及到Java 5中引入的泛型和注释等特性时。您可能认为Guice填补了核心Java所缺少的特性。理想情况下,该语言本身将提供大部分相同的功能,但是在出现这种语言之前,我们还有Guice。

Guice 帮助您设计更好的API,而Guice API本身就是一个很好的例子。Guice不是厨房的水槽。我们用至少三个用例来证明每个特性。当我们有疑问时,我们就忽略它。我们构建的通用功能使您能够扩展Guice,而不是将每个特性都添加到核心框架中。

Guice 的目标是使开发和调试更容易、更快,而不是更困难、更慢。在这方面,Guice 避开了惊喜和魔法。您应该能够理解带有或不带有工具的代码,尽管工具可以使事情变得更简单。当错误确实发生时,Guice会加倍努力以生成有用的消息。

在很多框架中都使用到了 Guice,比如携程的配置中心 Apollo、Elastic-Seach Java 客户端。下面就距离说明 Guice 中几种常见的依赖注入方式:

1、简单依赖注入

Guice 简单的依赖注入包含三个部分:

  • 服务接口及实现类
  • 服务调用方类构造器和 @Inject 依赖注入
  • 依赖注入配置Module

1.1 服务接口及实现

LogService 定义了接口 log ,然后调用方传入 msg 就可以打印传入的日志信息

public interface LogService {

	void log(String msg);

}

LogServiceImpl 实现了 LogService 定义的 log 接口。

public class LogServiceImpl implements LogService {
	@Override
	public void log(String msg) {
		System.out.println("------LOG:" + msg);
	}
}

1.2 服务调用方

需要依赖注入的类即可以是一个实现了接口的类,也可以是普通的类。这里 MyApp 就是一个普通的类。它通过构造器以及注解 @Inject 注入 LogService 的实现 LogServiceImpl 并且调用了 LogService#log日志打印服务。

public class MyApp  {

	private LogService logService;

	@Inject
	public MyApp(LogService logService) {
		this.logService = logService;
	}

	public void work() {
		logService.log("程序正常运行");
	}
}

1.3 依赖注入配置类

MyAppModule 实现了 Guice 内部的 AbstractModule 类,通过重写方法 configure 把对象添加到 Guice 这个对象容器当中。下面的意思是从容器当中获取 LogService 接口实例会得到 LogServiceImpl,获取 Application 接口实例会得到 MyApp 对象实例。

public class MyAppModule extends AbstractModule {

	@Override
	protected void configure() {
		bind(LogService.class).to(LogServiceImpl.class);
		bind(MyApp.class);
	}

}

1.4 单元测试

public class MyAppTest {

	private static Injector injector;

	@BeforeClass
	public static void init() {
		injector = Guice.createInjector(new MyAppModule());
	}

	@Test
	public void testMyApp() {
		MyApp myApp = injector.getInstance(MyApp.class);
		myApp.work();
	}

}

上面的代码逻辑是在通过 Guice#createInjector 传入配置的依赖注入配置类获取依赖注入容器 Injector。然后通过 injector.getInstance 方法获取对象实例 MyApp。在 MyApp 中通过构造器与注解 @Inject 注入接口 LogService 的对象实现 LogServiceImpl。然后调用 Application#work 方法,其实就是调用 Application 的实现类 MyApp#work 它会调用依赖注入的 LogServiceImpl#log 方法,完成整个依赖注入以及方法的调用。

上面代码如果能够正常运行,说明可以使用 Guice 进行依赖注入。运行上面的 TestCase 发现并没有异常。

2、注解限定依赖注入

使用 Guice 进行依赖注入的时候,其实还可以通过注解来限定依赖注入,主要应用场景是一个接口有多个实现。下面就举例一个简单的例子。

2.1 定义限定注解

限定注解 @Message 注解与 @Count 来限定注入,需要使用 Guice 框架中的 @Qualifier 注解标注到限定注解上。

​@Qualifier
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Message {
}


@Qualifier
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Count {
}

2.2 定义依赖配置

public class DemoModule extends AbstractModule {

	@Override
	protected void configure() {
		bind(Key.get(String.class, Message.class)).toInstance("hello world");
	}

	@Provides
	@Count
	public Integer provideCount() {
		return 3;
	}

}

2.3 需要依赖注入的类

public class Greeter {

	private final String message;
	private final int count;

	@Inject
	public Greeter(@Message String message, @Count int count) {
		this.message = message;
		this.count = count;
	}

	public void sayHello() {
		for (int i=0; i < count; i++) {
			System.out.println(message);
		}
	}

}

2.4 测试用例

public class AnnotationTest {

	private static Injector injector;

	@BeforeClass
	public static void init() {
		injector = Guice.createInjector(new DemoModule());
	}

	@Test
	public void testGreeter(){
		Greeter greeter = injector.getInstance(Greeter.class);
		greeter.sayHello();
	}

}

运行上面的测试用例会打印 3 次 hello world,没有问题。

3、实例绑定依赖注入

当需要注入的对象是 String 或者 8 种基本对象类型及其包装类型时,可以使用 Guice 框架提供的 @Named 注解或者自定义命名注解来进行属性绑定。

3.1 绑定简单对象的类

@Data
public class Config {

	private String jdbcUrl;

	private int loginTimeoutSeconds;

	private int port;

	@Inject
	public Config(@Named("JDBC URL") String jdbcUrl,@Named("login timeout seconds") int loginTimeoutSeconds,@HttpPort int port) {
		this.jdbcUrl = jdbcUrl;
		this.loginTimeoutSeconds = loginTimeoutSeconds;
		this.port = port;
	}
	
}

3.2 自定义命名注解

同要的自定义的命名注解也需要使用 Guice 框架提供的 @Qualifier 注解。

@Qualifier
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HttpPort {
}

3.3 依赖注入配置

public class InstanceBindingsModule extends AbstractModule {

	@Override
	protected void configure() {
		bind(String.class).annotatedWith(Names.named("JDBC URL")).toInstance("jdbc:mysql://localhost/pizza");
		bind(Integer.class).annotatedWith(Names.named("login timeout seconds")).toInstance(10);
		bindConstant().annotatedWith(HttpPort.class).to(8080);
	}

}

3.4 测试用例

public class InstanceBindingsTest {

	private static Injector injector;

	@BeforeClass
	public static void init() {
		injector = Guice.createInjector(new InstanceBindingsModule());
	}

	@Test
	public void testMyApp() {
		Config config = injector.getInstance(Config.class);
		Assert.assertTrue("jdbc:mysql://localhost/pizza".equals(config.getJdbcUrl()));
		Assert.assertTrue(config.getLoginTimeoutSeconds() == 10);
		Assert.assertTrue(config.getPort() == 8080);
	}

}

4、对象 Scope

在 Guice 框架定义对象的时候还可以指定对象的 Scop,默认是单例模式。

4.1 服务接口及实现

public interface HelloService {

	String sayHello(String name);

}


public class HelloServiceImpl implements HelloService {
	@Override
	public String sayHello(String name) {
		return "hello, " + name;
	}
}

4.2 依赖注入配置类

public class UserModule extends AbstractModule {

	@Override
	protected void configure() {
		bind(HelloService.class).to(HelloServiceImpl.class).in(Singleton.class);
//		bind(HelloService.class).to(HelloServiceImpl.class).asEagerSingleton();;
//		bind(HelloService.class).to(HelloServiceImpl.class).in(Scopes.SINGLETON);
//		bind(HelloService.class).to(HelloServiceImpl.class).in(RequestScoped.class);
//		bind(HelloService.class).to(HelloServiceImpl.class).in(SessionScoped.class);
	}

}

可以通过 4 种方式来指定配置类是单例对象。除了上面配置类的前面三种方式指定这个对象是单例模式,还可以在实现类的上面标注 Guice 框架的 @Singleton 注解来表示这个对象是单例。下面就是 Guice 框架与 Servlet 规范整合的时候可以指定对象为 Servlet 容器中的 Request 范围以及和 Session 绑定的 SessionScoped。

4.3 需要依赖注入的类

public class User {

	private HelloService helloService;

	@Inject
	public User(HelloService helloService) {
		this.helloService = helloService;
	}

	public String sayHello(String name) {
		return helloService.sayHello(name);
	}

}

4.4 测试用例

public class UserTest {

	private static Injector injector;

	@BeforeClass
	public static void init() {
		injector = Guice.createInjector(new UserModule());
	}

	@Test
	public void testGreeter(){
		User user = injector.getInstance(User.class);
		Assert.assertTrue("hello, carl".equals(user.sayHello("carl")));
	}

}

5、链式绑定对象

在 Guice 当中,普通情况是一个接口绑定一个实现类。如果这个接口绑定的实现类再去绑定它的继承类,那么通过 Guice 容器依赖注入的类就是这个接口的实现类的继承类。

5.1 接口定义及实现

public interface TransactionLog {
}

public class DatabaseTransactionLog implements TransactionLog {
}

public class MySqlDatabaseTransactionLog extends DatabaseTransactionLog {
}

5.2 接口依赖类

@Data
public class LinkedBindingService {

	private TransactionLog transactionLog;

	@Inject
	public LinkedBindingService(TransactionLog transactionLog) {
		this.transactionLog = transactionLog;
	}
}

5.3 依赖配置类

下面就是链式依赖配置。

public class LinkedBindingModule extends AbstractModule {

	@Override
	protected void configure() {
		bind(TransactionLog.class).to(DatabaseTransactionLog.class);
		bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
	}

}

5.4 测试用例

public class LinkedBindingTest {

	private static Injector injector;

	@BeforeClass
	public static void init() {
		injector = Guice.createInjector(new LinkedBindingModule());
	}

	@Test
	public void testGreeter(){
		LinkedBindingService linkedBindingService = injector.getInstance(LinkedBindingService.class);
		Assert.assertTrue(linkedBindingService.getTransactionLog() instanceof MySqlDatabaseTransactionLog);
	}

}

6、 @Provides 注解定义对象

@Provides 注解可以在依赖注入配置类中定义对象,如果有同一个对象实例需要定义多个,就需要我们上面第二小节说到的通过注解来限定依赖注入。

6.1 限定依赖注解

@Qualifier
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface One {
}


@Qualifier
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Two {
}

6.2 @Provides 注解定义对象

通过 @Provides 注解定义来对象分为两种情况,第一种只定义声明一个这个对象的一个实例。

public class ProvidesMethodsSingleModule extends AbstractModule {

	@Provides
	public ProviderMethod provideMethod() {
		ProviderMethod providerMethod = new ProviderMethod("default");
		return providerMethod;
	}

}

另一种情况就是定义声明一个对象的多个实例。

public class ProvidesMethodsAnnotationModule extends AbstractModule {

	@Override
	protected void configure() {
		bind(ProviderMethod.class).to(Key.get(ProviderMethod.class, One.class));
	}

	@Provides
	@One
	public ProviderMethod provideMethodOne() {
		ProviderMethod providerMethod = new ProviderMethod("one");
		return providerMethod;
	}

	@Provides
	@Two
	public ProviderMethod provideMethodTwo() {
		ProviderMethod providerMethod = new ProviderMethod("two");
		return providerMethod;
	}

}

这种情况下就需要使用注解来限定注入对象实例。

6.3 需要纳入 Guice 管理的类

@RequiredArgsConstructor
@Getter
public class ProviderMethod {

	private final String name;

}

6.4 测试用例

public class ProvidesMethodsTest {

	@Test
	public void testDefault() {
		Injector injector = Guice.createInjector(new ProvidesMethodsAnnotationModule());
		ProviderMethod providerMethod = injector.getInstance(ProviderMethod.class);
		Assert.assertTrue("one".equals(providerMethod.getName()));
	}

	@Test
	public void testOne() {
		Injector injector = Guice.createInjector(new ProvidesMethodsAnnotationModule());
		ProviderMethod providerMethod = injector.getInstance(Key.get(ProviderMethod.class, One.class));
		Assert.assertTrue("one".equals(providerMethod.getName()));
	}

	@Test
	public void testTwo() {
		Injector injector = Guice.createInjector(new ProvidesMethodsAnnotationModule());
		ProviderMethod providerMethod = injector.getInstance(Key.get(ProviderMethod.class, Two.class));
		Assert.assertTrue("two".equals(providerMethod.getName()));
	}

	@Test
	public void testProvider() {
		Injector injector = Guice.createInjector(new ProvidesMethodsSingleModule());
		ProviderMethod providerMethod = injector.getInstance(ProviderMethod.class);
		Assert.assertTrue("default".equals(providerMethod.getName()));
	}

}

上面只是列举了 Guice 框架比较常用的依赖注入方式。还有其它的方式大家可以去 Guice Github 官网上面去查询

参考信息:

发布了195 篇原创文章 · 获赞 248 · 访问量 74万+

猜你喜欢

转载自blog.csdn.net/u012410733/article/details/105523846