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