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