Annotation Interface Injector
included types and exposes the services declared
on the injector interface.
Component Graph
The component graph is created in multiple phases.
The first phase involves collecting all the types that are declared via the includes()
property and resolving each entry to a Sting-managed contribution. This adds a binding (a.k.a. a
potential component) for every Injectable annotated type and a binding for every method
in Fragment annotated types. If an included type is annotated with an annotation that is
meta-annotated by StingProvider, Sting first resolves that framework-managed type to the
provider type and then adds the provider's contributions.
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.
Resolving Services
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, or it is an
optional injector input) and the service request does not explicitly allow optional bindings then the
compiler generates an error as it is not able to determine statically that the service will be available.
Optional bindings may be consumed by nullable instance requests,
Optional-based requests and Collection<T> requests. For
Collection<T> requests, any optional binding that yields null is omitted from the
resulting collection.
If no matching binding is found then the compiler will attempt direct auto-discovery by looking
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 that does not succeed, Sting will attempt provider-backed auto-discovery for
framework-managed types annotated with an annotation meta-annotated by StingProvider. In
the provider-backed case, the resolved provider type must publish the requested framework-managed
type using the default qualifier. If no matching binding is found and the service is not optional
then the compiler will generate an error.
Generated Classname
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
Service methods
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. Sting actively processes
Named on these methods to qualify the requested service and Nullable
to mark an instance request as optional. Typed is ignored on injector output methods.
Service methods may also request optional services via Optional,
Supplier<Optional<T>> and Collection<Supplier<Optional<T>>>.
Instantiation
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
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.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic @interfaceA specification of a service that is supplied to an injector during construction. -
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionbooleanA flag controlling whether explicitly included types must be@Fragment-annotated.A flag controlling whether the injector implementation will be optimized for compilation by GWT.Class<?>[]A list of types that contribute to the object graph.booleanA flag controlling whether the injector implementation can be added to other injectors.A list of services that must be passed into the injector.
-
Element Details
-
includes
A list of types that contribute to the object graph. WhenfragmentOnly()is true, these types must be@Fragment-annotated interfaces. WhenfragmentOnly()is false, these types can also be@Injectable-annotated classes. Types annotated with annotations meta-annotated byStingProvidermay also be explicitly included, in which case Sting resolves the framework-managed type to the provider type before contributing bindings to the object graph. The de-duplicated contributions of the@Fragment-annotated interfaces in theincludes, 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. This first attempts direct auto-discovery for
@Injectabletypes and then provider-backed auto-discovery for framework-managed types annotated with an annotation meta-annotated byStingProvider. In the provider-backed case, the resolved provider must publish the framework-managed type using the default qualifier. The current implementation includes 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.- Returns:
- a list of types that contribute to the injector's object graph.
- Default:
- {}
-
fragmentOnly
boolean fragmentOnlyA flag controlling whether explicitly included types must be@Fragment-annotated. If set to false, the injector may explicitly include@Injectable-annotated types. This also applies when an explicit include resolves viaStingProviderto anInjectable-annotated provider type.- Returns:
- true to require explicit includes to be
@Fragment-annotated, false otherwise.
- Default:
- true
-
inputs
A list of services that must be passed into the injector. The annotation processor will generate a constructor with one parameter for every input. Each input value MUST specify the type parameter otherwise the annotation processor is unable to determine the type of the binding.- Returns:
- a list of services that must be passed into the injector.
- Default:
- {}
-
injectable
boolean injectableA flag controlling whether the injector implementation can be added to other injectors. If set to true, then the injector can be included in another injector. Theinputs()are services that need to be provided while the service methods will define services that this injector provides.- Returns:
- true to make the injector able to be included in another injector, false otherwise.
- Default:
- false
-
gwt
A flag controlling whether the injector implementation will be optimized for compilation by GWT. This primarily involves the addition of the@DoNotInlineannotation 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.AUTODETECTthen the optimization for gwt will be enabled if the classjavaemul.internal.annotations.DoNotInlineis present on the classpath.- Returns:
- true to optimize the injector implementation for transpilation to javascript, false otherwise.
- Default:
- AUTODETECT
-