一、什么是Guice?
Guice是谷歌(Google)开发的一个轻量级依赖注入框架,它帮助我们更容易地编写模块化的代码,同时提高了代码的可测试性和可复用性。
Guice通过定义模块(module)以及注入注解(injection annotations)的方式实现依赖注入(dependency injection),表示一个组件(Component)可以Inject(被注入)依赖实现类以满足它的need(需求) 。Guice使用注入注解来指示它应该在何处注入依赖项,比如@Inject
、@Provides
、@Singleton
等等。
Guice框架解决了很多传统JavaEE组件交互性的问题,例如模块初始化的异常、资源泄漏、Factory实现冗长等等。而且,Guice不仅能够轻松自然地解决依赖注入的问题,还能够更好的支持模块化开发。
二、Guice核心组件
1. 模块(Module)
Module可以理解为一个程序组的某一特定部分,它定义了组件的绑定以及如何将组件组装在一起。每个模块提供一定的服务(Service):业务功能、DAO、工具类等。Guice框架根据传好的Module实例(或模块类)来组装这些Service。 为了减少无用的启动时间,Guice的Module是惰性加载的。Guice在运行时会根据实际需要去加载Module。
public class ExampleModule extends AbstractModule {
@Override
protected void configure() {
bind(MyService.class).annotatedWith(Red.class).to(RedServiceImpl.class); // 绑定RedServiceImpl
bind(MyService.class).annotatedWith(Green.class).to(GreenServiceImpl.class); // 绑定GreenServiceImpl
bind(MyService.class).annotatedWith(Blue.class).to(BlueServiceImpl.class); // 绑定BlueServiceImpl
}
}
2. 组件(Component)
组件(Component)是我们自己开发的应用程序中的类。Guice能够实现依赖注入,应用程序中的组件需要标记如何注入这些依赖项。
组件可以使用Guice的注入注解,如@Inject
、@Provides
和@Singleton
等。@Inject
表示组件需要一个依赖项,Guice会自动为组件注入它所需要的依赖项;@Provides
是使用方法来提供依赖项;@Singleton
标志着这个类的实例应该是单例的,只有一个实例。
public class MyServiceComponent {
private final MyService redService;
private final MyService greenService;
private final MyService blueService;
@Inject
public MyServiceComponent(
@Red MyService redService,
@Green MyService greenService,
@Blue MyService blueService) {
this.redService = redService;
this.greenService = greenService;
this.blueService = blueService;
}
}
3. 组件初始化器(Injector)
Injector表示一个类实例(Element),该实例包含有从module中绑定的类的实例。Injector使我们能够自动将依赖项注入组件中,从而让组件能够使用它们所依赖的其他组件实例。
所有的Injector都需要一个Module实例,所有绑定都要在Module的configure()
方法中完成。可以在一个应用程序中定义多个Injector,每个Injector可以使用特定的Module实例。Injector可以理解为是一种静态工厂方法,创建的每个对象都具有相同的依赖项。
Injector injector = Guice.createInjector(new ExampleModule());
MyServiceComponent myComponent = injector.getInstance(MyServiceComponent.class);
三、Guice实战
1. 使用Guice的依赖注入机制
Guice可以很轻松地构建高内聚、低耦合的组件,提高我们代码的可维护性和可扩展性。下面是一个使用Guice的代码示例:
public interface MyService {
String getName();
}
public class MyServiceImpl implements MyService {
@Override
public String getName() {
return "My Service is Running";
}
}
public class MyComponent {
private final MyService myService;
@Inject
public MyComponent(MyService myService) {
this.myService = myService;
}
public void doSomething() {
System.out.println(myService.getName());
}
}
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(MyService.class).to(MyServiceImpl.class);
}
}
public class MyApp {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new MyModule());
MyComponent myComponent = injector.getInstance(MyComponent.class);
myComponent.doSomething();
}
}
2. 使用Guice的AOP机制
Guice支持AOP(面向切面编程),它可以使我们更加轻松地插入通用的一致性功能,比如日志记录、异常处理、缓存管理等。下面是一个使用Guice AOP的代码示例:
// 实现一个简单的日志切面类
@Singleton
public class LoggingAspect {
@Inject
public LoggingAspect() {
}
@AroundInvoke
public Object profile(InvocationContext context) throws Exception {
long startTime = System.currentTimeMillis();
try {
return context.proceed();
} finally {
long totalTime = System.currentTimeMillis() - startTime;
System.out.println(
context.getMethod().getName() + " in " + totalTime + " millis");
}
}
}
public interface MyService {
String getName();
}
public class MyServiceImpl implements MyService {
@Override
public String getName() {
return "My Service is Running";
}
}
public class MyComponent {
private final MyService myService;
@Inject
public MyComponent(MyService myService) {
this.myService = myService;
}
@Log
public void doSomething() {
System.out.println(myService.getName());
}
}
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(MyService.class).to(MyServiceImpl.class);
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Log.class), new LoggingAspect());
}
}
public class MyApp {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new MyModule());
MyComponent myComponent = injector.getInstance(MyComponent.class);
myComponent.doSomething();
}
}
3. 使用Guice的多线程机制
Guice对线程间通信提供了一个简单的解决方案。Guice中有一个ThreadLocal子类,在注入的时候动态创建和存储实例,每个线程都有各自的实例。下面是一个使用Guice多线程的代码示例:
public class MyService {
private final ThreadLocal<String> message = new ThreadLocal<>();
public String getMessage() {
return message.get();
}
public void setMessage(String message) {
this.message.set(message);
}
}
public class MyComponent {
private final MyService myService;
@Inject
public MyComponent(MyService myService) {
this.myService = myService;
}
public void doSomething() {
System.out.println(myService.getMessage());
}
}
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(MyService.class);
}
@Provides
public String provideString(MyService myService) {
String message = "Hello from " + Thread.currentThread().getName();
myService.setMessage(message);
return message;
}
}
public class MyApp {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new MyModule());
Runnable task = () -> {
MyComponent myComponent = injector.getInstance(MyComponent.class);
myComponent.doSomething();
};
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(task);
executorService.submit(task);
executorService.shutdown();
}
}
四、总结
Guice是一个轻量级依赖注入框架,它通过定义模块和注入注解的方式实现依赖注入,并且帮助我们更容易地编写模块化的代码,提高了代码的可测试性和可复用性。同时,Guice还支持AOP和多线程机制。