guice是google开发的一个轻量级的IOC框架,maven则使用的是guice来进行其内部的IOC操作。而guice本身是比较简单的。下面的代码展示了guice的主要功能,也展示了它主要的角色。guice内部可以看作是一个<Key,Provider>的map。通过各种module将<Key,Provider>注册到guice中,guice则将这些<Key,Provider>的binding构建一个injector给用户,而用户则通过injector利用key来获取对那个的provider来获得对象。
public class Test {
interface A{
void say() ;
}
static class B implements A{
@Override
public void say() {
System.out.println("hahaha");
}
}
static class TestModule extends AbstractModule{
@Override
protected void configure() {
bind(Key.get(A.class)).toProvider(() -> new B()).in(Singleton.class);
}
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new TestModule()) ;
A a = injector.getInstance(A.class) ;
a.say();
}
}
Guice提供了多种的对象的绑定的方式:
下面展示了不同方式绑定的实例。
public class TestBinding {
private static class A{}
private static class B{}
private static class C{C(){new RuntimeException().printStackTrace();}}
private static class D{}
private static class E{}
@ImplementedBy(F.class)
private interface IF{}
private static class F implements IF{}
private static class P implements Provider<E> {
@Override
public E get() {
System.out.println("create e") ;
return new E();
}
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Constructor constructor = A.class.getDeclaredConstructors()[0] ;
bind(A.class).annotatedWith(Names.named("a")).toConstructor(constructor) ; //ConstructorBinding
bind(B.class).annotatedWith(Names.named("b")).to(B.class); //LinkedBinding
bind(C.class).annotatedWith(Names.named("c")).toProvider(()->new C()) ; //ProviderInstanceBinding
bind(D.class).annotatedWith(Names.named("d")).toInstance(new D());//InstanceBinding
bind(E.class).annotatedWith(Names.named("e")).toProvider(Key.get(P.class));//ProviderKeyBinding
bind(IF.class) ; //UntargettedBinding
}
}) ;
System.out.println(injector.getInstance(Key.get(A.class,Names.named("a"))));
System.out.println(injector.getInstance(Key.get(B.class,Names.named("b"))));
System.out.println(injector.getInstance(Key.get(C.class,Names.named("c"))));
System.out.println(injector.getInstance(Key.get(D.class,Names.named("d"))));
System.out.println(injector.getInstance(Key.get(E.class,Names.named("e"))));
System.out.println(injector.getInstance(IF.class));
}
}
guice主要是提供了4种参数,这4种参数的配置直接是通过binder()中的方法来进行绑定的。
下面则分别是上面4个参数地注解和其相关地解释
//A能正常获取,B不能,因为A的构造器中显式地加了@Inject注解
public class TestAtInjectOnConstructorOption {
private static class A{@Inject A(){}}
private static class B{}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().requireAtInjectOnConstructors();
}
}) ;
System.out.println(injector.getInstance(A.class)) ;
System.out.println(injector.getInstance(B.class)) ;
}
}
//IB因为禁止了ExplicitBindings而不能通过ImplementedBy注入对应的实现类
//导致不能获取IB而报错
public class TestRequireExplicitBinding {
private static class A{
@Inject IB b ;
}
@ImplementedBy(B.class)
private interface IB{}
private static class B implements IB{}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(A.class).annotatedWith(Names.named("a")).to(A.class) ;
binder().requireExplicitBindings();
}
}) ;
System.out.println(injector.getInstance(A.class)) ;
System.out.println(injector.getInstance(IB.class));
}
}
//A和B循环依赖了,加了disableCircularProxies配置就会直接报错
public class TestDisableCircularProxiesOption {
private static class A{
@Inject B b ;
}
private static class B{
@Inject A a ;
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().disableCircularProxies();
}
}) ;
System.out.println(injector.getInstance(A.class)) ;
}
}
//绑定是Named.class这种注解形式,获取的时候是Names.named("a")这种形式
//如果不加requireExactBindingAnnotations,下面是可以获取到对应对象的
public class TestExactBindingAnnotations {
private static class A {}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(A.class).annotatedWith(Named.class).to(A.class).asEagerSingleton();
//不加这个配置,下面是可以获取数据,加了就会报错
binder().requireExactBindingAnnotations();
}
}) ;
A a = injector.getInstance(Key.get(A.class,Names.named("a"))) ;
System.out.println(a) ;
}
}
这个主要时绑定转换器,它的是通过binder的convertToTypes方法注册的,主要作用是将字符串转换为对应的需要的类型。
下面代码是使用例子,可以看到它注册了一个注解为@Names("b")的字符串,并且绑定了一个对类型为B的转换器,这个转换器可以将字符串转换为实例B。但是没有将类型B注册到Injector中,但是可以直接从injector中获取类型为B的对象。
import com.google.inject.*;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.name.Names;
public class TestConvertBinding {
private static class B{
String name ;
B(String name){this.name=name;}
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().convertToTypes(new AbstractMatcher<TypeLiteral<?>>() {
@Override
public boolean matches(TypeLiteral<?> typeLiteral) {
return typeLiteral.getRawType().isAssignableFrom(B.class) ;
}
}, (value, toType) -> new B(value));
binder().bindConstant().annotatedWith(Names.named("b")).to("hahaha->b");
}
}) ;
B b = injector.getInstance(Key.get(B.class,Names.named("b"))) ;
System.out.println(b.name) ;
}
}
这两个都是Lookup,即在初始化阶段就获取了对provider和membersInjector地对象,但是在初始化阶段是不能调用对应对象地相关方法,需要在初始化后等guice内部将代理对象注册到这些对象中,才能通过这些对象掉用代理对象来进行相应地操作。
public class TestProviderLookup {
private static class A{}
public static void main(String[] args){
AtomicReference<Provider> reference = new AtomicReference<>();
Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Provider provider = binder().getProvider(Key.get(A.class, Names.named("a")));
reference.set(provider);
bind(A.class).annotatedWith(Names.named("a")).to(A.class) ;
}
}) ;
System.out.println(reference.get().get()) ;
}
}
public class TestMembersInjector {
private static class A{
@Inject @Named("b") B b ;
}
private static class B{}
public static void main(String[] args){
AtomicReference<MembersInjector> reference = new AtomicReference<>();
Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
MembersInjector<A> membersInjector = binder().getMembersInjector(A.class) ;
reference.set(membersInjector);
bind(B.class).annotatedWith(Names.named("b")).to(B.class).asEagerSingleton(); ;
}
}) ;
A a1 = new A() ;
reference.get().injectMembers(a1);
}
}
这两个都是进行属性注入操作,不同的是StaticInjectionRequest是对静态变量进行属性注入,而InjectionRequest则是对已经构建好的对象进行属性注入。
下面代码展示的是这两种不同的注入方式,可以看到两个都是再A类中注入B。不同的是上面是静态的属性B,并且传入的是A的class对象,而下面传入的是A对象,其也是对A对象进行属性注入。
public class TestStaticInjection {
static class A{
@Inject
static B b ;
}
static class B{
}
public static void main(String[] args){
Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().bind(B.class).annotatedWith(Names.named("foo")).to(B.class) ;
binder().requestStaticInjection(A.class);
}
});
System.out.println(A.b);
}
}
public class TestInjectionRequest {
private static class A{
@Inject private B b ;
}
private static class B{}
public static void main(String[] args){
final A a = new A() ;
Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().requestInjection(a);
bind(B.class).annotatedWith(Names.named("b")).to(B.class) ;
}
});
System.out.println(a.b);
}
}
这个主要是可以自定义scope,guice自身提供地注解主要就是singleton和noScope两种,像maven就会自定义sessionScope来进行扩充,guice子定义scope主要是定义一个带有@ScopeAnnotation注解地注解,并且将这个注解绑定到实现了Scope类地对象中,guie就会对带有这个注解的对象通过注册的Scope对象来进行管理。
下面例子注册了一个@CacheAnno注解的scope,这个scope主要是维护一个cache,对里面的对象实现1秒钟过期的策略,可以看到在一次获取的三个A对象中,前面两个A对象是通过一个对象,而最后一个对象则不同,因为中间等了2秒,缓存消失了,故新创建了一个对象。
public class TestScopeBinder {
private static class CacheScope implements Scope{
Cache<Key,Object> cache = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.SECONDS).build();
@Override
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
return ()->{
try {
return (T) cache.get(key,()->unscoped.get());
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
};
}
}
@Retention(RUNTIME)
@ScopeAnnotation
private @interface CacheAnno{
}
@CacheAnno
private static class A{}
public static void main(String[] args) throws InterruptedException {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindScope(CacheAnno.class,new CacheScope());
}
});
System.out.println(injector.getInstance(A.class));
System.out.println(injector.getInstance(A.class));
Thread.sleep(2000);
System.out.println(injector.getInstance(A.class));
}
}
这两种类主要是在构建和属性赋值期间进行相关的处理。下面展示了相关例子,以及对应的注释
/**
* TypeListener主要是在构建binding对应的MembersInjectorImpl时调用hear方法
* 在hear方法中注册了属性注册的后处理器,即在对应的属性注册完成后进行的操作
* 并且注册了MembersInjector,正内部的memberInjector对A注入了B后进行了相关操纵又将A中的b属性赋值为空
*/
public class TestTypeListener {
private static class A{
@Inject B b ;
}
@Singleton
static class B{}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().bindListener(Matchers.any(),new TypeListener(){
//hear方法在构建MembersInjectorImpl的时候进行调用
@Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
//此处注册的InjectionListener在每次所有的属性注册结束后进行调用
encounter.register((InjectionListener<I>) injectee -> {
System.out.println("after inject " + injectee) ;
});
encounter.register((MembersInjector<I>) instance -> {
if(instance instanceof A){
A a = (A)instance ;
System.out.println("a.b = " + a.b);
a.b = null ;
}
});
}
});
}
}) ;
A a = injector.getInstance(A.class) ;
System.out.println(a.b) ;
}
}
public class TestProvisionListener {
static class A{
@Inject B b ;
A(){System.out.println("construct a");}
}
static class B{
B(){System.out.println("construct b");}
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
binder().bindListener(Matchers.any(), new ProvisionListener() {
@Override
public <T> void onProvision(ProvisionInvocation<T> provision) {
System.out.println("=====before=====" + provision.getBinding());
provision.provision();
System.out.println("=====after========" + provision.getBinding());
}
});
}
});
injector.getInstance(A.class) ;
}
}
这主要是在当前的Binder下构建一个privateBinder,然后将不想外部能直接访问的binding注册到这个privateBinder中,而这个privateBinder最终会构建为一个当前injector的子injector,在当前的injector中无法直接访问这个子injector的对象,但是这个子injector对象内部是可以互相注入的,并且其还可以通过expose方法将其想要外部可以访问的对象暴露出去。
下面即为这种情况的例子,并且注释中对其进行了描述
/**
* 此处A,B都在PrivateBinder中注册的,其实际是注册进了当前injector的子injector中
* 当前的Injector是无法直接获取两个对象,但是这两个对象因为是自身在同一个子injector中
* 故此他们之直接是可以互相注册的,所有B对象中是可以注册了A的,并且其expose了B,所以能在外部
* 获取B对象,但是无法直接获取A对象
*/
public class TestPrivateElement {
@Singleton
private static class A{}
private static class B{
@Named("a") @Inject A a;
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
PrivateBinder binder = binder().newPrivateBinder();
binder.bind(A.class).annotatedWith(Names.named("a")).to(A.class) ;
binder.bind(B.class).annotatedWith(Names.named("b")).to(B.class) ;
binder.expose(Key.get(B.class,Names.named("b")));
}
});
B b = injector.getInstance(Key.get(B.class,Names.named("b"))) ;
System.out.println(b.a);
System.out.println(injector.getInstance(Key.get(A.class,Names.named("a")))) ;
}
}
guice可以通过在module的实现类中利用@Provides,@ProvidesIntoSet,@ProvidesIntoMap等注解的形式注册binding,这种注册方式最终会将其转换为ProviderMethod的形式,ProviderMethod则会通过反射这些方法来获取这些数据。对于@ProvidesIntoSet这些集合的注解,其在注入单个binding时会将其加上一个@Element注解来区分单个binding,并且通过ModuleAnnotatedMethodScanner将其对应的集合的<Key,Provider>的binding注入。
public class TestModuleAnnotatedMethodScanner {
private static class B{
}
public static void main(String[] args){
Injector injector = Guice.createInjector(new AbstractModule() {
@ProvidesIntoSet private String a(){return "a";}
@ProvidesIntoSet private String b(){return "b";}
@StringMapKey("bb")
@ProvidesIntoMap
private B bb(){return new B() ; }
@StringMapKey("bb2")
@ProvidesIntoMap
private B bb2(){return new B() ; }
@Provides
private B bb3(){return new B();}
});
System.out.println(injector.getInstance(Key.get(new TypeLiteral<Map<String,B>>(){})));
System.out.println(injector.getInstance(Key.get(new TypeLiteral<Set<String>>(){})));
System.out.println(injector.getInstance(B.class));
}
}