EJB初步学习

来源:互联网 发布:m3轮毂数据 编辑:程序博客网 时间:2024/05/16 09:12

 

今天简单学习了传说中的EJB,首先总的感觉,就是他的最重要的一个特点吧,就是能够使远程用户访问到本地或是服务器上的资源服务器。打个比方吧,传统的,还记得我们的第一个JAVA项目吧,那是个简单的对数据库增删改查的操作,用简单的界面来显示数据。那么当我们把这个项目打包发布之后,事必要把你自己的数据库也贡献出去,你做的软件在进行增删改查时也就只能对你机子上的一个数据库,别人如果想要对你这个数据库进行访问,那必须到你数据库所在的终端设备上才可以。而传说中EJB结合JBOSS正好可以克服这个问题,关于JBOSS是一个EJB容器,但似乎也有WEB服务器的作用,关于这点,我自己也不是很清楚,我使用过tomcat那个我知道是WEB服务器,这里我想可以是JBOSSEJB容器,但同时也集成了WEB服务器的功能吧。好,从官网上下载到JBOSS,该设置的都设置了,JBOSS服务器成功运行,关于JBOSS的设置,网上很多资料,这里不在重复。但在这里讲我设置时遇到的一个上错误吧,当我在正确设置JBOSS-HOME后,但在以后的配置文件中调用系统这个属性时,经常是把JBOSS-HOME的变量在最后加上一个分号,从而使自己程序在访问这些配置时经常出错,当时我是用全路径名称。后来才发现不知道为什么,在我的JBOSS-HOME所在的路径中自动生成了一个带有分号的文件夹,我想可能是我的JBOSS-HOME指到那个地方去了吧。把这个文件夹删除后就可以正常使用了。
好慢慢来,首先是我的第一个EJB的例子,HelloWorld.首先要说明的是Bean,关于这个Bean有很多,会话,实例等等。那么究竟什么是Bean呢在字典中查的意思也都不对。在这里暂且把Bean当作是一个包吧,把一些应用封装,毕竟是人为规定的,不用太计较了。
让我先来看看会话Beansession beansession bean是实现业务逻辑的地方,简单来说,像我们要实现两数相加或是从数据库中读取数据,都江堰市是通过Session Bean来实现的。这样说来很像三层开发模式中的逻辑层,也像MAC中的C层。根据是否可以维护会话状态,Session Bean分为有状态bean和无状态bean.有状态bean可以维护会话状态,无状态bean不维护会话状态。要维护会话状态,意味EJB窗口要为每个用户创建一个bean实例,并通过该实例保存着与用户的会话状态。不维护会话状态,意味着一个bean实例不需要保存与某个用户的会话状态,这时一个bean实例可以为多个用户服务。可能说了这么多大家还是不太懂,怎么说呢,当我们把这个会话bean提交给JBOSS服务器后,客户端会请求调用这些bean来处理信息数据等,当客户端发送请求时,服务器端要生成bean对象给这个用户。无状态bean的意思就是这些创建之后都会放在一个bean池中,客户调用时池中有就从池中取,池中没有就创建,而有状态会对每个不同的用户创建一个bean实例。而要开发一个Session Bean,我们按面向对象的思维,要给这个bean定义接口和相应的实现类。从而这个接口分为了远程(remote)和本地(local)接口,在这里先说明一下,不强制要求你实现remotelocal接口,但实现两者是一个比较好的方法。
远程接口(remote interface),定义了session bean的业务方法,而这些方法可以被来自EJB容器之外的其它应用使用。他通过的是Socket通信原理。
本地接口(local interface),定义了session bean的业务方法,而这些方法可以被同处于EJB容器内的其它应用使用。因为local接口允许bean之间直接通过内在交互,没有分布式对象协议的开销,从而发送了性能。
Bean(bean class) bean class 包含了业务逻辑,它是实现接口所有的方法。
下面用一个简单的图来表示bean的运行流程:
 
 
 

浏览器

http://localhost:8080/Helloworld/index.jsp
 
JSP Engineer
index.jsp
HelloWord helloword=(Helloword)ctx.lookUp();
Session bean EJB container
Jndi.properties
 
HelloWorldBean.class
浏览器请求index.jsp
编译JSP文件
查找存根对象
 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

好下面开始编写bean了,在这之前首先要把JBOSS的一些JAR文件导入到项目中去,首先是bean接口,很简单根普通接口一样,方要是用来定义方法,和在调用时返回实现这个接口的存根。
然后是实现这个接口方法的实现类,当然这里叫session bean,建议类名后面加上bean表示这是一个实现类。在这个类上有几个标签要注意,第一组是@Stateless,@Stateful.第一个指定这个bean是无状态的.第二个当然是用状态的了。在这里面还可以指定这个bean的名字,为什么要指定呢,我打个比方,如果不指定名字那一个接口类有两bean实现,那我们调用的时候,究竟是调用哪个呢,这时编译器会报错的。当然我只是打个比方,其实在这里我们是可以不指定名字的,那肯定有默认的了,那这个默认的名字是什么呢,其实就是这个实现类的类名(不包含包结构),所以只要我们的类名不同,那我们完全可以不指定每个bean的名字。也可以很好的控制了。当然在这里还是要告诉大家指定bean名字的方法:@Staeless(name=”xxx”),还有一组是@Remote(接口的class文件名), @Local(接口的class文件名),这组是用来指定接口是远程还是本地的。当然我们也可以指定多个接口。方法是:@Local({接口1class文件名,接口2class文件名。。。。。})或分开有的是远程,有的本地。
好了这两步完成之后一个简单的EJB就完成了,接下来就是把它布暑到JBOSS中去了。关于布暑也有两种方法一种是利用开发工具自带的jar导出功能。这个很简单点next就行。还有一种方法也是在真正开发过程中常用的Ant打包方法。这种打包方法十分的方便。首先在当前项目的根目录下新建一个XML文件,build.xml
 
<?xml version="1.0" encoding="UTF-8"?>
<!--.表示同一目录 ..表示在上一目录-->
 
<projectname="HelloWorld"basedir="."> //basedir是指定该项目的目录,点表示为当前目录,也就是build.xml所在的目录
   <!--指定项目的源文件夹 -->
   <propertyname="src.dir"value="${basedir}/src"/>
   <!--指定系统中的变量因为我们要从系统变量中得到值参考以下,简单的说就是引用系统的环境变量 -->
   <propertyenvironment="env"/>
   <propertyname="jboss.home"value="${env.JBOSS_HOME}"/>
   <!-- 指定目前Jboss的配置项 -->
   <propertyname="jboss.server.config"value="default"/>
   <!-- 指定后面编译类时存储的路径 -->
   <propertyname="build.dir"value="${basedir}/build"/>
    <!-- 指定我们要用到jbossjar文件 -->
    <pathid="build.classpath">
   
        <filesetdir="${jboss.home}/client">
            <includename="*.jar"/>
         </fileset>
    <pathelementlocation="${build.dir}"/>
    </path>
 
    <!-- 是用来创建文件夹的 -->
    <targetname="prepare">
       <deletedir="${build.dir}"/>
       <mkdirdir="${build.dir}"/>
    </target>
 
    
    <targetname="compile"depends="prepare"description="编译">
    <javacsrcdir="${src.dir}"destdir="${build.dir}">
       <classpathrefid="build.classpath"/>
    </javac>
    </target>
 
    <!-- 打包 -->
    <targetname="ejbjar"depends="compile"description="创建ejb发布包">
       <jarjarfile="${basedir}/${ant.project.name}.jar">
           <filesetdir="${build.dir}">
            <includename="**/*.class"/>
            </fileset>
 
      
 
       
       </jar>
    </target>
 
    <targetname="deploy"depends="ejbjar"description="ejb发布">
       <copyfile="${basedir}/${ant.project.name}.jar"todir="F:/DirectRunSoft/jboss-5.1.0.GA-jdk6/jboss-5.1.0.GA/server/${jboss.server.config}/deploy"/>
    </target>
                                                                                                                                                                               
    <targetname="undeploy"description="卸载ejb">
       <deletefile="F:/DirectRunSoft/jboss-5.1.0.GA-jdk6/jboss-5.1.0.GA/server/${jboss.server.config}/deploy/${ant.project.name}.jar"/>
    </target>
                                                                                                                                                                              
  
 
</project>
 
当这个Bean发布成功后,我们可以到http://localhost:8080/jmx-console中去查看是否发布成功。(发布时要启动JBOSS服务器)
发布成功后就会生成一个jndi.
什么叫jndi呢,其实不用管他,只要记着你发布之后,那JBOSS会给你一个号。而别人调用时,也是查这个号。差不多就是这意思了。
那么接下来我们就要创建一个客户端来访问这个bean服务了。说白了就是那个实现类中提供的方法。那如上文所说,首先是查找这个jndi号了。在查之前还有一个工作就是我们必须设置应用服务器(JBOSS)的上下文信息。主要是jndi的驱动类名(java.naming.factory.initial)和命名服务器提供者URL(java.naming.provider.url).
那这两个是什么东东呢,呵呵,第一个,你可以作为jndi的驱动,上文不是讲你把session bean发布到了应用服务器之后,服务器会生成jndi吗,你查找的时候也是查找这个所谓的jndi,那么我们肯定需要驱动吧,打个比方吧,你查找数据库时,也是要Class.Form(driver),呵呵,对不。这个驱动在JBOSS中是有的,只要加载一下(org.jnp.interface.NamingContextFactory)
第二个,url根数据库操作提供的url有点像吧,那么我们加快一下数据库中的url是用来做什么的呢(url=jdbc:sqlserver://localhost8080;Datebase=Student)一他指定这个数据库所在的主机和访问时的端口,然后数据库名字等等信息,那么类比而来,我先把这个jndiurl写出来(127.0.0.1:1099)似乎比数据库的url更为简单,他其实是用来指定全程服务器的主机地址和端口号,你要查jndi,但你去哪查啊,从哪个端口去查。
除了以上两个属性还有两个属性也常用到,分别是(java.naming.security.principalContext.SECURITY_PRINCIPAL)(java.naming.security.credentialsContext.SECURITY_CREDENTIALS),他们类似于数据库的用户名和密码。
 
Properties props=new Properties();
       props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
       props.setProperty("java.naming.provider.url", "127.0.0.1:1099");
//上面是将配置信息注册进系统属性
        try {
            //这是实现一个jndi的对象。
           InitialContext ctx=new InitialContext();
//查找jndi
           HelloWorld hellworld=(HelloWorld)ctx.lookup("HelloWorldBean/remote");
//得到服务
           System.out.println(hellworld.sayHello("约定291天后"));
           System.out.println(hellworld.getClass().getName());
       } catch (NamingException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
除了上面的在代码中显示加载相应配置之外,我们还可以使用配置文件,你可能已经发现一个问题,在实例化jndi对象的时候就是上面的ctx,我们并没有看到对上面配置的调用,其实jndi对象实例也可以这样写InitialContext ctx=new InitialContext(props);,如此似乎更直观一点但在实际中,还是上面的写法,因为加入参数反而会影响程序的可移植性,那么我们不传入参数,那jndi实例化时是会自动调用System.getPropety()方法来查找配置的。下面介绍使用配置文件的方法。因为如果没有指定propertyjndi还会在classpath中寻找jndi.properties.那么水到渠成。建一个这个配置文件就O了。
 
 
 
WEB程序打包:
War打包
1.开发集成工具
2.压缩软件,压缩格式zip然后再改成war
3.Ant打包:
jarant类似,但也有不同:
<?xml version="1.0" encoding="UTF-8"?>
 
<project name="EJBClient" default="war" basedir=".">
    <target name="war" description="create web bag">
       <war warfile="${basedir}/EJBTest.war" webxml="${basedir}/WEB-INF/web.xml">
           <fileset dir="${basedir}">
              <include name="*.jsp" />
           </fileset>
           <classes dir="${basedir}/WEB-INF/classes">
              <include name="**/*.class" />
           </classes>
           <lib dir="${basedir}/WEB-INF/lib"></lib>
       </war>
    </target>
</project>
 
下面我们再来看看bean的生命周期,在Session bean的生命周期里,状态变化会触发生命周期事件的发生。如从does not exit状态进入method-ready pool状态会触发@PostConstrut事件。同样从method-read pool 状态进入does not exist 状态也会触发@PreDestory事件。有些时候我们需要定制session bean的管理过程,打个比方,你想在实例化bean的时候进行一些变量的初始化,又或者是在bean 实例被告销毁的时候关掉外部资源。这些都可以通过在bean类中定义生命周期的回调方法来实现。通过注释你可以将任何方法指定为回调方法,不同于EJB2.1的是,所有的回调方法都必须实现,即时是空。
  1. @PostConstruct: bean对象完成实例化时标注了这个注释的方法会立刻调用,每个bean class 只能定义一个这样的方法。此注释适用于有状态和无状态的bean.
  2. @PreDestory: 标注了这个注释的方法会在销毁一个无用的或者过期的bean实例之前调用。这个注释同时适用于有状态和无状态的bean.
  3. @PrePassivate: 当一个有状态的bean实例空闲时间过长,就会发生钝化(passivate).标注这个注释的方法会在钝化之前调用。Bean实例被钝化之后,在一段时间内如果仍然没有用户对bean实例进行操作,窗口将会从硬盘中删除它。以后任何针对该bean的方法的调用。窗器都会抛出例外。这个注释适用于有状态会话bean.
  4. @PostActivate当客户端再次使用已经被钝化的有状态bean时,EJB容器会重新实例化一个bean实例,并从中将之前的状态恢复,标注了这个注释的方法在洗涤完成时被调用。这个注释只适用于有状态会话bean.
  5. @Init 这个注释指定了有状态bean初始化的方法,它区别于@PostConstruct注释在于:多个@Init注释方法可以同时存在Session bean中,但每个bean实例只会有一个@Init注释的方法会被调用。@PostConstruct@Init之后被调用。
  6. Remove当客户端调用标注了@Remove注释的方法时,容器将在方法执行结束后把bean实例删除。
 
 
当我们在一个EJB中想调用另一个EJB对象时,传统的是实例化这个object就可以了,但在ejb中不行,因为EJB实例创建及销毁是由容器管理的。要在bean中要用其它EJB或资源,你必须通过jNID查找或注入注释。查找前文已经说过,这里介绍下注入方法:
使用@EJB (beanName=”XXX”) HelloWorldBean2 hellowrld;
 
 
 
 
 
还有许多功能细节,暂时还用不到吧。好了下面我们进入实例bean
持久化是位于JDBC之上的一个更高层的抽象。持久层将对象映射到数据库,以便在查询,装载,更新,或删除对象的时候,无须使用像JDBC那样繁琐的API,在EJB的早期版本中,持久化是EJB平台的一部分。从EJB30开始,持久化已经自成规范,被称为Java Persistence API.它定义了一种方法,可以将常规的普通JAVA对象(POJO)映射到数据库中去。这些普通的JAVA对象被称作entity bean.然后,创建一个Entity Bean对象相当于新建一个记录,删除一个Entity Bean会同时从数据库中删除对应记录。修改一个Entity Bean,容器也会自动将Entity bean的状态同步到数据库。同时Java Persistence API还定义了一种查询语句(JPQL)以便对java对象的处理。
 
 
 
下面我们就开发这个实体bean,首先进行的就是对Jboss数据源的配置,因为你创建实体bean是映射到数据库中去的,所以要加载相应的资源,在JBOSS安装目录/docs/emamples/jca目录中找到,名称为数据库名+-ds.xml的例子文件。在资源部署前,要把相应的数据库驱动拷贝到server/default/lib目录下,完成后,要重启JBOSS服务器,完成了,可以在http://localhost:8080/jmx-console/ 查看相应的数据源信息。
原创粉丝点击