@Documented @Retention(value=RUNTIME) @Target(value=TYPE) @StingProvider(value="[FlatEnclosingName]Sting_[SimpleName]_Provider") public @interface Injector
included
types and
exposes the services declared on the injector interface.
The component graph is created in multiple phases.
The first phase involves collecting all the types that are declared via the includes()
property and adding a binding (a.k.a. a potential component) for every Injectable
annotated
type and a binding for every method in Fragment
annotated types.
The second phase involves building a set of actual components created by the injector. Any potential
binding that is annotated with the Eager
annotation is added to the set of components. The
inputs
are also modelled as eager components. The compiler then examines the services
declared by the service methods and attempts to resolve the services into
components. When a component is added to the list of component, the service dependencies are added to the
sert of services to resolve. When there is no services left to resolve, the injector is considered
complete and the compiler terminates this phase.
The next phase will identify the binding for each component will mark the component as eager and if the
binding is annotated with the Eager
annotation. All dependencies of the component that are not
Supplier
dependencies are marked as eager. This process is recursively applied to dependencies of
dependencies. At the end of this phase the components are all categorized into those that are eager and
created when the injector is instantiated and components that are lazy and are created on demand.
The final phase performs validation and correctness checking. It is during this phase that circular dependencies are detected and rejected and that injection requests for injection of singular values with multiple bindings that satisfy are detected.
A service is resolved into a component by looking at the set of bindings present in the injector. If a binding exists that publishes the same type with the same qualifier then the binding is considered a match.
If the binding is marked as optional (i.e. the component is created by a method annotated with
Nullable
in a type annotated with the Fragment
annotation) and the service
is not optional then the compiler generates an error as it is not able to determine statically that the
service will be available.
If no matching binding is found then the compiler will attempt to look for a class annotated with
Injectable
that has the same name as the type of the service. If found and the type matches the service
then the class will be added to the set of components created. If not found and the service is not optional then
the compiler will generate an error.
The generated class will have the name of the type annotated with the @Injector
annotation
prepended with Sting_
. For example, the class mybiz.MyInjector
will produce
an implementation named mybiz.Sting_MyInjector
. Nested classes are also supported but their names
have the $
sign replaced with a _
. i.e. The nested class named mybiz.MyOuterClass.MyInjector
will generate an implementation named mybiz.MyOuterClass_Sting_MyInjector
Instance methods defined on the injector allow access to services contained within the injector and
also define the root services that are used to build the component graph. The instance methods must be
abstract, have zero parameters, throw no exceptions and return services. The methods can be annotated with
Named
to qualify the service and Nullable
to mark the service as optional.
A injector is created by invoking the constructor of the generated class. The generated class is package
access so unless you are only using the injector from within the package it was created, you need to expose
a method to create the injector. The usual pattern is to define a static method named create
on the
injector interface that creates an instance of the injector.
Example:
@Injector(includes = {BackendFragment.class, FrontendFragment.class})
public interface MyInjector {
public static MyInjector create() {
return new Sting_MyInjector();
}
MyWidget myWidget();
}
public class Main {
public static void main(String[] args) {
MyInjector injector = MyInjector.create();
... injector.myWidget() ...
}
}
The Injector
annotation use the inputs()
parameter that declares services that are
passed into the injector. These services are made available to components within the component graph and
maybe be qualified and/or marked as optional. Each input service is supplied to the injector as a constructor
parameter in the generated class.
Example of using input services:
@Injector( includes = {BackendFragment.class, FrontendFragment.class},
inputs = { @Injector.Input( type = MyService.class ),
@Injector.Input( qualifier = "hostname", type = String.class ) } )
interface MyInjector {
MyWidget myWidget();
}
public class Main {
public static void main(String[] args) {
MyService service = ...;
MyInjector injector = new Sting_MyInjector(service, "mybiz.com");
}
}
Circular dependencies within the injector are detected and rejected during the compilation phase.
Circular dependencies can be broken by passing a Supplier
dependency. i.e The developer injects
the type Supplier<OtherType>
instead of OtherType
and then calls Supplier.get()
on the supplier when access to the service is needed.
Modifier and Type | Optional Element and Description |
---|---|
Feature |
gwt
A flag controlling whether the injector implementation will be optimized for compilation by GWT.
|
Class<?>[] |
includes
A list of types that contribute to the object graph.
|
boolean |
injectable
A flag controlling whether the injector implementation can be added to other injectors.
|
Injector.Input[] |
inputs
A list of services that must be passed into the injector.
|
@Nonnull public abstract Class<?>[] includes
@Fragment
-annotated interfaces or @Injectable
-annotated classes.
The de-duplicated contributions of the @Fragment
-annotated interfaces in the
includes
, and of their inclusions recursively, are all contributed
to the object graph.
If the annotation processor detects a dependency that is required but not explicitly included in the
includes list then it will attempt to automatically add the type to the graph if it is annotated with
@Injectable
. The current implementation include types
if they were compiled in the same invocation of the java compiler. In the future the annotation processor
will load the descriptors from the filesystem.
@Nonnull public abstract Injector.Input[] inputs
public abstract boolean injectable
inputs()
are services that need to be provided while the service methods will define services that this
injector provides.@Nonnull public abstract Feature gwt
@DoNotInline
annotation to lazy component accessors
within the injector implementation to avoid inlining a component accessor and all transitive lazy component
accessors that can increase code-size, compilation time and run time.
If set to Feature.AUTODETECT
then the optimization for gwt will be enabled if the class
javaemul.internal.annotations.DoNotInline
is present on the classpath.