时间到了,继续我心中的那个她,每天晚上坚持写博客,感觉真的很好,我想把自己所想的写出来。这两天出奇的冷,北京的天气真郁闷,是不是后天要来了,这种温度让我的大脑都结冰了,都语无伦次了,虽然很冷,但是,还是要用我那冰冷的手指敲打键盘。
昨天说到美女生气了,她的沉默使我也很沉默,我也不是怎么会哄女孩的人,天生的呆板,属于榆木脑袋那种,只能等她心情好的时候跟她多说几句话。还好没过几天,她的心情多云转晴,我也就自然的晴天了。过了段时间,作为我对她细心培训的回报,她要请我吃饭。这顿饭可把我害苦了,就是从这顿饭后,每天脑子里都是她的身影(有点过了),但是每当想到她的时候,脑袋就乱,而且很不舒服。吃饭那天自己都不知道说了些什么,本来有很多话要说的,但是不知为什么,没有说出口。回家的时候她把我送到了车站,在回家的车上,脑子一片空白。还好自己知道在哪里下车,出站的时候突然感觉自己好像丢了什么,心里空荡荡的,原来把自己的心丢了,以前在学校的时候,知道没有思想的痛苦,跟她吃了顿饭,知道了没有心的痛苦,没有心的感觉真好,不管身心如何痛苦,时间不会因为你而停止,生活还在继续。以后的日子里,就不用说了,每天都在快乐并痛苦着,每天白天能有她相伴在身边是快乐的,下班回家之后想她是痛苦的。
时间一点点的流逝,转眼快到10.1了,大家都在讨论放假回家什么的,说到这里有点对不起自己的父母了,我离家里比较近,动车也就3个小时的路程,但是每年只有过年的时候才回家一次,感觉跟家里人相聚的时间太短了,愧对自己的父母,没办法啊,我们这种在外面漂流的人...说她吧,那次听到她要回家买票难什么的,我也没打算帮她买,我也没有那么大的本事,事情凑巧,以前的一个朋友打电话聊天,我想到他是做导游的应该很方便弄票,于是就跟兄弟说了一声,兄弟是答应了,但是我当她说的时候,她却说不用了(因为不知道做火车还是汽车),这是明显的搪塞,那天我生气了,生气的不是因为她搪塞,是因为她那不冷不热、莫不关心的态度,气的我和她保持沉默了,但是工作上的关系,又不得不说话。还好我的天气也不是那种南方的阴雨连绵,过几天就好了,这样我们还保持着普通朋友的关系。
明天就结束我们2个月的关系了,自己都没想到,想一个人会是这个样子,可能她不适合我,我也不适合她,朋友们都说,自己都做程序员了,还找个程序员做老婆。不合适,我也这么觉得。希望我们能成为好朋友,如果你给我当朋友的话,我也会视你为我的朋友。你只能活在我的记忆中了,不是我的心底。因为你没有走进我。
时间真快,又一个小时了,明天早起去吃早餐,^_^,天气凉了,胃口上来了。
Good night 我的那个她。
posted @
2009-11-03 00:15 王永庆 阅读(249) |
评论 (3) |
编辑 收藏
会话Bean同其他企业Bean类型最主要的区别是生命周期的差异性。会话Bean实例是存活短暂的对象,会话Bean实例并不能够在多客户间共享。
通常,客户会话的持续期决定了使用中的会话Bean的存活期,一旦应用服务器瘫痪,会话Bean实例也应该不复存在。因为,会话Bean仅仅是内存对象,一旦其生存的周边环境遭到破坏,会话Bean也将不复存在。会话Bean并不是持久化的,因此,会话Bean并不会保存到持久化存储源中,这同实体Bean不一样,会话Bean能够操作RDBMS,但是其本身并不是持久化对象。
会话指客户同EJB组件的交互,它由客户和EJB组件间的多次方法调用构成。会话Bean存在2种子类型:有状态会话Bean和无状态会话Bean,各自用于建模不同类型的会话。有状态会话Bean是这样一种EJB,即其服务的业务过程能够延伸到多个方法请求或者事务中,为完成这种业务过程,有状态会话Bean需要为单个客户保存状态信息。如果在方法调用期间有状态会话Bean的状态发生改变,则这种改变必须反映到同一客户的随后调用中。无状态会话Bean是这样一种EJB,即其服务的业务过程只需要单个业务方法即可完成。由于他们不需维护客户多个方法调用间的会话状态,因此它是无状态的。在每次方法调用结束后,EJB容器可能会销毁无状态会话Bean实例,或者实例化新的实例,或者清楚掉上次方法调用中的相关信息。
无状态意指不存在会话状态。无状态会话Bean能够含有同特定客户不相关的状态信息,比如所有客户将使用到数据库链接工厂,开发者可以将它存储在private变量中。如果开发者将数据存储在private变量中,则将随时丢失其中存储的数据。
EJB容器将维护EJB实例池,而且这些EJB实例是可重用的。在每次方法调用时,都会有不同EJB实例或同一实例服务客户。为了限制内存中运行的有状态会话Bean实例的数量,EJB容器需要将有状态会话Bean的会话状态保存到硬盘或者其他存储源中。该过程称之为挂起。在挂起有状态会话Bean后,会话状态被安全的保存下来,而且其释放的内存可以供其他应用使用。一旦被挂起的有状态会话Bean实例的客户再次调用它,被挂起的会话状态将重新回到有状态会话Bean实例中,该过程称之为激活。
有状态会话Bean实例的会话状态必须遵循Java对象序列化设定的规则。在挂起有状态会话Bean实例时,EJB容器借助于对象序列化将会话状态转换成二进制blob,然后将它写入到硬盘中。在转移会话状态信息后,有状态会话Bean实例(指刮起了会话状态的那些EJB实例)还能够服务于其他客户,即同新的客户进行新的会话过程。
一旦EJB中的成员变量符合如下条件,则可以认为它是会话状态的组成部分之一。
1、成员变量是非transient类型的java原始类型。
2、成员变量是非transient类型的Java对象类型。
当容器将EJB实例挂起时,它需要将实例的会话状态写入到二级存储源中,比如文件或者RDBMS中。通过调用EJB实例的ejbPassivate()回调方法,容器能够完成实例的挂起工作。借助于ejbPassivate()方法能够告知EJB实例:EJB容器需要挂起它,这使得释放其持有的资源成为可能。比如EJB实例可能持有的资源有:RDBMS连接、已打开的Socket和文件或者其他任何资源。
在实际场合中,客户调用了EJB对象中的某个方法,而当时在内存中暂时找不到该EJB对象,与此同时,EJB容器持有的企业Bean实例的个数已经到达了设定的上限。因此在处理客户请求前,容器需要挂起最近未使用的EJB实例,在挂起它后,容器才能够获得所需的EJB对象。
有状态会话Bean的部署描述符。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<enterprise-beans>
<session>
<ejb-name>Count</ejb-name>
<home>examples.CountHome</home>
<remote>examples.Count</remote>
<ejb-class>examples.CountBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
服务端企业Bean:
package com.wyq.ejb02;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示有状态Bean
* 它会初始化val,并提供业务方法。
* 该实例演示了最简单的有状态会话Bean,并给出了挂起、激活的工作机理。
*/
public class CountBean implements SessionBean {
//会话状态
public int val;
//业务方法
public int count(){
System.out.println("count");
return ++val;
}
public void ejbCreate(int val)throws CreateException{
this.val = val;
System.out.println("ejbCreate()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
}
}
客户端调用:
package com.wyq.ejb02;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import com.wyq.ejb01.HelloHome;
/**
* 1、获得JNDI InitialContext上下文。
* 2、借助于JNDI,定义Home对象。
* 3、使用Home对象创建3个不同的CountEjb对象。因此,这将建立起3个不同的会话过程,而且模拟了3个不同的客户。
* 4、由于内存中仅能存活2个EJB实例,因此在创建Count EJB实例期间,EJB容器需要完成实例的挂起操作。
* 5、调用各个EJB对象的count()方法。
* 6、最后,删除所有的EJB对象。
*/
public class CountClient {
/**
* 客户代码示例
*
* 此时,创建了3个EJB对象。但我们规定容器:在内存中最多存在2个实例。
* 因为,能够看到挂起操作的发生。
*/
public static void main(String[] args) {
try{
/*
* 获得JNDI环境属性
*/
Properties props = System.getProperties();
/*
* 获得对Home对象的引用,Home对象是EJB对象的工厂
*/
Context ctx = new InitialContext(props);
Object obj = ctx.lookup("CountHome");
CountHome home =(CountHome)javax.rmi.PortableRemoteObject.narrow(obj,CountHome.class);
/*
* 能够持有3个Count对象的数组
*/
Count count[] = new Count[3];
int countVal = 0;
/*
* 创建EJB实例,并调用各自的count()
*/
System.out.println("Instantiating beans");
for(int i=0;i<3;i++){
/*
* 创建EJB对象,并初始化它们
*/
count[i] = home.create(countVal);
/*
* 加1,并打印出来
*/
countVal = count[i].count();
System.out.print(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 调用各个EJB对象的count()方法,从而能够浏览到EJB被挂起,并被成功激活
*/
System.out.println("Calling count() on beans");
for(int i=0;i<3;i++){
/*
* 加1,并打印出来
*/
countVal = count[i].count();
System.out.println(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 使用完后,销毁它们
*/
for(int i=0;i<3;i++){
count[i].remove();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
会话Bean的声明周期流程图
1、起初,EJB实例并不存在。
2、EJB容器将决定是否需要实例化新的EJB实例。容器将何时实例化新的EJB实例,取决于容器使用的EJB实例池策略。
3、容器实例化EJB Bean类。EJB容器将调用Class.newInsatance("HelloBean.class");即动态创建HelloBean实例,这使得容器不会将EJBBean类的名字硬编码在Java代码中。最后,这使得容器更具通用性,能够操控任何企业Bean.
4、容器调用setSessionContext()方法。这为EJB实例设置了上下文对象。最终,EJB实例将能够访问到EJB容器。
5、容器调用ejbCreate().这将初始化EJB实例。由于无状态会话Bean的ejbCreate()方法并不存在参数,因此EJB客户不可能为它提供任何启动EJB实例的参数信息。
6、EJB容器调用EJB实例的业务方法。对于EJB实例提供的所有业务方法,EJB容器都可以使用。由于所有EJB实例间不存在区别,因此完全不同的客户可以调用相同的业务方法。在业务方法调用结束后,各个无状态会话Bean实例依然是相同的,因此,EJB容器能够针对客户请求,在每个方法级将各个EJB实例指定给客户,即使同一客户对同一业务方法的多次调用,都可以由不同的EJB实例响应它,当然,将EJB实例指定给客户的具体实现策略取决于具体的EJB容器。
7、最后,容器调用ejbRemove()方法。
posted @
2009-11-02 16:35 王永庆 阅读(183) |
评论 (0) |
编辑 收藏
刚洗了个澡,看了下时间,还不算太晚,23点刚过一点,突然想写博客了,有点神经质了,o(∩_∩)o...
眼看就要辞职了,离开现在的公司了,马上就要离开旁边的那个她了,不知道离开之后,她还能不能想起旁边的我,我们虽然不是一类人,我这么感觉,不知道离开之后,她的工作还顺利不。我还在公司的时候,每当她遇到问题,还有耐心的我帮她解决,遇到问题的时候应该让她自己去解决,感觉她还是比较独立的,可能解决问题费劲一点。她不适合做程序员,现在太多人不适合做程序员了,现在的程序员已经不是程序员了。
认识她是在2个月前,那时我已经打算辞职了,但是还没有提出来。这时的工作到了关键的时候,面临着上线的压力,公司还在不断的招人,这时她进入了我们组,第一眼看到她的时候,没什么感觉,咋一看去,很普通的一个女孩,算不上特别漂亮,但是也有几分姿色,万没有想到就是这么一个有几分姿色的女孩对我有这么大的杀伤力。不知道经理怎么分的,让我去带她,可能经理早就知道我要走。没有过几天,公司的座位开始调整,因为现在组员太分散了,比较难沟通,所以又重新排列了座位,不知道怎么回事,把我分到了她的旁边,也可能比较容易培训吧。教导她的时候可谓是尽心尽力了,虽然我人比较好,对任何人的请求来之不惧,但是不知不觉中我感觉对她的付出超出了我的义务。开始的时候,感觉她还真把我当了个哥哥,旁边的几个好兄弟也总是拿我们开玩笑,总是管她叫嫂子,他们叫嫂子的时候,我的心里可紧张,紧张她生气,紧张她的心情,紧张她...,不过还好,她的性格很傻很天真的那种,一个耳朵进,一个耳朵出,今天生气,明天就好的那种,不知道哪一天她真的生气了,好严肃,:-),一脸的阴霾,呵呵,女人生起气来也很可怕,她用她自己独特的方式回击了我们,沉默。沉默得让我的心不能平静,不知道从什么时候起,我的心中有了她的一席之地,一颗被栓着的心真的很痛苦,之后的几天里,她的脸色依旧严肃着,感觉和她的距离拉远了,之后和她说话也都是工作上的多。
0:00,今天先写到这里吧,明天白天还要继续技术博客呢,晚上继续我的那个她,1个小时才写了这么点,因为认真的回忆了,能让我回忆的东西真的不多。现在的心中还有这个结,这个结打的好死,费了我好多脑细胞差不多打开了。
脱衣服,准备睡觉,晚安,我心中的那个她。
posted @
2009-11-02 00:03 王永庆 阅读(317) |
评论 (2) |
编辑 收藏
分散配置:
有时你会发现将配置文件分成几个分散的配置文件是很有益的。
将Data Source配置到Bean装配文件中不是很适合。数据库细节是一个发布信息。而Bean装配文件的目的是定义如何装配系统的各个模块。如果使用ApplicationContext当作Spring容器,那么,在Spring中分离属性配置是很简单的。使用Spring的PropertyPlaceholderConfigurer告诉Spring从外部属性文件装载一些配置信息。
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>jdbc:hsqldb:Training</value>
</property>
<property name="driverClassName">
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name="username">
<value>appUser</value>
</property>
<property name="password">
<value>password</value>
</property>
</bean>
location属性告诉Spring从哪里找到属性文件。location属性允许你使用单个配置文件。如果你想将配置信息分散到多个配置文件中,请使
用PropertyPlaceholderConfigurer的locations属性设置文件列表,使用这种方式,可以使用占位符变量代替Bean装
配文件中的硬编码配置了。
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
<property name="locations">
<list>
<value>jdbc.properties</value>
<value>security.properties</value>
<value>application.properties</value>
</list>
</property>
</bean>
<bean id="dataSources" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>${database.url}</value>
</property>
<property name="driverClassName">
<value>${database.driver}</value>
</property>
<property name="username">
<value>${database.user}</value>
</property>
<property name="password">
<value>${database.password}</value>
</property>
</bean>
定制属性编辑器:
java.beans.PropertyEditor接口提供了将字符串值映射成非String类型的方法。有一个好用的这个接口的实现-
java.beans.PropertyEditorSupport,它有2个方法我们会感兴趣:
1、getAsText():方法返回一个表示属性值的字符串。
2、setAsText(String value):将传递进来的字符串赋给Bean的属性。
Spring带了几种建立在propertyEditorSupport之上的定制编辑器,包括
org.springframework.beans.propertyeditors.URLEditor,它是一个用于将字符串与java.net.URL相互转换的定制编辑器。
Spring提供的其他定制编辑器包括:
1、ClassEditor-使用包含全称类名的字符串设置java.lang.Class属性。
2、CustormDateEditor-使用某种java.text.DateFormat对象将一个字符串设置给java.util.Date属性。
3、FileEditor-使用包含文件路径的字符串设置java.io.File属性。
4、LocalEditor-使用包含地域信息的字符串设置java.util.Local属性。
5、StringArrayPropertyEditor-将一个包含逗号的String转换成String数组属性。
6、StringTrimmerEditor-自动修正字符串属性,可以选择将空字符串转变为null.
<bean id="costomEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.wyq.spring.PhoneNumber">
<bean id="phoneEditor" class="com.wyq.spring.PhoneEditor"></bean>
</entry>
</map>
</property>
</bean>
其中的map中的key表示要添加自定义属性的类,value表示自定义属性实现的类。
package com.wyq.spring;
import java.beans.PropertyEditorSupport;
public class PhoneEditor extends PropertyEditorSupport {
public void setAsText(String textValue) throws IllegalArgumentException {
String stripped = stripNonNumberic(textValue);
String areaCode = stripped.substring(0,3);
String prefix = stripped.substring(3,6);
String number = stripped.substring(6);
PhoneNumber phone = new PhoneNumber(areaCode,prefix,number);
setValue(phone);
}
private String stripNonNumberic(String original){
StringBuffer allNumberic = new StringBuffer();
for(int i=0;i<original.length();i++){
char c = original.charAt(i);
if(Character.isDigit(c)){
allNumberic.append(c);
}
}
return allNumberic.toString();
}
}
posted @
2009-10-31 17:54 王永庆 阅读(315) |
评论 (0) |
编辑 收藏
为将企业Bean的中间件服务需求告知EJB容器,开发者还需生成部署描述符。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<!--
<ejb-name>:企业Bean的昵称。在部署描述符的其他地方能够引用它,供设置其他参数使用。
<home>:Home接口的全限定名。
<remote>:远程接口的全限定名。
<local-home>:本地Home接口的全限定名。
<local>:本地接口的全限定名。
<ejb-class>:企业Bean类的全限定名。
<session-type>:标识企业Bean是有状态的,还是无状态的。
<transaction-type>:事务类型。
-->
<enterprise-beans>
<session>
<ejb-name>Hello</ejb-name>
<home>examples.HelloHome</home>
<remote>examples.Hello</remote>
<local-home>examples.HelloLocalHome</local-home>
<local>examples.HelloLocal</local>
<ejb-class>examples.HelloBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
Ejb-jar文件:
HelloWorld EJB组件准备好了之后只需将他们打包成Ejb-jar文件。手工创建jar文件
jar cf HelloWorld.jar *
注意:ejb-jar.xml文件必须放置在META-INF子目录中。当EJB容器初次打开Ejb-jar文件时,将在ejb-jar.xml中查找Ejb-jar所包含的EJB组件信息。
如何调用EJB组件:
目前,存在2类客户:
1、基于Java RMI-IIOP的客户。这类客户使用JNDI,即通过网络查找对象。另外,它们使用JTA控制事务。
2、CORBA客户。这类客户村寻CORBA标准。这对于使用其他语言访问EJB组件的客户而言,意义深远,CORBA客户使用CORBA命名服务,即通过网络查找对象。
访问EJB组件的步骤:
1、查找Home对象
2、使用Home对象创建EJB对象
3、调用EJB对象的业务方法
4、销毁EJB对象
查找Home对象:
开发EJB应用的最主要目标之一是,应用代码能够"一次编写,到处运行".如果将已部署在机器A上的EJB应用迁移到机器B上,EJB应用代码不需要改动,因为EJB实现了位置透明性。
借助于命名和目录服务能够实现EJB的位置透明性。命名和目录服务是能够在网络中存储和查找资源的产品。
在企业部署场景中,EJB服务器使用命名服务存储位置信息,而这些位置信息是用于标识资源的,其中的资源涉及到EJB Home对象、企业Bean环境属性、数据库JDBC驱动、消息服务驱动和其他资源等。通过使用命名服务,应用代码不用将具体机器或资源名硬编码在代码中,这就是EJB所具备的位置透明性,它使得代码具有便携性。
为实现位置透明性,EJB容器需要屏蔽掉Home对象的具体位置,使得EJB组件的客户代码感知不到其具体位置。客户不用将Home对象宿主的机器名硬编码于代码中。相反,使用JNDI就能够查找到Home对象。物理上,Home对象可以存在于网络上的任何地方,比如在运行EJB容器的同一进程地址空间中,其他机器上的EJB容器中。
客户为定位Home对象,它必须提供企业Bean的Home对象的JNDI昵称。客户使用该昵称标识所需的Home对象。一旦将EJB组件部署到容器中,容器会自动将HelloHome昵称绑定到Home对象。因此,在对已部署EJB组件的物理机器位置不知情时,开发者能够在任一机器上使用该昵称查找到上述Home对象,借助于JNDI能够查找到它。JNDI能够在网络上查找命名服务,或在JNDI树中查找到Home对象,当然,Home对象也可能位于客户同一进程中,最后一旦找到Home对象,客户将获得对它的引用。
package com.wyq.ejb;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
* 客户代码实例,用于调用简单、无状态会话Bean中的方法。
*
*/
public class HelloClient {
public static void main(String[] args)throws Exception {
/*
* 设置属性,用于JNDI初始化,从命令行读入属性。
*/
Properties props = System.getProperties();
/*
* 初始上下文是连接到JNDI树的入口。
* 借助于环境属性,能够完成JNDI驱动、服务器的网络地址等的设置
*/
Context ctx = new InitialContext(props);
/*
* 获得对Home对象的引用。Home对象是创建EJB对象的工厂。
*/
Object obj = ctx.lookup("HelloHome");
/*
* Home对象是RMI-IIOP对象。因此,需要借助于特殊的RMI-IIOP造型操作将它们造型成RMI-IIOP对象
*/
HelloHome home =(HelloHome)javax.rmi.PortableRemoteObject.narrow(obj,HelloHome.class);
/*
* 使用工厂,来创建Hello EJB对象。
*/
Hello hello = home.create();
/*
* 调用EJB对象的hello()方法。它会将调用委派给EJB Bean类实例。一旦接收到响应结果,它便会返回。
*/
System.out.println(hello.hello());
/*
* 在使用完EJB对象后,需要销毁它。EJB容器负责销毁EJB对象。
*/
hello.remove();
}
}
posted @
2009-10-31 12:36 王永庆 阅读(121) |
评论 (0) |
编辑 收藏
为创建和销毁EJB对象,开发者需要使用Home接口。Home接口的实现是Home对象,而Home对象由EJB服务器提供的工具生成。
package com.wyq.ejb;
import javax.ejb.EJBHome;
/**
* Hello EJB组件的Home接口。它由EJB服务器提供的工具实现。Home接口实现
* 称之为Home对象。Home对象充当了创建EJB对象的工厂。
*
* 在该Home接口中,存在create()方法。它对应于HelloBean中的ejbCreate()方法。
*/
public interface HelloHome extends EJBHome {
/**
* 创建EJB对象,
* @return 新建的EJB对象。
*/
Hello create() throws java.rmi.RemoteException,javax.ejb.CreateException;
/**
* 1、为获得对EJB对象的引用,客户需要使用create方法。其中,create方法还能够完成
* EJB组件的初始化工作。
* 2、create方法抛出了如下异常:RemoteException和CreateException。由于Home对象是网络
* 使能的基于RMI-IIOP的远程对象,因此要抛出RemoteException异常。
*/
}
本地Home接口是Home接口的高性能版本。
package com.wyq.ejb;
import javax.ejb.EJBLocalHome;
/**
* Hello EJB组件的本地Home接口。它由EJB服务器提供的工具实现。
* 本地Home接口实现称之为本地Home对象。本地Home对象充当了创建EJB本地对象的工厂。
*
*/
public interface HelloLocalHome extends EJBLocalHome {
/**
* 创建EJB本地对象
* @return 返回新创建的EJB本地对象。
*/
HelloLocal create()throws javax.ejb.CreateException;
}
创建企业Bean类
package com.wyq.ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示无状态会话Bean.
*
*/
public class HelloBean implements SessionBean {
private SessionContext ctx;
//EJB规范定义的方法
public void ejbCreate(){
System.out.println("ejbCreate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
/**
* 由于企业Bean宿主在受管容器中,因此容器能够在任何时候调用企业Bean的方法。但如果企业Bean
* 需要查询容器的当前信息的话,需要为EJB提供何种信息呢?比如在EJB中,组件本身可能需要查询当前用户
* 的安全性凭证信息。
*
* 容器将这些信息包含在称之为"EJB上下文"的对象中。EJB上下文对象是通往EJB容器的关口。其中,EJB上
* 下文是容器的组成部分,开发者能够在企业Bean中访问到它。因此,在某种程度上,EJB上下文是为企业Bean
* 访问容器提供的回调。该回调有助于企业Bean探知其自身的状态,并修改他们。
*
* EJB上下文对于会话Bean、实体Bean、消息驱动Bean而言很有用。比如会话Bean含有会话上下文、实体Bean
* 含有实体上下文、消息驱动Bean含有消息驱动上下文。
* 依据EJB类型的不同,开发者能够分别通过调用setSessionContext、setEntityContext、setMessageDivenContext
* 方法设置EJB上下文信息。
*/
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
this.ctx = ctx;
}
//业务方法
public String hello(){
System.out.println("hello()");
return "Hello,World!";
}
}
posted @
2009-10-30 13:01 王永庆 阅读(146) |
评论 (0) |
编辑 收藏
首先开发远程接口,远程接口支持EJB组件暴露的所有业务方法。
package com.wyq.ejb;
import javax.ejb.EJBObject;
/**
* Hello EJB组件的远程接口
*
* 在客户同EJB对象交互时,需要使用这一接口。容器厂商会实现这一接口,而相应的实现对象
* 就是EJB对象。EJB对象会将客户请求委派给实际的EJB Bean类。
*
*/
public interface Hello extends EJBObject {
/**
* 返回欢迎信息给客户。
*/
public String hello()throws java.rmi.RemoteException;
/**
* 注意:远程接口继承于javax.ejb.EJBObject.其含义是:容器生成的EJB对象实现了远程接口(EJBObject)
* 即javax.ejb.EJBObject接口中定义的各个方法。其中包括比较不同EJB对象的方法,删除EJB对象的方法等。
* 仅实现了单个业务方法,即hello().HelloWorld EJB组件的Bean类需要实现hello方法。另外,由于Hello
* 接口是RMI-IIOP类型的远程接口,因此必须抛出远程异常。这也是企业Bean类中hello方法签名同远程接口中
* hello方法签名的区别。
*/
}
为访问EJB组件的业务方法,本地客户应该使用本地接口,而不是远程接口。
package com.wyq.ejb;
import javax.ejb.EJBLocalObject;
/**
* Hello EJB组件的本地接口。
*
* 当本地客户同EJB本地对象交互时,需要使用这一接口。容器厂商会实现这一接口。
* 而相应的实现对象就是EJB本地对象。EJB本地对象会将客户请求委派给实际的EJB Bean类。
*
*/
public interface HelloLocal extends EJBLocalObject {
/**
* 返回欢迎信息给客户,本地接口继承EjbLocalObject接口,并且不需要抛出RemoteException.
*/
public String hello();
}
posted @
2009-10-30 13:00 王永庆 阅读(114) |
评论 (0) |
编辑 收藏
由于EJB对象可以运行在与客户不同的机器上,因此客户不能够直接实例化EJB对象。EJB推崇位置透明性,因此从这个角
度考虑客户不应该对EJB对象的位置信息进行关注。
为获得对EJB对象的引用,客户代码需要从EJB对象工厂中请求EJB对象。该工厂负责实例化EJB对象。EJB规范将这种工厂
称之为Home对象。Home对象的职责主要有:
1、创建EJB对象
2、查找现有的EJB对象
3、删除EJB对象
同EJB对象一样,Home对象专属于特定的EJB容器。Home对象含有容器特定的逻辑,如负载均衡逻辑、借助于图形化管理
控制台追踪信息等。与此同时,Home对象也是EJB容器的组成部分,通过容器提供的工具能够自动创建它。
Home对象是创建EJB对象的工厂。但是Home对象是如何实例化EJB对象的呢?为创建Home对象,EJB容器需要掌握这方面的
信息。通过指定Home接口给容器即可完成这方面信息的注入。Home接口简单的定义了用于创建、销毁和查找EJB对象的方法。
容器的Home对象实现了Home接口。
使用Home接口存在一个问题,即通过Home接口创建EJB实例速度很慢,而且,借助于远程接口调用EJB实例也是如此。当
访问EJB对象时,通常会依次触发如下内容:
1、客户调用本地存根
2、存根将参数压包成适合网络传输格式
3、存根借助于网络将参数传递给骨架
4、骨架将参数解包成适合Java的格式。
5、骨架调用EJB对象。
6、EJB对象获得所需的中间件服务,如连接池、事务、安全性和生命周期服务。
7、EJB对象调用企业Bean实例,Bean实例处理客户请求。
从EJB2.0开始,客户能够通过本地对象(而不是EJB对象)快速、高效的访问企业Bean组件。具体过程如下:
1、客户访问本地对象
2、本地对象获得所需的中间件服务
3、一旦企业Bean实例处理完客户请求,则将结果返回给本地对象,最终传回给客户。
EJB对象指请求拦截器,远程接口指供请求拦截器使用的接口,Home对象指工厂,Home接口指工厂接口。
本地接口存在的缺点:
1、只在同一进程中有效。如在同一应用服务器中存在访问银行账号实体Bean的银行出纳会话Bean,如果EJB组件代码本身依赖
于本地接口实现,则不能够通过远程访问到它。
2、通过引用(传址),而不是传值来marshal参数。
部署描述符:借助于部署描述符文件,EJB组件能够声明其依赖的中间件服务。然后,EJB容器将通过部署描述符了解到组件
待使用的中间件服务。
posted @
2009-10-29 15:56 王永庆 阅读(195) |
评论 (0) |
编辑 收藏
构造函数注入:
Set注入法的缺点是,它无法清晰的表示出哪些属性是必须的,哪些是可选的。而构造函数注入法的优势是通过构造函数来强制依赖关系。
使用Set注入时,我们通过<property>元素来注入属性的值。构造函数注入也一样,只不过是通过<bean>元素下的<constructor-arg>元素来指定实例化这个bean的时候需要传递的参数。constructor-arg没有name属性。
解决构造函数参数的不确定性:
有2种方法可以用来处理构造函数的不确定性:通过序号和类型。<constructor-arg>元素有一个可选的index属性,可以用它来指定构造函数的顺序。
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg index="1">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg index="0">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
另一种方法是使用type属性。可以通过type属性确定参数的类型
<bean id="foo" class="com.springinaction.Foo">
<constructor-arg type="java.lang.String">
<value>http://www.manning.com</value>
</constructor>
<constructor-arg type="java.net.URL">
<value>http://www.springinaction.com</value>
</constructor>
</bean>
使用构造函数注入的理由:
1、构造函数注入强制使用依赖契约。就是如果没有提供所有需要的依赖,一个Bean就无法被实例化。
2、由于Bean的依赖都通过它的构造函数设置了,所以没有必要再写多余的Set方法。
3、因为只能通过构造函数设置类的属性,这样你有效的保证了属性的不可变性。
Set注入的依据:
1、如果Bean有很多依赖,那么构造函数的参数列表会很长。
2、构造函数只能通过参数的个数和类型来区分。
3、如果构造函数的参数中有2个以上是相同类型的,那么很难确定每个参数的用途。
4、构造函数注入不利于自身的继承。
自动装配:
你可以让Spring自动装配,只要设置需要自动装配的<bean>中的autowire属性。
<bean id="foo" class="com.springinaction.Foo" autowire="autowire type"/>
byName-视图在容器中寻找和需要自动装配的属性名相同的Bean.
byType-视图在容器中寻找一个与需要自动配置的属性类型相同的Bean.
constructor-视图在容器中查找与需要自动装配的Bean的构造参数一致的一个或多个Bean.
autodetect-首先尝试使用congstructor来自动装配,然后使用byType方式。
使用Spring的特殊Bean
编写一个后处理Bean,Spring为你提供了2次机会,让你切入到Bean的生命周期中,检查或者修改它的配置,这叫做后处理,后处理实在Bean实例化以及装配完成之后发生的。postProcessBeforeInitialization()方法在Bean初始化之前被调用。postProcessAfterInitialization()方法在初始化之后马上被调用。
package com.wyq.hibernate;
import java.lang.reflect.Field;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class Fuddifier implements BeanPostProcessor {
/**
* postProcessAfterInitialization()方法循环Bean的所有属性,寻找java.lang.String类型的属性。对于每个String属性,把它传递给
* fuddify()方法,这个方法将String将变成唠叨用语。
*/
public Object postProcessAfterInitialization(Object bean, String name)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
try{
for(int i=0;i<fields.length;i++){
if(fields[i].getType().equals(java.lang.String.class)){
fields[i].setAccessible(true);
String original = (String)fields[i].get(bean);
fields[i].set(bean,fuddify(original));
}
}
}catch(IllegalAccessException e){
e.printStackTrace();
}
return bean;
}
/**
* postProcessBeforeInitialization()方法没有做任何有意义的工作,它只是简单的返回没有修改过的Bean.
*/
public Object postProcessBeforeInitialization(Object bean, String name)
throws BeansException {
return bean;
}
private String fuddify(String orig){
if(orig==null)return orig;
return orig.replaceAll("(r|l)", "w").replaceAll("(R|L)", "W");
}
}
注册后处理Bean:如果你的应用系统运行在Bean工厂中,你需要调用工厂的addBeanPostProcessor()方法来注册BeanPostProcessor.
BeanPostProcessor fuddifier = new Fuddifier();
factory.addBeanPostProcessor(fuddifier);
如果你是使用应用上下文,你只需要像注册其他Bean那样注册后处理Bean.
posted @
2009-10-29 14:59 王永庆 阅读(250) |
评论 (0) |
编辑 收藏
传统建立应用系统对象之间关联关系的方法会导致复杂的代码,使它们很难被服用,很难做单元测试。在Spring中,组件无需自己负责与其他组件的关联。取而代之的是,容器负责把协作左键的引用给予各个组件。创建系统组件之间协作关系的这个动作叫做装配。
容器是Spring框架的核心。Spring容器使用IOC管理所有组成应用系统的组件。这包括在协作组件之间建立关联。Spring实际上有2种不同的容器:Bean工厂(org.springframewor.bens.factory.BeanFactory接口定义)是最简单的容器,提供了基础的依赖注入支持。应用上下文(由org.springframework.contextApplicationContext接口定义)建立在Bean工厂基础之上,提供了系统架构服务。
(1)、BeanFactory这个类负责创建和分发Bean.由于Bean工厂知道应用系统中的很多对象,所以它可以在实例化这些对象的时候,创建协作对象间的关联关系。这样就把配置的负担从Bean自身以及Bean的调用者中脱离出来。在Spring中有几种BeanFactory的实现。其中最常使用的是org.springframework.beans.factory.xml.XmlBeanFactory,它根据XML文件中的定义装载Bean.
例如:BeanFactory factory = new XmlBeanFactory(new FileInputStream("beans.xml"));
这行代码告诉Bean工厂从XML文件中读取Bean的定义信息。但是,现在Bean工厂还没有实例化Bean.Bean是被延迟载入到Bean工厂中的,就是说Bean工厂会立即把Bean定义信息载入进来,但是Bean只有在被需要的时候才被实例化。
MyBean myBean = (MyBean)factory.getBean("myBean");
当getBean()方法被调用的时候,工厂就会实例化Bean并且使用依赖注入开始设置Bean的属性。
(2)、ApplicationContext的诸多实现中,有3个实现经常用到:
ClassPathXmlApplicationContext-从类路径中的XML文件载入上下文定义信息,把上下文定义文件当成类路径资源。
FileSystemXmlApplicationContext-从文件系统中的XML文件载入上下文定义信息。
XmlWebApplicationContext-从web系统中的XML文件载入上下文定义信息。
例如:ApplicationContext context = new FileSystemXmlApplicationContext("c:/foo.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("foo.xml");
FileSystemXmlApplicationContext只能在指定的路径中寻找foo.xml文件,而ClassPathXmlApplicationContext可以在整个类路径中寻找foo.xml.
应用上下文与Bean工厂的另一个重要区别是关于单实例Bean是如何被载入的。Bean工厂延迟载入所有的Bean,知道getBean()方法被调用时Bean才被创建。应用上下文启动后预载入所有的单实例Bean.
Spring中的Bean缺省情况下是单例模式。在容器分配Bean的时候,它总是返回同一个实例。如果想每次得到不同的实例你需要将Bean定义为原型模式。定义为原型模式意味着你是定义一个Bean蓝图,而不是一个单一的Bean.<bean>的singleton属性告诉这个bean是否是单例的,如果是true表示是单例的,如果是false表示是原型的。
<bean id="connectionPool" class="com.springinaction.chapter02.MyConnectionPool" init-method="initialize" destroy-method="close"/>
按这样配置,MyConnectionPool被实例化后initialize()方法马上被调用,给Bean初始化池的机会。在Bean从容器中删除之前,close()方法将释放数据库连接。
设值注入:它是一种基于标准命名规范的设置Bean属性的技术。JavaBean规范规定使用对应的set和get方法来设置和获得Bean的属性值。<property>元素的子元素<value>用来设置普通类型的属性,子元素<ref bean="">用来设置要关联的Bean.
内部Bean:另一种不常使用的装配Bean引用的方法是在<property>元素中嵌入一个<bean>元素。
<bean id="courseService" class="com.springinaction.service.training.CourseServiceImpl">
<property name="studentService">
<bean class="com.springinaction.service.training.StudentServiceImpl"/>
</property>
</bean>
这种装配Bean引用的方式的缺点是你无法在其他地方重用这个StudentServiceImpl实例,因为它是一个专门的courseServiceBean建立的实例。
注入的是对象集:如果你有一个属性是一个列表或是一个Bean引用集合,Spring支持多种类型的集合作为属性。
<list><set><map><props>
装配List和数组:装配一个List属性,List里的元素可以是任何一种元素,包括<value><ref>甚至是其他<list>
<property name="barList">
<list>
<value>bar1</value>
<ref bean="bar2"/>
</list>
</property>
装配Set:和List一样,Set可以包含任何类型的属性。
<property name="barSet">
<set>
<value>bar1</value>
<ref bean="bar2"/>
</set>
</property>
<set>的使用方法和<list>是一样的。唯一不同的地方是它被装到什么样的属性中。<list>是向java.util.List或数组里装配数据,而<set>是向java.util.Set中装配数据。
装配Map:
<property name="barMap">
<map>
<entry key="key1">
<value>bar1</value>
</entry>
<entry key="key2">
<value>bar2</value>
</entry>
</map>
</property>
Map中的<entry>的数值和<list>以及<set>的一样,可以是任何有效的属性元素。重申一边,包括<value>、<ref>、<list>甚至是另一个<map>。注意,配置<entry>时,属性key的值只能是String.这对于java.util.Map的功能来说有一点限制,java.util.Map是允许任何类型的对象作为主键的。
装配properties:java.util.Properties集合是最后一个能在Spring中装配的集合类。使用<props>元素来装配它。使用<prop>元素表示每条属性。
<property name="barProps">
<props>
<prop key="key1">bar1</prop>
<prop key="key2">bar2</prop>
</props>
</property>
设置null
为了将一个属性设为null,你只要使用<null/>元素就行了。
例如:
<property name="foo"><null/><property>
posted @
2009-10-29 10:28 王永庆 阅读(1587) |
评论 (2) |
编辑 收藏