001package io.avaje.inject;
002
003import io.avaje.lang.NonNullApi;
004import io.avaje.lang.Nullable;
005
006import java.lang.annotation.Annotation;
007import java.lang.reflect.Type;
008import java.util.List;
009import java.util.Map;
010import java.util.Optional;
011
012/**
013 * Holds beans created by dependency injection.
014 * <p>
015 * The beans have singleton scope, support lifecycle methods for postConstruct and
016 * preDestroy and are created (wired) via dependency injection.
017 * </p>
018 *
019 * <h3>Create a BeanScope</h3>
020 * <p>
021 * We can programmatically create a BeanScope via {@code BeanScope.builder()}.
022 * </p>
023 * <pre>{@code
024 *
025 *   // create a BeanScope ...
026 *
027 *   try (BeanScope scope = BeanScope.builder()
028 *     .build()) {
029 *
030 *     CoffeeMaker coffeeMaker = context.get(CoffeeMaker.class);
031 *     coffeeMaker.makeIt()
032 *   }
033 *
034 * }</pre>
035 *
036 * <h3>External dependencies</h3>
037 * <p>
038 * We can supporting external dependencies when creating the BeanScope. We need to do 2 things.
039 * we need to specify these via
040 * </p>
041 * <ul>
042 *   <li>
043 *       1. Specify the external dependency via {@code @InjectModule(requires=...)}.
044 *       Otherwise at compile time the annotation processor detects it as a missing dependency and we can't compile.
045 *   </li>
046 *   <li>
047 *       2. Provide the dependency when creating the BeanScope
048 *   </li>
049 * </ul>
050 * <p>
051 * For example, given we have Pump as an externally provided dependency.
052 *
053 * <pre>{@code
054 *
055 *   // tell the annotation processor Pump is provided externally
056 *   // otherwise it thinks we have a missing dependency
057 *
058 *   @InjectModule(requires=Pump.class)
059 *
060 * }</pre>
061 * <p>
062 * When we build the BeanScope provide the dependency via {@link BeanScopeBuilder#bean(Class, Object)}.
063 *
064 * <pre>{@code
065 *
066 *   // provide external dependencies ...
067 *   Pump pump = ...
068 *
069 *   try (BeanScope scope = BeanScope.builder()
070 *     .bean(Pump.class, pump)
071 *     .build()) {
072 *
073 *     CoffeeMaker coffeeMaker = context.get(CoffeeMaker.class);
074 *     coffeeMaker.makeIt()
075 *   }
076 *
077 * }</pre>
078 */
079@NonNullApi
080public interface BeanScope extends AutoCloseable {
081
082  /**
083   * Build a bean scope with options for shutdown hook and supplying external dependencies.
084   * <p>
085   * We can optionally:
086   * <ul>
087   *   <li>Provide external dependencies</li>
088   *   <li>Specify a parent BeanScope</li>
089   *   <li>Specify specific modules to wire</li>
090   *   <li>Specify to include a shutdown hook (to fire preDestroy lifecycle methods)</li>
091   *   <li>Use {@code forTesting()} to specify mocks and spies to use when wiring tests</li>
092   * </ul>
093   *
094   * <pre>{@code
095   *
096   *   // create a BeanScope ...
097   *
098   *   try (BeanScope scope = BeanScope.builder()
099   *     .build()) {
100   *
101   *     CoffeeMaker coffeeMaker = context.get(CoffeeMaker.class);
102   *     coffeeMaker.makeIt()
103   *   }
104   *
105   * }</pre>
106   */
107  static BeanScopeBuilder builder() {
108    return new DBeanScopeBuilder();
109  }
110
111  /**
112   * Return a single bean given the type.
113   *
114   * <pre>{@code
115   *
116   *   CoffeeMaker coffeeMaker = beanScope.get(CoffeeMaker.class);
117   *   coffeeMaker.brew();
118   *
119   * }</pre>
120   *
121   * @param type an interface or bean type
122   * @throws java.util.NoSuchElementException When no matching bean is found
123   */
124  <T> T get(Class<T> type);
125
126  /**
127   * Return a single bean given the type and name.
128   *
129   * <pre>{@code
130   *
131   *   Heater heater = beanScope.get(Heater.class, "electric");
132   *   heater.heat();
133   *
134   * }</pre>
135   *
136   * @param type an interface or bean type
137   * @param name the name qualifier of a specific bean
138   * @throws java.util.NoSuchElementException When no matching bean is found
139   */
140  <T> T get(Class<T> type, @Nullable String name);
141
142  default <T> T getGen(Class<T> type, @Nullable String name) {
143
144          return null;
145  }
146
147  /**
148   * Return a single bean given the generic type and name.
149   *
150   * @param type The generic type
151   * @param name the name qualifier of a specific bean
152   * @throws java.util.NoSuchElementException When no matching bean is found
153   */
154  <T> T get(Type type, @Nullable String name);
155
156  /**
157   * Optionally return a single bean given the type and empty if it is not found.
158   *
159   * @param type an interface or bean type
160   */
161  <T> Optional<T> getOptional(Class<T> type);
162
163  /**
164   * Optionally return a single bean given the type and name and empty if it is not found.
165   *
166   * @param type an interface or bean type
167   * @param name the name qualifier of a specific bean
168   */
169  <T> Optional<T> getOptional(Type type, @Nullable String name);
170
171  /**
172   * Return the list of beans that have an annotation. The annotation must have a @Retention policy of RUNTIME
173   *
174   * <pre>{@code
175   *
176   *   // e.g. register all controllers with web a framework
177   *   // .. where Controller is an annotation on the beans
178   *
179   *   List<Object> controllers = beanScope.listByAnnotation(Controller.class);
180   *
181   * }</pre>
182   *
183   * @param annotation An annotation class.
184   */
185  List<Object> listByAnnotation(Class<? extends Annotation> annotation);
186
187  /**
188   * Return the list of beans for a given type.
189   *
190   * <pre>{@code
191   *
192   *   // e.g. register all routes for a web framework
193   *
194   *   List<WebRoute> routes = beanScope.list(WebRoute.class);
195   *
196   * }</pre>
197   *
198   * @param type The type of beans to return.
199   */
200  <T> List<T> list(Class<T> type);
201
202  /**
203   * Return the list of beans that implement the given type.
204   */
205  <T> List<T> list(Type type);
206
207  /**
208   * Return the list of beans that implement the interface sorting by priority.
209   */
210  <T> List<T> listByPriority(Class<T> type);
211
212  /**
213   * Return the beans that implement the interface sorting by the priority annotation used.
214   * <p>
215   * The priority annotation will typically be either <code>javax.annotation.Priority</code>
216   * or <code>jakarta.annotation.Priority</code>.
217   *
218   * @param type     The interface type of the beans to return
219   * @param priority The priority annotation used to sort the beans
220   */
221  <T> List<T> listByPriority(Class<T> type, Class<? extends Annotation> priority);
222
223  /**
224   * Return the beans for this type mapped by their qualifier name.
225   * <p>
226   * Beans with no qualifier name get a generated unique key to use instead.
227   */
228  <T> Map<String, T> map(Type type);
229
230  /**
231   * Return all the bean entries from the scope.
232   * <p>
233   * The bean entries include entries from the parent scope if it has one.
234   *
235   * @return All bean entries from the scope.
236   */
237  List<BeanEntry> all();
238
239  /**
240   * Return true if the bean scope contains the given type.
241   */
242  boolean contains(Type type);
243
244  /**
245   * Return true if the bean scope contains the given type.
246   */
247  boolean contains(String type);
248
249  /**
250   * Close the scope firing any <code>@PreDestroy</code> lifecycle methods.
251   */
252  @Override
253  void close();
254}