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