Wraz z powstaniem Spring Framework proces tworzenia aplikacji klasy enterprise w Javie zosta┼é znaczenie uproszczony. Sta┼éo si─Ö tak dzi─Öki kontenerowi zale┼╝no┼Ťci, kt├│ry odseparowa┼é kreacje obiekt├│w od ich u┼╝ycia. Nie musimy si─Ö martwi─ç o zarz─ůdzenie zale┼╝no┼Ťciami, Spring za nas je wykryje i uporz─ůdkuje. Dokonuje on tego automatycznie lub na podstawie naszej w┼éasnor─Öcznie zdefiniowanej konfiguracji. Zacznijmy od kr├│tkiego opisu dzia┼éania kontenera zale┼╝no┼Ťci Springa.

Dzia┼éanie kontenera zale┼╝no┼Ťci Springa

Spring Framework
Jeden z najbardziej rozpoznawalnych framework├│w Javowych

Kontener zale┼╝no┼Ťci opiera si─Ö na mechanizmie wstrzykiwania zale┼╝no┼Ťci, czyli Dependency Injection. Dzi─Öki niemu jeste┼Ťmy w stanie odda─ç kontrol─Ö frameworkowi, kt├│ry za nas b─Ödzie zarz─ůdza┼é cyklem ┼╝ycia obiekt├│w w aplikacji. B─Ödzie odpowiedzialny za ich stworzenie oraz odpowiednie po┼é─ůczenie. Przed tym podej┼Ťciem to programista mia┼é za zadanie kreowa─ç obiekty poprzez s┼éowo kluczowe new i przekazywa─ç je do innych obiekt├│w, kt├│re ich wymagaj─ů.

1
2
3
ItemRepository itemRepository = new ItemRepository();
EventPublisher eventPublisher = new EventPublisher();
OrderService orderService = new OrderService(itemRepository, eventPublisher);

Dzi─Öki u┼╝yciu kontenera zale┼╝no┼Ťci Springa nie trzeba si─Ö ju┼╝ d┼éu┼╝ej martwi─ç, aby zapisywa─ç tego typu kod. Operacja tworzenia zostanie oddelegowana do frameworka, a my b─Ödziemy mogli skupi─ç si─Ö na implementowaniu przypadk├│w biznesowych. No dobrze, ale sk─ůd Spring b─Ödzie wiedzia┼é, kt├│re obiekty za nas stworzy─ç oraz w jaki spos├│b? Musimy mu w tym pom├│c poprzez u┼╝ycie odpowiednich adnotacji (je┼Ťli nie wiesz czym s─ů adnotacje to zapraszam na stron─Ö devcave.pl): @Component lub @Bean.

Adnotacja @Component

W dokumentacji Springa mo┼╝na przeczyta─ç, ┼╝e adnotacja @Component jest odpowiedzialna za:

Indicates that an annotated class is a ÔÇťcomponentÔÇŁ. Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.

Dokumentacja Springa

Oznaczaj─ůc dan─ů klas─Ö tak─ů adnotacj─ů zostanie ona potencjalnym kandydatem do automatycznego wykrycia przez Springa. Oczywi┼Ťcie stanie si─Ö tak je┼Ťli znajdzie si─Ö w pakiecie, kt├│ry podlega skanowaniu (zach─Öcam do zapoznania si─Ö z @ComponentScan). Jest to naprawd─Ö wygodne rozwi─ůzanie, przez dos┼éownie jedn─ů linijk─Ö kodu jeste┼Ťmy zwolnieni z my┼Ťlenia o sposobie tworzenia danego obiektu. Jednak ma to minus w postaci wnikania mechanizm├│w frameworka do naszego kodu, chodzi tu g┼é├│wnie o domen─Ö biznesow─ů, kt├│ra powinna by─ç niezale┼╝na od ÔÇť┼Ťwiata zewn─ÖtrznegoÔÇŁ. Je┼Ťli b─Ödziemy zmuszeni do zmiany infrastruktury to nie chcieliby┼Ťmy, aby nasza logika biznesowa na tym ucierpia┼éa. Pragn─Öliby┼Ťmy, ┼╝eby pozosta┼éa ona niezale┼╝na od jakichkolwiek zmian technicznych. Z tego powodu warto przyjrze─ç si─Ö konfiguracji przy pomocy adnotacji @Bean.

Na koniec kr├│tki przyk┼éad jak mo┼╝e wygl─ůda─ç kod wraz z adnotacj─ů @Component, oczywi┼Ťcie ka┼╝d─ů z poni┼╝szych klas nale┼╝y rozdzieli─ç na osobne pliki. Warto doda─ç, ┼╝e od Springa 4.3 adnotacja @Autowired nie jest konieczna nad konstruktorem je┼Ťli klasa posiada tylko jeden konstruktor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
public class ItemRepository {
  ...
}

@Component
public class EventPublisher {
  ...
}

@Component
public class OrderService {

  private final ItemRepository itemRepository;
  private final OrderService orderService;

  @Autowired // mo┼╝na pomin─ů─ç t─ů adnotacj─Ö
  public OrderService(ItemRepository itemRepository, OrderService orderService) {
    this.itemRepository = itemRepository;
    this.orderService = orderService;
  }

  ...
}

Adnotacja @Bean

Wszyscy mamy nadziej─Ö, ┼╝e czasy konfiguracji poprzez XML w Springu ju┼╝ dawno min─Ö┼éy. Teraz mo┼╝emy wszystko konfigurowa─ç na poziomie klasy. Wystarczy stworzy─ç klas─Ö konfiguracyjna, oznaczy─ç j─ů za pomoc─ů adnotacji @Configuration i zadeklarowa─ç w niej odpowiednie metody kreacyjne tworz─ůce instancje odpowiednich klas. Oczywi┼Ťcie je┼Ťli dana klasa wymaga do jej stworzenia zale┼╝no┼Ťci to mo┼╝emy wstrzykn─ů─ç potrzebny obiekt jako parametr metody. Wygl─ůda to mniej wi─Öcej w spos├│b przekazany poni┼╝ej.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration(proxyBeanMethods = false)
public class AppConfiguration {

  @Bean
  ItemRepository itemRepository() {
    return new ItemRepository();
  }

  @Bean
  EventPublisher eventPublisher() {
    return new EventPublisher();
  }

  @Bean
  OrderService orderService(
    ItemRepository itemRepository, 
    EventPublisher eventPublisher
  ) {
    return new OrderService(itemRepository, eventPublisher);
  }
}

Tutaj ma┼éa dygresja do adnotacji @Configuration, doda┼éem w niej w┼éasno┼Ť─ç ÔÇśproxyBeanMethods = falseÔÇÖ, kt├│rej u┼╝ycie zosta┼éo dobrze uzasadnione na StackOverflow. W skr├│cie chodzi o to, aby nie tworzy─ç niepotrzebnie proxy i lepiej kontrolowa─ç ile zale┼╝no┼Ťci jest wymagane przez dan─ů klas─Ö (kieruj─ůc si─Ö zasad─ů Single Responsible Principle). Wracaj─ůc do g┼é├│wnego w─ůtku, dzi─Öki klasie konfiguracyjnej nasza logika biznesowa mo┼╝e pozosta─ç niezale┼╝na. Je┼Ťli chcieliby┼Ťmy przej┼Ť─ç na inny framework posiadaj─ůcy kontener zale┼╝no┼Ťci to nie dotykamy w og├│le serca naszej aplikacji. Wyrzucamy tylko klasy konfiguracyjne Springa i korzystamy z innych mo┼╝liwo┼Ťci nowego frameworka, przez to zmiana infrastruktury staje si─Ö naprawd─Ö prosta. Wszystko ma minusy, wi─Öc i to rozwi─ůzanie nie jest idealne. Nie mo┼╝emy oddelegowa─ç sposobu tworzenia obiekt├│w do Springa, sami musimy napisa─ç konfiguracj─Ö kreuj─ůc─ů obiekty. Moim zdaniem ma to spor─ů korzy┼Ť─ç w postaci tego, ┼╝e mo┼╝emy weryfikowa─ç czy jaka┼Ť klasa nie ma zbyt wielu odpowiedzialno┼Ťci kosztem kilkunastu linijek kodu wi─Öcej.

Oczywi┼Ťcie w przyk┼éadzie powy┼╝ej korzystamy ze s┼é├│wka kluczowego new jednak wykorzystywane jest ono tylko w celu napisania przepisu tworzenia obiekt├│w. W kodzie odpowiedzialnym za logi─Ö biznesow─ů nie musimy si─Ö ju┼╝ tym w og├│le martwi─ç!

Bonus korzystania z **@Bean w testach

Podej┼Ťcie z u┼╝yciem adnotacji @Bean ma te┼╝ korzystny efekt uboczny przy pisaniu test├│w. Wykorzystuj─ůc produkcyjny schemat konstruowania obiekt├│w mo┼╝emy nadpisa─ç metod─Ö tworz─ůc─ů po┼é─ůczenie np. z baz─ů danych na tak─ů korzystaj─ůc─ů z danych w pami─Öci. Dzi─Öki temu jeste┼Ťmy w stanie przygotowa─ç szybki test, kt├│ry nie stawia ca┼éego kontenera zale┼╝no┼Ťci Springa. Jest on w stanie przetestowa─ç integracj─Ö obiekt├│w naszej g┼é├│wnej logiki biznesowej bez kosztownych po┼é─ůcze┼ä z zewn─Ötrznymi komponentami.

Konfiguracja w AppConfiguration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration(proxyBeanMethods = false)
public class AppConfiguration {

  @Bean
  ItemRepository itemRepository() {
    return new ItemRepository();
  }

  @Bean
  OrderVerification orderVerification(ItemRepository itemRepository) {
    return new OrderVerification(itemRepository);
  }

  @Bean
  OrderService orderService(OrderVerification orderVerification) {
    return new OrderService(orderVerification);
  }
}

Zmodyfikowana konfiguracja dla test├│w w klasie TestConfiguration.

1
2
3
4
5
6
7
8
9
10
public class TestConfiguration extends AppConfiguration {

  OrderVerification testOrderVerification() {
    return new OrderVerification(new InMemoryItemRepository());
  }

  OrderService testOrderService() {
    return orderService(testOrderVerification());
  }
}

Klasa testowa TestClass.

1
2
3
4
5
6
7
8
9
10
11
12
public class TestClass {

  @Test
  void manualConfigurationForTest() {

    OrderService orderService = new TestConfiguration().testOrderService();

    OrderResult result = orderService.verify(sameData());

    assertThat(result.getStatus()).isEqualTo(OrderStatus.ORDERED);
  }
}

Wa┼╝ne, ┼╝eby nie stosowa─ç w klasie TestConfiguration adnotacji @Bean oraz @Configuration. Korzystamy z niej jak ze zwyk┼éej klasy, w kt├│rej tylko zdefiniowali┼Ťmy spos├│b tworzenia naszych obiekt├│w.

Podsumowanie

Frameworki w programowaniu s─ů naprawd─Ö ┼Ťwietnym rozwi─ůzaniem, istniej─ů po to, aby u┼éatwi─ç nam prac─Ö. Nie ma sensu tworzy─ç w┼éasnego kontenera zale┼╝no┼Ťci zw┼éaszcza jak na rynku jest wiele dost─Öpnych rozwi─ůza┼ä napisanych przez do┼Ťwiadczonych programist├│w. Jednak potrafi─ů one wr─Öcz ÔÇťzaw┼éadn─ů─çÔÇŁ nasz─ů aplikacj─ů, wnikn─ů─ç w jej bebechy. Dlatego polecam korzysta─ç z takich p├│┼é┼Ťrodk├│w jak @Bean i @Configuration zamiast uzale┼╝nia─ç si─Ö od Springa przez adnotacj─Ö @Component. Chyba, ┼╝e to jest ma┼éa aplikacja, kt├│ra b─Ödzie mia┼éa kr├│tki czas ┼╝ycia i nie b─Ödzie jej przeszkadza┼éo wymieszanie logiki biznesowej z infrastruktur─ů. Oczywi┼Ťcie wszystko zale┼╝y od Ciebie i to Ty powiniene┼Ť decydowa─ç o najlepszym rozwi─ůzaniu dla Twojego projektu!