EJB例項教程筆記(一)

magic_dreamer發表於2010-07-04
EJB例項教程筆記(一)

電子書EJB3例項教程byLiHuoming.pdf筆記

第一章 EJB知識和執行環境配置
EJB定義了三種企業Bean:會話Bean(Session Bean),實體Bean(Entity Bean)和訊息驅動Bean(Message Driven Bean)
訊息驅動BEAN(MDB)
使用於業務執行的時間長,執行結果無需實時給使用者反饋的場合。

安裝JDK1.6,Eclipse3.5,JBOSS5.1
我在eclipse中啟動JBOSS5.1,然後通過瀏覽器訪問地址 http://localhost:8080 點選管理控制檯Administration Console使用者名稱和密碼預設是admin

執行第一個個EJB示例
原始碼下載地址:http://www.foshanshop.net/download.html

找到helloword目錄F:\book\ejb\EJB3例項教程程式碼示例\sourcecode\HelloWorld,將HelloWorld.jar放到
目錄 E:\jboss-5.1.0.GA\server\default\deploy

找到F:\book\ejb\EJB3例項教程程式碼示例\sourcecode\EJBTest下面的EJBTest.war拷貝到jboss釋出目錄。

啟動JBOSS,訪問地址:http://localhost:8080/EJBTest/Test.jsp , 就看到hello world了。呵呵。

JBOSS自動查詢deploy目錄中的.jar .war .ear,JBOSS通過對比時間戳來檢測變化。開始學習示例。

HelloWorld匯入eclipse,匯入server----jboss等,就去掉了紅叉了。啟動JBOSS,遠端呼叫HelloWorldBean,執行單元測試HelloWorldTest.java,執行成功。

測試類如下HelloWorldTest.java:
public class HelloWorldTest {
protected static HelloWorld helloworld;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ctx = new InitialContext(props);
helloworld = (HelloWorld)ctx.lookup("HelloWorldBean/remote");
}

@Test
public void testSayHello() {
String result = helloworld.SayHello("佛山人");
//System.out.println(result);
assertEquals("佛山人說:你好!世界,這是我的第一個EJB3哦.", result);
}
}
無狀態的會話Bean如下HelloWorldBean.java:
package com.foshanshop.ejb3.impl;

import com.foshanshop.ejb3.HelloWorld;

import javax.ejb.Remote;
import javax.ejb.Stateless;

@Stateless
@Remote ({HelloWorld.class})
public class HelloWorldBean implements HelloWorld {
public String SayHello(String name) {
return name +"說:你好!世界,這是我的第一個EJB3哦.";
}
}

第二章 會話Bean(Session Bean)

分為有狀態和無狀態Bean
有狀態BEAN可以維護會話狀態,EJB容器要為每個使用者建立一個BEAN例項,並通過該例項儲存與使用者的會話狀態;
無狀態BEAN,不維護會話狀態,BEAN例項不需要儲存與某個使用者的會話狀態,一個BEAN例項可以為多個使用者服務。

無狀態BEAN,使用物件池技術,少量例項處理多請求。

2.1 Stateless Session Beans(無狀態bean)開發

2.1.1 開發只實現Remote介面的無狀態Session Bean

示例中呼叫EJB的JSP程式碼如下:
<%@ page contentType="text/html; charset=GBK"%>
<%@ page import="com.foshanshop.ejb3.HelloWorld, javax.naming.*, java.util.Properties"%>
<%
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
try {
InitialContext ctx = new InitialContext(props);
HelloWorld helloworld = (HelloWorld) ctx.lookup("HelloWorldBean/remote");
out.println(helloworld.SayHello("佛山人"));
} catch (NamingException e) {
out.println(e.getMessage());
}
%>

@Stateless 無狀態會話Bean
@Remote Remote介面
多個介面 @Remote({HelloWorld.class,Hello.class,World.class})

釋出了EJB後,可以在這裡檢視,地址:http://localhost:8080/jmx-console/
裡面的JBOSS.J2EE

JNDI名稱規則
如果是打成EAR包的,
本地介面:EAR_FILE_BASE_NAME/EJB_CLASS_NAME/local
遠端介面:EAR_FILE_BASE_NAME/EJB_CLASS_NAME/remote

EJB應用打包為.jar的模組檔案,預設的全域性JNDI名稱
本地介面:EJB_CLASS_NAME/local
遠端介面:EJB_CLASS_NAME/remote

EJB-CLASS-NAME是不帶包名的,如果通過@stateless.name() @Stateful.name()指定了EJB名稱,那麼EJB-CLASS-NAME就要替換為EJB名稱。

props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");

java.naming.factory.initial或Context.INITIAL_CONTEXT_FACTORY,環境屬性名,用於指定InitialContext工廠(JDNI驅動),類似於JDBC指定資料庫驅動類。這裡指定JBOSS提供的驅動類:
org.jnp.interfaces.NamingContextFactory

java.naming.provider.url或Context.PROVIDER_URL,環境屬性名,包含提供命名服務的主機地址和埠號。類似於JDBC指定資料庫的連線URL。連線到JBOSS的格式為:jnp://host:port,其中jnp:是指使用的協議,JBOSS使用的是基於Socket/RMI的協議。

另外還有
java.naming.security.principal or Context.SECURITY_PRINCIPAL
java.naming.credential or Context.SECURITY_CREDENTIALS

避免硬編碼,還可以在classpath中加入,jndi.properties檔案
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
對於HelloWorld的Bean class,並不要求必須實現HelloWorld介面,可以去掉介面實現,但是一般不推薦這樣。按照介面程式設計約束,介面要引用目標類,那麼目標類必須實現介面才可以,否則介面是引用不了實現類的。事實上,EJB客戶端介面引用的物件並非Bean類,跟Bean類物件的互動也不是直接進行的,這要看EJB的呼叫機制,才能得到答案。

2.1.2 開發只實現Local介面的無狀態Session Bean
將原有的標籤@Remote改為@Local。Local介面的客戶端與EJB容器必須在同一個JVM中。

2.1.3 開發實現了Remote與Local介面的無狀態Session Bean
我的整個BUILD檔案build.xml如下:
<project name="${app.name}" default="all" xmlns:ivy="antlib:org.apache.ivy.ant">
<!-- some variables used -->
<property file="build.properties" />
<property name="app.name" value="${app.name}" />
<property name="src.dir" value="src" />
<property name="conf.dir" value="conf" />
<property name="build.dir" value="build" />
<property name="dist.dir" value="dist" />
<property name="web.dir" value="WebContent" />
<property name="lib.dir" value="${web.dir}/WEB-INF/lib" />
<property name="tomcat.dir" value="${catalina.home}" />
<property name="jboss.dir" value="${jboss.home}" />
<property name="jboss.server" value="${jboss.server.config}" />

<property name="ivy.install.version" value="2.0.0" />
<property name="ivy.home" value="${user.home}/.ant" />
<property name="ivy.jar.dir" value="${ivy.home}/lib" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<!-- paths used for compilation and run -->
<path id="compile.path.id">
<fileset dir="${jboss.dir}\client">
<include name="*.jar" />
</fileset>
<fileset dir="${tomcat.dir}\lib" />
<fileset dir="${lib.dir}" />
<path location="${build.dir}" />
</path>

<taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpath="${ivy.jar.file}" />
<target name="download-ivy">
<mkdir dir="${ivy.jar.dir}" />
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar" dest="${ivy.jar.file}" usetimestamp="true" />
</target>

<!-- ================================= target: resolve ================================= -->
<target name="resolve" depends="download-ivy" description="--> retreive dependencies with ivy">
<ivy:retrieve pattern="${web.dir}/WEB-INF/lib/[artifact]-[revision].[ext]" />
</target>

<!-- ================================= target: clean-cache ================================= -->
<target name="clean-cache-lib" description="--> clean the ivy cache">
<delete dir="${ivy.home}/cache" />
</target>

<!-- ================================= target: clean ================================= -->
<target name="clean" description="--> clean the project">
<delete dir="${build.dir}" />
<delete dir="${dist.dir}" />
<delete file="${jboss.dir}\server\${jboss.server}\deploy\${app.name}.jar" />
</target>

<!-- ================================= target: prepare ================================= -->
<target name="prepare" description="--> make-dir build , dist">
<tstamp />
<mkdir dir="${build.dir}" />
<mkdir dir="${dist.dir}" />
<echo message="built at ${DSTAMP}-${TSTAMP}" />
<echo message="ant.version - ${ant.version}" />
<echo message="ant.java.version - ${ant.java.version}" />
<echo message="ivy.cache.dir - ${ivy.home}/cache" />
</target>

<!-- ================================= target: compile ================================= -->
<target name="compile" depends="prepare" description="--> Compile Java sources">
<!-- Compile Java classes as necessary -->
<property name="compile.java.encoding" value="${ant.encoding}" />
<mkdir dir="${build.dir}/WEB-INF/classes" />
<javac srcdir="${src.dir}" destdir="${build.dir}/WEB-INF/classes" debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${compile.optimize}" encoding="${ant.encoding}" source="${java.level}">
<classpath refid="compile.path.id" />
</javac>
<copy todir="${build.dir}/WEB-INF/classes">
<fileset dir="${src.dir}">
<include name="**/*.xml" />
<include name="**/*.txt" />
<include name="**/*.properties" />
<exclude name="config.properties" />
<exclude name="log4j.properties" />
</fileset>
</copy>
</target>

<!-- ================================= target: javadoc ================================= -->
<target name="javadoc" depends="compile" description="-->Create Javadoc API documentation">
<mkdir dir="${dist.dir}/api-docs" />
<javadoc sourcepath="${src.dir}" destdir="${dist.dir}/api-docs" packagenames="*">
<classpath refid="compile.path.id" />
</javadoc>
</target>

<target name="copyWeb">
<copy todir="${build.dir}">
<fileset dir="${web.dir}">
<exclude name="WEB-INF/classes/**" />
</fileset>
</copy>
</target>

<target name="web.war" depends="compile,copyWeb" description="--> build web application war package">
<war destfile="${dist.dir}/${app.name}.war" webxml="${build.dir}/WEB-INF/web.xml">
<fileset dir="${build.dir}" />
</war>
<copy tofile="${dist.dir}/config.properties" file="${conf.dir}/config.properties" />
<copy tofile="${dist.dir}/log4j.properties" file="${conf.dir}/log4j.properties" />
</target>

<target name="web.deploy" depends="web.war">
<copy file="${dist.dir}\${app.name}.war" todir="${jboss.dir}\server\${jboss.server}\deploy" />
</target>

<target name="ejb.jar" depends="compile" description="build the ejb jar">
<jar jarfile="${dist.dir}\${app.name}.jar">
<fileset dir="${build.dir}/WEB-INF/classes">
<include name="com/sillycat/ejb/**/*.class" />
</fileset>
</jar>
</target>

<target name="ejb.deploy" depends="ejb.jar">
<copy file="${dist.dir}\${app.name}.jar" todir="${jboss.dir}\server\${jboss.server}\deploy" />
</target>

<target name="all" depends="clean,web.deploy,ejb.deploy" description="clean, build jar package, generate api-doc">
</target>

</project>


build.properties檔案如下:
app.name=easyejb
catalina.home=E:\\tool/apache-tomcat-6.0.20/
ant.encoding=UTF-8
java.level=1.5

jboss.home=E:\\jboss-5.1.0.GA/
jboss.server.config=default
啟動JBOSS後,可以直接進行單元測試:
package com.sillycat.ejb;

import static org.junit.Assert.assertEquals;

import javax.naming.InitialContext;

import org.junit.BeforeClass;
import org.junit.Test;

public class HelloWorldTest {
protected static HelloWorld helloworld;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
InitialContext ctx = new InitialContext();
helloworld = (HelloWorld) ctx.lookup("HelloWorldBean/remote");
}

@Test
public void testSayHello() {
String result = helloworld.SayHello("EJB&SPRING");
assertEquals("EJB&SPRING回憶過去,痛苦的相思忘不了!", result);
}
}

按照要求撰寫了Operation,OperationLocal,OperationBean,將jar和war都發布到JBOSS執行,出錯如下:
Caused by: org.jboss.xb.binding.JBossXBException: Failed to create a new SAX parser
at org.jboss.xb.binding.parser.sax.SaxJBossXBParser.<init>(SaxJBossXBParser.java:97)
at org.jboss.xb.binding.UnmarshallerImpl.<init>(UnmarshallerImpl.java:56)
at org.jboss.xb.binding.UnmarshallerFactory$UnmarshallerFactoryImpl.newUnmarshaller(UnmarshallerFactory.java:96)
... 73 more
Caused by: java.lang.ClassCastException: org.apache.xerces.parsers.XML11Configuration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration
at org.apache.xerces.parsers.SAXParser.<init>(Unknown Source)
at org.apache.xerces.parsers.SAXParser.<init>(Unknown Source)

網上搜尋了一下,說道的解決辦法都是
刪掉專案中的xerces-2.6.2.jar和xml-apis.jar兩個檔案!
我先測試EJB3,所以也刪除掉了這些東東,當然這只是暫時這麼做,這不是最後解決方案。

OperationBean.java如下:
package com.sillycat.ejb.impl;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import com.sillycat.ejb.Operation;
import com.sillycat.ejb.OperationLocal;
@Stateless
@Remote(Operation.class)
@Local(OperationLocal.class)
public class OperationBean implements Operation, OperationLocal {
private int total = 0;
public int Addup() {
total++;
return total;
}
}

測試的JSP,ejbTest1.jsp如下:
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page import="com.sillycat.ejb.*, javax.naming.*, java.util.Properties"%>
<%
InitialContext ctx = new InitialContext();
try {
//通過遠端介面呼叫EJB
Operation remote = (Operation) ctx.lookup("OperationBean/remote");
out.println("<br>(通過遠端介面呼叫EJB)累加的結果是:"+ remote.Addup());
} catch (Exception e) {
out.println("<br>遠端介面呼叫失敗");
}
out.println("<br>==============================================");
try {
//通過本地介面呼叫EJB
OperationLocal local = (OperationLocal) ctx.lookup("OperationBean/local");
out.println("<br>(通過本地介面呼叫EJB)累加的結果是:"+ local.Addup());
} catch (Exception e) {
out.println("<br>本地介面呼叫失敗");
}
%>
頁面顯示的結果是:
(通過遠端介面呼叫EJB)累加的結果是:7
==============================================
(通過本地介面呼叫EJB)累加的結果是:8

Stateless Session Bean一旦例項化就被加入例項池中,各個使用者都可以用,即使使用者已經消亡,Stateless Session Bean的生命也不一定結束。如果它有自己的屬性(變數),那麼這些變數就會受到所有使用它的使用者的影響。

2.2 例項池化(Instance Pooling)
避免每次使用者方法呼叫進行例項的建立和銷燬。

2.2 stateless session bean的生命週期
它只有兩個狀態:does not exist 和 method-ready pool

相關文章