从Spring到Java EE 6
這最終導(dǎo)致兩個(gè)主要問(wèn)題:
- 我們可以在Java EE 6中完成Spring可以做的一切嗎?
- 我們可以像在Spring那樣簡(jiǎn)單嗎?
好吧,我要說(shuō)的是,在全球范圍內(nèi),答案是:是的,我們可以!
我不想重新開(kāi)始(無(wú)休止的)辯論,要知道在Spring和Java EE 6之間哪個(gè)是最好的。 不,我只想與您分享我有關(guān)遷移的經(jīng)驗(yàn)。 我曾經(jīng)是,現(xiàn)在仍然是一個(gè)真正的Spring狂熱愛(ài)好者(從歷史上來(lái)說(shuō),我是在EJB 1.0令人反感之后才發(fā)現(xiàn)的),但我也知道最近在Linux中引入的進(jìn)展,而不僅僅是簡(jiǎn)化。這些年來(lái)的Java EE,以及Java EE 6應(yīng)用程序服務(wù)器方面令人印象深刻的速度改進(jìn)。
現(xiàn)在讓我們?cè)敿?xì)研究“企業(yè)”應(yīng)用程序的一些典型要求,并比較在兩種情況下要產(chǎn)生的代碼:
- 上下文和依賴(lài)注入
- 訊息傳遞
- 交易管理
- 網(wǎng)頁(yè)服務(wù)
如果您不愿從一種技術(shù)遷移到另一種技術(shù),那么這種比較應(yīng)該為您提供一些具體的決策要素……
第一部分:上下文和依賴(lài)注入(CDI)
Spring允許您使用各種構(gòu)造型(例如@ Repository,@ Service,@ Controller和@Component)定義bean。 選擇的選項(xiàng)不是那么重要(這不是完全正確。例如,將DAO標(biāo)記為@Repository將添加SQL異常的自動(dòng)翻譯),因?yàn)檫@種區(qū)別主要是針對(duì)IDE的(以便對(duì)bean進(jìn)行分類(lèi))。 (可選)您可以為您的bean命名。
public interface MyInterface {...}import org.springframework.stereotype.Component;@Component("firstBean") public class MySpringBean implements MyInterface {...}@Component("firstBeanMock") public class MockImpl implements MyInterface {...}Java EE提供了非常相似的注釋(@Named),但其使用應(yīng)僅限于純pojo。 如果是面向服務(wù)的Bean(尤其是事務(wù)性粗粒度服務(wù)),請(qǐng)考慮使用(最好是無(wú)狀態(tài)的)EJB,即因?yàn)樗鼈兲峁┝烁玫目缮炜s性。
import javax.inject.Named;@Named("firstBean") public class MyJeeBean implements MyInterface {...}import javax.ejb.Stateless;@Stateless(name="firstService") public class MyJeeService implements MyInterface {...}還應(yīng)注意,與Spring相反,應(yīng)在Java EE中將單例顯式標(biāo)記為:
import javax.inject.Singleton;@Singleton public class MyJeeSingleton implements MyInterface {...}備注:在“ javax.inject.Singleton”和“ javax.ejb.Singleton”之間進(jìn)行選擇時(shí),您可能會(huì)感到困惑。 第一個(gè)定義由容器(在Java EE世界中也稱(chēng)為“ Managed Bean ”)管理的標(biāo)準(zhǔn)POJO,而第二個(gè)定義“ Enterprise Bean”。 請(qǐng)記住,后者是為并發(fā)訪問(wèn)而設(shè)計(jì)的(客戶(hù)端無(wú)需擔(dān)心可能同時(shí)調(diào)用單例相同方法的任何其他客戶(hù)端),并且還提供了事務(wù)管理功能(請(qǐng)參閱進(jìn)一步)。
現(xiàn)在我們已經(jīng)注冊(cè)了(并可以選擇命名)我們的bean,我們可以將它們注入其他bean中。 再一次,雙方的程序有點(diǎn)類(lèi)似:
彈簧
import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier;@Component public class UseCaseHandler {@Autowired@Qualifier("firstBean")private MyInterface serviceFacade;}JAVA EE 6
import javax.inject.Named; import javax.inject.Inject;@Named public class UseCaseHandler {@Inject@Named("firstBean") private MyInterface serviceFacade;}備注:JSR-330統(tǒng)一了注入托管bean的方式。 具體來(lái)說(shuō),這意味著@Inject批注可用于注入簡(jiǎn)單的POJO和EJB(從而使@EJB批注過(guò)時(shí))。
很好! 但是,在現(xiàn)實(shí)世界中,我們要注入的bean的名稱(chēng)(例如“ firstBean”)可能是動(dòng)態(tài)的。 當(dāng)您使用行為模式,泛型等時(shí),尤其如此。
在Spring,這非常容易。 例如,您可以使您的bean能夠識(shí)別ApplicationContext,以便隨后可以使用注入的Spring上下文來(lái)查找特定的bean實(shí)例:
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Service;import com.javacodegeeks.Request;@Service public class Dispatcher implements ApplicationContextAware {private ApplicationContext appContext;public void setApplicationContext(ApplicationContext ctx) throws BeansException {appContext = ctx;}public void dispatch(Request request) throws Exception {String beanName = "requestHandler_" + request.getRequestTypeId();RequestHandler myHandler = appContext.getBean(beanName, RequestHandler.class);myHandler.handleRequest(request);}}public interface RequestHandler {public void handleRequest(Request request); }@Component("requestHandler_typeA") public class HandlerA implements RequestHandler {...}@Component("requestHandler_typeB") public class HandlerB implements RequestHandler {...}在Java EE 6中,這是可能的,但是需要更多的代碼行(可以集中在幫助程序類(lèi)中):
import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager;import com.javacodegeeks.Request;@Named public class Dispatcher @Injectprivate BeanManager beanManager;public void dispatch(Request request) throws Exception {String beanName = "requestHandler_" + request.getRequestTypeId();RequestHandler myHandler = this.getBean(beanName, RequestHandler.class);myHandler.handleRequest(request);}@SuppressWarnings("unchecked")private <T> T getBean(String name, Class<T> clazz) throws Exception {Set<Bean<?>> founds = beanManager.getBeans(name);if ( founds.size()==0 ) {throw new Exception("No such bean found: "+name);} else {Bean<T> bean = (Bean<T>) founds.iterator().next();CreationalContext<T> cc = beanManager.createCreationalContext(bean);T instance = (T) beanManager.getReference(bean, clazz, cc);return instance;}}}public interface RequestHandler {public void handleRequest(Request request); }@Named("requestHandler_typeA") public class HandlerA implements UseCaseHandler {…}@Named("requestHandler_typeB") public class HandlerB implements UseCaseHandler {...}第二部分:JMS
Java Messaging Service簡(jiǎn)化了松散耦合的分布式通信的實(shí)現(xiàn)。
這就是為什么它已成為企業(yè)應(yīng)用程序集成(EAI)中的經(jīng)典技術(shù)的原因。
Spring具有出色的JMS支持。 您可以非常快速地設(shè)置JMS生產(chǎn)者或使用者,
使用目標(biāo)解析器,還可以選擇將JMS消息自動(dòng)轉(zhuǎn)換為pojos(反之亦然)。 另一方面,J2EE帶有一組豐富的注釋,以便訪問(wèn)或定義JMS資源,例如隊(duì)列/主題,連接或面向消息的Bean。
讓我們從接收消息的JMS客戶(hù)端開(kāi)始,該客戶(hù)端是消息使用者(或訂戶(hù)):
彈簧
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"><property name="environment"><props>…</props></property> </bean><bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiTemplate" ref="jndiTemplate" /><property name="jndiName" value="java:/JmsXA" /> </bean><bean id="jndiDestResolver"class="org.springframework.jms.support.destination.JndiDestinationResolver"><property name="jndiTemplate" ref="jndiTemplate" /> </bean><bean id="jmsContainer"class="org.springframework.jms.listener.DefaultMessageListenerContainer"><property name="connectionFactory" ref="jmsConnectionFactory"/> <property name="destinationResolver" ref="jndiDestResolver"/> <property name="destinationName" value="queue/myQueue"/><property name="messageListener" ref="myMsgConsumer" /> </bean><bean id="myMsgConverter" class="com.javacodegeeks.MsgToRequestConverter"/><bean id="myMsgConsumer" class="com.javacodegeeks.MsgConsumer"/>import javax.jms.Message; import javax.jms.MessageListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.support.converter.MessageConverter;import com.javacodegeeks.Request; import com.javacodegeeks.Dispatcher;/*** Example of message consumer (Message-Driven-Pojo) in Spring*/ public class MsgConsumer implements MessageListener {@Autowiredprivate MessageConverter msgConverter;@Autowiredprivate Dispatcher dispatcher;public void onMessage(Message message) { try { Request request = (Request) msgConverter.fromMessage(message);dispatcher.dispatch(request); } catch (Exception e) {e.printStackTrace(); } }}JAVA EE 6
import javax.inject.Inject; import javax.jms.Message; import javax.jms.MessageListener; import javax.ejb.MessageDriven; import javax.ejb.ActivationConfigProperty;import com.javacodegeeks.Request; import com.javacodegeeks.Dispatcher ; import com.javacodegeeks.MsgToRequestConverter;/*** Example of message consumer (Message-Driven-Bean) in JEE*/ @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),@ActivationConfigProperty(propertyName="destination", propertyValue="queue/myQueue") } ) public class MsgConsumer implements MessageListener {@Injectprivate MsgToRequestConverter msgConverter;@Inject private Dispatcher dispatcher;public void onMessage(Message message) { try { Request request = msgConverter.fromMessage(message);dispatcher.dispatch(request); } catch (Exception e) {e.printStackTrace(); } }}現(xiàn)在,讓我們編寫(xiě)一個(gè)用于創(chuàng)建和發(fā)送消息的JMS客戶(hù)端,即消息生產(chǎn)者(或發(fā)布者):
彈簧
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"><property name="environment"><props>…</props></property> </bean><bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"><property name="jndiTemplate" ref="jndiTemplate" /><property name="jndiName" value="java:/JmsXA" /> </bean><bean id="jndiDestResolver"class="org.springframework.jms.support.destination.JndiDestinationResolver"><property name="jndiTemplate" ref="jndiTemplate" /> </bean><bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"><property name="connectionFactory" ref="jmsConnectionFactory" /><property name="destinationResolver" ref="jndiDestResolver" /><property name="messageConverter" ref="myMsgConverter" /> </bean><bean id="myMsgConverter" class="com.javacodegeeks.MsgConverter">import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.core.JmsTemplate; import com.javacodegeeks.Request;/*** Example of message producer component in Spring*/ @Component public class MsgProducer {@Autowiredprivate JmsTemplate jmsTemplate;public void postRequest(Request request) throws Exception {jmsTemplate.convertAndSend("queue/myQueue", request);}}JAVA EE 6
import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import javax.inject.Inject; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.Session; import javax.ejb.Stateless; import javax.ejb.EJBException;import com.javacodegeeks.Request; import com.javacodegeeks.MsgToRequestConverter;/*** Example of message producer (here a session bean) in JEE*/ @Stateless(name="msgProducer") public class MsgProducer {@Injectprivate MsgToRequestConverter msgConverter;@Resource(mappedName="java:/JmsXA")private ConnectionFactory connectionFactory;@Resource(mappedName="queue/myQueue")private Queue queue;private Connection jmsConnection;@PostConstructprivate void initialize() {try {jmsConnection = connectionFactory.createConnection();} catch (JMSException e) {throw new EJBException(e);}}@PreDestroyprivate void cleanup() {try {if (jmsConnection!=null) jmsConnection.close();} catch (JMSException e) {throw new EJBException(e);}}public void postRequest(Request request) throws Exception { Session session = null;MessageProducer producer = null;try {session = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);producer = session.createProducer(queue);Message msg = msgConverter.toMessage(request, session);producer.send(msg); } finally { try { if (producer!=null) producer.close();if (session!=null) session.close();} catch (Exception e) {System.err.println("JMS session not properly closed: "+ e);}}}}備注:
- 不要忘記,與JMS連接和JMS隊(duì)列相反,JMS會(huì)話不是線程安全的。 因此,會(huì)話不應(yīng)由所有bean實(shí)例共享,也不應(yīng)在構(gòu)造函數(shù)或PostConstruct方法中創(chuàng)建。
- PostConstruct和PreDestroy方法應(yīng)該只拋出運(yùn)行時(shí)異常。 這就是為什么必須將JMS異常(例如)包裝到EJB異常中的原因。
第三部分:交易管理
事務(wù)的需求在系統(tǒng)體系結(jié)構(gòu)中至關(guān)重要,尤其是在SOA出現(xiàn)時(shí)。 在這樣的體系結(jié)構(gòu)中,可以通過(guò)組裝現(xiàn)有的(可能還有交易的)較小的服務(wù)(“ 微服務(wù) ”)來(lái)構(gòu)建粗粒度的交易服務(wù)。
Spring和Java EE都通過(guò)提供強(qiáng)大的聲明式(基于注釋)事務(wù)管理來(lái)滿(mǎn)足這一需求。
彈簧
<!-- Recognize @Transactional annotations in our beans --> <tx:annotation-driven transaction-manager="txManager"/><!-- The transaction manager to use (here the JPA implementation) --> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">... </bean>import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Propagation;import com.javacodegeeks.Request; import com.javacodegeeks.RequestProcessor;@Service public class RequestProcessorImpl implements RequestProcessor {@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)public void process(Request request) throws Exception {...}}JAVA EE 6
import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.ejb.TransactionManagement; import javax.ejb.TransactionManagementType;import com.javacodegeeks.Request; import com.javacodegeeks.RequestProcessor;@Stateless @TransactionManagement(value=TransactionManagementType.CONTAINER) public class RequestProcessorImpl implements RequestProcessor {@TransactionAttribute(TransactionAttributeType.REQUIRED)public void process(Request request) throws Exception {...}}對(duì)Java EE中的運(yùn)行時(shí)/未經(jīng)檢查的異常要非常小心。 默認(rèn)情況下,它們被EJB容器自動(dòng)包裝到EJBException中,這可能會(huì)導(dǎo)致令人驚訝的結(jié)果(尤其是在try…catch語(yǔ)句中!)。 如果您需要對(duì)回滾情況進(jìn)行更好的調(diào)整,請(qǐng)考慮使用@ApplicationException批注或通過(guò)如下擴(kuò)展ejb描述符將此類(lèi)運(yùn)行時(shí)異常標(biāo)記為適用性異常:
<ejb-jar><assembly-descriptor><application-exception><exception-class>java.lang.NullPointerException</exception-class><rollback>true</rollback></application-exception></assembly-descriptor> </ejb-jar>第四部分:寧?kù)o的Web服務(wù)
企業(yè)應(yīng)用程序通常需要通過(guò)Internet將其某些服務(wù)公開(kāi)給外界。 這就是Web服務(wù)發(fā)揮作用的地方。 與JMS(用于異步通信)一樣,Web服務(wù)是另一種經(jīng)典的集成技術(shù),用于使用XML(或JSON)作為交換格式實(shí)現(xiàn)面向請(qǐng)求響應(yīng)的同步通信。
彈簧
<servlet><servlet-name>ws</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping><servlet-name>ws</servlet-name><url-pattern>/services/*</url-pattern> </servlet-mapping><!-- Dispatch requests to controllers + use JAXB (if found in the classpath) --> <mvc:annotation-driven />import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;import com.javacodegeeks.Geek; import com.javacodegeeks.GeekService;@Controller @RequestMapping("/geeks") public class GeekWebService {@AutowiredGeekService bizService;@RequestMapping(value="/{id}", method=RequestMethod.GET)@ResponseBodypublic Geek getGeek(@PathVariable("id") long geekId) {return bizService.findGeek(geekId);}}import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name="geek") public class Geek {private String name;private Long id;@XmlElementpublic String getName() {return name;}public void setName(String name) {this.name = name;}@XmlAttributepublic Long getId() {return id;}public void setId(Long id) {this.id = id;}}JAVA EE 6
import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;import com.javacodegeeks.Geek; import com.javacodegeeks.GeekService;@Path("/geeks") @Produces(MediaType.APPLICATION_XML) public class GeekWebService {@InjectGeekService bizService;@GET@Path("/{id}")public Geek getGeek(@PathParam("id") long geekId) {return bizService.findGeek(geekId);}}import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name="geek") public class Geek {private String name;private Long id;@XmlElementpublic String getName() {return name;}public void setName(String name) {this.name = name;}@XmlAttributepublic Long getId() {return id;}public void setId(Long id) {this.id = id;}}備注:一些JAX-RS實(shí)現(xiàn),例如JBoss RestEasy,不需要修改web.xml即可配置和安裝Web服務(wù)…
第五部分:結(jié)論
認(rèn)為與Java EE相比,Spring中的事情要簡(jiǎn)單得多,輕得多是不正確的。 這只是一個(gè)品味問(wèn)題。 此外,最近的Java EE 6應(yīng)用服務(wù)器(例如GlassFish 3或JBoss 6和7)的啟動(dòng)速度非常快,實(shí)際上幾乎與Spring應(yīng)用程序一樣快。 然而,從“同類(lèi)最佳”的角度來(lái)看,將兩種技術(shù)結(jié)合起來(lái)可能仍然很有趣。 這將是我下一期JCG文章的主題:-)
參考: 從Spring到我們的W4G合作伙伴 Bernard Ligny的 Java EE 6 。
相關(guān)文章 :
- 什么是CDI,它與@EJB和Spring有什么關(guān)系?
- Spring Singleton,請(qǐng)求,會(huì)話Bean和線程安全
- Devoxx 2011印象
- Java EE6事件:JMS的輕量級(jí)替代品
- Java EE6 CDI,命名組件和限定符
- Java EE過(guò)去,現(xiàn)在和云7
- Java SE 7、8、9 –推進(jìn)Java
翻譯自: https://www.javacodegeeks.com/2011/11/from-spring-to-java-ee-6.html
總結(jié)
以上是生活随笔為你收集整理的从Spring到Java EE 6的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 农村厨房设置(农村厨房怎么设置好看)
- 下一篇: 奥林巴斯xz-1(奥林巴斯xz-1充电口