Uzorci dizajna u proljetnom okviru

1. Uvod

Obrasci dizajna važan su dio razvoja softvera. Ova rješenja ne samo da rješavaju ponavljajuće probleme već i pomažu programerima da razumiju dizajn okvira prepoznavanjem uobičajenih obrazaca.

U ovom uputstvu ćemo pogledati četiri najčešća uzorka dizajna koja se koriste u Spring Framework-u:

  1. Jednokraki uzorak
  2. Obrazac tvorničke metode
  3. Proxy obrazac
  4. Uzorak predloška

Također ćemo pogledati kako Spring koristi ove obrasce kako bi smanjio opterećenje za programere i pomogao korisnicima da brzo izvršavaju zamorne zadatke.

2. Jednokraki uzorak

Uzorak pojedinca je mehanizam koji osigurava da postoji samo jedan primjerak objekta po aplikaciji. Ovaj obrazac može biti koristan pri upravljanju zajedničkim resursima ili pružanju unakrsnih usluga, poput bilježenja.

2.1. Grah singleton

Općenito, singleton je globalno jedinstven za aplikaciju, ali u proljeće je ovo ograničenje opušteno. Umjesto toga, Proljeće ograničava po jedan jedini objekt po Spring IoC spremniku. U praksi to znači da će Spring stvoriti samo jedan grah za svaku vrstu po kontekstu aplikacije.

Springov pristup razlikuje se od stroge definicije singletona jer aplikacija može imati više Springovih spremnika. Stoga, više objekata iste klase može postojati u jednoj aplikaciji ako imamo više spremnika.

Prema zadanim postavkama Spring stvara sve grah kao pojedinačne.

2.2. Automatski ožičeni pojedinačni zvučnici

Na primjer, možemo stvoriti dva kontrolera u okviru jednog konteksta aplikacije i u svaki ubrizgati grah istog tipa.

Prvo stvorimo a Repozitorij knjiga koja upravlja našim Knjiga objekti domene.

Dalje, mi stvaramo LibraryController, koji koristi Repozitorij knjiga za vraćanje broja knjiga u knjižnici:

@RestController javna klasa LibraryController {@Autowired privatno spremište BookRepository; @GetMapping ("/ count") public Long findCount () {System.out.println (spremište); vratiti spremište.count (); }}

Na kraju, kreiramo BookController, koji se usredotočuje na Knjiga-specifične radnje, poput pronalaska knjige prema njezinom ID-u:

@RestController javna klasa BookController {@Autowired privatno spremište BookRepository; @GetMapping ("/ book / {id}") javna knjiga findById (@PathVariable long id) {System.out.println (spremište); vratiti spremište.findById (id) .get (); }}

Zatim pokrećemo ovu aplikaciju i izvodimo GET /računati i / knjiga / 1:

curl -X GET // localhost: 8080 / count curl -X GET // localhost: 8080 / book / 1

U izlazu aplikacije vidimo da je oboje Repozitorij knjiga objekti imaju isti ID objekta:

[zaštićena e-poštom] [zaštićena e-poštom]

The Repozitorij knjiga ID-ovi objekata u LibraryController i BookController su isti, što dokazuje da je Spring ubacio isti grah u oba kontrolera.

Možemo stvoriti zasebne instance Repozitorij knjiga grah promjenom opsega graha iz jednokrevetna do prototip koristiti @Opseg (ConfigurableBeanFactory.SCOPE_PROTOTYPE)bilješka.

To nalaže Springu da stvori zasebne objekte za svaki od Repozitorij knjiga grah koji stvara. Stoga, ako pregledamo ID objekta Repozitorij knjiga u svakom od naših kontrolera ponovno vidimo da oni više nisu isti.

3. Obrazac tvorničke metode

Obrazac tvorničke metode uključuje tvorničku klasu sa apstraktnom metodom za stvaranje željenog objekta.

Često želimo stvoriti različite objekte na temelju određenog konteksta.

Na primjer, naša aplikacija može zahtijevati objekt vozila. U nautičkom okruženju želimo stvoriti čamce, ali u zrakoplovnom okruženju želimo stvoriti zrakoplove:

Da bismo to postigli, možemo stvoriti tvorničku implementaciju za svaki željeni objekt i vratiti željeni objekt iz metode tvornice betona.

3.1. Kontekst aplikacije

Spring koristi ovu tehniku ​​u korijenu svog okvira za ubrizgavanje ovisnosti (DI).

Temeljno, Proljetne poslasticespremnik za grah kao tvornica koja proizvodi grah.

Dakle, Proljeće definira BeanFactory sučelje kao apstrakcija spremnika za grah:

javno sučelje BeanFactory {getBean (klasa requiredType); getBean (klasa requiredType, Object ... args); getBean (naziv niza); // ...]

Svaki od getBean metode smatra se tvorničkom metodom, koji vraća grah koji odgovara kriterijima dostavljenim metodi, poput vrste i naziva zrna.

Proljeće se zatim proteže BeanFactory s ApplicationContext sučelje koje uvodi dodatnu konfiguraciju aplikacije. Spring koristi ovu konfiguraciju za pokretanje spremnika za grah na temelju neke vanjske konfiguracije, poput XML datoteke ili Java napomena.

Koristiti ApplicationContext izvedbe klasa poput AnnotationConfigApplicationContext, tada možemo stvoriti grah raznim tvorničkim metodama naslijeđenim od BeanFactory sučelje.

Prvo kreiramo jednostavnu konfiguraciju aplikacije:

@Configuration @ComponentScan (basePackageClasses = ApplicationConfig.class) javna klasa ApplicationConfig {}

Dalje, kreiramo jednostavnu klasu, Foo, koji ne prihvaća argumente konstruktora:

@Component javni razred Foo {}

Zatim stvorite drugu klasu, Bar, koji prihvaća jedan argument konstruktora:

@Component @Scope (ConfigurableBeanFactory.SCOPE_PROTOTYPE) Bar javne klase {naziv privatnog niza; javna traka (naziv niza) {this.name = name; } // Dohvati ...}

Na kraju, svoj grah stvaramo kroz AnnotationConfigApplicationContext provedba 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 očekujeName = "Neko ime"; ApplicationContext context = new AnnotationConfigApplicationContext (ApplicationConfig.class); Linija trake = context.getBean (Bar.class, očekivanoName); assertNotNull (traka); assertThat (bar.getName (), je (očekivanoIme)); }

Koristiti getBean tvorničkom metodom, možemo stvoriti konfigurirani grah koristeći samo tip klase i - u slučaju Bar - parametri konstruktora.

3.2. Vanjska konfiguracija

Ovaj je obrazac svestran jer možemo potpuno promijeniti ponašanje aplikacije na temelju vanjske konfiguracije.

Ako želimo promijeniti implementaciju automatski ožičenih objekata u aplikaciji, možemo prilagoditi ApplicationContext implementacija koju koristimo.

Na primjer, možemo promijeniti AnnotationConfigApplicationContext na klasu konfiguracije temeljenu na XML-u, kao što je ClassPathXmlApplicationContext:

@Test javna praznina danaXmlConfiguration_whenGetPrototypeBean_thenReturnConstructedBean () {String očekujeName = "Neki naziv"; ApplicationContext context = new ClassPathXmlApplicationContext ("context.xml"); // Isti test kao i prije ...}

4. Proxy obrazac

Proxyji su prikladan alat u našem digitalnom svijetu i vrlo ih često koristimo izvan softvera (poput mrežnih proxyja). U kodu, proxy obrazac je tehnika koja omogućuje jednom objektu - proxyju - da kontrolira pristup drugom objektu - subjektu ili usluzi.

4.1. Transakcije

Da bismo stvorili proxy, kreiramo objekt koji implementira isto sučelje kao i naš subjekt i sadrži referencu na subjekt.

Tada umjesto subjekta možemo koristiti proxy.

U proljeće se grah proksiira da kontrolira pristup temeljnom zrnu. Ovaj pristup vidimo kada koristimo transakcije:

@Service javna klasa BookManager {@Autowired privatno spremište BookRepository; @Transactional public book create (Autor niza) {System.out.println (repository.getClass (). GetName ()); vratiti spremište.create (autor); }}

U našem Upravitelj knjiga razreda, bilježimo stvoriti metoda s @Transational bilješka. Ova bilješka upućuje Springu da atomski izvrši naš stvoriti metoda. Bez proxyja Spring ne bi mogao kontrolirati pristup našem Repozitorij knjiga grah i osigurajte njegovu dosljednost u transakcijama.

4.2. CGLib punomoćnici

Umjesto toga, Proljeće stvara proxy koji obavija naš Repozitorij knjiga grah i instrumente naš grah da izvrši naš stvoriti metoda atomska.

Kad nazovemo svoje BookManager # stvori metodu, možemo vidjeti izlaz:

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

Tipično bismo očekivali da ćemo vidjeti standard Repozitorij knjiga ID objekta; umjesto toga, vidimo EnhancerBySpringCGLIB ID objekta.

Iza scene, Proljeće je zamotalo naše Repozitorij knjiga objekt iznutra kao EnhancerBySpringCGLIB objekt. Proljeće tako kontrolira pristup našem Repozitorij knjiga objekt (osiguravanje dosljednosti transakcija).

Općenito, Spring koristi dvije vrste proxyja:

  1. CGLib proxyji - koriste se kod proksiranja klasa
  2. JDK dinamički proksiji - koristi se kod proksiranja sučelja

Iako smo koristili transakcije za izlaganje temeljnih proxyja, Spring će koristiti proxyje za bilo koji scenarij u kojem mora kontrolirati pristup grahu.

5. Uzorak metode predloška

U mnogim okvirima, značajan dio koda čini šifra uzorka.

Na primjer, prilikom izvršavanja upita u bazi podataka, mora se izvršiti isti niz koraka:

  1. Uspostavite vezu
  2. Izvršite upit
  3. Izvršite čišćenje
  4. Zatvorite vezu

Ovi su koraci idealan scenarij za obrazac metode predloška.

5.1. Predlošci i povratni pozivi

Uzorak metode predloška tehnika je koja definira korake potrebne za neku radnju, implementirajući korake uzorka i ostavljajući prilagodljive korake apstraktnim. Podrazredi tada mogu implementirati ovu apstraktnu klasu i pružiti konkretnu implementaciju za korake koji nedostaju.

U slučaju upita naše baze podataka možemo stvoriti predložak:

javna sažetak DatabaseQuery {javna void izvrši () {Veza veze = createConnection (); executeQuery (veza); closeConnection (veza); } zaštićena veza CreateConnection () {// Poveži se s bazom podataka}} zaštićena void closeConnection (veza veze) {// Zatvori vezu ...} zaštićena apstraktna void izvršenjeQuery (veza veze); }

Alternativno, korak koji nedostaje možemo pružiti davanjem metode povratnog poziva.

Metoda povratnog poziva je metoda koja omogućuje subjektu da klijentu signalizira da je neka željena radnja dovršena.

U nekim slučajevima subjekt ovaj povratni poziv može koristiti za izvršavanje radnji - poput mapiranja rezultata.

Na primjer, umjesto da imate executeQuery metodom, možemo isporučiti izvršiti metoda niz upita i metoda povratnog poziva za rukovanje rezultatima.

Prvo kreiramo metodu povratnog poziva koja uzima a Rezultati objekt i preslikava ga na objekt tipa T:

javno sučelje ResultsMapper {javna T karta (rezultati rezultata); }

Tada mijenjamo svoje DatabaseQuery klasa za korištenje ovog povratnog poziva:

javna sažetak DatabaseQuery {javno T izvršenje (String upit, Map Mapper ResultsMapper) {Veza veze = createConnection (); Rezultati rezultata = executeQuery (veza, upit); closeConnection (veza); return mapper.map (rezultati); ] zaštićeni rezultati executeQuery (veza veze, niz upita) {// Izvrši upit ...}}

Ovaj mehanizam povratnog poziva je upravo pristup koji Spring koristi s JdbcTemplate razred.

5.2. JdbcTemplate

The JdbcTemplate razred pruža upit metoda koja prihvaća upit Niz i ResultSetExtractor objekt:

javna klasa JdbcTemplate {javni T upit (konačni String sql, konačni ResultSetExtractor rse) baca DataAccessException {// Izvrši upit ...} // Ostale metode ...}

The ResultSetExtractor pretvara Postavi rezultat objekt - koji predstavlja rezultat upita - u objekt tipa domene T:

@FunctionalInterface javno sučelje ResultSetExtractor {T extractData (ResultSet rs) baca SQLException, DataAccessException; }

Spring dodatno smanjuje šifru uzorka stvaranjem specifičnijih sučelja za povratni poziv.

Na primjer, RowMapper sučelje se koristi za pretvaranje jednog reda SQL podataka u objekt tipa domene T.

@FunctionalInterface javno sučelje RowMapper {T mapRow (ResultSet rs, int rowNum) baca SQLException; }

Da biste prilagodili RowMapper sučelje s očekivanim ResultSetExtractor, Proljeće stvara RowMapperResultSetExtractor razred:

javna klasa JdbcTemplate {javni popis upita (String sql, RowMapper rowMapper) baca DataAccessException {povratni rezultat (upit (sql, novi RowMapperResultSetExtractor (rowMapper))); } // Ostale metode ...}

Umjesto da pruži logiku za pretvaranje cjeline Postavi rezultat objekt, uključujući iteraciju preko redaka, možemo pružiti logiku za pretvaranje jednog retka:

javna klasa BookRowMapper implementira RowMapper {@Override public Book mapRow (ResultSet rs, int rowNum) baca SQLException {Book book = new Book (); book.setId (rs.getLong ("id")); book.setTitle (rs.getString ("naslov")); book.setAuthor (rs.getString ("autor")); knjiga povratka; }}

Pomoću ovog pretvarača možemo upitati bazu podataka pomoću JdbcTemplate i mapirajte svaki rezultirajući redak:

JdbcTemplate template = // izradi predložak ... template.query ("SELECT * FROM books", new BookRowMapper ());

Osim upravljanja JDBC bazom podataka, Spring također koristi predloške za:

  • Java Message Service (JMS)
  • API trajnosti Java (JPA)
  • Hibernate (sada zastarjelo)
  • Transakcije

6. Zaključak

U ovom smo tutorijalu pogledali četiri najčešća uzorka dizajna primijenjena u Spring Springwork-u.

Također smo istražili kako Spring koristi ove obrasce za pružanje bogatih značajki, istovremeno smanjujući teret za programere.

Kôd iz ovog članka možete pronaći na GitHubu.