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}