Pozostając w kontekście rejestrowania beanów wykorzystując w tym celu adnotację @Configuration
. Natrafiłem ostatnio na ciekawe rozwiązanie dostępne out-of-the-box w Springu. Chodzi o opcjonalność zależności dostarczanych dla tworzonego beana. Jeśli oczekiwany w parametrze metody, oznaczonej przez adnotację @Bean
, parametr nie zostanie znaleziony (taki bean nie został zarejestrowany) to niekoniecznie musi to oznaczać, że aplikacja nie wstanie. Po prostu możemy żyć dalej i np. utworzyć jakąś domyślną “zależność”. Lepiej to oczywiście będzie pokazać na przykładzie.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface BaseInterface {
}
public class BaseDefaultClass implements BaseInterface {
}
public class BaseSpecifiedClass implements BaseInterface {
}
public class AggregationClass {
public final BaseInterface baseInterface;
public AggregationClass(BaseInterface baseInterface) {
this.baseInterface = baseInterface;
}
}
Teraz należy stworzyć klasę konfiguracyjną rejestrującą bean klasy AggregationClass
. Jednak w metodzie oddelegowanej do tego zadania zamiast korzystać z konkretnego typu w parametrze to użyjemy wrappera w postaci Optional
. W ten sposób jeśli bean typu BaseInterface
nie zostanie znaleziony to Optional
będzie pusty, a my będziemy mogli skorzystać z jakiejś domyślnej implementacji. W innym przypadku znajdzie się tam oczekiwana przez nas instancja.
Oczywiście całość rozwiązania powstała w oparciu o dokumentację Springa.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Configuration(proxyBeanMethods = false)
public class ConfigurationClass {
@Bean
public AggregationClass aggregationClass(Optional<BaseInterface> baseInterface) {
return new AggregationClass(baseInterface.orElseGet(() -> new BaseDefaultClass()));
}
@Bean
public BaseSpecifiedClass baseSpecifiedClass() {
return new BaseSpecifiedClass();
}
}
@SpringBootTest
class ConfigurationOptionalClassTest {
@Autowired
AggregationClass aggregationClass;
@Test
void beanCreation() {
assertInstanceOf(BaseSpecifiedClass.class, aggregationClass.baseInterface);
}
}
Test tylko potwierdził oczekiwane zachowanie. Oczywiście warto jeszcze sprawdzić sytuację, w której nie zarejestrujemy beana typu BaseInterface
. Czy faktycznie za polem baseInterface
klasy AggregationClass
będzie się kryła instancja typu BaseDefaultClass
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
public class ConfigurationClass {
@Bean
public AggregationClass aggregationClass(Optional<BaseInterface> baseInterface) {
return new AggregationClass(baseInterface.orElseGet(BaseDefaultClass::new));
}
}
@SpringBootTest
class ConfigurationOptionalClassTest {
@Autowired
AggregationClass aggregationClass;
@Test
void beanCreation() {
assertInstanceOf(BaseDefaultClass.class, aggregationClass.baseInterface);
}
}
Jak najbardziej, test pozytywnie zweryfikował powyższą tezę. Moim zdaniem warto mieć gdzieś z tyłu głowy to rozwiązanie. Być może przyda się w jakiś ekstremalnych przypadkach. A jeśli nie to uważam, że na rozmowie rekrutacyjnej zaplusujemy pokazując, że znamy trochę bardziej narzędzie, z którego korzystamy niż tylko na poziomie podstawowym.