Tervezési minták a tavaszi keretben
1. Bemutatkozás
A tervezési minták a szoftverfejlesztés elengedhetetlen részei. Ezek a megoldások nemcsak az ismétlődő problémákat oldják meg, hanem a közös minták felismerésével segítenek a fejlesztőknek megérteni a keretrendszert.
Ebben az oktatóanyagban a tavaszi keretben használt négy leggyakoribb tervezési mintát vizsgáljuk meg:
- Singleton minta
- Gyári módszer mintája
- Proxy minta
- Sablon minta
Megvizsgáljuk azt is, hogy a Spring hogyan használja ezeket a mintákat a fejlesztők terheinek csökkentésére és a felhasználók gyors unalmas feladatok elvégzésének elősegítésére.
2. Singleton minta
A szingulettmintázat olyan mechanizmus, amely biztosítja, hogy alkalmazásonként csak egy objektum létezik. Ez a minta hasznos lehet megosztott erőforrások kezelésekor vagy horizontális szolgáltatások, például naplózás biztosításakor.
2.1. Singleton Beans
Általában egy szingulett globálisan egyedülálló egy alkalmazás számára, de tavasszal ez a korlát enyhül. Helyette, A tavasz korlátozza egyetlen tárgy egy tárgyhoz tavaszi IoC konténerenként. A gyakorlatban ez azt jelenti, hogy a Spring alkalmazáskörnyezetenként csak egy babot hoz létre minden típushoz.
Spring megközelítése különbözik a szingulettek szigorú meghatározásától, mivel egy alkalmazásnak több Spring konténere is lehet. Ebből kifolyólag, Ugyanabban az osztályban több objektum is létezhet egyetlen alkalmazásban, ha több tárolónk van.

Alapértelmezés szerint a Spring minden babot szingliként hoz létre.
2.2. Autowired Singletons
Például létrehozhatunk két vezérlőt egyetlen alkalmazási környezetben, és mindegyikbe befecskendezhetünk egy azonos típusú babot.
Először létrehozunk egy BookRepository hogy kezeli a mi Könyv domain objektumok.
Ezután létrehozunk LibraryController, amely a BookRepository a könyvtárban lévő könyvek számának visszaadásához:
@RestController public class LibraryController {@Autowired private BookRepository repository; @GetMapping ("/ count") public Long findCount () {System.out.println (adattár); return repository.count (); }}
Végül létrehozunk egy BookController, amelyre összpontosít Könyv-specifikus műveletek, például egy könyv megtalálása az azonosítója alapján:
@RestController public class BookController {@Autowired private BookRepository repository; @GetMapping ("/ book / {id}") public Book findById (@PathVariable long id) {System.out.println (adattár); return repository.findById (id) .get (); }}
Ezután elindítjuk ezt az alkalmazást, és elvégezzük a GET-et /számol és / book / 1:
curl -X GET // localhost: 8080 / count curl -X GET // localhost: 8080 / book / 1
Az alkalmazás kimenetében azt látjuk, hogy mindkettő BookRepository az objektumok azonos objektumazonosítóval rendelkeznek:
[e-mail védett] [e-mail védett]
A BookRepository objektumazonosítók a LibraryController és BookController azonosak, bizonyítva, hogy Spring ugyanazt a babot injektálta mindkét kontrollerbe.
Külön példányokat hozhatunk létre a BookRepository babot a bab hatókörének megváltoztatásával szingli nak nek prototípus használni a @Hatókör (ConfigurableBeanFactory.SCOPE_PROTOTYPE)annotáció.
Ezzel utasítja Springet, hogy hozzon létre külön objektumokat az egyes BookRepository babot hoz létre. Ezért, ha megvizsgáljuk a BookRepository ismét mindegyik vezérlőnkben látjuk, hogy már nem ugyanazok.
3. Gyári módszer minta
A gyári metódus egy gyári osztályt tartalmaz, absztrakt módszerrel a kívánt objektum létrehozásához.
Gyakran különböző tárgyakat szeretnénk létrehozni egy adott kontextus alapján.
Például alkalmazásunkhoz szükség lehet jármű objektumra. Tengeri környezetben csónakokat akarunk létrehozni, repülőgép-környezetben azonban repülőgépeket:

Ennek megvalósításához minden kívánt objektumhoz létrehozhatunk egy gyári megvalósítást, és a betongyári módszerből visszaküldjük a kívánt objektumot.
3.1. Az alkalmazás kontextusa
Spring ezt a technikát alkalmazza a Dependency Injection (DI) keretrendszer gyökerében.
Alapvetően, Tavaszi csemegékbabtartály, mint babot termelő gyár.
Így Spring meghatározza a BeanFactory interfész babtartály absztrakciójaként:
nyilvános felület BeanFactory {getBean (Class requiredType); getBean (Class requiredType, Object ... érvel); getBean (karakterlánc neve); // ...]
Mindegyik getBean módszerek gyári módszernek számítanak, amely egy olyan babot ad vissza, amely megfelel a metódus kritériumainak, például a bab típusának és nevének.
A tavasz ekkor meghosszabbodik BeanFactory a ... val ApplicationContext interfész, amely további alkalmazáskonfigurációt vezet be. A Spring ezt a konfigurációt használja egy babtartály elindításához néhány külső konfiguráció alapján, például XML fájl vagy Java megjegyzések alapján.
Használni a ApplicationContext osztályos megvalósítások, mint AnnotationConfigApplicationContext, ezután babot hozhatunk létre a BeanFactory felület.
Először létrehozunk egy egyszerű alkalmazáskonfigurációt:
@Configuration @ComponentScan (basePackageClasses = ApplicationConfig.class) public class ApplicationConfig {}
Ezután létrehozunk egy egyszerű osztályt, Foo, amely nem fogad el konstruktor érveket:
@Component public class Foo {}
Ezután hozz létre egy másik osztályt, Rúd, amely egyetlen konstruktor argumentumot fogad el:
@Component @Scope (ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Bar {private String name; nyilvános sáv (karakterlánc neve) {this.name = név; } // Getter ...}
Végül a babonkat a AnnotationConfigApplicationContext végrehajtása ApplicationContext:
@Test public void whenGetSimpleBean_thenReturnConstrucedBean () {ApplicationContext context = new AnnotationConfigApplicationContext (ApplicationConfig.class); Foo foo = context.getBean (Foo.osztály); assertNotNull (foo); } @Test public void whenGetPrototypeBean_thenReturnConstrucedBean () {String várhatóNév = "Néhány név"; ApplicationContext context = új AnnotationConfigApplicationContext (ApplicationConfig.class); Bar bar = context.getBean (Bar.class, várhatóNév); assertNotNull (bár); assertThat (bar.getName (), van (várhatóNév)); }
Használni a getBean gyári módszerrel konfigurált babot hozhatunk létre csak az osztálytípus és - esetén Rúd - konstruktor paraméterei.
3.2. Külső konfiguráció
Ez a minta sokoldalú, mert a külső konfiguráció alapján teljesen megváltoztathatjuk az alkalmazás viselkedését.
Ha meg akarjuk változtatni az alkalmazásban az automatikusan bekötött objektumok megvalósítását, akkor a ApplicationContext az általunk alkalmazott megvalósítás.

Például megváltoztathatjuk a AnnotationConfigApplicationContext egy XML-alapú konfigurációs osztályba, mint pl ClassPathXmlApplicationContext:
@Test public void givenXmlConfiguration_whenGetPrototypeBean_thenReturnConstrucedBean () {String várhatóNév = "Néhány név"; ApplicationContext context = new ClassPathXmlApplicationContext ("context.xml"); // Ugyanaz a teszt, mint korábban ...}
4. Proxy minta
A meghatalmazottak hasznos eszköz a digitális világunkban, és nagyon gyakran használjuk őket a szoftvereken kívül (például a hálózati proxykon). Kódban, a proxy minta egy olyan technika, amely lehetővé teszi az egyik objektumnak - a proxy - a hozzáférést egy másik objektumhoz - az alanyhoz vagy a szolgáltatáshoz.

4.1. Tranzakciók
Proxy létrehozásához létrehozunk egy objektumot, amely ugyanazt az interfészt valósítja meg, mint az alanyunk, és tartalmaz egy hivatkozást az alanyra.
Ezután a tárgy helyett használhatjuk a proxyt.
Tavasszal a babot meghatalmazva ellenőrzik az alapul szolgáló babhoz való hozzáférést. Ezt a megközelítést látjuk tranzakciók használata során:
@Service public class BookManager {@Autowired private BookRepository repository; @Transactional public Book create (karakterlánc-készítő) {System.out.println (repository.getClass (). GetName ()); return repository.create (szerző); }}
Miénkben BookManager osztályban jegyzeteljük a teremt módszer a @ Tranzakció annotáció. Ez az annotáció arra utasítja Springet, hogy atomikusan hajtsa végre a mi feladatunkat teremt módszer. Meghatalmazó nélkül Spring nem tudná ellenőrizni a mi hozzáférésünket BookRepository babot, és biztosítja annak tranzakciós következetességét.
4.2. CGLib Proxies
Helyette, A tavasz létrehoz egy proxyt, amely bebugyolálja a mieinket BookRepository bab és műszereket ad a babunkra, hogy kivégezzük teremt módszer atomi úton.
Amikor hívjuk a miénk BookManager # létrehozás módszerrel láthatjuk a kimenetet:
com.baeldung.patterns.proxy.BookRepository $$ EnhancerBySpringCGLIB $$ 3dc2b55c
Jellemzően elvárnánk, hogy színvonalat találjunk BookRepository objektum azonosítója; ehelyett egy EnhancerBySpringCGLIB objektum azonosítója.
A színfalak mögött, A tavasz bebugyolálta BookRepository tárgy belül EnhancerBySpringCGLIB tárgy. A tavasz így ellenőrzi a hozzáférést a mi BookRepository objektum (ügyleti konzisztencia biztosítása).

Általában a Spring kétféle proxyt használ:
- CGLib proxyk - osztályok proxykozásakor használják
- JDK Dynamic Proxies - Felületek proxyként történő használatakor használatos
Míg tranzakciókat használtunk az alapul szolgáló proxyk felfedésére, A tavasz minden olyan forgatókönyv esetén meghatalmazottat használ, amelyben ellenőriznie kell a babhoz való hozzáférést.
5. Sablon módszer mintája
Számos keretrendszerben a kód jelentős része a kazánlap kódja.
Például egy lekérdezés adatbázisban történő végrehajtásakor ugyanazt a lépéssorozatot kell végrehajtani:
- Hozza létre a kapcsolatot
- Végezze el a lekérdezést
- Végezze el a tisztítást
- Zárja le a kapcsolatot
Ezek a lépések ideális forgatókönyvek a sablon módszer mintájához.
5.1. Sablonok és visszahívások
A sablon metódus egy olyan technika, amely meghatározza az egyes műveletekhez szükséges lépéseket, végrehajtja a kazán lépéseit, és a testreszabható lépéseket elvontként hagyja. Ezután az alosztályok megvalósíthatják ezt az absztrakt osztályt, és konkrét megvalósítást nyújthatnak a hiányzó lépésekhez.
Hozhatunk létre sablont az adatbázis lekérdezésünk esetében:
public abstract DatabaseQuery {public void execute () {Connection connection = createConnection (); executeQuery (kapcsolat); closeConnection (kapcsolat); } védett Connection createConnection () {// Csatlakozás adatbázishoz ...} védett void closeConnection (Csatlakozási kapcsolat) {// Kapcsolat bezárása ...} védett absztrakt void executeQuery (Csatlakozási kapcsolat); }
Alternatív megoldásként a visszahívási módszer megadásával biztosíthatjuk a hiányzó lépést.
A visszahívási módszer olyan módszer, amely lehetővé teszi az alany számára, hogy jelezze az ügyfélnek, hogy valamilyen kívánt művelet végrehajtásra került.
Bizonyos esetekben az alany ezt a visszahívást használhatja olyan műveletek végrehajtására, mint például az eredmények feltérképezése.

Például ahelyett, hogy executeQuery módszerrel tudjuk szállítani a végrehajtani metódus egy lekérdezési karakterlánc és egy visszahívási módszer az eredmények kezeléséhez.
Először létrehozzuk a visszahívási módszert, amely a Eredmények objektumot, és egy típusú objektumra térképezi fel T:
nyilvános felület ResultsMapper {nyilvános T térkép (Eredmények eredményei); }
Aztán változtatunk DatabaseQuery osztály használja ezt a visszahívást:
public abstract DatabaseQuery {public T végrehajtani (String lekérdezés, ResultsMapper leképező) {Connection connection = createConnection (); Eredményeredmények = executeQuery (kapcsolat, lekérdezés); closeConnection (kapcsolat); return mapper.map (eredmények); ] védett eredmények executeQuery (csatlakozási kapcsolat, karakterlánc lekérdezés) {// Lekérdezés végrehajtása ...}}
Ez a visszahívási mechanizmus pontosan azt a megközelítést alkalmazza, amelyet Spring a JdbcTemplate osztály.
5.2. JdbcTemplate
A JdbcTemplate osztály biztosítja a lekérdezés metódus, amely elfogad egy lekérdezést Húr és ResultSetExtractor tárgy:
public class JdbcTemplate {public T query (végleges String sql, végleges ResultSetExtractor rse) dobja a DataAccessException {// lekérdezés végrehajtása ...} // Egyéb módszerek ...}
A ResultSetExtractor átalakítja a ResultSet objektum - amely a lekérdezés eredményét képviseli - egy típusú tartomány objektummá T:
@FunctionalInterface nyilvános felület ResultSetExtractor {T extractData (ResultSet rs) dobja az SQLException, DataAccessException; }
A Spring tovább csökkenti a kazánlap kódját azáltal, hogy specifikusabb visszahívási interfészeket hoz létre.
Például a RowMapper interfész segítségével egyetlen sor SQL adat konvertálható egy típusú tartomány objektummá T.
@FunctionalInterface nyilvános felület RowMapper {T mapRow (ResultSet rs, int rowNum) dob SQLException; }
A RowMapper interfész a várható ResultSetExtractor, Tavasz létrehozza a RowMapperResultSetExtractor osztály:
public class JdbcTemplate {public list query (String sql, RowMapper rowMapper) throws DataAccessException {return result (query (sql, new RowMapperResultSetExtractor (rowMapper))); } // Egyéb módszerek ...}
Ahelyett, hogy logikát adna egy egész átalakításához ResultSet objektumot, beleértve a sorok feletti iterációt, logikát tudunk adni arról, hogyan lehet egyetlen sort konvertálni:
public class BookRowMapper megvalósítja a RowMapper {@Orride public Book mapRow (ResultSet rs, int rowNum) dob SQLException {Book book = new Book (); book.setId (rs.getLong ("id")); book.setTitle (rs.getString ("cím")); book.setAuthor (rs.getString ("szerző")); visszatérő könyv; }}
Ezzel a konverterrel ezután lekérdezhetünk egy adatbázist a JdbcTemplate és térképezze fel az egyes kapott sorokat:
JdbcTemplate template = // sablon létrehozása ... template.query ("SELECT * FROM könyvek", új BookRowMapper ());
A JDBC adatbázis-kezelés mellett a Spring sablonokat is használ:
- Java üzenetszolgáltatás (JMS)
- Java Persistence API (JPA)
- Hibernálás (már elavult)
- Tranzakciók
6. Következtetés
Ebben az oktatóanyagban a tavaszi keretben alkalmazott négy leggyakoribb tervezési mintát vizsgáltuk.
Megvizsgáltuk azt is, hogy a Spring hogyan használja ezeket a mintákat gazdag funkciók biztosítására, miközben csökkenti a fejlesztők terheit.
A cikk kódja a GitHub oldalon található.