001package io.avaje.inject; 002 003import java.io.Closeable; 004import java.lang.annotation.Annotation; 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 BeanContext</h3> 015 * <p> 016 * We can programmatically create a BeanContext via BeanContextBuilder. 017 * </p> 018 * <pre>{@code 019 * 020 * // create a BeanContext ... 021 * 022 * try (BeanContext context = new BeanContextBuilder() 023 * .build()) { 024 * 025 * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class); 026 * coffeeMaker.makeIt() 027 * } 028 * 029 * }</pre> 030 * 031 * <h3>Implicitly used</h3> 032 * <p> 033 * The BeanContext is implicitly used by SystemContext. It will be created as needed and 034 * a shutdown hook will close the underlying BeanContext on JVM shutdown. 035 * </p> 036 * <pre>{@code 037 * 038 * // BeanContext created as needed under the hood 039 * 040 * CoffeeMaker coffeeMaker = SystemContext.getBean(CoffeeMaker.class); 041 * coffeeMaker.brew(); 042 * 043 * }</pre> 044 */ 045public interface BeanContext extends Closeable { 046 047 /** 048 * Build a bean context with options for shutdown hook and supplying test doubles. 049 * <p> 050 * We would choose to use BeanContextBuilder in test code (for component testing) 051 * as it gives us the ability to inject test doubles, mocks, spy's etc. 052 * </p> 053 * 054 * <pre>{@code 055 * 056 * @Test 057 * public void someComponentTest() { 058 * 059 * MyRedisApi mockRedis = mock(MyRedisApi.class); 060 * MyDbApi mockDatabase = mock(MyDbApi.class); 061 * 062 * try (BeanContext context = BeanContext.newBuilder() 063 * .withBeans(mockRedis, mockDatabase) 064 * .build()) { 065 * 066 * // built with test doubles injected ... 067 * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class); 068 * coffeeMaker.makeIt(); 069 * 070 * assertThat(... 071 * } 072 * } 073 * 074 * }</pre> 075 */ 076 static BeanContextBuilder newBuilder() { 077 return new DBeanContextBuilder(); 078 } 079 080 /** 081 * Return the module name of the bean context. 082 * 083 * @see ContextModule 084 */ 085 String getName(); 086 087 /** 088 * Return the names of module features this bean context provides. 089 * 090 * @see ContextModule 091 */ 092 String[] getProvides(); 093 094 /** 095 * Return the names of modules this bean context depends on. 096 * 097 * @see ContextModule 098 */ 099 String[] getDependsOn(); 100 101 /** 102 * Return a single bean given the type. 103 * 104 * <pre>{@code 105 * 106 * CoffeeMaker coffeeMaker = beanContext.getBean(CoffeeMaker.class); 107 * coffeeMaker.brew(); 108 * 109 * }</pre> 110 * 111 * @param type an interface or bean type 112 */ 113 <T> T getBean(Class<T> type); 114 115 /** 116 * Return a single bean given the type and name. 117 * 118 * <pre>{@code 119 * 120 * Heater heater = beanContext.getBean(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 */ 128 <T> T getBean(Class<T> type, String name); 129 130 /** 131 * Return the wiring candidate bean with name and priority. 132 */ 133 <T> BeanEntry<T> candidate(Class<T> type, String name); 134 135 /** 136 * Return the list of beans that have an annotation. 137 * 138 * <pre>{@code 139 * 140 * // e.g. register all controllers with web a framework 141 * // .. where Controller is an annotation on the beans 142 * 143 * List<Object> controllers = SystemContext.getBeansWithAnnotation(Controller.class); 144 * 145 * }</pre> 146 * 147 * <p> 148 * The classic use case for this is registering controllers or routes to 149 * web frameworks like Sparkjava, Javlin, Rapidoid etc. 150 * 151 * @param annotation An annotation class. 152 */ 153 List<Object> getBeansWithAnnotation(Class<?> annotation); 154 155 /** 156 * Return the list of beans that implement the interface. 157 * 158 * <pre>{@code 159 * 160 * // e.g. register all routes for a web framework 161 * 162 * List<WebRoute> routes = SystemContext.getBeans(WebRoute.class); 163 * 164 * }</pre> 165 * 166 * @param interfaceType An interface class. 167 */ 168 <T> List<T> getBeans(Class<T> interfaceType); 169 170 /** 171 * Return the list of beans that implement the interface sorting by priority. 172 */ 173 <T> List<T> getBeansByPriority(Class<T> interfaceType); 174 175 /** 176 * Return the beans that implement the interface sorting by the priority annotation used. 177 * <p> 178 * The priority annotation will typically be either <code>javax.annotation.Priority</code> 179 * or <code>jakarta.annotation.Priority</code>. 180 * 181 * @param interfaceType The interface type of the beans to return 182 * @param priority The priority annotation used to sort the beans 183 */ 184 <T> List<T> getBeansByPriority(Class<T> interfaceType, Class<? extends Annotation> priority); 185 186 /** 187 * Sort the beans by javax.annotation.Priority annotation. 188 * 189 * @param list The beans to sort by priority 190 * @return A new list of beans sorted by priority 191 */ 192 <T> List<T> sortByPriority(List<T> list); 193 194 /** 195 * Sort the beans using the given Priority annotation. 196 * <p> 197 * The priority annotation will typically be either <code>javax.annotation.Priority</code> 198 * or <code>jakarta.annotation.Priority</code>. 199 * 200 * @param list The beans to sort by priority 201 * @param priority The priority annotation used to sort the beans 202 * @return A new list of beans sorted by priority 203 */ 204 <T> List<T> sortByPriority(List<T> list, final Class<? extends Annotation> priority); 205 206 /** 207 * Start the context firing any <code>@PostConstruct</code> methods. 208 */ 209 void start(); 210 211 /** 212 * Close the context firing any <code>@PreDestroy</code> methods. 213 */ 214 void close(); 215}