最近项目进入系统测试阶段,全赖袁大虾领导有力,保持一周零bug记录,这也让自己腾出不少时间补充知识。花了两天时间把“传智播客EJB3.0”看完了,EJB基本的知识也有些了解,在这记录下EJB的部分知识,以供自己以后复习使用。
EJB是sun的服务器端组件模型,最大的用处是部署分布式应用程序。EJB (Enterprise JavaBean)是J2EE的一部分,定义了一个用于开发基于组件的企业多重应用程序的标准。EJB依赖Web服务器,通常入门级推荐使用Jboss服务器,并且Jboss服务器提供了Ejb必备的jar包。
EJB中包含三种bean:
①会话bean:负责与客户端交互的bean,主要完成业务逻辑操作,类似于J2EE三层架构(表现层、业务层、持久层)中的业务逻辑层。
②实体bean:java持久化规范(JPA)中的技术,类似于三层架构的持久层,与数据库打交道,Hibernate等经典ORM框架实现了JPA技术。
③消息驱动bean:用户异步处理java消息的组件,具有处理大量并发消息的能力,比如转发短信功能。
bean分两种状态:
①有状态bean:一个实例仅供一个用户使用,性能开发较大。
有状态bean管理对象使用的是激活技术:
用户初次调用bean时,首先实例化bean
用户调用结束,垃圾回收器会回收bean,在回收前EJB会将该bean对象通过序列化放入硬盘中(此为钝化)
当用户再次调用该bean时,EJB又从硬盘反序列对象到内存中形成实例供用户使用(此为激活)
@Stateful //指定当前Bean为有状态Bean @Remote(StatefulRemote.class) @Local(StatefulLocal.class) public class StatefulImpl implements StatefulLocal,StatefulRemote{ @Override public String say(String s) { return "有状态Bean :"+s; } }
②无状态bean:一个实例可供多个用户使用,性能高,但线程可能不安全。
无状态bean使用实例池技术管理:类似于数据库连接池,预先创建几个bean实例,用户请求来之后EJB给用户分配空闲的bean实例,如果都不空闲则等待,直到有空闲实例或超时为止。
@Stateless //指定当前bean为无状态bean @Remote(StatelessRemote.class) @Local(StatelessLocal.class) public class StatelessImpl implements StatelessLocal,StatelessRemote{ @Override public String say(String s) { return "无状态Bean :"+s; } }
EJB的分布式技术决定了EJB可以远程调用,所以EJB的bean又分两种类型:远程和本地
①远程接口:客户端与EJB可以不在同一台电脑或者同一个服务器,只要网络畅通,可以通过远程调用即可获得客户需要的信息。客户远程访问EJB是通过Socket通信实现,具体的实现方法已经写好,用户只需要指定该接口为远程即可。
②本地接口:如果客户端与EJB在同一部署服务器下,客户端即可调用本地接口的EJB。此时因为在同一台电脑下,EJB不会通过Socket的方式通信,所以本地接口的运行速度是比远程接口要快的。
用户通过注解可以很方便的定义哪些接口为远程,哪些接口为本地(不是说所有bean都必须要有远程和本地两接口,这是根据项目需要来定,你也可以只做一个本地接口或一个远程接口)。
@Remote(StatelessRemote.class) @Local(StatelessLocal.class)
【部署EJB】
通常用户需要把EJB打成jar包,然后放入Jboss服务器(可以是启动状态)server下的指定目录deploy文件夹下即可,具体的部署方式建议大家看视频或百度谷歌学习。
【调用EJB】
如果EJB部署成功,用户可以通过JNDI调用指定bean来测试。前面已经说了,本地接口类型的bean是需要新建一个j2ee项目并且部署到同一jboss下测试;而远程接口的bean则只要客户端不在jboss服务器下均可测试。
远程接口测试方法:
public class EjbClient { public static void main(String[] args) throws NamingException { //获取创建EJB JNDI InitialContext ctx = new InitialContext(); //有状态bean使用激活管理bean StatefulRemote stateful = (StatefulRemote) ctx.lookup("StatefulImpl/remote"); System.out.println(stateful.say("xxoo")); System.out.println("---------------------"); //无状态bean使用 实例池技术管理bean StatelessRemote stateless = (StatelessRemote) ctx.lookup("StatelessImpl/remote"); System.out.println(stateless.say("ooxx")); } }
注意InitialContext会默认读取名为“jndi.properties”文件的信息以获得JNDI上下文环境。
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=localhost:1099
本地接口的测试方法:
首先新建一个web项目,进入时默认访问一个jsp页面,在jsp页面中写如下代码:
<body> <% //获取创建EJB JNDI InitialContext ctx = new InitialContext(); //调用本地接口 StatefulLocal stateful = (StatefulLocal) ctx.lookup("StatefulImpl/local"); %> <%=stateful.say("only") %> <br/>------------------------------------<br/> <% //调用本地接口 StatelessLocal stateless = (StatelessLocal) ctx.lookup("StatelessImpl/local"); %> <%=stateless.say("nani") %> <br/>------------------------------------<br/> <% //ejb调用ejb IocBean ioc = (IocBean)ctx.lookup("IocBeanImpl/local"); %> <%=ioc.say() %> </body>
最后将web项目部署到同一jboss服务器,再访问web项目即可。
【EJB注入】
在开发中,我们难免会遇到bean调用bean对象的情况,注意:通过new xxBean的方式是不正确的,那不是一个EJB正确的调用方式。通常通过注解能非常轻松的注入bean:
@Stateless public class IocBeanImpl implements IocBean{ //指定输入名为StatelessImpl的会话bean @EJB(beanName="StatelessImpl") StatelessLocal stateless; @Override public String say() { return "注入Bean :" + stateless.say("注入者"); }
【JPA技术实现实体bean】
首先我们必须配置数据源:确定使用什么数据库,帐号密码等。如果文件名以-ds结尾会被认定为数据源:
<datasources> <local-tx-datasource> <jndi-name>MySqlDS</jndi-name> <connection-url>jdbc:mysql://localhost:3306/mtjdbc?characterEncoding=utf-8</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>root</user-name> <password>sa</password> <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name> <metadata> <type-mapping>mySQL</type-mapping> </metadata> </local-tx-datasource> </datasources>
将数据源xml文件放入deploy目下即可。
然后是在EJB项目中定义persistence.xml文件,该文件必须在META-INF目录下,功能等同于平常hibernate的config文件,定义数据源的JNDI、指定映射文件以及数据库框架的额外设置:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <!-- 定义持久化单元 --> <persistence-unit name="jpademo" transaction-type="JTA"> <!-- 指定数据源的JNDI名 --> <jta-data-source>java:MySqlDS</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="update" /> <!-- 打印sql语句 --> <property name="hibernate.show_sql" value="true" /> <!-- 格式化sql语句 --> <property name="hibernate.format_sql" value="true"/> </properties> </persistence-unit> </persistence>
了解hibernate的应该知道javabean是orm的关键,用户可以写一个xml+一个javabean实现orm映射,也可以使用单纯的javabean再通过注解定义orm关系:
@Entity @Table(name = "userinfo") public class Userinfo implements Serializable { private static final long serialVersionUID = -1983888820266030468L; @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Column(name = "code") private String code; @Column(name = "name") private String name; @Column(name = "sex") private Integer sex; @Column(name = "phone") private String phone; @Column(name = "money") private double money; ...........
编写增删改查bean,JPA标准定义了增删改查方法接口,hibernate也很好的完成了接口的实现。
@Stateless @Remote(UserinfoService.class) public class UserinfoServiceImpl implements UserinfoService{ //unitName指定persistence.xml持久化单元名 @PersistenceContext(unitName="jpademo") EntityManager em; @Override public void delete(int user) { //getReference类似于load em.remove(em.getReference(Userinfo.class, user)); } @Override public Userinfo get(int user) { return em.find(Userinfo.class, user); } @Override public void save(Userinfo user) { em.persist(user); } @Override public void update(Userinfo user) { em.merge(user); } @SuppressWarnings("unchecked") @Override public List<Userinfo> getAll() { return em.createQuery("SELECT user FROM Userinfo user").getResultList(); } }
【JMS消息服务】
java消息服务用于实现两个客户端消息通信,但两客户端不是直接通过Socket或URL通信,而是通过中间服务器控制转发消息:
客户端一----消息---->Jboss服务器-----消息---->客户端二
通过服务器控制转发可以保证消息稳定、安全、效率。
消息传递系统的中心就是Message,一条Message由三个部分组成:
头(header)、属性(property)和主体(body)
Message接口有多种实现:StreamMessage流消息、MapMessage键值对消息、TextMessage文本字符串消息、ObjectMessage序列化对象消息和ByteMessage字节流消息。
JMS支持两种消息传递模型:点对点模型和发布/订阅模型
点对点模型指定了一条消息只能从一个客户端传递到一个接收方(Queue模式)
发布/订阅模型指定一条消息可以被多个订阅的客户接收(Topick模式)
【EJB的Webservice】
Web Services是由企业发布的完成其特定商务需求的在线应用服务,其他公司或应用软件能够通过Internet来访问并使用这项在线服务。
JavaEE为WebService提供了两种编程模型:EJB容器模型以及Web容器模型。
注意普通的EJB bean并非web service,这些bean只能被java代码调用,而实现web service标准的bean会被java以及其它编程语言调用。
其实EJB实现webservice非常简单,只需要配置一个注解,定义好相应属性即可。
@javax.jws.WebService( //让整个类成为webservice targetNamespace="http://com.ejb.webservice", //指定命名空间名 name="WebService", //修改端点接口名 serviceName="WebServiceImpl" //服务的名称 ) @Stateless @Remote(WebService.class) public class WebServiceImpl implements WebService{ //@WebService 如果只让某个方法成为webservice可以在该方法头设置 @Override public List<User> findUsers() { List<User> lst = new ArrayList<User>(); for(int i=0;i<10;i++){ lst.add(new User(i, "微博"+i)); } return lst; } @Override public String getName(String name) { return "name:"+name; } @Override public User getUser(int id) { return new User(id, "微博"); } }