Kurs Java

Beany i stereotypy

Na sam początek pytanie...co by było gdyby nie było Springa? I w ogóle na chwilę załóżmy, że nie istnieje żaden inny framework Java wspierający tworzenie kodu. Udając że żyjemy w takim świecie, spróbujmy stworzyć zalążek projektu zaczynając od metody main.

Tworzenie programu bez Springa

Nie komplikujmy zbytnio sprawy i przyjmijmy od razu, że będziemy chcieli wprowadzić tylko kilka szablonów klas (bez większej implementacji). Pierwsza klasa ma przyjąć żądanie powstałe w wyniku akcji użytkownika (kontroler), druga (serwis) ma za zadanie wykonać dowolne algorytmy, a trzecia zapisać wynik tych algorytmów w bazie (takie DAO, ale przyjmijmy tutaj nazwę repozytorium). Dorzućmy do tego jeszcze klasę, której odpowiedzialnością będzie wykonanie dowolnej dodatkowej operacji, nieco na uboczu, ale wielokrotnie - po prostu komponent do wykonywania "czegoś". Na koniec, niech domeną naszego programu będzie klasa Item.

Tak naprawdę to my już stworzyliśmy dla Ciebie taki projekt. Posiada on klasy ItemController, ItemService, ItemRepository oraz ItemComponent i został nazwany nieco przewrotnie - spring-boot-materialy-praktyczne-bez-springa:
W Springu mamy takie coś co się nazywa BeanFactory, więc i my stworzyliśmy sobie klasę CustomBeanFactory (w pakiecie custom), tak abyśmy mogli poudawać trochę framework, a przez to mieć swoje miejsce, w którym będziemy tworzyć i przechowywać obiekty. W CustomBeanFactory, w trakcie pierwszego odwołania się do niego, inicjowany jest zestaw naszych obiektów. Jest tam też metoda do pobierania stworzonego obiektu po klasie tego obiektu:
public class CustomBeanFactory {

    public static Map<Class<?>, CustomBean> customBeans = new LinkedHashMap<>();
    
    static {
        customBeans.put(ItemController.class, CustomBeanFactory.createBean(ItemController.class));
        customBeans.put(ItemService.class, CustomBeanFactory.createBean(ItemService.class));
        customBeans.put(ItemRepository.class, CustomBeanFactory.createBean(ItemRepository.class));
        customBeans.put(ItemComponent.class, CustomBeanFactory.createBean(ItemComponent.class));
        
        customBeans.values().stream()
        		.forEach(customBean -> customBean.onApplicationEvent(new CustomApplicationReadyEvent()));
    }
    
    static CustomBean createBean(Class<?> requiredType) {
    
        if (requiredType.equals(ItemComponent.class)) {
            return new ItemComponent();
        } else if (requiredType.equals(ItemController.class)) {
            return new ItemController();
        } else if (requiredType.equals(ItemService.class)) {
            return new ItemService();
        } else if (requiredType.equals(ItemRepository.class)) { 
            return new ItemRepository();
        }
        
        return null;
    }
    
    @SuppressWarnings("unchecked")
    public static <T> T getBean(Class<?> requiredType) {
        return (T) customBeans.get(requiredType);
    }
}
W projekcie mamy też klasę uruchomieniową SpringBootInPracticeNoSpring i to jest miejsce, na którym chcemy się teraz skupić.
public class SpringBootInPracticeNoSpring {

    private static final Logger LOG = LoggerFactory.getLogger(SpringBootInPracticeNoSpring.class);
    
    public static void main(String[] args) {
    
        ItemComponent itemComponent = CustomBeanFactory.getBean(ItemComponent.class);
        LOG.info("Application contains: {}", itemComponent.getName());
        
        ItemController itemController = CustomBeanFactory.getBean(ItemController.class);
        ItemService itemService = CustomBeanFactory.getBean(ItemService.class);
        ItemRepository itemRepository = CustomBeanFactory.getBean(ItemRepository.class);
        
        LOG.info("Application contains: {}", itemController.getName());
        LOG.info("Application contains: {}", itemService.getName());
        LOG.info("Application contains: {}", itemRepository.getName());
    }
}
Po wykonaniu linijki pobierającej beana (pierwsze odwołanie do naszego factory) mamy już stworzone wszystkie obiekty (ponieważ tworzenie odbywa się w bloku static). Od tej pory możemy z nich korzystać. Tym samym można teraz pobrać kolejno każdy z tych obiektów i wykonać na nim metodę. Mamy do nich dostęp wszędzie, gdzie tylko posiadamy dostęp do fabryki. Rozbudowując projekt, w dowolnej innej klasie będziemy mogli pobrać taki obiekt i go użyć. Nie musimy go tworzyć. Fabryka zrobiła to za nas i tyle. Istnieje tylko jedna instancja każdego z takich obiektów.

Wykonując ten krótki program zobaczysz efekt końcowy w postaci wydruku na konsoli:
Kolor pomarańczowy jest dosyć oczywisty. Dostajesz wydruk nazwy, a nazwa ta bierze się stąd, że określamy ją dla każdego z nich za pomocą wcześniej zdefiniowanej metody, na przykład:
public class ItemController implements CustomBean {

    private static final Logger LOG = LoggerFactory.getLogger(ItemController.class);
    
    public void onApplicationEvent(CustomApplicationReadyEvent event) {
        LOG.info("ItemController {} is ready", this.getClass().getName());
    }	
    
    public String getName() {
        return "CONTROLLER";
    }	
}
Co do pozostałych linii na konsoli, to one są generowane przez metodę onApplicationEvent, zdefiniowaną w każdej z klas. Skąd ona jest odpalana? To już nawet nie jest istotne. Co prawda jej wywołanie znajduje się w naszej fabryce, ale nie musisz się do tego za nadto przywiązywać. Ważne jest to, że metoda startuje dopiero po stworzeniu wszystkich obiektów.

Podsumujmy

Pokazaliśmy projekt, w którym pewien mechanizm tworzy obiekty, a potem udostępnia je do użycia w całej aplikacji. Dodatkowo udowodniliśmy, że po uruchomieniu programu dostajemy dane z właściwych, faktycznie zbudowanych obiektów. W ten sposób przygotowaliśmy coś co można nazwać ekstremalnie małym frameworkiem do zarządzania obiektami. Jest on dość ułomny (2-3 klasy z kilkoma metodami ciężko określić inaczej), ale jednak pokazuje ideę.

W tym momencie wróćmy do Springa. Teraz przez analogie zobaczysz jak będzie wyglądała podobna struktura i jak będzie działał cały program, gdy wdrożymy do niego Springa i odpalimy za pomocą Spring Boota. Zasadniczą różnicą w stosunku do właśnie opisanego projektu jest to, że stworzone obiekty będą pracowały tu nie pod kontrolą pseudo, mikro frameworka, ale profesjonalnego i rozbudowanego Spring Frameworka wraz z jego kontenerem. Co ważne, obiekty te nazywać będziemy beanami.

Tworzenie programu przy pomocy Springa

Tak, obiekty tworzone i zarządzane przez kontener Springa nazywane są beanami. Teraz zobaczymy jak w projekcie można się nimi posługiwać i jak wyglądają beany stworzone dzięki adnotacjom, które w Springu nazywamy stereotypami. Nie będziemy tutaj jeszcze łączyć (wiązać) tych beanów ze sobą, ale pokażemy Ci jak wygląda szkielet projektu springowego z kilkoma beanami i w jaki sposób można uzyskać do nich dostęp w trakcie uruchamiania programu. Dopiero w kolejnych trzech rozdziałach omówimy wiązania, przykład po przykładzie.

Mamy więc projekt stworzony za pomocą Spring Initializr i jest to pusty projekt Springa z klasą uruchomieniową oznaczoną adnotacją @SpringBootApplication. To jest pierwsza zmiana w stosunku do zwykłego nie-springowego programu.

Druga ważna zmiana to nowe wpisy w pliku pom.xml. Konfiguracja wskazuje - jako rodzica naszego projektu - Spring Boota w wersji 2.1.6, a dodatkowo ma podpięte zależności w postaci startera Spring Boot oraz startera testów.
Projekt, który posiada wymagane zależności, posiada adnotację @SpringBootApplication oraz ma odpowiednio zdefiniowaną metodę main jest już projektem Springa, dlatego w trakcie uruchamiania takiego projektu możemy zobaczyć na konsoli jak wstaje kontekst (zaczyna "bić serce") Springa:
W projekcie - tak jak poprzednio - posiadamy kilka przygotowanych wcześniej klas. Mamy więc ItemController, ItemService, ItemRepository oraz ItemComponent. Tym razem projekt nazwaliśmy spring-boot-materialy-praktyczne-beany-stereotypy:
Na pierwszy rzut oka nie różni się on za bardzo od projektu bez Springa. Na pewno nie ma w nim dodatkowego pakietu custom, bo teraz nie potrzebujemy już tworzyć własnej przechowalni dla obiektów. Wszystkim zajmuje się kontener Springa. Musimy mu jednak trochę w tym pomóc...
Pełny materiał ze wszystkimi źródłami kodu jest dostępny po wykupieniu pakietu. Zapraszamy!
Jeśli zakupiłeś materiał zaloguj się tutaj, aby uzyskać dostęp.

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy:
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .