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}