OD

OD is a simple Service Locator library for Java 6+.

While the Service Locator pattern is widely discredited, it may still have merits in some situations; see article In Defense of Service Locator. We created this library for people who may consider using Service Locator in their applications.

[ od-1.0.0.jar ] [ javadoc ] [ github repo ] [ google group ]

To lookup an object of a certain type, call OD.get(type)

Foo foo = OD.get( Foo.class );

Prior to the lookup, typically during the startup phase, the application needs to bind the type to a supplier. For example

OD.bind( Foo.class ).to( ()->new FooImpl() ); // or, `to(FooImpl::new)` OD.bind( Foo.class ).to( FooImpl.class ); // if lambda is not available

The supplier will be invoked for every call of OD.get(Foo.class) .

To bind Foo to a singleton

OD.bind( Foo.class ).to( new FooImpl() );

If type alone is not sufficient as lookup key, tags can be added as qualifiers. Tags can be an arbitrary array of Object s, for example

OD.bind(Integer.class).tags("bar", 1).to(42); ... OD.get(Integer.class, "bar", 1); // returns `42`

A local binding is only visible to the current thread

OD.Local.bind( Foo.class ).to( someFoo ); // later in the same thread OD.get(Foo.class); // someFoo

Local bindings can be saved and restored

List<OD.Binding> prev = OD.Local.getBindings(); ... OD.Local.setBindings(prev); // restore bindings

Generic class or interface types can be used in lookup/binding; these types are represented by ClassType .

For example, to bind and look up on a generic type List<Integer>

OD.bind( new ClassType< List<Integer> >(){} ).to( Arrays.asList(7,8,9) ); // type literal ------------- OD.get( ClassType.of(List.class, Integer.class) ); // type factory ---- -------

See package bayou.jtype for type representations in our library.

Wildcard

To avoid confusion, we do not automatically extend bindings to super classes/interfaces. For example, a binding of Integer does not imply a binding of Number . If you do need that, you can manually add multiple bindings.

On the other hand, we do extend bindings to super types through wildcards; therefore, a binding of List<Integer> can be looked up by List<? extends Number> .

OD.get( new ClassType< List<? extends Number> >(){} ); ----------------------

Now, let's give a formal description of how OD works.

A Binding is an arbitrary mapping of (type,tags)->supplier . A binding is applicable to a particular (type,tags) if it's mapped to a non-null supplier.

There is a global binding list, populated by OD.bind() .

There is a local binding list per thread, managed by OD.Local .

For each lookup on (type,tags) , the local binding list is checked first; if no applicable local binding is found, the global binding list is checked.

A binding list is ordered; if there are multiple bindings applicable to a (type,tags) , the latest one is used.

You can create a custom Binding that implements arbitrary binding strategy. For example,

// bind every class to its no-arg constructor, if there's one. OD.Binding defaultBinding = new OD.Binding() { @Override public <T> OD.Supplier<? extends T> map(ClassType<T> type, Object... tags) { Constructor<T> constructor; try { constructor = type.getTheClass().getConstructor(); } catch (Exception e) { return null; } return ()-> { try { return constructor.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } }; } @Override public Set<? extends Class> getApplicableClasses() { return null; // `null` means set of all classes } }; OD.bind(defaultBinding);

Tags

Tags are not intended to be arbitrary construction parameters, even though you could do that in a custom binding.

Tags are intended to be a limited set of lookup keys; typically, they should be compile time constants.

For example, instead of doing something like

OD.get(Shoe.class, size, color) // get a Shoe of any size and color

it's better to redesign it with a factory that takes construction parameters

OD.get(ShoeMaker.class).make(size, color);

When we do a binding like

OD.bind(Dao.class).to(MyDao.class);

where Dao and MyDao are generic

public class MyDao<T> implements Dao<T> { ...

the binding is actually equivalent to multiple bindings of Dao<t> to MyDao<t> , for every type t . Therefore, if we do a lookup on Dao<Cat> , we'll get a new MyDao<Cat>() .

Type args

The constructor of MyDao can accept parameters that represent the type arguments of the generic class

public class MyDao<T> implements Dao<T> { public MyDao(Class<T> classT) { //System.out.println(classT);

When we do a lookup on Dao<Cat> , we'll get a new MyDao<Cat>(Cat.class) .

For more details, see javadoc of bind(type).to(implClass) .

See also source code of TypeArgConstructor and ImplClassBinding .