Dizajnové vzory v jarnom rámci

1. Úvod

Dizajnové vzory sú podstatnou súčasťou vývoja softvéru. Tieto riešenia nielenže riešia opakujúce sa problémy, ale tiež pomáhajú vývojárom pochopiť návrh rámca rozpoznaním bežných vzorov.

V tomto tutoriáli sa pozrieme na štyri najbežnejšie návrhové vzory používané v jarnom rámci:

  1. Singleton vzor
  2. Vzor výrobnej metódy
  3. Proxy vzor
  4. Vzor šablóny

Pozrime sa tiež na to, ako Spring používa tieto vzorce na zníženie záťaže pre vývojárov a na pomoc používateľom rýchlo vykonávať zdĺhavé úlohy.

2. Singletonov vzor

Vzor singleton je mechanizmus, ktorý zaisťuje, že v aplikácii existuje iba jedna inštancia objektu. Tento model môže byť užitočný pri správe zdieľaných zdrojov alebo poskytovaní prierezových služieb, ako je napríklad prihlásenie.

2.1. Fazuľa Singleton

Všeobecne platí, že singleton je pre aplikáciu globálne jedinečný, ale na jar je toto obmedzenie uvoľnené. Namiesto toho Jar obmedzuje singleton pre jeden objekt na každý Spring IoC kontajner. V praxi to znamená, že jar vytvorí pre každý typ iba jednu fazuľu pre každý kontext aplikácie.

Jarný prístup sa líši od striktnej definície jedného slova, pretože aplikácia môže mať viac ako jeden jarný kontajner. Preto v jednej aplikácii môže existovať viac objektov tej istej triedy, ak máme viac kontajnerov.

Spring štandardne vytvára všetky fazule ako singletons.

2.2. Samostatne zapojené jednotlivci

Napríklad môžeme vytvoriť dva radiče v rámci jedného kontextu aplikácie a do každého vložiť fazuľu rovnakého typu.

Najskôr vytvoríme a BookRepository ktorý spravuje naše Kniha doménové objekty.

Ďalej tvoríme LibraryController, ktorý používa BookRepository vrátiť počet kníh v knižnici:

@RestController verejná trieda LibraryController {@Autowired private BookRepository repository; @GetMapping ("/ count") public Long findCount () {System.out.println (úložisko); return repository.count (); }}

Nakoniec vytvoríme a BookController, ktorá sa zameriava na Kniha-špecifické akcie, ako napríklad vyhľadanie knihy podľa jej ID:

@RestController verejná trieda BookController {@Autowired private BookRepository repository; @GetMapping ("/ book / {id}") verejná kniha findById (@PathVariable dlhé id) {System.out.println (úložisko); return repository.findById (id) .get (); }}

Potom spustíme túto aplikáciu a vykonáme GET / počítať a / kniha / 1:

curl -X GET // localhost: 8080 / počet curl -X GET // localhost: 8080 / kniha / 1

Na výstupe z aplikácie vidíme, že oboje BookRepository objekty majú rovnaké ID objektu:

[chránené e-mailom] [chránené e-mailom]

The BookRepository ID objektov v LibraryController a BookController sú rovnaké, čo dokazuje, že Spring vstrekla do oboch ovládačov to isté zrno.

Môžeme vytvoriť samostatné inštancie BookRepository fazuľa zmenou rozsahu fazule z singleton do prototyp pomocou @Rozsah (ConfigurableBeanFactory.SCOPE_PROTOTYPE)anotácia.

Ak to urobíte, dá sa Springovi pokyn, aby pre každý z nich vytvoril samostatné objekty BookRepository fazuľa, ktorú vytvára. Preto, ak skontrolujeme ID objektu BookRepository v každom z našich ovládačov opäť vidíme, že už nie sú rovnaké.

3. Vzor továrenskej metódy

Vzor továrenskej metódy znamená, že továrenská trieda má abstraktnú metódu na vytvorenie požadovaného objektu.

Často chceme vytvárať rôzne objekty na základe konkrétneho kontextu.

Naša aplikácia môže napríklad vyžadovať objekt vozidla. V námornom prostredí chceme vytvoriť člny, ale v leteckom prostredí chceme vytvoriť lietadlá:

Aby sme to dosiahli, môžeme vytvoriť továrenskú implementáciu pre každý požadovaný objekt a vrátiť požadovaný objekt z metódy konkrétnej továrne.

3.1. Kontext aplikácie

Jar používa túto techniku ​​pri koreni svojho rámca Dependency Injection (DI).

Zásadne, Jarné dobrotynádoba na fazuľu ako továreň na výrobu fazule.

Jar tak definuje BeanFactory rozhranie ako abstrakcia fazuľového kontajnera:

verejné rozhranie BeanFactory {getBean (Class requiredType); getBean (Class requiredType, Object ... args); getBean (názov reťazca); // ...]

Každá z getBean metódy sa považuje za továrenskú metódu, ktorá vráti fazuľu vyhovujúcu kritériám dodaným metóde, napríklad typ a názov fazule.

Jar sa potom predlžuje BeanFactory s ApplicationContext rozhranie, ktoré zavádza ďalšiu konfiguráciu aplikácie. Spring používa túto konfiguráciu na spustenie kontajnera fazule na základe nejakej externej konfigurácie, napríklad súboru XML alebo anotácií Java.

Pomocou ApplicationContext triedne implementácie ako AnnotationConfigApplicationContext, potom môžeme fazuľa vytvárať pomocou rôznych továrenských metód zdedených z BeanFactory rozhranie.

Najskôr vytvoríme jednoduchú konfiguráciu aplikácie:

@Configuration @ComponentScan (basePackageClasses = ApplicationConfig.class) verejná trieda ApplicationConfig {}

Ďalej vytvoríme jednoduchú triedu, Foo, ktorý neprijíma žiadne argumenty konštruktora:

@ Komponenta verejná trieda Foo {}

Potom vytvorte ďalšiu triedu, Bar, ktorý prijíma jediný argument konštruktora:

@Component @Scope (ConfigurableBeanFactory.SCOPE_PROTOTYPE) verejná trieda Bar {súkromný názov reťazca; public Bar (názov reťazca) {this.name = name; } // Getter ...}

Nakoniec vytvoríme fazuľu cez AnnotationConfigApplicationContext implementácia ApplicationContext:

@Test public void whenGetSimpleBean_thenReturnConstructedBean () {ApplicationContext context = new AnnotationConfigApplicationContext (ApplicationConfig.class); Foo foo = context.getBean (Foo.class); assertNotNull (foo); } @Test public void whenGetPrototypeBean_thenReturnConstructedBean () {String expectName = "Some name"; ApplicationContext context = nový AnnotationConfigApplicationContext (ApplicationConfig.class); Bar bar = context.getBean (Bar.class, expectName); assertNotNull (bar); assertThat (bar.getName (), je (očakáva sa)); }

Pomocou getBean továrenskou metódou, môžeme vytvoriť nakonfigurované fazule iba pomocou typu triedy a - v prípade Bar - parametre konštruktora.

3.2. Externá konfigurácia

Tento vzor je všestranný, pretože môžeme úplne zmeniť správanie aplikácie na základe externej konfigurácie.

Ak chceme zmeniť implementáciu objektov s automatickým pripojením do aplikácie, môžeme upraviť ApplicationContext implementáciu, ktorú používame.

Napríklad môžeme zmeniť AnnotationConfigApplicationContext do konfiguračnej triedy založenej na XML, ako napr ClassPathXmlApplicationContext:

@Test public void givenXmlConfiguration_whenGetPrototypeBean_thenReturnConstructedBean () {String expectName = "Some name"; ApplicationContext context = nový ClassPathXmlApplicationContext ("context.xml"); // Rovnaký test ako predtým ...}

4. Proxy vzor

Proxy servery sú v našom digitálnom svete užitočným nástrojom a veľmi často ich používame aj mimo softvéru (napríklad sieťové proxy servery). V kóde vzor proxy je technika, ktorá umožňuje jednému objektu - proxy - riadiť prístup k inému objektu - subjektu alebo službe.

4.1. Transakcie

Aby sme vytvorili proxy, vytvoríme objekt, ktorý implementuje rovnaké rozhranie ako náš predmet a obsahuje odkaz na predmet.

Potom môžeme namiesto subjektu použiť proxy.

Na jar sú fazule proxy určené na riadenie prístupu k podkladovej fazuli. Tento prístup vidíme pri použití transakcií:

@Service public class BookManager {@Autowired private BookRepository repository; @ Transakčné verejné vytvorenie knihy (autor reťazca) {System.out.println (repository.getClass (). GetName ()); return repository.create (autor); }}

V našom BookManager triedy, anotujeme vytvoriť metóda s @ Transakčné anotácia. Táto anotácia dáva spoločnosti Spring pokyn, aby atómovo vykonala našu vytvoriť metóda. Bez proxy by Spring nemohla kontrolovať prístup k našim BookRepository fazuľa a zabezpečiť jej transakčnú konzistenciu.

4.2. CGLib proxy

Namiesto toho Jar vytvára proxy, ktoré zalamuje naše BookRepository fazuľa a nástroje, ktoré naša fazuľa vykonáva vytvoriť metóda atómovo.

Keď zavoláme svoje BookManager # vytvoriť metóda, môžeme vidieť výstup:

com.baeldung.patterns.proxy.BookRepository $$ EnhancerBySpringCGLIB $$ 3dc2b55c

Spravidla by sme čakali, že sa dočkáme nejakého štandardu BookRepository ID objektu; namiesto toho vidíme EnhancerBySpringCGLIB ID objektu.

V zákulisí, Jar zabalila naše BookRepository objekt vo vnútri ako EnhancerBySpringCGLIB objekt. Jar tak riadi prístup k nášmu BookRepository objekt (zabezpečenie transakčnej konzistencie).

Spring všeobecne používa dva typy proxy serverov:

  1. CGLib Proxies - Používa sa pri triedach proxy
  2. JDK Dynamic Proxies - Používa sa pri rozhraní proxy servera

Aj keď sme transakciami odhalili podkladové proxy, Jar použije proxy pre každý scenár, v ktorom musí riadiť prístup k fazuli.

5. Vzor metódy šablóny

V mnohých rámcoch je významnou časťou kódu štandardný kód.

Napríklad pri vykonávaní dotazu na databáze je potrebné dokončiť rovnakú sériu krokov:

  1. Nadviazať spojenie
  2. Vykonať dopyt
  3. Vykonajte čistenie
  4. Ukončite pripojenie

Tieto kroky sú ideálnym scenárom pre vzor metódy šablóny.

5.1. Šablóny a spätné volania

Vzor metódy šablóny je technika, ktorá definuje kroky potrebné na vykonanie niektorých krokov, implementácia štandardných krokov a ponechanie prispôsobiteľných krokov ako abstraktných.. Podtriedy potom môžu implementovať túto abstraktnú triedu a poskytnúť konkrétnu implementáciu chýbajúcich krokov.

Šablónu môžeme vytvoriť v prípade nášho databázového dotazu:

public abstract DatabaseQuery {public void execute () {Connection connection = createConnection (); executeQuery (pripojenie); closeConnection (pripojenie); } chránené pripojenie createConnection () {// Pripojiť k databáze ...} chránené void closeConnection (Pripojené pripojenie) {// Uzavrieť pripojenie ...} chránené abstraktné void executeQuery (Pripojenie k pripojeniu); }

Prípadne môžeme chýbajúci krok poskytnúť dodaním metódy spätného volania.

Metóda spätného volania je metóda, ktorá umožňuje subjektu signalizovať klientovi, že sa vykonala požadovaná akcia.

V niektorých prípadoch môže subjekt použiť toto spätné volanie na vykonávanie akcií - napríklad mapovania výsledkov.

Napríklad namiesto toho, aby ste mali executeQuery metóda, môžeme dodať vykonať na spracovanie výsledkov reťazec dotazu a metóda spätného volania.

Najskôr vytvoríme metódu spätného volania, ktorá trvá a Výsledky objekt a mapuje ho na objekt typu T:

verejné rozhranie ResultsMapper {verejná T mapa (Výsledky výsledkov); }

Potom zmeníme svoje DatabaseQuery triedy na využitie tohto spätného volania:

public abstract DatabaseQuery {public T execute (reťazcový dotaz, mapovač ResultsMapper) {pripojenie pripojenie = createConnection (); Výsledky výsledky = executeQuery (pripojenie, dopyt); closeConnection (pripojenie); návrat mapper.map (výsledky); ] chránené výsledky executeQuery (pripojenie, reťazec) {// vykonať dopyt ...}}

Tento mechanizmus spätného volania je presne prístup, ktorý Spring používa s JdbcTemplate trieda.

5.2. JdbcTemplate

The JdbcTemplate trieda poskytuje dopyt metóda, ktorá prijíma dotaz String a ResultSetExtractor objekt:

public class JdbcTemplate {public T query (final String sql, final ResultSetExtractor rse) throws DataAccessException {// Execute query ...} // Other methods ...}

The ResultSetExtractor prevádza Sada výsledkov objekt - predstavujúci výsledok dotazu - do doménového objektu typu T:

@ Verejné rozhranie @FunctionalInterface ResultSetExtractor {T extractData (ResultSet rs) hodí SQLException, DataAccessException; }

Jar ďalej redukuje štandardný kód vytvorením konkrétnejších rozhraní spätného volania.

Napríklad RowMapper rozhranie sa používa na prevod jedného riadku údajov SQL na doménový objekt typu T.

@ Verejné rozhranie @FunctionalInterface RowMapper {T mapRow (ResultSet rs, int rowNum) hodí SQLException; }

Prispôsobiť RowMapper rozhranie k očakávaným ResultSetExtractor, Jar vytvára RowMapperResultSetExtractor trieda:

public class JdbcTemplate {public List query (String sql, RowMapper rowMapper) throws DataAccessException {return result (query (sql, new RowMapperResultSetExtractor (rowMapper))); } // Ostatné metódy ...}

Namiesto logiky premeny celku Sada výsledkov objekt, vrátane iterácie nad riadkami, môžeme poskytnúť logiku pre prevod jedného riadku:

public class BookRowMapper implementuje RowMapper {@Override public Book mapRow (ResultSet rs, int rowNum) hodí SQLException {Book book = new Book (); book.setId (rs.getLong ("id")); book.setTitle (rs.getString ("názov")); book.setAuthor (rs.getString ("autor")); spiatočná kniha; }}

Pomocou tohto prevodníka potom môžeme vyhľadávať v databáze pomocou znaku JdbcTemplate a namapujte každý výsledný riadok:

JdbcTemplate template = // vytvoriť šablónu ... template.query ("VYBERTE * Z kníh", nové BookRowMapper ());

Okrem správy databáz JDBC používa Spring aj šablóny pre:

  • Java správa služieb (JMS)
  • Java Persistence API (JPA)
  • Hibernácia (teraz zastaraná)
  • Transakcie

6. Záver

V tomto tutoriáli sme sa pozreli na štyri najbežnejšie návrhové vzory použité v jarnom rámci.

Skúmali sme tiež to, ako Spring využíva tieto vzory na poskytovanie bohatých funkcií pri znižovaní záťaže pre vývojárov.

Kód z tohto článku nájdete na GitHub.