EJB3的功能很是强大,但中小企业很少使用它做开发,一般应用SSH足以应付。JAVAEE是做什么的?她是分布式企业级应用的规范,那EJB就是为实现这样的应用而开发的。
什么是分布式应用?听着名字很大。比如咱们的在线支付系统,淘宝、china-pub和amazon等都支持支付宝在线支付。难道它们分别都要将支付宝模块放在自己的服务器上?完全没必要。支付宝模块单独放在一个服务器上,其他服务器使用(调用)那一个服务器上的支付宝就可以了。在现实应用中还有一些类似的将某一应用模块单独取出放到另一个服务器上,供其他应用使用。这就是分布式应用了。(分布在不同的服务器上的应用)
一、JAVAEE回顾
JAVAEE到底是什么呢?一图更清(JavaEE5.0):
昨天我们实现的EJB组件接口使用的是“@Local”注解,此注解只提供给本地调用。外部机器无法访问。因为外部用户与本地不在同一个虚拟机中。我们编写的EJB组件不主要是给外部用户访问而使用分布试应用吗?正是。所以为了让外部机器可以使用,我们需要将“@Local”注解修改为“@Remote”注解。在外部调用远程的EJB组件时,应该这样使用:
InitialContext ctx = new InitialContext(); HelloWorld hw = (HelloWorld)ctx.lookup("HelloWorldBean/remote"); String str = hw.sayHello("changcheng"); |
二、EJB3中的EntityBean
EJB3中的EntityBean就是POJO(Plain old java object)+注解,这方面的知识我们已经在JPA中学习了。
三、EJB3中的SessionBean
SessionBean分为有状态SessionBean(StateFulSessionBean-sfsb)和无状态SessionBean(StateLessSessionBean-slsb)。
1.StateLessSessionBean
我们在之前是做的练习中,一般都有一个DAO层。DAO层不记录用户的状态,所以我们在使用EJB开发时,可以直接将DAO层定义为无状态Bean。
如何定义无状态Bean,我们在昨天的练习中使用的就是无状态Bean。直接在类上添加“@Stateless”注解,即可将类定义为无状态的Bean。
每次产生一个新的会话,EJB容器需要为会话创建相关的无状态Bean对象,无疑这会为服务器带来极大的资源开销。我们在学习JDBC时,使用了线程池专门用来处理用户与数据库的连接。因为无状态Bean不记录用户的状态,所以EJB3中也定义了无状态的Bean池。这样大大节省了用户访问的时间与服务器资源的开销。
无状态Bean具有两个事件,创建后和销毁前。我们在方法上添加“@PostConstruct”注解,此方法将在无状态Bean创建后调用,我们在方法上添加“@PreDestroy”注解,此方法将在无状态Bean销毁后被调用。
2.StateFulSessionBean
有状态Bean的典型案例是购物车,购物车需要记录用户购买的商品,所以它是有状态的Bean。我们知道无状态Bean有Bean池,那有状态Bean可否有Bean池呢?当然不可以。所以有状态Bean会给服务器带来极大的负担,能不用有状态Bean就不要使用有状态Bean。
但EJB3中也尽量减少有状态Bean所带来的负载,所以EJB3为有状态Bean添加了钝化和激活的功能。比如用户在超市购物,用户刚进入超市时推起购物车(CartBean对象,这个有状态Bean被创建)。当用户选择商品并谢谢购物车时(向CartBean对象添加状态)。突然用户有急事要离开一会,工作人员将购物车推到“等候区”(CartBean对象被钝化)。用户处理完事物之后回来继续购物,到“等候区”推出自己的购物车(CartBean对象被激活)。用户结账工取消购物时(CartBean对象被删除)。
EJB3中正是使用方法减少频繁创建有状态Bean所给服务器带来的负担,同时也为用户提供了方便。
EJB3中有状态Bean的事件:
1).有状态Bean被创建后:在某一方法上添加“@PostConstruct”注解,有状态Bean被创建后会调用此方法。
2).有状态Bean被钝化:在某一方法上添加“@PrePassivate”注解,调用此方法后,有状态Bean被钝化。EJB3容器会将此Bean序列化后,保存到硬盘上,并从内存中删除。有状态Bean需要实现“Serializable”接口。
3).有状态Bean被激活:在某一方法上添加“@PostActivate”注解,调用此方法后,有状态Bean被激活。EJB3容器会将此Bean反序列化后,放到内存中,并删除硬盘上的内容。
4).有状态Bean被销毁前:在某一方法上添加“@PreDestroy”注解,无状态Bean被销毁前会调用此方法。
5).有状态Bean被删除:在某一方法上添加“@Remove”注解,调用此方法后,通知EJB3容器删除有状态Bean。如果不添加“@Remove”注解,客户端将没有任何方式告诉服务器终止会话,结果将导致SFSB在超时后进行钝化至磁盘,再次超时后将对象销毁。在高并发的系统当中,无疑会有严重的性能问题。内存开销会居高不下,更不要提占用cpu和磁盘空间了。
四、EJB3中的消息驱动Bean
EJB3中的消息驱动分为队列消息(queue)和主题消息(Topic),是一种异步消息驱动。
1.队列消息
发送消息:
import javax.jms.*; import javax.naming.InitialContext; /** * 队列消息驱动bean */ public class MDBQueueApp { public static void main(String[] args) throws Exception { InitialContext ctx = new InitialContext(); //通过JNDI查找队列连接工厂,EJB自身包含的。 QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup("QueueConnectionFactory"); //通过连接工厂创建队列连接 QueueConnection qc = qcf.createQueueConnection(); //通过队列连接创建队列会话 QueueSession qs = qc.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); //通过JNDI查找可用队列,EJB3自身包含的。 Queue queue = (Queue) ctx.lookup("queue/testQueue"); //通过队列会话创建到指定队列的消息生产者 MessageProducer mp = qs.createProducer(queue); //通过队列回话创建文本消息 TextMessage msg = qs.createTextMessage(); msg.setText("Hi,你好吗?(队列消息)"); //通过消息的生产者发送消息 mp.send(msg); //释放资源 qs.close(); qc.close(); System.out.println("队列消息发送完毕!"); } } |
接收消息:
import javax.ejb.*; import javax.jms.*; @MessageDriven(activationConfig={@ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Queue"),@ActivationConfigProperty(propertyName="destination",propertyValue="queue/testQueue")}) public class MyQueueMDB implements MessageListener { public void onMessage(Message arg0) { if(arg0 instanceof TextMessage){ try { System.out.println("收到消息队列消息 : " + ((TextMessage)arg0).getText()); System.out.println("I'm fine,Thank you!"); } catch (Exception e) { e.printStackTrace(); } } } } |
队列消息只能被一个接收都接收到,队列消息一旦被某个接收者接收到后,消息将从队列中消失。所以其他接收同一队列消息的接收者无法收到。这个第一个接收到队列消息的接收者是最后一次被添加的接收者。
2.主题消息
发送消息:
import javax.jms.*; import javax.naming.InitialContext; /** * 主题消息驱动bean */ public class MDBAppTopic { public static void main(String[] args) throws Exception { InitialContext ctx = new InitialContext(); //通过JNDI查找主题连接工厂,EJB3自身包含的。 TopicConnectionFactory tcf = (TopicConnectionFactory) ctx.lookup("TopicConnectionFactory"); //通过连接工厂创建主题连接 TopicConnection tc = tcf.createTopicConnection(); //通过主题连接创建主题会话 TopicSession ts = tc.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); //通过JNDI查找可用主题,EJB3自身包含的。 Topic topic = (Topic) ctx.lookup("topic/testTopic"); //通过主题会话创建到指定主题的消息生产者 MessageProducer mp = ts.createProducer(topic); //通过主题回话创建文本消息 TextMessage msg = ts.createTextMessage(); msg.setText("Hi,你好吗?(主题消息)"); //通过消息的生产者发送消息 mp.send(msg); //释放资源 ts.close(); tc.close(); System.out.println("主题消息发送完毕!"); } } |
接收消息:
import javax.ejb.*; import javax.jms.*; @MessageDriven(activationConfig={@ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Topic"),@ActivationConfigProperty(propertyName="destination",propertyValue="topic/testTopic")}) public class MyTopicMDB implements MessageListener { public void onMessage(Message arg0) { if(arg0 instanceof TextMessage){ try { System.out.println("收到主题消息 : " + ((TextMessage)arg0).getText()); System.out.println("我很好,谢谢!"); } catch (Exception e) { e.printStackTrace(); } } } } |
主题消息是一种共享模式,相当于群。每个消息被当做主题,群内的所有成员都可以收到。
五、ANT
我们编写EJB组件和WEB应用时,需要手动打成JAR和WAR包并放置到JBOSS的deploy目录中。这显然是不理想的操作方式,我们每测试一个功能都这样做吗?ANT可以帮助我们解决这个问题。
当一个代码项目大了以后,每次重新编译,打包,测试等都会变得非常复杂而且重复,因此c语言中有make脚本来帮助这些工作的批量完成。在Java 中应用是平台无关性的,当然不会用平台相关的make脚本来完成这些批处理任务了,ANT本身就是这样一个流程脚本引擎,用于自动化调用程序完成项目的编译,打包,测试等。除了基于JAVA是平台无关的外,脚本的格式是基于XML的,比make脚本来说还要好维护一些。
《ANT IN ACTION (第2版)》是一本专门调解ANT的书籍,在此就不多总结了。