001package sting; 002 003import java.lang.annotation.Documented; 004import java.lang.annotation.ElementType; 005import java.lang.annotation.Retention; 006import java.lang.annotation.RetentionPolicy; 007import java.lang.annotation.Target; 008import java.util.function.Supplier; 009import javax.annotation.Nonnull; 010 011/** 012 * Annotates an interface for which a dependency-injected implementation is to be generated. 013 * This annotation is a top-level Sting processor entrypoint. The implementation builds a 014 * component graph from the {@link #includes() included} types and exposes the services declared 015 * on the injector interface. 016 * 017 * <h2>Component Graph</h2> 018 * 019 * <p>The component graph is created in multiple phases.</p> 020 * 021 * <p>The first phase involves collecting all the types that are declared via the {@link #includes()} 022 * property and resolving each entry to a Sting-managed contribution. This adds a binding (a.k.a. a 023 * potential component) for every {@link Injectable} annotated type and a binding for every method 024 * in {@link Fragment} annotated types. If an included type is annotated with an annotation that is 025 * meta-annotated by {@link StingProvider}, Sting first resolves that framework-managed type to the 026 * provider type and then adds the provider's contributions.</p> 027 * 028 * <p>The second phase involves building a set of actual components created by the injector. Any potential 029 * binding that is annotated with the {@link Eager} annotation is added to the set of components. The 030 * {@link #inputs() inputs} are also modelled as eager components. The compiler then examines the services 031 * declared by the <a href="#service-methods">service methods</a> and attempts to resolve the services into 032 * components. When a component is added to the list of component, the service dependencies are added to the 033 * sert of services to resolve. When there is no services left to resolve, the injector is considered 034 * complete and the compiler terminates this phase.</p> 035 * 036 * <p>The next phase will identify the binding for each component will mark the component as eager and if the 037 * binding is annotated with the {@link Eager} annotation. All dependencies of the component that are not 038 * {@link Supplier} dependencies are marked as eager. This process is recursively applied to dependencies of 039 * dependencies. At the end of this phase the components are all categorized into those that are eager and 040 * created when the injector is instantiated and components that are lazy and are created on demand.</p> 041 * 042 * <p>The final phase performs validation and correctness checking. It is during this phase that circular 043 * dependencies are detected and rejected and that injection requests for injection of singular values with 044 * multiple bindings that satisfy are detected.</p> 045 * 046 * <h2>Resolving Services</h2> 047 * 048 * <p>A service is resolved into a component by looking at the set of bindings present in the injector. 049 * If a binding exists that publishes the same type with the same qualifier then the binding is considered 050 * a match.</p> 051 * 052 * <p>If the binding is marked as optional (i.e. the component is created by a method annotated with 053 * {@link javax.annotation.Nullable} in a type annotated with the {@link Fragment} annotation, or it is an 054 * optional injector input) and the service request does not explicitly allow optional bindings then the 055 * compiler generates an error as it is not able to determine statically that the service will be available. 056 * Optional bindings may be consumed by {@link javax.annotation.Nullable nullable instance} requests, 057 * {@link java.util.Optional Optional}-based requests and {@code Collection<T>} requests. For 058 * {@code Collection<T>} requests, any optional binding that yields {@code null} is omitted from the 059 * resulting collection.</p> 060 * 061 * <p>If no matching binding is found then the compiler will attempt direct auto-discovery by looking 062 * for a class annotated with {@link Injectable} that has the same name as the type of the service. 063 * If found and the type matches the service then the class will be added to the set of components 064 * created. If that does not succeed, Sting will attempt provider-backed auto-discovery for 065 * framework-managed types annotated with an annotation meta-annotated by {@link StingProvider}. In 066 * the provider-backed case, the resolved provider type must publish the requested framework-managed 067 * type using the default qualifier. If no matching binding is found and the service is not optional 068 * then the compiler will generate an error.</p> 069 * 070 * <h2>Generated Classname</h2> 071 * 072 * <p>The generated class will have the name of the type annotated with the {@code @Injector} annotation 073 * prepended with {@code Sting_}. For example, the class {@code mybiz.MyInjector} will produce 074 * an implementation named {@code mybiz.Sting_MyInjector}. Nested classes are also supported but their names 075 * have the {@code $} sign replaced with a {@code _}. i.e. The nested class named {@code mybiz.MyOuterClass.MyInjector} 076 * will generate an implementation named {@code mybiz.MyOuterClass_Sting_MyInjector}</p> 077 * 078 * <a id="service-methods">Service methods</a> 079 * <h2>Service methods</h2> 080 * 081 * <p>Instance methods defined on the injector allow access to services contained within the injector and 082 * also define the root services that are used to build the component graph. The instance methods must be 083 * abstract, have zero parameters, throw no exceptions and return services. Sting actively processes 084 * {@link Named} on these methods to qualify the requested service and {@link javax.annotation.Nullable} 085 * to mark an instance request as optional. {@link Typed} is ignored on injector output methods. 086 * Service methods may also request optional services via {@link java.util.Optional Optional}, 087 * {@code Supplier<Optional<T>>} and {@code Collection<Supplier<Optional<T>>>}.</p> 088 * 089 * <h2>Instantiation</h2> 090 * 091 * <p>A injector is created by invoking the constructor of the generated class. The generated class is package 092 * access so unless you are only using the injector from within the package it was created, you need to expose 093 * a method to create the injector. The usual pattern is to define a static method named {@code create} on the 094 * injector interface that creates an instance of the injector.</p> 095 * 096 * <p>Example:</p> 097 * 098 * <pre><code> 099 * {@literal @}Injector(includes = {BackendFragment.class, FrontendFragment.class}) 100 * public interface MyInjector { 101 * 102 * public static MyInjector create() { 103 * return new Sting_MyInjector(); 104 * } 105 * 106 * MyWidget myWidget(); 107 * } 108 * 109 * public class Main { 110 * public static void main(String[] args) { 111 * MyInjector injector = MyInjector.create(); 112 * ... injector.myWidget() ... 113 * } 114 * }</code></pre> 115 * 116 * 117 * <p>The {@link Injector} annotation use the {@link #inputs()} parameter that declares services that are 118 * passed into the injector. These services are made available to components within the component graph and 119 * maybe be qualified and/or marked as optional. Each input service is supplied to the injector as a constructor 120 * parameter in the generated class.</p> 121 * 122 * <p>Example of using input services:</p> 123 * 124 * <pre><code> 125 * {@literal @}Injector( includes = {BackendFragment.class, FrontendFragment.class}, 126 * inputs = { {@literal @}Injector.Input( type = MyService.class ), 127 * {@literal @}Injector.Input( qualifier = "hostname", type = String.class ) } ) 128 * interface MyInjector { 129 * MyWidget myWidget(); 130 * } 131 * 132 * public class Main { 133 * public static void main(String[] args) { 134 * MyService service = ...; 135 * MyInjector injector = new Sting_MyInjector(service, "mybiz.com"); 136 * } 137 * }</code></pre> 138 * 139 * <h3>Circular Dependencies</h3> 140 * 141 * <p>Circular dependencies within the injector are detected and rejected during the compilation phase. 142 * Circular dependencies can be broken by passing a {@link Supplier} dependency. i.e The developer injects 143 * the type {@link Supplier Supplier<OtherType>} instead of {@code OtherType} and then calls {@link Supplier#get()} 144 * on the supplier when access to the service is needed.</p> 145 */ 146@Documented 147@Retention( RetentionPolicy.RUNTIME ) 148@Target( ElementType.TYPE ) 149@StingProvider( "[FlatEnclosingName]Sting_[SimpleName]_Provider" ) 150public @interface Injector 151{ 152 /** 153 * A list of types that contribute to the object graph. 154 * When {@link #fragmentOnly()} is true, these types must be {@link Fragment @Fragment}-annotated interfaces. 155 * When {@link #fragmentOnly()} is false, these types can also be {@link Injectable @Injectable}-annotated 156 * classes. Types annotated with annotations meta-annotated by {@link StingProvider} may also be 157 * explicitly included, in which case Sting resolves the framework-managed type to the provider 158 * type before contributing bindings to the object graph. 159 * The de-duplicated contributions of the {@code @Fragment}-annotated interfaces in the {@code includes}, 160 * and of their inclusions recursively, are all contributed to the object graph. 161 * 162 * <p>If the annotation processor detects a dependency that is required but not explicitly included in the 163 * includes list then it will attempt to automatically add the type to the graph. This first 164 * attempts direct auto-discovery for {@link Injectable @Injectable} types and then 165 * provider-backed auto-discovery for framework-managed types annotated with an annotation 166 * meta-annotated by {@link StingProvider}. In the provider-backed case, the resolved provider 167 * must publish the framework-managed type using the default qualifier. The current implementation 168 * includes types if they were compiled in the same invocation of the java compiler. In the future 169 * the annotation processor will load the descriptors from the filesystem.</p> 170 * 171 * @return a list of types that contribute to the injector's object graph. 172 */ 173 @Nonnull 174 Class<?>[] includes() default {}; 175 176 /** 177 * A flag controlling whether explicitly included types must be {@link Fragment @Fragment}-annotated. 178 * If set to false, the injector may explicitly include {@link Injectable @Injectable}-annotated types. 179 * This also applies when an explicit include resolves via {@link StingProvider} to an 180 * {@link Injectable}-annotated provider type. 181 * 182 * @return true to require explicit includes to be {@link Fragment @Fragment}-annotated, false otherwise. 183 */ 184 boolean fragmentOnly() default true; 185 186 /** 187 * A list of services that must be passed into the injector. 188 * The annotation processor will generate a constructor with one parameter for every input. Each input 189 * value MUST specify the type parameter otherwise the annotation processor is unable to determine the 190 * type of the binding. 191 * 192 * @return a list of services that must be passed into the injector. 193 */ 194 @Nonnull 195 Input[] inputs() default {}; 196 197 /** 198 * A flag controlling whether the injector implementation can be added to other injectors. 199 * If set to true, then the injector can be included in another injector. The {@link #inputs()} 200 * are services that need to be provided while the service methods will define services that this 201 * injector provides. 202 * 203 * @return true to make the injector able to be included in another injector, false otherwise. 204 */ 205 boolean injectable() default false; 206 207 /** 208 * A flag controlling whether the injector implementation will be optimized for compilation by GWT. 209 * This primarily involves the addition of the {@code @DoNotInline} annotation to lazy component accessors 210 * within the injector implementation to avoid inlining a component accessor and all transitive lazy component 211 * accessors that can increase code-size, compilation time and run time. 212 * 213 * <p>If set to {@link Feature#AUTODETECT} then the optimization for gwt will be enabled if the class 214 * {@code javaemul.internal.annotations.DoNotInline} is present on the classpath.</p> 215 * 216 * @return true to optimize the injector implementation for transpilation to javascript, false otherwise. 217 */ 218 @Nonnull 219 Feature gwt() default Feature.AUTODETECT; 220 221 /** 222 * A specification of a service that is supplied to an injector during construction. 223 * The service is added to the the component graph and is made available for other components to consume 224 */ 225 @Retention( RetentionPolicy.RUNTIME ) 226 @Documented 227 @Target( {} ) 228 @interface Input 229 { 230 /** 231 * An opaque string that qualifies the service. 232 * The string is user-supplied and used to distinguish two different services with the same {@link #type()} 233 * but different semantics. 234 * 235 * @return an opaque qualifier string. 236 */ 237 @Nonnull 238 String qualifier() default ""; 239 240 /** 241 * The java type of the service. 242 * 243 * <p>Sting does not support classes defined with type parameters.</p> 244 * 245 * @return the java type of the service. 246 */ 247 Class<?> type(); 248 249 /** 250 * A flag indicating whether the input is optional and may be null or required. 251 * 252 * @return a flag indicating whether the input is optional and may be null or required. 253 */ 254 boolean optional() default false; 255 } 256}