基本概念:
1.ejb的背景(什么是分布式的应用程序?分布式面临的问题?)
2.什么是javaee?
3.什么是ejb?有什么用?
4.ejb为什么用java来写?
5.什么是服务?
ejb的背景(什么是分布式的应用程序?分布式面临的问题?):
JAVAEE服务器和容器:
分布式多层应用图:
我们说的企业级应用就是分布式应用,什么是分布式应用?
client1 client2 client3
| | |
server server
/ /
data
多个客户端访问多个服务器,多个服务器访问公共数据库,应用程序分布在多台服务器上,他们共同完成一个目标,以往用的是Host/Terminal,所有的程序在一个服务器上,这样做的问题是服务器端的压力过大,Client/Server方式解决了这个问题,它将程序的一部分写在了客户端但是,问题是升级维护比较麻烦,采用分布式的B/S架构解决了这个问题,它可以设置多个服务器,然后由程序将用户的请求分流给各个服务器,各个服务器之间互相访问,并且各个服务器的系统可能是异构的系统,这样一是减轻了服务器的压力,也解决了维护不便的问题
分布式面临的问题:
1.远程方法调用
2.群集系统:考虑给各个服务器分配请求平衡的问题
3.事务问题:如何控制事务,事务传输问题,分布式事务问题
4.动态的重新部署:系统升级不影响已在使用中的客户
5.超市下班问题:超市下班对进入关闭,但是正在购物的客户还可以选购
6.组件生命周期管理...等等
这些问题说明了分布式程序的复杂性
什么是javaee:
企业应用中需要的各个服务组件的集合:web组件,业务组件,邮件服务,命名服务等
什么是ejb?有什么用?
我们知道企业级应用=分布式应用,分布式应用有很多要考虑的问题,这些问题比较复杂,所以将不变的做成中间件服务器(类),也就是ejb容器(如sun公司的产品)将变的做成业务组件(类),通过一系列配置,完成业务逻辑,EJB定义了业务组件和中间件的标准(规范,开发流程),组件框架,主要做业务开发,一些分布式程序产生的问题不需要考虑,开发人员只需要根据ejb规范开发业务逻辑就可以了,这些问题由ejb容器负责
作用:统一的标准(ejb容器),快速应用开发
ejb为什么用java来写?
和java本身的特性呼应:接口实现分离,安全
什么是服务?
服务:数据的交换,例如:请求资源数据到服务器,服务器中的服务接收并返回数据
第一个ejb:
1.开发步骤
1.手工方式
2.使用IDE工具方式
开发步骤:
1.安装javaee jdk
2.安装服务器(我们使用的是sun服务器)
3.创建domain(域)
4.设置classpath,配置jar包appserv-rt and javaee.jar 这两个包在服务器中
5.启动服务器
6.创建ejb模块
7.一个远程接口,一个包含远程方法的bean类
8.编译,打包 将这个ejb模块打包
9.部署到域中的autodeploy目录下
10.写客户端调用这个远程方法
手工方式:
创建域:
假定你的宿主目录是/home/soft01/ , 新建domain 为domain2 x:/home/soft01/自己定义一个域的目录
$ /opt/SUNWappserver/bin/asadmin create-domain --domaindir /home/soft01/sundomains --adminport
4848 domain2
asadmin create-domain --domaindir f:/ejb --adminport 4848 domain
输入管理员用户名>admin
输入管理员密码>adminadmin
再次输入管理员密码>adminadmin
输入主密码>adminadmin
创建ejb模块:
1 准备CLASSPATH:
$export CLASSPATH=.:/opt/SUNWappserver/lib/appserv-rt.jar:/opt/SUNWappserver/lib/javaee.jar
2 启动server(假定你的domain 是domain2 ,domain 目录是/home/soft01/sundomains/)
$ /opt/SUNWappserver/bin/asadmin start-domain --domaindir /home/soft01/sundomains domain2
输入主密码>adminadmin
asadmin start-domain --domaindir F:/ejb domain
3 创建一个ejb模块目录:
$mkdir ~/myejbs
4 在myejbs目录中创建包first
$cd myejbs
$mkdir first
5 first 目录中创建两个java 文件
$cd first
$touch HelloBean.java HelloRemote.java:暴露bean中的远程方法,要想调用一个远程方法,必须有一个暴露接口去暴露这个方法
_________________________________________
package yuchen.first;
/*
* @Remote:说明这个接口是远程接口
* 如果想调用远程类中的远程方法,必须提供一个远程的接口
* 而且要写抽象的远程方法
*/
import javax.ejb.*;
@Remote
public interface HelloRemote {
String sayHello(String user);
}
__________________________________________
package yuchen.first;
/*
@Stateless:说明这个bean类是远程的bean类Bean类,Bean类即可以实现商业接口,也可以不实现,但是其中的方法签名要和接口中相同,不过最好还是实现商业接口避免书写错误
*/
import javax.ejb.*;
@Stateless
public class HelloBean implements HelloRemote {
public String sayHello(String user) {
return "hello , " + user;
}
}
__________________________________________
6 编译,打包 将这个ejb模块打包
$cd .. (确保pwd是/home/soft01/myejbs)
$javac first/*.java
$jar cvf HelloEJB.jar .
jar cvf HelloEJB.jar .
7 部署
cp HelloEJB.jar /home/soft01/sundomains/domain2/autodeploy/
域:包含javaee服务配置的集合
8 写一个客户端测试ejb:调用远程方法
Test.java
------------------------------------------
/*
* 程序目标:调用远程方法,输出一些字符串
*/
package yuchen.first;
import javax.naming.*;
import yuchen.first.*;
public class Test {
public static void main(String[] args) throws Exception {
Context context = new InitialContext();
HelloRemote hello = (HelloRemote) context.lookup(HelloRemote.class
.getName());
System.out.println(hello.sayHello("yuchen"));
}
}
------------------------------------------
9 编译,运行
使用IDE工具方式:
1.打开NetBeans,创建ejb容器:Runtime->Servers->”add”,域设置(4个选项)
注意端口: ORB listener Port (3700) Https Port (8181)
2.创建ejb模块,写接口and实现类
3.倒入包到ejb模块中
4.打包,Deploy Projects
5.创建客户端工程,写测试类,需要倒入ejb模块的jar包
6.运行客户端文件
ejb分布式远程方法调用的原理?
面向对象的远程方法调用都是通过代理模式实现的
什么是代理模式?有什么用?
客户端定义的接口和远程接口的一样,那么它的作用是什么呢?
接口的实现对象哪来的?
是通过jndi查询从远程服务器上获得的,用客户端接口引用它
这个对象是不是就是远程bean类的对象呢?
不是,而是bean的远程代理
什么是bean的远程代理?有什么用?
和bean一样,都实现了接口,当客户端调用远程代理对象的实现方法的时候,它去做网络连接,传递参数和返回值,做序列化和反序列化
客户端—Jndi查询到服务器的bean,容器创建的远程代理对象(存根)----服务端代理对象(骨架)—service服务对象(请求拦截器)—服务端的bean :代理对象的一系列操作流程是不变的,这些是由应用服务器实现,业务组件,也就是bean是变化的,由程序员来开发
服务对象拦截客户端装载一系列服务,如事务,安全等,这些都是不可见的,是应用服务器实现,那么服务器如何知道哪个方法需要哪些服务呢?标注,部署描述符加载服务
存根是服务对象的代理,服务对象是bean的代理
使用web调用一个ejb:
1.非MVC方式:
2.MVC方式:
非MVC方式:
程序目标:在浏览器端输入盘符,服务端将该盘符中的内容显示给用户
web:index.jsp
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@ page import="javax.naming.*,session.*" %>
<!DOCTYPE HTML PUBLIC "-//W 3C //DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<%!
ListRemote lr;
public void jspInit(){
try{
Context context=new InitialContext();
lr=(ListRemote)context.lookup(ListRemote.class.getName());
}catch(Exception e){
e.printStackTrace();
}
}
%>
<form method="get" action="index.jsp">
<input type="text" name="path" /><br>
<input type="submit" value="list" />
</form>
<%
String rst="";
String path=request.getParameter("path");
if(path!=null){
rst=lr.list(path);
}
%>
<%=path%>:<br><%=rst%>
</body>
</html>
ejb remote:
package session;
import javax.ejb.Remote;
/**
*
* @author yuchen
*/
@Remote
public interface ListRemote {
public String list(String path);
}
ejb bean:
package session;
import java.io.File;
import javax.ejb.Stateless;
/**
*
* @author yuchen
*/
@Stateless
public class ListBean implements session.ListRemote {
/** Creates a new instance of ListBean */
public ListBean() {
}
public String list(String path) {
File file=new File(path);
if(!file.isDirectory()){
return "meiyou";
}
String[] f=file.list();
String rst="";
for(String s:f){
rst+=s+"<br>";
}
return rst;
}
}
MVC方式:
web index.jsp:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W 3C //DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>JSP Page</h1>
<form action="list.do" method="post">
<input type="text" name="path" />
<input type="submit" value="list" />
</form>
</body>
</html>
web view.jsp:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W 3C //DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>JSP Page</h1>
<%
String view=(String)request.getAttribute("view");
%>
${view}
</body>
</html>
web servlet:
package controller;
import java.io.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.*;
import javax.servlet.http.*;
import session.ListRemote;
/**
*
* @author yuchen
* @version
*/
public class NewServlet extends HttpServlet {
/** Processes requests for both HTTP <code>GET</code> and
<code>POST</code> methods.
* @param request servlet request
* @param response servlet response
*/
ListRemote lr;
public void init(){
try {
Context context=new InitialContext();
lr=(ListRemote)context.lookup(ListRemote.class.getName());
} catch (NamingException ex) {
ex.printStackTrace();
}
}
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
String path=request.getParameter("path");
String rst=lr.list(path);
request.setAttribute("view",rst);
RequestDispatcher rd=request.getRequestDispatcher("view.jsp");
rd.forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
processRequest(request, response);
}
public String getServletInfo() {
return "Short description";
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>NewServlet</servlet-name>
<servlet-class>controller.NewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>NewServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
</welcome-file-list>
</web-app>
ejb:使用上面的ejb
SessionBean
1.什么是sessionBean?有状态?无状态?
2.sessionbean生命周期?
3.会话bean实例池
4.有状态会话bean的实例池
5.两种bean之间的区别
6.三种方法实现生命周期回调方法(将回调方法写到bean中 单独写一个类 部署描述符)
什么是sessionbean:
Sessionbean和其他企业bean的区别在于生命周期,SessionBean分为有状态的sessionbean和无状态的sessionbean,sessionbean的作用就是处理客户请求做业务处理的,如:订单登陆,数据库操作等
ejb中的sessionbean和普通的javabean的区别是什么?
Javabean不支持远程方法调用,所以用javabean做业务逻辑组件在分布式应用中是不行的,所以需要写支持分布式的sessionbean
会话bean实例池:
由于会话bean可能占用大量的资源,并且经常会使用,所以做个池来维护这些实例,作用就是提高效率,这个池相当于计算机的缓存一样,在计算机中cpu为了提高效率,往往会使用它,这比cpu访问内存和硬盘的速度快很多
sessionbean生命周期:
客户调用sessionbean的方法,ejb容器创建sessionbean实例,当客户不再使用了,容器
放回到会话池中,客户也可以显示调用@Remove方法销毁该实例,一般情况下,一个客户的持续期间为sessionbean的存活期,多用户不能同时使用一个sessionbean,sessionbean是服务完一个再去服务其他的客户
用户会话的持续期:
1.打开浏览器到关闭浏览器
2.java applet
3.一个java应用等等
这些期间为用户的会话的持续期
什么是无状态sessionbean:
如果客户端只有一个请求就能够完成业务需求,并且不需要保存各种信息,那么就使用
无状态bean,特点是不存在会话状态,注意,一个bean的实例可能会服务多个客户
所以实例池中的bean都可以重用,所以ejb容器不用考虑多请求之间需要保存的资源
什么是有状态sessionbean:
不能在客户间共享,与客户是一对一的
当在做购物车等功能的时候,在多个请求之间需要保存客户的资源,这个时候可以使用
有状态的sessionbean实现,他跟踪每个客户的状态
有状态的实例池:
客户端调用bean的方法—容器实例化实例池并取得一个bean实例--如果此时另一个客户调用该bean,并且池中已经没有实例了,那么容器将最近最少使用的那个bean实例的会话状态挂起—然后得到该实例为客户使用—当第一个客户再次调用的时候,容器从二级存储源中读取该用户被挂起的状态,得到一个实例
当挂起的时候:将状态信息序列化到硬盘中,而挂起的那个bean实例还可以服务其他的
调用者
注意,当再次得到bean的时候,不一定是原来的那个对象,当ejb容器将bean挂起的时候,
容器会把bean对象的状态保存到硬盘中,再次激活的时候,会得到这个状态
激活和挂起:
考虑一个问题:客户的数量无限,而内存有限,怎么办?也就是说有1000个客户访问,但是你的服务器只能存储100个实例,这样如何处理?
比如:有五个客户,但是实例池中只有4个,那么容器会将最近最少使用的那个bean实例
挂起,就是说将bean实例的会话状态存储到二级存储源中,也就是硬盘上或者是数据库
中等,例如购物车,容器将购物车中的商品信息存到硬盘上,然后这个实例可以服务第五个
客户了,如果被挂起的那个客户又要使用自己的购物车了,那么再挂起其他的实例,然后激活自己的状态到内存中
例如:实例池中有两个bean实例,现在有三个客户访问,第一个和第二个得到实例并进行操作,然后第三个访问到来,发现池中没有实例了,这个时候将其中的一个实例的会话状态保存到二级存储源中,然后再为第三个用户使用,当那个失去实例的用户需要再次调用bean实例的时候,容器会激活,并读取以前保存的会话状态
挂起:当出现实例阻塞的时候发生,也就是我要使用bean了,发现池中没有了
激活:当用户再次访问业务方法的时候,激活被挂起的状态
状态不能被挂起的情况:
1.当bean中的成员变量为transient类型的对象(标注为transient的类型)
2.容器帮助维护状态的成员变量(也不能序列化):本地或远程引用,上下文,SessionContext,UserTransaction,EntityManager,EntityManagerFactory,Timer
激活和挂起的回调方法:
我们知道数据库连接不能被序列化,那么像这样的资源容器如何处理呢?
容器定义了回调方法,这些方法的作用就是用来释放和获得资源的
@PrePassivate:在挂起bean实例前调用,一般用来释放数据库连接等资源
@PostActivate:激活,当激活bean实例后调用它,可以获得数据库连接等资源
有状态的sessionbean和无状态的sessionbean的区别:
看业务要求需不需在要跨方法,跨请求时保存会话状态:
什么叫跨方法?指调用不同的方法,如果方法之间需要另一个方法的执行结果,那么需要保存这个结果数据,这时使用有状态的sessionbean
什么叫跨请求:指多次调用同一个方法,调用后如果需要保存一些数据,那么就使用有状态的会话bean,这些数据就是会话的状态
例:业务需要当调用一个方法的时候输出一句话,没有数据要保存,使用无状态会话bean
购物车,当每次点添加商品的时候(跨请求),购物车对象的内容都不一样,这些内容在每次添加商品方法调用的时候都需要保存,所以使用有状态会话bean
有状态会话bean通过属性来保存会话状态
无状态的会话bean可以更有效率的被客户重用,因为不需要激活挂起会话状态信息
无状态实例:
接口:
package Stateless;
import javax.ejb.Remote;
/**
* This is the business interface for StatefulSession enterprise bean.
*/
@Remote
public interface StatelessSessionRemote {
public int count();
public void setval(int val);
public void remove();
}
sessionbean:
/*
* StatefulSessionBean.java
*
* Created on 2007 年5 月14 日 , 上午10:44
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package Stateless;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
/**
*
* @author yuchen
*/
@Stateless
@Interceptors(CountCallbacks.class)
public class StatelessSessionBean implements StatelessSessionRemote {
/** Creates a new instance of StatefulSessionBean */
private int val;
public StatelessSessionBean() {
}
public int count() {
System.out.println("count...");
return ++val;
}
public void setval(int val) {
this.val=val;
System.out.println("setval...");
}
@Remove
public void remove() {
System.out.println("remove...");
}
}
生命周期回调:
package Stateless;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.interceptor.InvocationContext;
/**
*
* @author yuchen
*/
public class CountCallbacks {
/** Creates a new instance of CountCallbacks */
public CountCallbacks() {
}
//创建ejb实例后调用
@PostConstruct
public void construct(InvocationContext ctx) throws Exception{
System.out.println("创建ejb实例后调用");
ctx.proceed();
}
//激活ejb实例后调用
@PostActivate
public void activate(InvocationContext ctx) throws Exception{
System.out.println("激活ejb实例后调用");
ctx.proceed();
}
//挂起前调用
@PrePassivate
public void passivate(InvocationContext ctx) throws Exception{
System.out.println("挂起前调用");
ctx.proceed();
}
//销毁前调用
@PreDestroy
public void destroy(InvocationContext ctx) throws Exception{
System.out.println("销毁前调用");
ctx.proceed();
}
}
打包
测试端:
倒入ejb包,需要使用包中的接口
package teststatelesssessionbean;
import Stateless.StatelessSessionRemote;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
*
* @author yuchen
*/
public class Main {
/** Creates a new instance of Main */
public Main () {
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws NamingException,
InterruptedException {
// TODO code application logic here
Context context=new InitialContext();
//创建一个stateless sessionbean的实例
StatelessSessionRemote remote =(StatelessSessionRemote)
context.lookup(StatelessSessionRemote.class.getName());
remote.setval(0);
//将属性加一
int var=remote.count();
System.out.println(var);
//再加一
var=remote.count();
System.out.println(var);
remote.setval(0);
System.out.println("创建三个实例");
StatelessSessionRemote [] rem=new StatelessSessionRemote[2];
for(int i=0;i<rem.length;i++){
rem[i]=(StatelessSessionRemote) context.lookup
(StatelessSessionRemote.class.getName());
System.out.println(rem[i].count());
Thread.sleep(500);
}
for(int i=0;i<rem.length;i++){
System.out.println(rem[i].count());
Thread.sleep(100);
}
}
}
无状态实例二:
程序目标:测试无状态会话bean的生命周期,在bean类中写回调方法
Remote接口:
package lifecycle;
import javax.ejb.Remote;
import javax.ejb.Remove;
/**
* This is the business interface for Lifecycle enterprise bean.
*/
@Remote
public interface LifecycleRemote {
public int add(int a,int b);
@Remove
void remove();
}
bean类:
package lifecycle;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Remove;
import javax.ejb.Stateless;
/**
*
* @author yuchen
*/
@Stateless
public class LifecycleBean implements lifecycle.LifecycleRemote {
/** Creates a new instance of LifecycleBean */
public LifecycleBean() {
}
//回调方法,先调用bean的构造方法,然后是依赖注入,再调用此方法
@PostConstruct
public void postinit(){
System.out.println("init");
}
//bean实例销毁前调用此方法
@PreDestroy
public void destroy(){
System.out.println("destroy");
}
public int add(int a, int b) {
return a+b;
}
@Remove
public void remove(){
System.out.println("delete...");
}
}
client:
public class Main {
/** Creates a new instance of Main */
public Main () {
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws NamingException {
// TODO code application logic here
Context context=new InitialContext();
LifecycleRemote remote= (LifecycleRemote) context.lookup
(LifecycleRemote.class.getName());
int str=remote.add(15,20);
System.out.println(str);
remote.remove();
}
}
调用结果:init delete...
为什么没有调用@PreDestroy标注的方法呢?
因为容器在客户调用完此方法的时候并没有销毁该对象,只是将这个实例放回到了实例
池中,供下次调用使用
无状态实例三:
程序目标:测试生命周期,使用单独得类写生命周期拦截类
Remote接口:
package session;
import javax.ejb.Remote;
/**
* This is the business interface for statelessbean enterprise bean.
*/
@Remote
public interface statelessbeanRemote {
public int add(int a,int b);
}
bean类:
package session;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
/**
*
* @author yuchen
*/
@Stateless
@Interceptors(lifecycle.class)
public class statelessbeanBean implements session.statelessbeanRemote {
/** Creates a new instance of statelessbeanBean */
public statelessbeanBean() {
System.out.println("构造方法");
}
public int add(int a, int b) {
return a+b;
}
}
回调方法类:
package session;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.interceptor.InvocationContext;
/**
*
* @author yuchen
*/
public class lifecycle {
/** Creates a new instance of lifecycle */
public lifecycle() {
}
//创建ejb实例后调用
@PostConstruct
public void construct(InvocationContext ctx) throws Exception{
System.out.println("创建ejb实例后调用");
ctx.proceed();
}
@PreDestroy
public void destroy(InvocationContext ctx) throws Exception{
System.out.println("销毁前调用");
ctx.proceed();
}
}
client:
package testlifecycle2;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import session.lifecycle;
import session.statelessbeanRemote;
/**
*
* @author yuchen
*/
public class Main {
/** Creates a new instance of Main */
public Main () {
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws NamingException {
// TODO code application logic here
Context context=new InitialContext();
statelessbeanRemote sr=(statelessbeanRemote) context.lookup
(statelessbeanRemote.class.getName());
System.out.println(sr.add(10,20));
}
}
使用这个方式写回调方法有些区别:
1.在回调方法类中,回调方法需要有个参数,这个参数告诉这个类需要的bean对象的环境是什么
2.在bean类中要用标注引用回调类
有状态实例:
接口:
package stateful;
import javax.ejb.Remote;
/**
* This is the business interface for StatefulSession enterprise bean.
*/
@Remote
public interface StatefulSessionRemote {
public int count();
public void setval(int val);
public void remove();
}
bean:
package stateful;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.interceptor.Interceptors;
/**
*
* @author yuchen
*/
@Stateful
@Remote(StatefulSessionRemote.class)
@Interceptors(CountCallbacks.class)
public class StatefulSessionBean implements stateful.StatefulSessionRemote
{
/** Creates a new instance of StatefulSessionBean */
private int val;
public StatefulSessionBean() {
}
public int count() {
System.out.println("count...");
return ++val;
}
public void setval(int val) {
this.val=val;
System.out.println("setval...");
}
@Remove
public void remove() {
System.out.println("remove...");
}
}
回调:
package stateful;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.interceptor.InvocationContext;
/**
*
* @author yuchen
*/
public class CountCallbacks {
/** Creates a new instance of CountCallbacks */
public CountCallbacks() {
}
//创建ejb实例后调用
@PostConstruct
public void construct(InvocationContext ctx) throws Exception{
System.out.println("创建ejb实例后调用");
ctx.proceed();
}
//激活ejb实例后调用
@PostActivate
public void activate(InvocationContext ctx) throws Exception{
System.out.println("激活ejb实例后调用");
ctx.proceed();
}
//挂起前调用
@PrePassivate
public void passivate(InvocationContext ctx) throws Exception{
System.out.println("挂起前调用");
ctx.proceed();
}
//销毁前调用
@PreDestroy
public void destroy(InvocationContext ctx) throws Exception{
System.out.println("销毁前调用");
ctx.proceed();
}
}
客户端类:
倒入ejb实例包
package testsessionful;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import stateful.StatefulSessionRemote;
/**
*
* @author yuchen
*/
public class Main {
/** Creates a new instance of Main */
public Main () {
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws NamingException,
InterruptedException {
// TODO code application logic here
Context context=new InitialContext();
//创建ejb实例,通过上下文得到ejb实例(这个实例是代理对象)
//它去做网络连接,传递参数和返回值,做序列化和反序列化
StatefulSessionRemote remote=(StatefulSessionRemote)
context.lookup(StatefulSessionRemote.class.getName());
//调用代理对象的方法,网络连接到远程,并可能被拦截器加载一些服务
remote.setval(0);
int val=remote.count();
//打印变量,val=1
System.out.println(val);
//再次调用:原来的val的值还保存着
val=remote.count();
//打印变量,val=2
System.out.println(val);
//再次设置为0
remote.setval(0);
System.out.println("创建三个实例,代表三个客户端,因为一个实例只服务
一个客户");
StatefulSessionRemote[] rem=new StatefulSessionRemote[2];
int var;
for(int i=0;i<rem.length;i++){
rem[i]=(StatefulSessionRemote) context.lookup
(StatefulSessionRemote.class.getName());
var=rem[i].count();
System.out.println(var);
Thread.sleep(500);
}
System.out.println("证明");
for(int i=0;i<rem.length;i++){
var=rem[i].count();
System.out.println(var);
rem[i].remove();
Thread.sleep(100);
}
}
}
基本概念(二)
1.ejb使用中间件服务的方式
2.什么是SOA?有什么用?
3.ejb的客户端类型
4.什么是web service?有什么用?
5.JavaEE技术多有哪些?
6.EJB的运行
ejb使用中间件服务的方式:
1.显式调用中间件服务:使用提供的服务API
2.隐式的调用中间件服务:使用标注和部署描述符
什么是SOA?有什么用?
系统A(应用:ERP 服务:C++) 系统B(应用:OA系统 服务:Java)
SOA的目标就是将异构的系统集成,并实现互操作
这些系统就是服务,也就是将整个应用看成是服务,不是单个组件
什么是服务?服务就是数据的交换,比如:客户请求资源,服务器响应,给出数据,这是种服务,从程序的角度就是一些组件的集合,一个业务功能,一个完整的应用系统都可以看成是服务
那么,SOA的目标是将异构的服务集成并实现互操作
什么是web service?有什么用?
Web service是SOA的实现,它真正实现了将异构的系统集成并互操作的想法,它采用的是xml语言定义的,所以能够实现各个语言开发的系统间的互操作
对整个服务如何集成呢?
两个问题:
1.需要对这个服务进行描述,也就是说这个服务提供了什么功能:公共接口WSDL(web服务描述语言)
2.如何调用这个服务:互操作协议
售货员(这是一个服务):
1.描述提供什么服务(公共接口):卖货的服务
2.如何使用这个服务(互操作协议):给钱
Ejb应用(服务):
1.描述该应用提供的服务:公共接口,就是远程接口,它暴露了bean的业务方法
2.如何使用这个服务:远程方法调用协议,基于internet的一个协议
ejb的客户端类型:
java应用 and web应用
JavaEE技术多有哪些?
Ejb
Jax-ws:java对web服务定义的api
Rmi-iiop:远程对象访问协议
Jndi
Jdbc
Jta:java事务API,jts:java事务服务
JMS:java消息服务
Servlets
Jsp
Jsf
Jca
Jaxp
Jaxb:xml的邦定
Jaas:java认证和授权