Guice——一个轻量级依赖注入框架

发布时间:2023-05-19

一、什么是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和多线程机制。