您的位置:

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

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