Spring Bean vs. EJB - En funksjonssammenligning

1. Oversikt

Gjennom årene har Java-økosystemet utviklet seg og vokst enormt. I løpet av denne tiden er Enterprise Java Beans and Spring to teknologier som ikke bare har konkurrert, men lært av hverandre symbiotisk.

I denne veiledningen, vi tar en titt på deres historie og forskjeller. Selvfølgelig vil vi se noen kodeeksempler på EJB og deres ekvivalenter i vårverdenen.

2. En kort historie om teknologiene

Til å begynne med, la oss ta en rask titt på historien til disse to teknologiene og hvordan de har utviklet seg jevnlig gjennom årene.

2.1. Enterprise Java Beans

EJB-spesifikasjonen er en delmengde av Java EE (eller J2EE, nå kjent som Jakarta EE) -spesifikasjonen. Den første versjonen kom ut i 1999, og det var en av de første teknologiene designet for å gjøre det enklere å utvikle applikasjoner på serversiden på Java.

Det påtok Java-utviklerens byrde med samtidighet, sikkerhet, utholdenhet, transaksjonsbehandling, og mer. Spesifikasjonen overleverte disse og andre vanlige bedriftsproblemer til implementeringsapplikasjonsservernes containere, som håndterte dem sømløst. Imidlertid var bruk av EJB-er som de var litt tungvint på grunn av den nødvendige konfigurasjonen. Videre viste det seg å være en ytelsesflaskehals.

Men nå, med oppfinnelsen av merknader og hard konkurranse fra våren, er EJB-er i deres siste 3.2-versjon mye enklere å bruke enn debutversjonen. Enterprise Java Beans i dag låner tungt fra vårens avhengighetsinjeksjon og bruk av POJOer.

2.2. Vår

Mens EJB-er (og Java EE generelt) slet med å tilfredsstille Java-samfunnet, kom Spring Framework som et friskt pust. Den første milepælutgivelsen kom ut i 2004 og tilbød et alternativ til EJB-modellen og dens tunge containere.

Takk til våren, Java-bedriftsapplikasjoner kan nå kjøres på IOC-containere med lettere vekt. Videre tilbød den også avhengighetsinversjon, AOP og dvalemodusstøtte blant utallige andre nyttige funksjoner. Med enorm støtte fra Java-samfunnet har Spring nå vokst eksponentielt og kan betegnes som et fullstendig Java / JEE-applikasjonsrammeverk.

I sin siste avatar støtter Spring 5.0 til og med den reaktive programmeringsmodellen. En annen avlegger, Spring Boot, er en komplett spillveksler med innebygde servere og automatiske konfigurasjoner.

3. Forspill til sammenligning av funksjoner

Før vi hopper til funksjons sammenligning med kodeeksempler, la oss etablere noen grunnleggende.

3.1. Grunnleggende forskjellen mellom de to

For det første er den grunnleggende og tilsynelatende forskjellen den EJB er en spesifikasjon, mens våren er et helt rammeverk.

Spesifikasjonen er implementert av mange applikasjonsservere som GlassFish, IBM WebSphere og JBoss / WildFly. Dette betyr at vårt valg om å bruke EJB-modellen for applikasjonens backend-utvikling ikke er nok. Vi må også velge hvilken applikasjonsserver vi skal bruke.

Teoretisk sett er Enterprise Java Beans bærbare på tvers av app-servere, selv om det alltid er en forutsetning at vi ikke skal bruke noen leverandørspesifikke utvidelser hvis interoperabilitet skal holdes som et alternativ.

Sekund, Våren som teknologi er nærmere Java EE enn EJB når det gjelder den brede porteføljen av tilbud. Mens EJB bare spesifiserer backend-operasjoner, har Spring, som Java EE, også støtte for UI-utvikling, RESTful APIer og Reaktiv programmering for å nevne noen.

3.2. Nyttig informasjon

I avsnittene som følger vil vi se sammenligningen av de to teknologiene med noen praktiske eksempler. Siden EJB-funksjoner er en delmengde av det mye større vårøkosystemet, vil vi gå etter deres typer og se deres tilsvarende vårekvivalenter.

For å forstå eksemplene best, bør du først lese gjennom Java EE Session Beans, Message Driven Beans, Spring Bean og Spring Bean Annotations.

Vi bruker OpenJB som den innebygde beholderen vår for å kjøre EJB-prøvene. For å kjøre de fleste av våreksemplene, vil IOC-containeren være tilstrekkelig; for Spring JMS, trenger vi en innebygd ApacheMQ-megler.

For å teste alle prøvene våre, bruker vi JUnit.

4. Singleton EJB == Vår Komponent

Noen ganger trenger vi beholderen for å lage bare en enkelt forekomst av en bønne. La oss for eksempel si at vi trenger en bønne for å telle antall besøkende til webapplikasjonen vår. Denne bønnen må bare opprettes en gang under oppstart av applikasjonen.

La oss se hvordan vi kan oppnå dette ved hjelp av en Singleton Session EJB og en vår Komponent.

4.1. Singleton EJB Eksempel

Vi trenger først et grensesnitt for å spesifisere at vår EJB har muligheten til å håndteres eksternt:

@Remote offentlig grensesnitt CounterEJBRemote {int count (); String getName (); void setName (strengnavn); }

Det neste trinnet er å definere en implementeringsklasse med kommentaren javax.ejb.Singletonog bratsj! Singleton vår er klar:

@Singleton offentlig klasse CounterEJB implementerer CounterEJBRemote {private int count = 1; privat strengnavn; public int count () {return count ++; } // getter og setter for navn} 

Men før vi kan teste singleton (eller et annet EJB-kodeeksempel), må vi initialisere ejbContainer og få kontekst:

@BeforeClass offentlig tomrom initializeContext () kaster NamingException {ejbContainer = EJBContainer.createEJBContainer (); kontekst = ejbContainer.getContext (); context.bind ("injiser", dette); } 

La oss nå se på testen:

@Test offentlig ugyldighet givenSingletonBean_whenCounterInvoked_thenCountIsIncremented () kaster NamingException {int count = 0; CounterEJBRemote firstCounter = (CounterEJBRemote) context.lookup ("java: global / ejb-beans / CounterEJB"); firstCounter.setName ("første"); for (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 count2 = 0; for (int i = 0; i <10; i ++) {count2 = secondCounter.count (); } assertEquals (20, count2); assertEquals ("first", secondCounter.getName ()); } 

Noen få ting å merke seg i eksemplet ovenfor:

  • Vi bruker JNDI-oppslaget for å få counterEJB fra containeren
  • telle2 plukker opp fra poenget telle forlot singleton på, og legger opp til 20
  • secondCounter beholder navnet vi setter for firstCounter

De to siste punktene viser betydningen av en singleton. Siden den samme bønneinstansen brukes hver gang den blir sett opp, er det totale antallet 20 og verdien som er satt for den ene forblir den samme for den andre.

4.2. Singleton Spring Bean Eksempel

Den samme funksjonaliteten kan oppnås ved hjelp av fjærkomponenter.

Vi trenger ikke implementere noe grensesnitt her. I stedet legger vi til @Komponent kommentar:

@Component public class CounterBean {// samme innhold som i EJB}

Komponenter er faktisk singletoner som standard på våren.

Vi må også konfigurere våren for å skanne etter komponenter:

@Configuration @ComponentScan (basePackages = "com.baeldung.ejbspringcomparison.spring") offentlig klasse ApplicationConfig {} 

I likhet med hvordan vi initialiserte EJB-konteksten, setter vi nå vårkonteksten:

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

La oss nå se vår Komponent i aksjon:

@Test offentlig ugyldig nårCounterInvoked_thenCountIsIncremented () kaster NamingException {CounterBean firstCounter = context.getBean (CounterBean.class); firstCounter.setName ("første"); int-antall = 0; for (int i = 0; i <10; i ++) {count = firstCounter.count (); } assertEquals (10, count); assertEquals ("first", firstCounter.getName ()); CounterBean secondCounter = context.getBean (CounterBean.class); int count2 = 0; for (int i = 0; i <10; i ++) {count2 = secondCounter.count (); } assertEquals (20, count2); assertEquals ("first", secondCounter.getName ()); } 

Som vi kan se, er den eneste forskjellen med hensyn til EJBs hvordan vi får bønnen fra vårbeholderens sammenheng, i stedet for JNDI-oppslag.

5. Stateful EJB == Spring Komponent med prototype omfang

Noen ganger kan du si når vi bygger en handlekurv, vi trenger vår bønne for å huske tilstanden mens vi går frem og tilbake mellom metodeanrop.

I dette tilfellet trenger vi containeren vår for å generere en separat bønne for hver påkallelse og redde staten. La oss se hvordan dette kan oppnås med de aktuelle teknologiene våre.

5.1. Stateful EJB Eksempel

I likhet med EJB-prøven vår, trenger vi en fjernkontroll grensesnitt og implementering av det. Bare denne gangen er det kommentert med javax.ejb.Stateful:

@Stateful public class ShoppingCartEJB implementerer ShoppingCartEJBRemote {private strengnavn; privat liste handlevogn; public void addItem (String item) {shoppingCart.add (item); } // konstruktør, getters og setters}

La oss skrive en enkel test for å sette en Navn og legg til ting i a badevogn. Vi sjekker størrelsen og verifiserer navnet:

@Test offentlig ugyldighet gittStatefulBean_whenBathingCartWithThreeItemsAdded_thenItemsSizeIsThree () kaster NamingException {ShoppingCartEJBRemote bathingCart = (ShoppingCartEJBRemote) context.lookup ("java: global / ejb-beans / ShoppingCartEJB) bathingCart.setName ("bathingCart"); bathingCart.addItem ("såpe"); bathingCart.addItem ("sjampo"); bathingCart.addItem ("olje"); assertEquals (3, bathingCart.getItems (). størrelse ()); assertEquals ("bathingCart", bathingCart.getName ()); } 

Nå, for å demonstrere at bønnen virkelig opprettholder tilstand på tvers av tilfeller, la oss legge til en annen shoppingCartEJB til denne testen:

ShoppingCartEJBRemote fruitCart = (ShoppingCartEJBRemote) context.lookup ("java: global / ejb-beans / ShoppingCartEJB"); fruitCart.addItem ("epler"); fruitCart.addItem ("appelsiner"); assertEquals (2, fruitCart.getItems (). størrelse ()); assertNull (fruitCart.getName ()); 

Her satte vi ikke Navn og dermed var verdien null. Husk fra singleton-testen at navnet som ble satt i en forekomst ble beholdt i en annen. Dette demonstrerer at vi ble skilt ShoppingCartEJB forekomster fra bønnebassenget med forskjellige forekomsttilstander.

5.2. Stateful Spring Bean Eksempel

For å få den samme effekten med våren, trenger vi en Komponent med prototype omfang:

@Component @Scope (verdi = ConfigurableBeanFactory.SCOPE_PROTOTYPE) offentlig klasse ShoppingCartBean {// samme innhold som i EJB} 

Det er det, bare kommentarene er forskjellige - resten av koden forblir den samme.

For å teste vår Stateful bønne, kan vi bruke den samme testen som beskrevet for EJB. Den eneste forskjellen er igjen hvordan vi får bønnen fra beholderen:

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

6. Stateless EJB! = Alt om våren

Noen ganger, for eksempel i et søke-API, vi bryr oss ikke om forekomsten av en bønne eller om det er en singleton. Vi trenger bare resultatene av søket vårt, som kan komme fra enhver bønneinstans for alt vi bryr oss om.

6.1. Stateless EJB Eksempel

For slike scenarier har EJB en statsløs variant. Beholderen har et forekomstbasseng av bønner, og noen av dem returneres til anropsmetoden.

Måten vi definerer det på er den samme som andre EJB-typer, med et eksternt grensesnitt, og implementering med javax.ejb. uformell kommentar:

@Stateless public class FinderEJB implementerer FinderEJBRemote {private Map alphabet; public FinderEJB () {alphabet = new HashMap (); alphabet.put ("A", "Apple"); // legg til flere verdier på kartet her} offentlig strengesøk (strengord) {return alphabet.get (søkeord); }} 

La oss legge til en annen enkel test for å se dette i aksjon:

@Test offentlig ugyldighet gittStatelessBean_whenSearchForA_thenApple () kaster NamingException {assertEquals ("Apple", alphabetFinder.search ("A")); } 

I eksemplet ovenfor, alphabetFinder injiseres som et felt i testklassen ved hjelp av kommentaren javax.ejb.EJB:

@EJB private FinderEJBRemote alphabetFinder; 

Den sentrale ideen bak statsløse EJB-er er å forbedre ytelsen ved å ha et eksempel på lignende bønner.

Derimot, Våren abonnerer ikke på denne filosofien og tilbyr bare singler som statsløse.

7. Message Driven Beans == Spring JMS

Alle EJB-er som er diskutert så langt var sesjonsbønner. En annen type er den meldingsdrevne. Som navnet antyder, de brukes vanligvis til asynkron kommunikasjon mellom to systemer.

7.1. MDB Eksempel

For å lage en meldingsdrevet Enterprise Java Bean, må vi implementere javax.jms.MessageListener grensesnitt som definerer sitt onMessage metode, og kommentere klassen som javax.ejb.MessageDriven:

@MessageDriven (aktiveringConfig = {@ActivationConfigProperty (propertyName = "destination", propertyValue = "myQueue"), @ActivationConfigProperty (propertyName = "destinationType", propertyValue = "javax.jms.Queue")}) public class RecieverMD Ressurs privat ConnectionFactory connectionFactory; @Resource (name = "ackQueue") privat kø ackQueue; public void onMessage (Message message) {prøv {TextMessage textMessage = (TextMessage) melding; String producerPing = textMessage.getText (); hvis (producerPing.equals ("marco")) {anerkjenner ("polo"); }} fange (JMSException e) {kaste ny IllegalStateException (e); }}} 

Legg merke til at vi også tilbyr et par konfigurasjoner for MDB:

      • destinasjonstype som
      • myQueue som mål kønavn, som vår bønne lytter til

I dette eksemplet, vår mottaker produserer også en bekreftelse, og er i den forstand en avsender i seg selv. Den sender en melding til en annen kø som heter ackQueue.

La oss nå se dette i aksjon med en test:

@Test offentlig ugyldighet gittMDB_whenMessageSent_thenAcknowledgementReceived () kaster InterruptedException, JMSException, NamingException {Connection connection = connectionFactory.createConnection (); connection.start (); Sessionsøkt = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); MessageProducer produsent = session.createProducer (myQueue); producer.send (session.createTextMessage ("marco")); MessageConsumer response = session.createConsumer (ackQueue); assertEquals ("polo", ((TextMessage) response.receive (1000)). getText ()); } 

Her vi sendte en melding til myQueue, som ble mottatt av vår @MessageDriven kommentert POJO. Denne POJO sendte deretter en bekreftelse, og testen vår mottok svaret som en MessageConsumer.

7.2. Vår JMS Eksempel

Vel, nå er det på tide å gjøre det samme med Spring!

Først må vi legge til litt konfigurasjon for dette formålet. Vi trenger å kommentere vår ApplicationConfig klasse fra før med @EnableJms og legg til noen bønner til oppsettet JmsListenerContainerFactory og JmsTemplate:

@EnableJms offentlig klasse ApplicationConfig {@Bean offentlig DefaultJmsListenerContainerFactory jmsListenerContainerFactory () {DefaultJmsListenerContainerFactory fabrikk = ny DefaultJmsListenerContainerFactory (); factory.setConnectionFactory (connectionFactory ()); tilbake fabrikken; } @Bean public ConnectionFactory connectionFactory () {returner ny ActiveMQConnectionFactory ("tcp: // localhost: 61616"); } @Bean public JmsTemplate jmsTemplate () {JmsTemplate template = new JmsTemplate (connectionFactory ()); template.setConnectionFactory (connectionFactory ()); returmal; }} 

Deretter trenger vi en Produsent - en enkel vår Komponent - som vil sende meldinger til myQueue og motta en bekreftelse fra ackQueue:

@Komponent offentlig klasse produsent {@Autowired private JmsTemplate jmsTemplate; offentlig ugyldig sendMessageToDefaultDestination (siste strengmelding) {jmsTemplate.convertAndSend ("myQueue", melding); } offentlig String mottageAkk () {return (String) jmsTemplate.receiveAndConvert ("ackQueue"); }} 

Så har vi en MottakerKomponent med en metode kommentert som @JmsListener å motta meldinger asynkront fra myQueue:

@ Komponent mottaker i offentlig klasse {@Autowired privat JmsTemplate jmsTemplate; @JmsListener (destinasjon = "myQueue") public void receiveMessage (String msg) {sendAck (); } privat ugyldig sendAck () {jmsTemplate.convertAndSend ("ackQueue", "polo"); }} 

Den fungerer også som avsender for å bekrefte mottak av melding kl ackQueue.

Som det er vår praksis, la oss bekrefte dette med en test:

@Test offentlig ugyldig gittJMSBean_whenMessageSent_thenAcknowledgementReceived () kaster NamingException {Producer producer = context.getBean (Producer.class); producer.sendMessageToDefaultDestination ("marco"); assertEquals ("polo", producer.receiveAck ()); } 

I denne testen sendte vi marco til myQueue og mottatt polo som en bekreftelse fra ackQueue, det samme som det vi gjorde med EJB.

En ting å merke seg her er at Spring JMS kan sende / motta meldinger både synkront og asynkront.

8. Konklusjon

I denne opplæringen så vi en en-til-en-sammenligning av Spring og Enterprise Java Beans. Vi forsto deres historie og grunnleggende forskjeller.

Så behandlet vi enkle eksempler for å demonstrere sammenligningen av vårbønner og EJB. Unødvendig å si, det er bare å skrape overflaten av hva teknologiene er i stand til, og det er mye mer å utforske videre.

Videre kan dette være konkurrerende teknologier, men det betyr ikke at de ikke kan eksistere samtidig. Vi kan enkelt integrere EJB i våren.

Som alltid er kildekoden tilgjengelig på GitHub.


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