依赖注入Guice
Spring框架的依赖注入是家喻户晓的,但是在实际的开发中我们想使用便捷的依赖注入功能, 可以使用Google Guice,首先在你的maven项目里引入
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.0</version>
</dependency>
Guice是一个非常干净的依赖注入框架,框架除了依赖注入功能之外,没有任何其它非相关模块功能。
注解 @Inject,@Singleton,@Implementedby
最常用的两个注解就是@Singleton和@Inject,Singleton表示构建的对象是单例的,Inject表示被标注的字段将使用Guice自动注入。在一般的项目中这两个注解一般可以完成90%以上的装配工作。
Guice需要实例化对象,请确保相应被实例化的对象有默认构造器。
当某个接口有多个实现时,我们使用@ImplementedBy注解在接口定义上,指定接口的具体实现类
import javax.inject.Singleton;
import com.google.inject.Guice;
import com.google.inject.ImplementedBy;
import com.google.inject.Inject;
import com.google.inject.Injector;
@ImplementedBy(SimpleHelloPrinter.class)
interface IHelloPrinter {
void print();
}
@Singleton
class SimpleHelloPrinter implements IHelloPrinter {
public void print() {
System.out.println("Hello, Simple World");
}
}
@Singleton
class ComplexHelloPrinter implements IHelloPrinter {
public void print() {
System.out.println("Hello, Complex World");
}
}
@Singleton
public class Sample {
@Inject
private IHelloPrinter printer;
public void hello() {
printer.print();
}
public static void main(String[] args) {
Injector injector = Guice.createInjector();
Sample sample = injector.getInstance(Sample.class);
sample.hello();
}
}
如果我们不用Singleton标注,每次获取实例时,Guice会重新构造一个,这个会有反射构造器的性能损耗,在高性能场景下,请谨慎。
用Module定义装配规则
我们可以不使用@ImplementedBy注解,因为这样不优雅,谁会在定义接口的时候就能预知实现类的名称呢。我们可以使用Guice Module定义装配规则,它相当于Spring的XML文件,只不过它的装配规则都是使用代码定义的。你可能会辩解说代码定义怎么能比得上XML定义呢,其实Guice Module在一个大型项目中也是非常的简洁,一般只会占用几十行代码,Module里面配置的仅仅是特殊的专配规则。能规则的可读性而言,代码要比XML舒服的多。
要点:
- 继承AbstractModule
- 在覆盖的configure()方法里手动bind(接口).to(实现类) 效果和在实现类上@ImplementedBy一样
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
interface IHelloPrinter {
void print();
}
@Singleton
class SimpleHelloPrinter implements IHelloPrinter {
public void print() {
System.out.println("Hello, Simple World");
}
}
@Singleton
class ComplexHelloPrinter implements IHelloPrinter {
public void print() {
System.out.println("Hello, Complex World");
}
}
class SampleModule extends AbstractModule {
@Override
protected void configure() {
bind(IHelloPrinter.class).to(SimpleHelloPrinter.class);
}
}
@Singleton
public class Sample {
@Inject
private IHelloPrinter printer;
public void hello() {
printer.print();
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new SampleModule());
Sample sample = injector.getInstance(Sample.class);
sample.hello();
}
}
@Name给实现类起别名指定bind
我们还可以使用@Named名称指令来指定依赖注入实现
要点
- 给要注入的实现类起别名,在configre里bind设置
- @Name 辅助@Inject 引入别名对应的注入,还是需要@Inject
import javax.inject.Named;
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Names;
interface IHelloPrinter {
void print();
}
@Singleton
class SimpleHelloPrinter implements IHelloPrinter {
public void print() {
System.out.println("Hello, Simple World");
}
}
@Singleton
class ComplexHelloPrinter implements IHelloPrinter {
public void print() {
System.out.println("Hello, Complex World");
}
}
class SampleModule extends AbstractModule {
@Override
protected void configure() {
bind(IHelloPrinter.class).annotatedWith(Names.named("simple")).to(SimpleHelloPrinter.class);
bind(IHelloPrinter.class).annotatedWith(Names.named("complex")).to(ComplexHelloPrinter.class);
}
}
@Singleton
public class Sample {
@Inject
@Named("simple")
private IHelloPrinter simplePrinter;
@Inject
@Named("complex")
private IHelloPrinter complexPrinter;
public void hello() {
simplePrinter.print();
complexPrinter.print();
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new SampleModule());
Sample sample = injector.getInstance(Sample.class);
sample.hello();
}
}
@Inject放构造函数/set方法上注入
我们不使用字段注入,改用构造器注入,如果我们需要在构造器里做一些特别的初始化工作
其实是自动根据入参来注入, 单参数多参数都可以。 也可以根据set方法注入。
package io.edurt.lc.guice;
import com.google.inject.Guice;
import com.google.inject.Inject;
public class TestGuiceConstructor
{
private GuiceConstructorService service;
@Inject
public TestGuiceConstructor(GuiceConstructorService service)
{
this.service = service;
}
public GuiceConstructorService getService()
{
return service;
}
public static void main(String[] args)
{
TestGuiceConstructor test = Guice.createInjector().getInstance(TestGuiceConstructor.class);
test.getService().print("Test Case 1");
}
}
Guice 获取实例
@Test
public void test_service()
{
Injector injector = Guice.createInjector(new GuiceBasicModule());
GuiceBasicService service = injector.getInstance(GuiceBasicService.class);
service.print("Hello Guice");
}
静态方法注入
关键代码和方法
Guice.createInjector(binder -> binder.requestStaticInjection(TestGuiceStatic.class));
其实是手动创建注入。
package io.edurt.lc.guice;
import com.google.inject.Guice;
import com.google.inject.Inject;
public class TestGuiceStatic
{
@Inject
private static GuiceConstructorService service;
public static void main(String[] args)
{
Guice.createInjector(binder -> binder.requestStaticInjection(TestGuiceStatic.class));
TestGuiceStatic.service.print("Static");
}
}
@Provides 类似Spring的@bean
Guice provides a way to create bindings with complex objects using @Provides annotation.
@Provides放函数上 return值就是可注入的Bean. Provides需要放在继承AbstrctModule的类函数上。
//Binding Module
class TextEditorModule extends AbstractModule {
@Override
protected void configure() {}
@Provides
public SpellChecker provideSpellChecker(){
String dbUrl = "jdbc:mysql://localhost:5326/emp";
String user = "user";
int timeout = 100;
SpellChecker SpellChecker = new SpellCheckerImpl(dbUrl, user, timeout);
return SpellChecker;
}
}
@BindingAnnotation 指定bind
通过自定义一个注解,然后注解上再加一个注解@BindingAnnotation就可以让@Inject注入时再指定这个自定义注解对应的实现类了,方便一个接口对应多个实现类,通过自定义注解区分对应到哪个实现类,和@Name类似。
As we can bind a type with its implementation. In case we want to map a type with multiple implmentations, we can create custom annotation as well. See the below example to understand the concept.
Create a binding annotation
@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
@interface WinWord {}
- @BindingAnnotation - Marks annotation as binding annotation.
Mapping using binding annotation
bind(SpellChecker.class).annotatedWith(WinWord.class).to(WinWordSpellCheckerImpl.class);
以上代码写在binding module里,对一个接口通过一直注解关联到对应实现类。
//Binding Module
class TextEditorModule extends AbstractModule {
@Override
protected void configure() {
bind(SpellChecker.class).annotatedWith(WinWord.class)
.to(WinWordSpellCheckerImpl.class);
}
}
Inject using binding annotation
用的时候和@Name类似,指定注解来指定了实现类。
@Inject
public TextEditor(@WinWord SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
Guice AOP
Important Classes
- Matcher - Matcher is an interface to either accept or reject a value. In Guice AOP, we need two matchers: one to define which classes participate, and another for the methods of those classes.
- MethodInterceptor - MethodInterceptors are executed when a matching method is called. They can inspect the call: the method, its arguments, and the receiving instance. We can perform cross-cutting logic and then delegate to the underlying method. Finally, we may inspect the return value or exception and return.
指定类和指定方法,再加个拦截器。
重要方法: bindInterceptor, 写在Module里。
拦截器需继承MethodInterceptor
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;
public class GuiceTester {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TextEditorModule());
TextEditor editor = injector.getInstance(TextEditor.class);
editor.makeSpellCheck();
}
}
class TextEditor {
private SpellChecker spellChecker;
@Inject
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
public void makeSpellCheck(){
spellChecker.checkSpelling();
}
}
//Binding Module
class TextEditorModule extends AbstractModule {
@Override
protected void configure() {
bind(SpellChecker.class).to(SpellCheckerImpl.class);
bindInterceptor(Matchers.any(),
Matchers.annotatedWith(CallTracker.class),
new CallTrackerService());
}
}
//spell checker interface
interface SpellChecker {
public void checkSpelling();
}
//spell checker implementation
class SpellCheckerImpl implements SpellChecker {
@Override @CallTracker
public void checkSpelling() {
System.out.println("Inside checkSpelling." );
}
}
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface CallTracker {}
class CallTrackerService implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before " + invocation.getMethod().getName());
Object result = invocation.proceed();
System.out.println("After " + invocation.getMethod().getName());
return result;
}
}
Spring vs Guice
参考
https://zhuanlan.zhihu.com/p/32299568