Spring Bean vs. EJB - porovnanie funkcií

1. Prehľad

V priebehu rokov sa ekosystém Java veľmi vyvíjal a rástol. Počas tejto doby sú Enterprise Java Beans a Spring dve technológie, ktoré nielenže navzájom konkurovali, ale navzájom sa učili symbioticky.

V tomto návode pozrieme sa na ich históriu a rozdiely. Samozrejme uvidíme niekoľko príkladov kódu EJB a ich ekvivalentov vo svete jari.

2. Stručná história technológií

Na začiatok sa poďme rýchlo pozrieť na históriu týchto dvoch technológií a na to, ako sa v priebehu rokov neustále vyvíjali.

2.1. Enterprise Java Beans

Špecifikácia EJB je podmnožinou špecifikácie Java EE (alebo J2EE, teraz známej ako Jakarta EE). Jeho prvá verzia vyšla v roku 1999 a bola to jedna z prvých technológií navrhnutých na uľahčenie vývoja podnikových aplikácií na strane servera v prostredí Java.

Znášalo to bremeno vývojárov Javy na súbežnosť, bezpečnosť, vytrvalosť a spracovanie transakcií, a viac. Špecifikácia odovzdala tieto a ďalšie bežné podnikové obavy kontajnerom implementujúcich aplikačných serverov, ktoré ich bezproblémovo zvládli. Používanie EJB tak, ako boli, však bolo trochu ťažkopádne kvôli množstvu požadovanej konfigurácie. Navyše sa ukázalo, že je to úzke miesto v oblasti výkonu.

Ale teraz, s vynálezom anotácií a tvrdou konkurenciou z jari, sa EJB v ich najnovšej verzii 3.2 používajú oveľa jednoduchšie ako ich debutová verzia. Dnešné Enterprise Java Beans si výrazne požičiavajú od jarnej injekcie závislostí a používania POJO.

2.2. Jar

Zatiaľ čo EJB (a všeobecne Java EE) bojovali o uspokojenie komunity Java, Spring Framework dorazil ako závan čerstvého vzduchu. Jeho prvé míľnikové vydanie vyšlo v roku 2004 a ponúklo alternatívu k modelu EJB a jeho kontajnerom s vysokou hmotnosťou.

Vďaka jari Podnikové aplikácie Java sa teraz dali spustiť na ľahších kontajneroch IOC. Okrem toho okrem mnohých ďalších užitočných funkcií ponúkla aj inverziu závislostí, podporu AOP a režim dlhodobého spánku. S obrovskou podporou komunity Java sa Spring teraz exponenciálne rozrástol a možno ho nazvať úplným aplikačným rámcom Java / JEE.

Spring 5.0 vo svojom najnovšom avatarovi dokonca podporuje model reaktívneho programovania. Ďalšia odnož, Spring Boot, je kompletný menič hier s integrovanými servermi a automatickými konfiguráciami.

3. Predohra k porovnaniu funkcií

Pred prechodom na porovnanie funkcií so vzorkami kódu si urobíme niekoľko základných informácií.

3.1. Základný rozdiel medzi týmito dvoma

Po prvé, zásadný a zjavný rozdiel je v tom EJB je špecifikácia, zatiaľ čo jar predstavuje celý rámec.

Špecifikáciu implementuje veľa aplikačných serverov, ako sú GlassFish, IBM WebSphere a JBoss / WildFly. To znamená, že naša voľba použitia modelu EJB na vývoj back-endu našej aplikácie nestačí. Musíme si tiež zvoliť, ktorý aplikačný server použijeme.

Enterprise Java Beans sú teoreticky prenosné medzi servermi aplikácií, hoci vždy existuje predpoklad, že by sme nemali používať žiadne rozšírenia špecifické pre dodávateľa, ak sa má zachovať interoperabilita ako možnosť.

Po druhé, Pokiaľ ide o široké portfólio ponúk, technológia Spring as je bližšie k Java EE ako EJB. Zatiaľ čo EJB špecifikujú iba operácie typu backend, Spring, podobne ako Java EE, má tiež podporu pre vývoj UI, RESTful API a reaktívne programovanie.

3.2. Užitočná informácia

V nasledujúcich častiach uvidíme porovnanie týchto dvoch technológií s niektorými praktickými príkladmi. Pretože funkcie EJB sú podmnožinou oveľa väčšieho jarného ekosystému, pôjdeme podľa ich typov a uvidíme ich zodpovedajúce jarné ekvivalenty.

Ak chcete týmto príkladom čo najlepšie porozumieť, najskôr zvážte prečítanie informácií o Java EE Session Beans, Message Driven Beans, Spring Bean a Spring Bean Annotations.

Na spustenie vzoriek EJB budeme používať OpenJB ako náš vložený kontajner. Na spustenie väčšiny jarných príkladov bude stačiť jeho kontajner IOC; pre Spring JMS budeme potrebovať zabudovaného sprostredkovateľa ApacheMQ.

Na otestovanie všetkých našich vzoriek použijeme JUnit.

4. Singleton EJB == jar Komponent

Niekedy potrebujeme, aby kontajner vytvoril iba jednu inštanciu fazule. Povedzme napríklad, že na vypočítanie počtu návštevníkov našej webovej aplikácie potrebujeme fazuľu. Túto fazuľu je potrebné vytvoriť iba raz počas spustenia aplikácie.

Pozrime sa, ako to dosiahnuť pomocou Singleton Session EJB a jari Komponent.

4.1. Príklad Singleton EJB

Najprv budeme potrebovať rozhranie, aby sme určili, že náš EJB má schopnosť diaľkového ovládania:

@ Diaľkové verejné rozhranie CounterEJBRemote {int count (); Reťazec getName (); void setName (názov reťazca); }

Ďalším krokom je definovať implementačnú triedu s anotáciou javax.ejb.Singleton, a viola! Náš singleton je pripravený:

@Singleton verejná trieda CounterEJB implementuje CounterEJBRemote {private int count = 1; súkromné ​​meno reťazca; public int count () {return count ++; } // getter a setter pre meno} 

Ale predtým, ako budeme môcť otestovať singleton (alebo ľubovoľnú inú ukážku kódu EJB), musíme inicializovať ejbContainer a získajte kontext:

@BeforeClass public void initializeContext () hodí NamingException {ejbContainer = EJBContainer.createEJBContainer (); kontext = ejbContainer.getContext (); context.bind ("vložiť", toto); } 

Teraz sa pozrime na test:

@ Test public void givenSingletonBean_whenCounterInvoked_thenCountIsIncremented () hodí NamingException {int count = 0; CounterEJBRemote firstCounter = (CounterEJBRemote) context.lookup ("java: global / ejb-beans / CounterEJB"); firstCounter.setName ("prvý"); pre (int i = 0; i <10; i ++) {count = firstCounter.count (); } assertEquals (10, count); assertEquals ("first", firstCounter.getName ()); CounterEJBRemote secondCounter = (CounterEJBRemote) context.lookup ("java: global / ejb-beans / CounterEJB"); int pocet2 = 0; pre (int i = 0; i <10; i ++) {count2 = secondCounter.count (); } assertEquals (20, počet2); assertEquals ("first", secondCounter.getName ()); } 

Vo vyššie uvedenom príklade je potrebné uviesť niekoľko vecí:

  • Na získanie používame vyhľadávanie JNDI counterEJB z kontajnera
  • pocet2 zdvihne z bodu počítať nechal singleton na a pridáva k 20
  • secondCounter zachováva názov, ktorý sme si nastavili firstCounter

Posledné dva body demonštrujú význam jednotlivca. Pretože sa pri každom vyhľadaní použije rovnaká inštancia fazule, celkový počet je 20 a hodnota nastavená pre jednu zostáva rovnaká pre druhú.

4.2. Príklad jarnej fazule Singleton

Rovnakú funkčnosť je možné získať pomocou komponentov Spring.

Tu nemusíme implementovať žiadne rozhranie. Namiesto toho pridáme @ Komponent anotácia:

@Component public class CounterBean {// rovnaký obsah ako na EJB}

V skutočnosti sú komponenty na jar predvolene singletony.

Musíme tiež nakonfigurovať Spring na skenovanie komponentov:

@Configuration @ComponentScan (basePackages = "com.baeldung.ejbspringcomparison.spring") verejná trieda ApplicationConfig {} 

Podobne, ako sme inicializovali kontext EJB, nastavíme teraz jarný kontext:

@BeforeClass public static void init () {context = new AnnotationConfigApplicationContext (ApplicationConfig.class); } 

Teraz sa pozrime na naše Komponent v akcii:

@ Test public void whenCounterInvoked_thenCountIsIncremented () hodí NamingException {CounterBean firstCounter = context.getBean (CounterBean.class); firstCounter.setName ("prvý"); počet int = 0; pre (int i = 0; i <10; i ++) {count = firstCounter.count (); } assertEquals (10, count); assertEquals ("first", firstCounter.getName ()); CounterBean secondCounter = context.getBean (CounterBean.class); int pocet2 = 0; pre (int i = 0; i <10; i ++) {count2 = secondCounter.count (); } assertEquals (20, počet2); assertEquals ("first", secondCounter.getName ()); } 

Ako vidíme, jediný rozdiel, pokiaľ ide o EJB, je to, ako dostávame fazuľu z kontextu jarného kontajnera namiesto vyhľadávania JNDI.

5. Stavový EJB == jar Komponent s prototyp Rozsah

Občas, povedzme, keď staviame nákupný košík, potrebujeme, aby si naša fazuľa pamätala na jej stav, keď sa medzi hovormi metódy pohybovala tam a späť.

V takom prípade potrebujeme, aby náš kontajner vygeneroval pre každé vyvolanie samostatnú fazuľu a uložil stav. Pozrime sa, ako je to možné dosiahnuť pomocou našich príslušných technológií.

5.1. Stavový príklad EJB

Podobne ako v našej jedinej vzorke EJB potrebujeme a javax.ejb.Vzdialené rozhranie a jeho implementácia. Iba tentoraz s poznámkou javax.ejb. Úctyhodný:

@Stateful public class ShoppingCartEJB implementuje ShoppingCartEJBRemote {private String name; private List shoppingCart; public void addItem (položka reťazca) {shoppingCart.add (položka); } // konštruktor, getre a setre}

Poďme napísať jednoduchý test na nastavenie a názov a pridať položky do a kúpací vozík. Skontrolujeme jeho veľkosť a overíme meno:

@Test public void givenStatefulBean_whenBathingCartWithThreeItemsAdded_thenItemsSizeIsThree () hodí NamingException {ShoppingCartEJBRemote bathingCart = (ShoppingCartEJBRemote) context.lookup ("java: global / ejb-beans / ShoppingCartEJB"); bathingCart.setName ("bathingCart"); bathingCart.addItem ("mydlo"); bathingCart.addItem ("šampón"); bathingCart.addItem ("olej"); assertEquals (3, bathingCart.getItems (). size ()); assertEquals ("bathingCart", bathingCart.getName ()); } 

Teraz, aby sme preukázali, že fazuľa si skutočne udržiava stav naprieč inštanciami, pridajme k tomuto testu ďalší shoppingCartEJB:

ShoppingCartEJBRemote fruitCart = (ShoppingCartEJBRemote) context.lookup ("java: global / ejb-beans / ShoppingCartEJB"); fruitCart.addItem ("jablká"); fruitCart.addItem ("pomaranče"); assertEquals (2, fruitCart.getItems (). size ()); assertNull (fruitCart.getName ()); 

Tu sme nenastavili názov a teda jeho hodnota bola nulová. Pripomeňme si z testu jediného jedinca, že názov nastavený v jednom prípade sa zachoval v inom. To ukazuje, že sme sa oddelili ShoppingCartEJB inštancie z fondu beanov s rôznymi stavmi inštancií.

5.2. Príklad stavovej jarnej fazule

Na dosiahnutie rovnakého efektu s jarou potrebujeme a Komponent s rozsahom prototypu:

@Component @Scope (value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) verejná trieda ShoppingCartBean {// rovnaký obsah ako v EJB} 

To je všetko, iba anotácie sa líšia - zvyšok kódu zostáva rovnaký.

Na testovanie našej stavovej fazule môžeme použiť rovnaký test, aký je popísaný pre EJB. Jediný rozdiel je opäť v tom, ako dostaneme fazuľu z nádoby:

ShoppingCartBean bathingCart = context.getBean (ShoppingCartBean.class); 

6. EJB bez štátnej príslušnosti = čokoľvek na jar

Niekedy, napríklad vo vyhľadávacom API, nezaujíma nás ani inštančný stav fazule, ani to, či ide o singleton. Potrebujeme iba výsledky nášho hľadania, ktoré by mohli pochádzať z akejkoľvek inštancie typu bean pre všetko, na čom nám záleží.

6.1. Príklad bez štátnej príslušnosti EJB

Pre takéto scenáre má EJB variant bez štátnej príslušnosti. Kontajner udržiava fond inštancií fazule a ktorýkoľvek z nich sa vracia do metódy volania.

Spôsob, akým to definujeme, je rovnaký ako u iných typov EJB, so vzdialeným rozhraním a implementáciou pomocou javax.ejb. bez štátnej príslušnosti anotácia:

@ Verejná trieda bez štátnej príslušnosti FinderEJB implementuje FinderEJBRemote {private Map alphabet; public FinderEJB () {alphabet = new HashMap (); alphabet.put ("A", "Apple"); // tu pridať viac hodnôt do mapy} verejné vyhľadávanie reťazcov (kľúčové slovo reťazec) {return alphabet.get (kľúčové slovo); }} 

Pridajme ďalší jednoduchý test, aby sme to videli v akcii:

@Test public void givenStatelessBean_whenSearchForA_thenApple () hodí NamingException {assertEquals ("Apple", alphabetFinder.search ("A")); } 

Vo vyššie uvedenom príklade alphabetFinder je vložené ako pole v testovacej triede pomocou anotácie javax.ejb.EJB:

@EJB private FinderEJBRemote alphabetFinder; 

Ústrednou myšlienkou bezstavových EJB je zvýšenie výkonu vytvorením skupiny inštancií podobných bôbov.

Avšak Spoločnosť Spring sa neprihlásila k tejto filozofii a iba bez štátnej príslušnosti ponúka jednolôžka.

7. Fazuľa riadená správami == jarná JMS

Všetky doteraz diskutované EJB boli fazuľa session. Ďalším druhom je správa založená na správach. Ako už názov napovedá, zvyčajne sa používajú na asynchrónnu komunikáciu medzi dvoma systémami.

7.1. Príklad MDB

Aby sme vytvorili Enterprise Java Bean založený na správach, musíme implementovať javax.jms.MessageListener rozhranie definujúce jeho onMessage metódou a anotovať triedu ako javax.ejb.MessageDriven:

@MessageDriven (activationConfig = {@ActivationConfigProperty (propertyName = "destination", propertyValue = "myQueue"), @ActivationConfigProperty (propertyName = "destinationType", propertyValue = "javax.jms.Queue")}) verejná trieda RecieverMDB implementuje MessageList Zdroj súkromný ConnectionFactory connectionFactory; @Resource (name = "ackQueue") súkromná fronta ackQueue; public void onMessage (správa správy) {try {TextMessage textMessage = (TextMessage) správa; Reťazec producerPing = textMessage.getText (); if (producerPing.equals ("marco")) {potvrdiť ("pólo"); }} catch (JMSException e) {throw new IllegalStateException (e); }}} 

Všimnite si, že pre našu MDB poskytujeme tiež niekoľko konfigurácií:

      • cieľový typ ako Fronta
      • myQueue ako destinácia názov frontu, ktorý naša fazuľa počúva

V tomto príklade náš prijímateľ tiež vydá potvrdenie a v tomto zmysle je sám o sebe odosielateľom. Pošle správu do iného volaného frontu ackQueue.

Teraz sa na to pozrieme spolu s testom:

@Test public void givenMDB_whenMessageSent_thenAcknowledgementReceived () vyvolá InterruptedException, JMSException, NamingException {Connection connection = connectionFactory.createConnection (); connection.start (); Relácia session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer (myQueue); producent.send (session.createTextMessage ("marco")); MessageConsumer response = session.createConsumer (ackQueue); assertEquals ("pólo", (((TextMessage) response.receive (1000)). getText ()); } 

Tu poslali sme správu na myQueue, ktoré dostali naši @MessageDriven komentovaný POJO. Tento POJO potom poslal potvrdenie a náš test dostal odpoveď ako MessageConsumer.

7.2. Jarný príklad JMS

Teraz je čas urobiť to isté pomocou programu Spring!

Najskôr budeme musieť kvôli tomuto účelu pridať trochu konfigurácie. Musíme anotovať naše ApplicationConfig trieda predtým s @EnableJms a pridajte pár fazule na nastavenie JmsListenerContainerFactory a JmsTemplate:

@EnableJms verejná trieda ApplicationConfig {@Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory () {DefaultJmsListenerContainerFactory factory = nový DefaultJmsListenerContainerFactory (); factory.setConnectionFactory (connectionFactory ()); vrátiť továreň; } @Bean public ConnectionFactory connectionFactory () {return new ActiveMQConnectionFactory ("tcp: // localhost: 61616"); } @Bean public JmsTemplate jmsTemplate () {šablóna JmsTemplate = nová JmsTemplate (connectionFactory ()); template.setConnectionFactory (connectionFactory ()); návratová šablóna; }} 

Ďalej potrebujeme a Výrobca - jednoduchá jar Komponent - ktorý bude posielať správy do myQueue a dostanete potvrdenie od ackQueue:

@Component public class Producer {@Autowired private JmsTemplate jmsTemplate; public void sendMessageToDefaultDestination (konečná reťazcová správa) {jmsTemplate.convertAndSend ("myQueue", správa); } public String receiveAck () {return (String) jmsTemplate.receiveAndConvert ("ackQueue"); }} 

Potom máme PrijímačKomponent metódou označenou ako @JmsListener prijímať správy asynchrónne z myQueue:

@ Komponent verejná trieda Prijímač {@Autowired private JmsTemplate jmsTemplate; @JmsListener (destination = "myQueue") public void receiveMessage (reťazcová správa) {sendAck (); } private void sendAck () {jmsTemplate.convertAndSend ("ackQueue", "polo"); }} 

Funguje tiež ako odosielateľ na potvrdenie prijatia správy na adrese ackQueue.

Ako je naša prax, overme to testom:

@Test public void givenJMSBean_whenMessageSent_thenAcknowledgementReceived () hodí NamingException {Producer producer = context.getBean (Producer.class); producer.sendMessageToDefaultDestination ("marco"); assertEquals ("pólo", producer.receiveAck ()); } 

V tomto teste sme poslali marco do myQueue a dostal pólo ako potvrdenie od ackQueue, to isté, čo sme robili s EJB.

Tu je potrebné poznamenať jednu vec Jarná služba JMS môže odosielať a prijímať správy synchrónne aj asynchrónne.

8. Záver

V tomto tutoriáli sme videli a individuálne porovnanie jarných a Enterprise Java Beans. Pochopili sme ich históriu a základné rozdiely.

Potom sme sa zaoberali jednoduchými príkladmi na demonštráciu porovnania jarných bôbov a EJB. Netreba hovoriť, je to len poškriabanie povrchu toho, čoho sú tieto technológie schopné, a je tu ešte veľa toho, čo treba ešte preskúmať.

Ďalej to môžu byť konkurenčné technológie, to však neznamená, že nemôžu existovať súčasne. EJB môžeme ľahko integrovať do jarného rámca.

Ako vždy, zdrojový kód je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found