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   * Deprecated - migrate to builder().
113   */
114  @Deprecated
115  static BeanScopeBuilder newBuilder() {
116    return builder();
117  }
118
119  /**
120   * Return a single bean given the type.
121   *
122   * <pre>{@code
123   *
124   *   CoffeeMaker coffeeMaker = beanScope.get(CoffeeMaker.class);
125   *   coffeeMaker.brew();
126   *
127   * }</pre>
128   *
129   * @param type an interface or bean type
130   * @throws java.util.NoSuchElementException When no matching bean is found
131   */
132  <T> T get(Class<T> type);
133
134  /**
135   * Return a single bean given the type and name.
136   *
137   * <pre>{@code
138   *
139   *   Heater heater = beanScope.get(Heater.class, "electric");
140   *   heater.heat();
141   *
142   * }</pre>
143   *
144   * @param type an interface or bean type
145   * @param name the name qualifier of a specific bean
146   * @throws java.util.NoSuchElementException When no matching bean is found
147   */
148  <T> T get(Class<T> type, @Nullable String name);
149
150  /**
151   * Return a single bean given the generic type and name.
152   *
153   * @param type The generic type
154   * @param name the name qualifier of a specific bean
155   * @throws java.util.NoSuchElementException When no matching bean is found
156   */
157  <T> T get(Type type, @Nullable String name);
158
159  /**
160   * Optionally return a single bean given the type and empty if it is not found.
161   *
162   * @param type an interface or bean type
163   */
164  <T> Optional<T> getOptional(Class<T> type);
165
166  /**
167   * Optionally return a single bean given the type and name and empty if it is not found.
168   *
169   * @param type an interface or bean type
170   * @param name the name qualifier of a specific bean
171   */
172  <T> Optional<T> getOptional(Type type, @Nullable String name);
173
174  /**
175   * Return the list of beans that have an annotation.
176   *
177   * <pre>{@code
178   *
179   *   // e.g. register all controllers with web a framework
180   *   // .. where Controller is an annotation on the beans
181   *
182   *   List<Object> controllers = beanScope.listByAnnotation(Controller.class);
183   *
184   * }</pre>
185   *
186   * @param annotation An annotation class.
187   */
188  List<Object> listByAnnotation(Class<?> annotation);
189
190  /**
191   * Return the list of beans for a given type.
192   *
193   * <pre>{@code
194   *
195   *   // e.g. register all routes for a web framework
196   *
197   *   List<WebRoute> routes = beanScope.list(WebRoute.class);
198   *
199   * }</pre>
200   *
201   * @param type The type of beans to return.
202   */
203  <T> List<T> list(Class<T> type);
204
205  /**
206   * Return the list of beans that implement the given type.
207   */
208  <T> List<T> list(Type type);
209
210  /**
211   * Return the list of beans that implement the interface sorting by priority.
212   */
213  <T> List<T> listByPriority(Class<T> type);
214
215  /**
216   * Return the beans that implement the interface sorting by the priority annotation used.
217   * <p>
218   * The priority annotation will typically be either <code>javax.annotation.Priority</code>
219   * or <code>jakarta.annotation.Priority</code>.
220   *
221   * @param type     The interface type of the beans to return
222   * @param priority The priority annotation used to sort the beans
223   */
224  <T> List<T> listByPriority(Class<T> type, Class<? extends Annotation> priority);
225
226  /**
227   * Return the beans for this type mapped by their qualifier name.
228   * <p>
229   * Beans with no qualifier name get a generated unique key to use instead.
230   */
231  <T> Map<String, T> map(Type type);
232
233  /**
234   * Return all the bean entries from the scope.
235   * <p>
236   * The bean entries include entries from the parent scope if it has one.
237   *
238   * @return All bean entries from the scope.
239   */
240  List<BeanEntry> all();
241
242  /**
243   * Return true if the bean scope contains the given type.
244   */
245  boolean contains(Type type);
246
247  /**
248   * Return true if the bean scope contains the given type.
249   */
250  boolean contains(String type);
251
252  /**
253   * Close the scope firing any <code>@PreDestroy</code> lifecycle methods.
254   */
255  void close();
256}