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