|
|
用户名:myvalley 笔名:缘客石 地区: 黑龙江-哈尔滨 行业:其他 |
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
AOP是什么?
为什么要区分J2EE容器和J2EE应用系统?
我们知道,J2EE应用系统只有部署在J2EE容器中才能运行,那么为什么划分为J2EE容器和J2EE应用系统? 通过对J2EE容器运行机制的分析(见我的电子教材“EJB实用原理”),我们可以发现:实际上J2EE容器分离了一般应用系统的一些通用功能,例如事务机制、安全机制以及对象池或线程池等性能优化机制。
这些功能机制是每个应用系统几乎都需要的,因此可以从具体应用系统中分离出来,形成一个通用的框架平台,而且,这些功能机制的设计开发有一定难度,同时运行的稳定性和快速性都非常重要,必须经过长时间调试和运行经验积累而成,因此,形成了专门的J2EE容器服务器产品,如Tomcat JBoss、Websphere、WebLogic等。
从J2EE系统划分为J2EE容器和J2EE应用系统两个方面,我们已经看到一种分散关注的思路(separation of concerns)。
分散关注
将通用需求功能从不相关类之中分离出来;同时,能够使得很多类共享一个行为,一旦行为发生变化,不必修改很多类,只要修改这个行为就可以。
AOP就是这种实现分散关注的编程方法,它将“关注”封装在“方面”中。
AOP是什么?
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
举例:假设有在一个应用系统中,有一个共享的数据必须被并发同时访问,首先,将这个数据封装在数据对象中,称为Data Class,同时,将有多个访问类,专门用于在同一时刻访问这同一个数据对象。
为了完成上述并发访问同一资源的功能,需要引入锁Lock的概念,也就是说,某个时刻,当有一个访问类访问这个数据对象时,这个数据对象必须上锁Locked,用完后就立即解锁unLocked,再供其它访问类访问。
使用传统的编程习惯,我们会创建一个抽象类,所有的访问类继承这个抽象父类,如下:
abstract class Worker{ abstract void locked(); } |
因此,一个新的程序结构应该是关注系统的纵向切面,例如这个应用的“锁”功能,这个新的程序结构就是aspect(方面)
在这个应用中,“锁”方面(aspect)应该有以下职责:
提供一些必备的功能,对被访问对象实现加锁或解锁功能。以保证所有在修改数据对象的操作之前能够调用lock()加锁,在它使用完成后,调用unlock()解锁。
AOP应用范围
很明显,AOP非常适合开发J2EE容器服务器,目前JBoss 4.0正是使用AOP框架进行开发。
具体功能如下:
Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging 调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence 持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务
AOP有必要吗?
当然,上述应用范例在没有使用AOP情况下,也得到了解决,例如JBoss 3.XXX也提供了上述应用功能,但是没有使用AOP。
但是,使用AOP可以让我们从一个更高的抽象概念来理解软件系统,AOP也许提供一种有价值的工具。可以这么说:因为使用AOP结构,现在JBoss 4.0的源码要比JBoss 3.X容易理解多了,这对于一个大型复杂系统来说是非常重要的。
从另外一个方面说,好像不是所有的人都需要关心AOP,它可能是一种架构设计的选择,如果选择J2EE系统,AOP关注的上述通用方面都已经被J2EE容器实现了,J2EE应系统开发者可能需要更多地关注行业应用方面aspect。
AOP具体实现
AOP是一个概念,并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点(如Java),目前AOP具体实现有以下几个项目:
AspectJ (TM): 创建于Xerox PARC. 有近十年历史,成熟
缺点:过于复杂;破坏封装;需要专门的Java编译器。
动态AOP:使用JDK的动态代理API或字节码Bytecode处理技术。
基于动态代理API的具体项目有:
JBoss 4.0 JBoss 4.0服务器
nanning 这是以中国南宁命名的一个项目,搞不清楚为什么和中国相关?是中国人发起的?
基于字节码的项目有:
aspectwerkz
spring ?
在以后其它文章中,我将继续对AOP概念进行分析,和大家一起学习进步
j2EE基础概念
| j2EE基础概念 | |
1,作用域public,protected,private,以及不写时的区别 2,ArrayList和Vector的区别,HashMap和Hashtable的区别 3,char型变量能不能定义为一个中文?为什么? 4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么? 5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么? 6,内部类的实现方式? 7,垃圾回收机制,如何优化程序? 8,float型float f=3.4是否正确? Jsp方面 1,jsp有哪些内置对象?作用分别是什么? 2,jsp有哪些动作?作用分别是什么? 3,include的两种实现方式的区别? 4,两种跳转方式分别是什么?有什么区别? Servlet方面 1,说一说Servlet的生命周期? 2,Servlet版本间(忘了问的是哪两个版本了)的不同? Jdbc,Jdo方面 1,可能会让你写一段Jdbc连Oracle的程序. 2,Class.forName的作用?为什么要用? 3,Jdo是什么? Xml方面 1,xml有哪些解析技术?区别是什么? 2,你在项目中用到了xml技术的哪些方面?如何实现的? 3,用jdom解析xml文件时如何解决中文问题?如何解析? EJB方面 1,EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别? MVC方面 1,MVC的各个部分都有那些技术来实现?如何实现? 设计模式方面: 1,开发中都用到了那些设计模式?用在什么场合? JavaScript方面 1,如何校验数字型? CORBA 1,CORBA是什么?用途是什么? 谁来做出解答阿! ------------------------------------------------------------- 回答一部分。 1,作用域public,protected,private,以及不写时的区别 public 在其他的包中的类也可以引用,protected只限于同一个包内的类,private只有自己可以使用。不写的时候和protected一样。 2,ArrayList和Vector的区别,HashMap和Hashtable的区别 ArrayList需要预先定义大小,Vector不用。HashMap和Hashtable的默认初始化容量(default initial capacity)不同 HashMap是16,Hashtable为11。 3,char型变量能不能定义为一个中文?为什么? 可以定义。因为中文也是16bit的。 4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么? 查看jdk文档。 5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么? 这个具体的去看。 6,内部类的实现方式? 内部类”是在另一个类的内部声明的类。从Java 1.1开始,你可在一个类中声明另一个类,这与声明字段和方法非常相似。 7,垃圾回收机制,如何优化程序? 在变量不使用的时候将其赋值为null。 8,float型float f=3.4是否正确? 不行。类型不匹配。改为float f=3.4f。 Servlet方面 1,说一说Servlet的生命周期? Servlet的生命周期是当服务器装载运行servlets,接收来自客户端的多个请求并且返回数据给客户端,然后再删除移开servlets的时间。 2,Servlet版本间(忘了问的是哪两个版本了)的不同? 我个人认为这个问题没有什么实际意义。 Jdbc,Jdo方面 1,可能会让你写一段Jdbc连Oracle的程序. 有通式。Connection conn = null; String driver = "oracle.jdbc.driver.OracleDriver"; String url = "jdbc:oracle:thin:@xxx:1521:xxx"; String user = "xxx"; String password = "xxx"; try { Class.forName("oracle.jdbc.driver.OracleDriver"); conn = DriverManager.getConnection(url, user, password); } catch (ClassNotFoundException e) { System.err.print("ClassNotFoundException: Load jdbc-driver failure!"); System.err.println(e.getMessage()); } catch (SQLException e) { e.printStackTrace(); try { if (conn != null) { conn.close(); conn = null; } } catch (SQLException se) { } } 2,Class.forName的作用?为什么要用? 返回一个指定名称的class对象。用它是为了load你指定名称的class。 3,Jdo是什么? 全称Java Data Objects。提供了在事务处理数据库中Java对象模型的明显的持久性,直接支持了Java类的实例,应用程序不用处理任何其它的数据模型。 Xml方面 1,xml有哪些解析技术?区别是什么? 1) CSS是Cascading Style Sheet的缩写,即“层叠样式表”,在1997年W3C颁布HTML4标准的同时也公布了有关样式单的第一个标准CSS1。 2)XSL(eXtensible Style Language,可扩展的样式语言)是最强大和灵活的样式语言,是特别为应用XML而设计的,它完全遵循XML规则,进一步完善了XML本身。 3)Data-Island。还有几个,但是主要是第二个,其他的我认为不是主流。 2,你在项目中用到了xml技术的哪些方面?如何实现的? xslt,fo,html解析,xml数据封装和解析。使用第三方软件包实现。 3,用jdom解析xml文件时如何解决中文问题?如何解析? 没用过,不太清楚。我使用的castor。(jbuilder9中有) JavaScript方面 1,如何校验数字型? isNaN(parseFloat(xxx))如果为true,则不是数字。 --- 做点力所能及的补充: (1)Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而 ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比 Vector好。 (2)当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而 ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。 (3)Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一 个实现。 (4)性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而 HashMap的不是。 (5)只有HashMap可以让你将空作为一个表的条目的key或value --- 1,xml有哪些解析技术?区别是什么? 有两种:DOM和SAX. DOM:可以得到一个包含文档中所有元素的树结构. SAX:在文档的不同治点产生事件,应用程序可以决定如何处理这些事件以从解析器中得到信息. --- Java基础方面:(未写的是已经有人回答或我自己也未知或不确定) 4,多线程有几种表示方法,都是什么?同步有几种实现方法,都是什么? ::: 我所知道的有两种实现方法: 一种是将方法同步:public synchronized methodName(...){....} 另一种是将对象同步(此对象不能是null值): synchronized (object) { ..... } 5,继承时候类的执行顺序问题,一般都是选择题,问你将会打印出什么? 继承时候类的执行顺序是: 父类中被static关键字定义的部分是按所定义的顺序而最先被初始化的; 父类构造函数(按调用顺序); 子类中被static关键字定义的部分是按所定义的顺序而最先被初始化的; 子类构造函数(按调用顺序); 其他则按方法的调用顺序. 6,内部类的实现方式? public class XXXXX{ // 只有一个public类 ... class YYYYY{ // 可以有的访问类型是[ protected | private | 无 ] ... } } 8,float型float f=3.4是否正确? 我也不知道是否正确. 我一般是这样:float f=3.4F; 或 float f=3.4f;// 后面跟一个英文大写或小写字母F --- JavaScript方面 1,如何校验数字型? var v = document.all.Telephone.value; if(isNan(v)){ alert("全是数字"); } --- yangtaylor的解释有一半以上是错误的,大家不要作标准答案背哦 noisysilence的解释很不错,说明你已经是高手了,不过说明一点,Vector的精妙设置是在于第二个参数的设置,其余的都基本正确。 关于“作用域public,protected,private,以及不写时的区别”的说明--- 前三个常用,就不用说了,关于不写时的情况我来说明一下: JAVA的作用域其实有5种,除了上面的3种外还有:private protected,default public--------------不说了 protected-----------除了所有的子类可访问外,同包的非子类也可以访问 private-------------也不说了 private protected---只有子类可以访问(这才是我们理解意义上的protected) default-------------只有同包的类可以访问,即使是子类但不同包仍不能访问 我也是看了很多参考书才最终得到的结果,在此献给大家了。 --- ArrayList和Vector的区别上面已说,我说说它们会带来得影响吧。 同为对象集合,ArrayList可由编译器检查而Vector则不会,所以如果用Vector返回对象集合,编译器是无法查错得,只有在运行时才能才能发现。例如: ********采用Vector************* server side: public Vector getCustomDataSet(int num) { Vector v = new Vector(); for(int i=0; i<num; i++) { CustomData customData = new CustomData(i); v.add(customData) //此处加入自定义的数据结构CustomData } return v; } client side: Vector v = getCustomDataSet(5); for(int i=0; i<v.size(); i++) { OtherData data = (OtherData)v.get(i);//此处取出的是customData却转换为OtherData,编译器通过 } ********采用ArrayList************* server side: public CustomData[] getCustomDataSet(int num) { CustomData[] customData = new CustomData[5]; for(int i=0; i<num; i++) { customData[i] = new CustomData(i); } return customData; } client side: CustomData[] datas = getCustomDataSet(5); for(int i=0; i<datas.length; i++) { OtherData data = datas[i];//编译不通过,类型不匹配 } --- 下边是以前我找的关于Hashtable和HashMap的不同之处。 Hashtable和HashMap Hashtable和HashMap类有三个重要的不同之处。第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。 也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。 第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。如果有必要,用containKey()方法来区别这两种情况。 一些资料建议,当需要同步时,用Hashtable,反之用HashMap。但是,因为在需要时,HashMap可以被同步,HashMap的功能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。 --- XML 的编程接口: DOM SAX JDOM JAXP 文档对象模型(通常称为 DOM)为 XML 文档的已解析版本定义了一组接口。解析器读入整个文档,然后构建一个驻留内存的树结构,然后您的代码就可以使用 DOM 接口来操作这个树结构。您可以遍历树以了解原始文档包含了什么,您可以删除树的几个部分,还可以重新排列树和添加新的分支 DOM 提供了一组丰富的功能,您可以用这些功能来解释和操作 XML 文档,但使用它们是有代价的。 DOM 构建整个文档驻留内存的树。如果文档很大,就会要求有极大的内存。 DOM 创建表示原始文档中每个东西的对象,包括元素、文本、属性和空格。如果您只需关注原始文档的一小部分,那么创建那些永远不被使用的对象是极其浪费的。 DOM 解析器必须在您的代码取得控制权之前读取整个文档。对于非常大的文档,这会引起显著的延迟 为了解决 DOM 问题,XML-DEV 参与者们(由 David Megginson 领导)创建了 SAX 接口。SAX 的几个特征解决了 DOM 的问题: SAX 解析器向您的代码发送事件。当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,它会告诉您。您可以决定什么事件对您重要,而且可以决定要创建什么类型的数据结构以保存来自这些事件的数据。如果您没有显式地保存来自某个事件的数据,它就被丢弃。 SAX 解析器根本不创建任何对象,它只是将事件传递给您的应用程序。如果希望基于那些事件创建对象,这将由您来完成。 SAX 解析器在解析开始的时候就开始发送事件。当解析器发现文档开始、元素开始和文本等时,代码会收到一个事件。您的应用程序可以立即开始生成结果;您不必一直等到整个文档被解析完毕。更妙的是,如果您只查找文档中某些内容,代码一旦找到所要找的东西就可以抛出一个异常。该异常会停止 SAX 解析器,然后代码用它找到的数据做它需要做的任何事。 SAX 解析器也有些问题引人关注: SAX 事件是无状态的。当 SAX 解析器在 XML 文档中发现文本时,它就向您的代码发送一个事件。该事件仅仅给您发现的文本;它不告诉您什么元素包含那个文本。如果您想知道这一点,则必须自己编写状态管理代码。 SAX 事件不是持久的。如果应用程序需要一个数据结构来对 XML 文档建模,则必须自己编写那样的代码。如果您需要从 SAX 事件访问数据,并且没有把那个数据存储在代码中,那么您不得不再次解析该文档。 JDOM 是基于 Java 技术的开放源码项目,它试图遵循 80/20 规则:用 DOM 和 SAX 20% 的功能来满足 80% 的用户需求。JDOM 使用 SAX 和 DOM 解析器,因此它是作为一组相对较小的 Java 类被实现的。 JDOM 的主要特性是它极大地减少了您必须编写的代码数量。尽管本篇介绍性教程并不深入讨论编程主题,但 JDOM 应用程序的长度通常是 DOM 应用程序的三分之一,大约是 SAX 应用程序的一半。(当然,坚持使用 DOM 的纯粹主义者会建议说:从长远来看,学习和使用 DOM 终会有所回报)。JDOM 并不做所有的事,但对于大多数您要做的解析,它可能正好适合您。 尽管 DOM、SAX 和 JDOM 为大多数常见任务提供了标准接口,但仍有些事情是它们不能解决的。例如,在 Java 程序中创建 DOMParser 对象的过程因 DOM 解析器的不同而不同。为了修正这个问题,Sun 发布了 JAXP(用于 XML 解析的 Java API,Java API for XML Parsing)。该 API 为使用 DOM、SAX 和 XSLT 处理 XML 文档提供了公共接口。 JAXP 提供的诸如 DocumentBuilderFactory 和 DocumentBuilder 之类的接口为不同的解析器提供了一个标准接口。还有一些方法可以允许您控制底层的解析器是否可以识别名称空间以及是否使用 DTD 或模式来验证 XML 文档。 为了确定哪种接口适合您,您需要理解所有接口的设计要点,而且需要理解应用程序用您将要处理的 XML 文档来做什么。考虑下面的问题将有助于您找到正确的方法。 要用 Java 编写应用程序吗?JAXP 使用 DOM、SAX 和 JDOM;如果您用 Java 编写代码,那么您应使用 JAXP 将您的代码与各种解析器实现的细节隔离。 应用程序将如何部署?如果您的应用程序将要作为 Java applet 部署,那么您会希望使要载的代码数量最小,别忘了 SAX 解析器比 DOM 解析器小。还要知道使用 JDOM 时,除了 SAX 或 DOM 解析器之外还要求编写少量的代码。 一旦解析了 XML 文档,还需要多次访问那些数据吗?如果您需要回过头来访问 XML 文件的已解析版本,DOM 可能是正确的选择。而 SAX 事件被触发时,如果您以后需要它,则由您(开发人员)自己决定以某种方式保存它。如果您需要访问不曾保存的事件,则必须再次解析该文件。而 DOM 自动保存所有的数据。 只需要 XML 源文件的少量内容吗?如果您只需要 XML 源文件的少量内容,那么 SAX 可能是正确的选择。SAX 不会为源文件中的每个东西创建对象;您要确定什么是重要的。使用 SAX,您要检查每个事件以了解它是否与您的需要有关,然后相应地处理它。更妙的是,一旦找到您正在寻找的东西,您的代码就会抛出一个异常来完全停止 SAX 解析器。 您正在一台内存很少的机器上工作吗?若是的话,不管您可能考虑到的其它因素是什么,SAX 是您的最佳选择。 |
Java虚拟机(JVM)对象创建浅析
Java虚拟机(JVM)对象创建浅析 |
| 你认为java虚拟机什么时候会创建一个对象?如果能回答出下面的问题,你就可以不用继续向下看了。 class A{ static final int a = 10; static{ System.out.println( "initializing A" ); } } class B{ public static void main(String[] args){ System.out.println(A.a); } static{ System.out.println( "initializing B" ); } } 我们调用B的main函数,会得到什么输出? 经过考虑后,你是不是认为应该得到: "initializing B" "initializing A" 10 这个结果?不对,其实结果是: "initializing B" 10 嗯?A没有被实例化? 其实,java虚拟机会在6种情况下创建一个对象,分别是: ·创建类的新实例 ·调用类中声明的静态方法 ·操作类或接口中声明的非常量静态字段 ·调用Java API中特定的反射方法 ·初始化一个类的子类 ·制定一个类作为Java虚拟机启动时的初始化类 java虚拟机只有在第一次主动使用一个类的情况下才会初始化该类。在以上6种情况下,虚拟机认为正在主动使用该类,所以会对其进行初始化,记住这6条,SCJP可能会出现(只是如果,可能考试不会涉及到虚拟机一层) 顺便说一下,任何一个类的初始化都要求它的所有祖先类(非祖先接口)预先被初始化,而接口的初始化则不需要祖先接口预先被初始化。 |
Java中Static、this、super、final用法
Java中Static、this、super、final用法 |
| 一、Static 请先看下面这段程序: TER> public class Hello 在1处,我们定义了一个静态的方法名为main,这就意味着告诉Java编译器,我这个方法不需要创建一个此类的对象即可使用。你还得你是怎么运行这个程序吗?一般,我们都是在命令行下,打入如下的命令(加下划线为手动输入): javac Hello.java 执行结果如你所料。在2中,你可能会想,为什么要这样才能输出。好,我们来分解一下这条语句。(如果没有安装Java文档,请到Sun的官方网站浏览J2SE API)首先,System是位于java.lang包中的一个核心类,如果你查看它的定义,你会发现有这样一行: public static final PrintStream out; public void println(String x) 静态方法 通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法。如下所示: class Simple 静态变量 静态变量与静态方法类似。所有此类实例共享此静态变量,也就是说在类装载时,只分配一块存储空间,所有此类的对象都可以操控此块存储空间,当然对于final则另当别论了。看下面这段代码: class Value v1.c=0 v2.c=0
public static void main v1.c=0 v2.c=0 正如在程序中所表现的,虽然v出现在v1和v2的前面,但是结果却是v1和v2的初始化在v的前面。在static{后面跟着一段代码,这是用来进行显式的静态变量初始化,这段代码只会初始化一次,且在类被第一次装载时。 如果你能读懂并理解这段代码,会帮助你对static关键字的认识。在涉及到继承的时候,会先初始化父类的static变量,然后是子类的,依次类推。非静态变量不是本文的主题,在此不做详细讨论,请参考Think in Java中的讲解。 静态类 通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。如下代码所示: public class StaticCls InnerCls 二、this & super 在上一篇拙作中,我们讨论了static的种种用法,通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量。但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的。好了,现在开始讨论this&super这两个关键字的意义和用法。 在Java中,this通常指当前对象,super则指父类的。当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当然,this的另一个用途是调用当前对象的另一个构造函数,这些马上就要讨论。 如果你想引用父类的某种东西,则非super莫属。由于this与super有如此相似的一些特性和与生俱来的某种关系,所以我们在这一块儿来讨论,希望能帮助你区分和掌握它们两个。 在一般方法中 最普遍的情况就是,在你的方法中的某个形参名与当前对象的某个成员有相同的名字,这时为了不至于混淆,你便需要明确使用this关键字来指明你要使用某个成员,使用方法是“this.成员名”,而不带this的那个便是形参。 另外,还可以用“this.方法名”来引用当前对象的某个方法,但这时this就不是必须的了,你可以直接用方法名来访问那个方法,编译器会知道你要调用的是那一个。下面的代码演示了上面的用法: public class DemoThis class Person DemoSuper: 在构造函数中 构造函数是一种特殊的方法,在对象初始化的时候自动调用。在构造函数中,this和super也有上面说的种种使用方式,且它还有特殊的地方,请看下面的例子: class Person 当然,在Chinese的各个重载构造函数中,this和super在一般方法中的各种用法也仍可使用,比如4处,你可以将它替换为“this.prt”(因为它继承了父类中的那个方法)或者是“super.prt”(因为它是父类中的方法且可被子类访问),它照样可以正确运行。但这样似乎就有点画蛇添足的味道了。 最后,写了这么多,如果你能对“this通常指代当前对象,super通常指代父类”这句话牢记在心,那么本篇便达到了目的,其它的你自会在以后的编程实践当中慢慢体会、掌握。另外关于本篇中提到的继承,请参阅相关Java教程。 final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的成员、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。 final成员 当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。 其初始化可以在两个地方,一是其定义处,也就是说在final变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。下面这段代码演示了这一点: import java.util.List; 然而有时你并不需要这种灵活性,你只需要在定义时便给定其值并永不变化,这时就不要再用这种方法。 在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型,输出结果中显示了这一点: I=100 List Type:class 然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。 另外方法中的内部类在用到方法中的参变量时,此参变也必须声明为final才可使用,如下代码所示: public class INClass 将方法声明为final,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。 另外有一种被称为inline的机制,它会使你在调用final方法时,直接将方法主体插入到调用处,而不是进行例行的方法调用,例如保存断点,压栈等,这样可能会使你的程序效率有所提高,然而当你的方法主体非常庞大时,或你在多处调用此方法,那么你的调用主体代码便会迅速膨胀,可能反而会影响效率,所以你要慎用final进行方法定义。 final类 当你将final用于类身上时,你就需要仔细考虑,因为一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。 对于final类中的成员,你可以定义其为final,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。你也可以明确的给final类中的方法加上一个final,但这显然没有意义。 下面的程序演示了final方法和final类的用法: final class final final在设计模式中的应用 在设计模式中有一种模式叫做不变模式,在Java中通过final关键字可以很容易的实现这个模式,在讲解final成员时用到的程序Bat.java就是一个不变模式的例子。 到此为止,this,static,supert和final的使用已经说完了,如果你对这四个关键字已经能够大致说出它们的区别与用法,那便说明你基本已经掌握。 然而,世界上的任何东西都不是完美无缺的,Java提供这四个关键字,给程序员的编程带来了很大的便利,但并不是说要让你到处使用,一旦达到滥用的程序,便适得其反,所以在使用时请一定要认真考虑。 |
JVM的垃圾回收机制详解和调优
1.JVM的gc概述
gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。
J2SE的发展史
1.J2SE:怀胎
Java的历史可以追溯到1991年4月,Sun公司的James Gosling领导的绿色计划(Green Project)开始着力发展一种分布式系统结构,使其能够在各种消费性电子产品上运行,他们使用了C/C++/Oak语言。由于电子产品种类繁多,运行环境各不相同,使得用这些语言进行开发的软件必须为不同的电子产品而专门设计,所以项目组疲于奔命,消费性电子产品软件环境的发展无法达到预期的目标,绿色计划也陷于停滞状态。
直至 1994年下半年,由于Internet的迅猛发展和环球信息网 WWW的快速增长,第一个全球信息网络浏览器Mosaic诞生了;此时,工业界对适合在网络异构环境下使用的语言有一种非常急迫的需求;Games Gosling决定改变绿色计划的发展方向,他们对Oak进行了小规模的改造,就这样,Java在1995年的3月23日诞生了!Java的诞生标志着互联网时代的开始,它能够被应用在全球信息网络的平台上编写互动性及强的Applet程序,而1995年的Applet无疑能给人们无穷的视觉和脑力震荡。
但没有相应的开发库而只靠Java语言来进行开发肯定是困难重重,所以Sun公司在1996年的1月23日发布了JDK1.0来帮助开发人员的开发。JDK包括两大部分:运行环境和开发工具。而运行环境包括五大部分:核心API,集成API,用户界面API,发布技术,Java虚拟机(JVM)。Java能够在信息网络时代快速发展,得益于它独特的组成结构,它并不直接被编译成所在平台的机器语言然后执行,而是先被编译成字节码,然后才到装有JVM的硬件上运行,所以它能够跨平台运行;而且,不是一定要使用Java语言来写程序才能编译成Java字节码,我们用C/C++/Javascript等语言来写程序也可以编译出JVM要求的字节码文件。在这个时期,人们使用最多的 Java API 无疑地就是 AWT,因为 JDK 1.0 时只有 AWT 称得上是一组完整的 framework,其它 Java API 都比较偏向个别的 class。
紧跟着,Sun公司在1997年2月18日发布了JDK1.1。JDK1.1相对于旧版本最大的改进,是推出了JIT(Just-In-Time)编译器。传统的JVM收到一道字节码指令后,解释成机器码,马上执行该机器码,然后丢掉该机器码,再接收下一道字节码指令……即便是以前执行过的字节码指令,依然需要重新解释成机器码才能执行。而JIT编译器会把常执行的部分先解释好放在内存,以后再次执行该指令时,不用再解释,直接从内存中取出机器码就可以执行了,这样提高了不少的效率。
在JDK1.1时代,Java平台分为PersonalJava与EmbedddJava,前者比较适用于运算资源和内存丰富的设备,而资源有限者适用于后者。这样的分类明显不符合时代发展的潮流,所以,Java平台处处蕴藏着新的翻天覆地的革命……
2.J2SE1.2:诞生
JDK1.2在1998年12月4日的隆重发布,标志着Java2平台的诞生。Java2的J2SE1.2时代是一个大变革时代,它进行了如下的三大革命:
* 市场推广革命
在Java1.0或者1.1中,称API库为JDK,但为了方便市场推广,Sun公司在Java1.2版以后将JDK1.2改名为J2SDK,将Java改名为Java 2。在99年Sun公司还将Java2平台分为三大块:J2SE,J2EE,J2ME。这次市场推广革命顺应了网络急速发展的潮流,对Java2平台的发展起到了很好的催化剂的作用。
* API供应标准革命
而随着供应商的不同,Java的API分为三大类:
Java Core API:由Sun公司制定的基本的API,所有的Java平台都应该提供。这就是我们平常所说的Java核心类库。
Java Optional API:由Sun公司制定的扩充API,Java平台可以有选择地提供。比如, JFC以前是Sun对JDK的功能扩展,不是Java的公共规范。Java2平台则包含了JFC技术,不需要再扩展了。
特殊API:由特殊厂商或者组织提供的API。比如,美国法律对出口中国的加解密算法的安全强度有一定的限制,Java平台中不能包括一些安全强度较高的加密解密算法,所以部分其它国家的公司,如加拿大的BouncyCastle公司等,可以提供一些自己的安全API。
* API制定过程的革命
如果你有需求不能通过遵循标准的API来实现,可以向 www.jcp.org 提出制定新的API的请求,经过审核,你的请求可能被通过或者驳回;如果是被通过,则开始进入制定该API的程序。
J2SE1.2时代进行的这些革命形成的制度一直沿用到现在,对Java技术的发展形成了深远的影响。
除了上述的三大革命,Java2 还支持并新增了许多新特性,最受追捧的当属Swing库。Swing 是轻量级 的 API,它不但有各式各样先进的组件,而且连组件风格都可抽换。Swing 出现之后,大家很快地就不太使用 AWT 了。但请不要以为 Swing 是用来取代 AWT 的,其实 Swing 是架构在 AWT 之上的, Swing 的出现只是减少程序员直接使用 AWT 的机会,而不会让 AWT 消失。Java2 还废弃了一些API,最重要的莫过于Thread类中对suspend(),resume()和stop()等方法的废弃。由于JDK1.1的集合类库中的Vector类和HashTable类都考虑了同步,在平常的使用中影响效率,所以Java2专门添加了对应的非同步类,并完善了集合类库。
Java2平台能够得到广泛的应用,还应归功于这个时期的一些优秀书籍,比如著名的<<Core Java2>>(卷一和卷二),该书随着JDK版本的变化已经出到第六版。
3. J2SE1.3:拓广
Java2平台推出后,得到了市场的强烈反响,所以,在2000年5月8日推出的J2SE1.3对J2SE1.2的改进,主要是对各种已有API的加强和对新API的拓展。
数字运算: 加入了java.lang.StrictMath,方便我们的一般的数字运算。
新的Timer API:相信大家对其中的java.util.Timer和java.util.TimerTask一定不陌生。
Collections包:??加入了一些新的API,方便我们的使用。
虚拟机停止钩子:J2SE1.3还加入了一个强大的功能,那就是虚拟机停止钩子(Virtual Machine Shutdown Hooks),这个功能使得我们能够在虚拟机停止时完成我们自己的操作,比如关闭网络连接或者保存会话状态或者清除临时文件等等。
DNS服务:在JNDI接口方面,加入了一个DNS服务的实现。
Jini实现:J2SE1.3包含了一个Jini实现,这使得我们可以方便地把诸如打印机、摄像机和磁盘驱动设备插入现有网络中,并且能自动搜索已在网上的设备可以提供的服务并享用这些服务。
XML支持:由于计算机网络和XML技术的快速发展, J2SE1.3在Optional API中引入了Java API for XML包。
HotSpot虚拟机:J2SE1.3引入了HotSpot虚拟机。在solaris版的JDK1.3中,已经不支持传统的虚拟机,而windows版的JDK1.3同时支持传统虚拟机和HotSpot虚拟机。
从上面的分析可以看出,J2SE1.3主要是对J2SE1.2查漏补缺和拓展新的API。从应用领域方面考虑,J2SE1.3已经涵盖了数据库、WEB、多媒体、网络、电话、影像、加解密、图形等等大部分的信息技术领域。
在这个时期Java2还有一个重要活动就是推出SCSL(Sun社区源代码许可)许可协议。Sun公司开放源代码项目的“女1号”Danese Cooper在1999年加入公司,负责Sun(包括Java)和开放源代码社区之间的协调工作。Sun一直尽可能在赢利和开放源代码之间寻求更好的平衡。
在J2SE1.3时代,Java2平台得到了充分的发展,各大应用服务器在这个时代渐渐成熟并被广泛采纳,而Java2相关的培训和书籍更是如火如荼,《Thinking in Java》当属不得不推荐的一本。
4. J2SE1.4:快速
J2SE1.4平台的推出发生在2002年2月13日,由于此前在Java平台和.Net平台间发生了规模浩大的孰优孰劣的论战,而论战中,Java平台最大的缺点就是性能问题,所以J2SE1.4平台把性能的改善放在了最重要的位置。
HotSpot虚拟机: HotSpot虚拟机能够很大程度上提高性能,所以J2SE1.4已经不支持传统的虚拟机。现在,启动应用程序应该通过-client或者-server选项来启动,如果你还是通过-classic选项来启动,会收到这样的错误提示:”Error: Classic VM no longer supported。”
锁机制: 由于旧版的HotSpot虚拟机的锁机制会导制严重的性能和功能问题,J2SE1.4已经改写了该锁机制。
安全API: Java Cryptography Extension(JCE),Java Secure Socket Extension(JSSE)和Java Authentication and Authorization(JAAS)这三大安全API从optional API移到了core API中。这样,J2SE1.4的安全域(SecureRandom)实现可以利用操作系统提供的安全机制,以便缩短应用程序的启动时间。同时从optional API移到core API的功能还有XML处理API和JDBC2.0 API。
RandomAccess标记接口: 加入了RandomAccess标记接口,如果一个List实现了该接口,则表示它支持快速的随机访问,这样可以提高List访问的速度。
LinkedHashMap: 加入了LinkedHashMap,这是一个插入排序的Map实现,但它的运行速度和HashMap一样快。
反射: 很多产品中都要使用反射(Reflection)机制,但大家知道,反射是相当耗时的,所以,J2SE1.4中重写了java.lang.reflect.Field、java.lang.reflect.Method.invoke()、java.lang.reflect.Constructor.newInstance()和Class.newInstance()等方法,使得我们利用反射也能写出高性能的应用程序。
64位计算: J2SE1.4支持64位计算。64位支持使J2SE1.4可以使用超过几百GB的内存,而以前的版本仅可以使用4G以内的内存。内存使用的扩大可以提高程序的运行效率。
新的I/O API: J2SE1.4在API层面最大的变动,就是它更新了原有的java.io包,以及加入了一组更有效率更多功能的新的I/O API。旧的I/O处理是以串流(stream)的方式来存取的,以字节为单位,而新的I/O处理是以块(block)的方式来存取的。一个块可能包含了512字节,而磁盘的存取本就是一个块一个块来进行的,所以新的I/O处理方式对于底层底数据存取更自然更有效率。
断言和日志处理: J2SE1.4版本在Java语言层面上加入了断言(assert关键字),在API层面上加入日志处理API,这些为程序的调试提供了强有力的支持 。
从上面的分析可以看出,Java 2平台在经过数年的发展后,已经比较成熟稳定,J2SE1.4主要是对平台的性能进行较多的考虑和修改。在分布式程序方面,1.4版比1.3版的运行效率提高了一半以上;而在客户端程序方面,1.4版比1.3版的效率提高了1/3。当然,除了考虑运行效率,J2SE1.4还有其它方面的改进,如推出了一个全新的运行Java应用程序的方法Java Web Start,该方法克服了Java Applet的安全性限制的问题和Java Application的软件发布难的问题,并结合了上面两种方法的好处,为Java软件客户提供了非常友好的使用体验。
J2SE1.4版是J2SE第一个参与了 Java共同体过程(JCP)的J2SE版本。 象Borland、Compaq、Fujitsu、 SAS、 Symbian、 IBM这样的公司,和Sun一起定义并发展了J2SE 1.4规范。在开放、良好的文档编撰与管理的过程中,形成了一个高质量的、代表了Java共同体的多样性的规范。J2SE1.4的Specification Lead是Doug Kramer。Doug Kramer是优秀的Java开发人员和著名的教育培训专家,他同时还是J2SE1.4.2的Specification Lead 和J2SE1.5的JVM的Specification Lead。
5. J2SE5.0:易用
在2004年十月J2SE5.0发布的时候,SUN公司这样解释这次版本名称不是J2SE1.5而是J2SE5.0的原因:“从Java诞生至今已有9年时间,而从有J2SE算起也有5个年头了;在这样的背境下,将该版本号从1.5改为5.0可以更好的反映出新版的J2SE的成熟度、稳定性、可伸缩性、安全性。”
J2SE的这次变更之重大和意义之深远,的确也值得我们为之把版本号变换到J2SE5.0。我们再看看Sun公司网站对J2SE5.0的features描述:“通过增强Java平台的力量,允许开发者更容易地使用,Java编程语言的这些改进将吸引大量各种Java开发者”,这是“Java技术发展历程的一个重要里程碑” 。从这个描述我们可以看出,J2SE5.0最大的目标是通过提供易用性而吸引各种开发者(当然包括以前的C/C++开发者) ,而它对以前版本的修改并不仅仅是API的升级,而且包括对Java语言层面的改进。
访问环境变量: 最初的Java语言有一个访问环境变量的方法System.getenv(),但因为Java宣称的”Write Once,Run AnyWhere”特性,所以在JDK1.0中去掉了这个能够访问平台专有信息的方法。在J2SE5.0中,它又来了,并有所扩充。由此可见J2SE5.0对编程方便的重视程度。
泛型 J2SE5.0提供了强大的泛型机制,让程序员可以减少代码重复,这个变化应该可以吸引小部分的C#开发人员吧。
增强的for循环 为了克服普通for循环的代码臃肿特点,J2SE5.0提供了增强的for循环,我们现在可以这样写一个for循环:
public void printAll(Collection<String> coll)
{
for(String str : coll)
{
System.out.println(str);
}
}
public void boxing(int i)
{
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(0,i);
System.out.println(“i的值是”+list.get(0));
}
EJB 3.0
SUN中国软件技术中心 王强 wynne.wang@sun.com
1 简化开发的目标
1.1我们的目标
EJB3.0是当前很多人谈论的话题,企业软件开发的一个关键是,提供一个尽量简单的的应用框架:它可以使开发人员不用关注于复杂的问题,比如事务处理、安全和持久化等。可以集中精力关注于商业逻辑,而不用关心那些低层的技术细节,从而提高开发者的效率,得到高质量的软件。这也是制定EJB 3.0规范的目标,简化开发!
1.2 当前的问题
EJB3.0希望开发人员能够从这种新的开发模式中受益,更好地推进J2EE的应用. 随着JAVA不断的进步和发展,越来越多的企业选择J2EE作为它們的解決方案,J2EE体系结构提供一个简化的中间层集成框架来满足应用的需求。然而,对于一般的开发人员,目前J2EE 1.4下的EJB 2.1 框架有些过于复杂了。 按照EJB2.1规范的定义,EJB组件必须事先很多的接口, 比如Home接口、Remote接口、local 接口,等等.还要针对各种应用类型定义许多的xml描述文件。当我们需要访问某个组件或者服务的时候,必须通过JDNI查找,通过名字绑定服务,才能找到我们需要的对象。
比如我们要使用某个EJB shopping cart, 就要首先实现一个initial context,然后通过他查找这个EJB 的home 接口,然后调用home街口的create方法,得到一个EJB object, 最后调用这个EJB object的商务方法. 下面是一个例子:
// EJB 2.1 Client view of the ShoppingCart Bean
...
Context initialContext = new InitialContext();
ShoppingCartHome myCartHome = (ShoppingCartHome)
initialContext.lookup(“JAVA:comp/env/ejb/cart”);
ShoppingCart myCart= myCartHome.create();
//Use the Bean
Collection widgets = myCart.startToShop(Widgets )
...
// Don't forget code to handle JAVAx.ejb.CreateException
...
EJB2.1的规范还要求我们必须实现Javax.EJB里面定义的接口, 实现里面的Methods比如 EJBCreate(), EJBPassivate(), and EJBActivate()。大多数情况下这些方法是不需要开发人员作任何修改的。这些规定实际上都和真正核心的商务逻辑没什么关系,都只是一些技术模板,规定了开发人员必须按照这样的模板进行开发. 比如下面这段代码:
人们开始思考,怎么样才能把EJB的开发变的更加简单,更好的利用这种技术. 当前正在制定的EJB 3.0的标准的目标就是简化开发, 让更多的开发人员被它的易用和强大功能所吸引过来,喜爱这项技术。为了达到这个目标,要做的第一件事情,也是最重要的事情,就是从一个开发人员的角度,将EJB的使用尽量的简化。
1.3 JCP专家组的工作
这个工作是由EJB3.0 专家组来完成的。我们知道JAVA的开发和推动是由一个开放的组织来完成的,这个组织的名字叫做JAVA Community Process。简称JCP. SUN的理念是: 创新无处不在. 所以JCP小组从世界的每个角落听取关于JAVA的建议,将各方面对JAVA的要求通过制定JSR(JAVA Specification Request )形式确定下来. EJB 3.0规范的JSR编号是 220. 整个专家组的制定成员包括J2EE 注册用户, 应用服务器的开发厂商和J2EE社区的成员.
简化一个现有的技术,尤其是得到广泛的开发人支持的技术,比如EJB, 不是一个简单的工作. 作为铺垫,专家组进行了大量的准备工作,检验了EJB技术的复杂性,当前EJB流域流行的各种模式和反模式,以及从客户和开发人员来的各种需求。开发人员和用户根据实际的需要,希望EJB能够提供他们满意的特性.
检验结果发现,大多数情况下,人们不需要更高级的技术,而是需要更简化的技术,来简化当前的开发模式。而不是像以前的EJB发布一样,在技术复杂度上的提高。定义一个新技术,不仅对老的技术有一定的更新, 也要能充分并容老的技术, 提供一定的向后兼容性. 因为采用这些技术的企业,已经在这些技术上投资了很多. 如果因为技术的更新就使得对应的IT系统和数据变得不能使用,是一件非常糟糕的事情.
所以,定义了EJB3.0规范的同时,如何支持现有的EJB技术是非常必要的. 要保证现有的EJB API是持续可用的, 还要和新的EJB3.0 API结合起来, 对早期的API应该续提供支持而不是标记为不赞成使用
1.4 标注的新功能
EJB3.0的很多新特性是通过JAVA SE5.0来实现的。这里我们就要谈到JAVA SE 5.0,它所提供的许多特性,其中最有趣的一点就是标注(Annotation)的功能。我们知道以前的JAVA语言都是命令格式的, 比如a.b(), 表示让类a做事情b, 但是很多时候我们只是需要对某个对象做一些注解,比如对某个类标记为可持续化的Serializable. 这只是一个标记,为了以后的处理提供说明,本身不需要做任何操作。
在Deploy 的时候, 提供了很多说明的XML文件,比如部署描述文件,里面说明了引用的EJB的名字,接口, 以及当前EJB的Transaction Type等等信息. 所有这些信息都是说明性的,而不是命令性的. 用来对某个对象的某个属性坐一段说明. 因此,有一个非常有趣的想法,能不能通过对JAVA语言的扩展,结合标注和命令这两者的优点 ?
这也是有一个专家组在JCP的组织内完成的, JSR规范的编号是JSR 175, 为JAVA SE 5.0支持注解(Annotation)的功能. 这个规范为EJB3.0 的简化实现提供了一些基本的支持, 也是最关键的支持. 标注可以有自己的属性,也可以定义自己的持续时间,表示这段信息是否保存到 源代码中,还是一直持续到Class中,或者一直保持到运行时间. 标注有自己的缺省值.大多数情况下,无须说明我们就可以推算出来这个对象的行为。
2 轻松的实现开发
2.1 减轻开发人员的负担
EJB3.0的简化工作包括下面几个部分:
提供一个简化的API, 包括对EJB的定义,对EJB的引用等等
减少开发的类数目,不再需要那么多的interface
相关性注入
简化的查询机制
从开发人员的角度不必要使用部署描述文件, 很多的工作可以放到代码里面用标注来说明,比如Entity Bean 的Transaction Type
简化的持久化功能
简化和改善数据对象的O/R Mapping.
标注可以应用到编程语言的一些基本元素上,比如类,方法,变量,包等等. 当我们在代码中使用了这些标注, 根据这个标注对应的持续策略, 它可以被编译到Class 文件中去,或者一直保持到运行的时候。大多数在 EJB 3.0 中定义的标注都是Runtime保持策略, 这样做的好处是提供了最大的灵活性。而且由于大量工作放到了运行的时候来做,也减少一部分Deploy的工作。
我们通过定义缺省的语法来说明大多数常见的情况。开发人员不需要再专门说明常见的情况,“OK, 没问题,缺省的设置就已经可以满足需要了“ 这样,开发人员的工作大大减轻了。
这也引出来了EJB3.0中的一个很有意思的概念 "Configuration By Exception" --只有在例外的情况下才需要我们的参与.
EJB3.0的目标是简化开发人员的工作,让他们专注于商务应用的开发而不是把精力放到很多繁琐的例行工作上,这些工作可以交给Container来完成。EJB通过注入来指定自己需要的资源,不用再写那些麻烦的方法. 将对象的创建和获取提取到外部。由外部容器提供需要的组件。这样,开发人员只用在开始的时候定义,说我需要这个资源, 后面就可以直接使用这个资源, 这样会大大的简化开发, 因为开发人员只用关心如何使用这个对象和商务方法, 而不用担心其他的技术细节。
2.2 抛开繁琐的细节
下面我们看看都作了那些简化。我们的目的是把那些繁琐的技术细节隐藏起来,程序开发人员只用关心自己的商务逻辑代码,而不用关心那些复杂的技术模板,必须实现的接口等,哪怕这些方法和接口根本不需要实现.
不再需要EJB的部件接口
每个EJB 都只是一个普通的JAVA Class
不再需要home接口, 我们不再用home 来创建这个EJB
不再需要实现javax.ejb.EnterpriseBean借口
对于需要在回调方法里实现的部分,我们采用标注的方式说明一个方法为回调方法
不再需要使用复杂的JNDI名字调用机制,对于需要服务或者资源的地方
我们采用了相关性注入的方法,另外也可以通过简化的lookup方法来查找资源
下面让我们看一个简单的无状态SessionBean的例子。无状态session Bean是最简单也是最常用 Bean,很多初学EJB的人都从无状态Session Bean开始。如何让无状态Session Bean 变的简单易用成为一个非常有意义的话题。
前面假设我们已经定义了相关的interface, 这个EJB2.1的的功能是对员工的工资做处理,打开一个数据库连接,进行员工工资信息的某些操作,等等.
// EJB 2.1
public Class PayrollBean implements JAVAx.ejb.SessionBean
{
SessionContext ctx;
DataSource empDB;
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
public void ejbCreate() {
Context initialContext = new InitialContext();
empDB = (DataSource)initialContext.lookup( JAVA:comp/env/jdbc/empDB );
}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
public void setBenefitsDeduction (int empId, double deduction) {
...
Connection conn = empDB.getConnection();
...
}
...
}
// NOTE deployment descriptor needed
这里我们首先要实现一个 sessionBean interface,保持一个对sessioncontext的引用,然后是在EJBcreate 方法里面我们调用JNDI得到一个datasource。 后面我们必须要定义一些回调方法,虽然这些方法我们不会实现任何逻辑。然后在 商务方法里面,我们打开一个数据库连接。
注意,我们还没有完。为了使用这个EJB, 必须加上xml的部署描述文件打好包。
下面是一个常见的部署描述文件可以是这样子的。
<session>
<ejb-name>PayrollBean</ejb-name>
<local-home>PayrollHome</local-home>
<local>Payroll</local>
<ejb-class>com.example.PayrollBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<resource-ref>
<res-ref-name>jdbc/empDB</res-ref-name>
<res-ref-type>javax.sql.DataSource</res-ref-type>
<res-auth>Container</res-auth>
</resource-ref>
</session>
...
<assembly-descriptor>
...
</assembly-descriptor>
从开发人员的角度来检查这样一个简单的EJB 是非常有意义的。我们可以更好的理解有那些地方可以有改动。一个主要的问题是,定义了这么多的方法和结构以后,程序的清晰化受到了影响,结构变得混乱。真正需要关注的商务方法没有很好的强调和体现。
下面让我们看一看EJB 3.0 是如何实现一个简单的stateless session Bean的。
// Same example, EJB 3.0
@Stateless public class PayrollBean implements Payroll {
@Resource DataSource empDB;
public void setBenefitsDeduction (int empId, double deduction) {
...
Connection conn = empDB.getConnection();
...
}
...
}
@Stateless 标注表示这是一个stateless session Bean. 使用这样的标注可以让我们不再需要使用SessionBean 接口, 这样就大大的简化了EJB的实现类。 Bean的商务接口是payroll. 这是一个普通的JAVA interface. 缺省的情况,container会把他作成一个local interface. 如果需要实现一个远程接口,只需再定义一个标注 @Remote. 注意在接口里面不需要定义 RemoteExceptions,它由Container 层在后面处理掉了
2.3 引用对象的新方法
在老的EJB规范中,还有一个比较复杂的地方是访问环境对象。EJB 2.1 里面访问环境要首先在组件定义的相关性引用,比如resource-refs, EJB-refs, 然后在JNDI名字空间里面配置这些环境对象。 最后查找运行的时候JNDI空间里面查找这些环境对象
EJB 3.0对此提供了两种简化的方案:
1) 相关性注入
2) 一个简化的查询lookup方法
相关性注入是一种技术,开发人员在原代码中加入标注的一个定义,说明需要这个环境对象,然后由Container在初始化的时候把真的环境对象注入里面。
目前EJB3.0 spec里面有两种注入方式,setter注入和变量注入, 这些在以后的规范中可能会有变化,比如一种统一的注入方式。 我们可以通过注入标记@EJB来定义一个EJB的引用,也可以通过注入标记@resource 表示我们要引用一个资源,它可以是EJB以外的一切环境对象. 专家组为了尽可能的简化开发,将使用注入的对象类型作了简化。
EJB3.0 将仍然提供一个动态查询的方法,但是从程序开发人员的角度,不需要再使用JNDI API,而是采用更为简化的EJB Context 的 Lookup方法。
在新的EJB 3.0规范中,因为不再需要复杂的home接口和EJB 接口。EJB client端的编码也大大的简化了,和访问一个普通的JAVA 对象没有什么区别。上面的那个EJB2.1的例子比较起来,在EJB 3.0 里面使用一个EJB变的非常简单,只需要两行代码:
// EJB 3.0 client view
@EJB ShoppingCart myCart;
...
Collection widgets = myCart.startToShop(Widgets );
...
首先定义一个EJB的应用,然后就可以直接调用这个EJB的商务方法,剩下的工作由Container来为我们完成。
2.4 新的事务管理
EJB中的事务(Transaction)管理大大简化了用户开发程序。把应用分成一个一个小的单元,叫做transaction. 事务系统保证了这个单元里面的任务是完整的,要么全部执,要么出错后完全退回到初始状态。
J2EE中有两种类型的事务, 容器管理的和Bean管理的。他们在如何启动和结束事务上是不同的。Bean 管理的事务由组件使用 UserTransaction类显式启动和结束的。代码中需要调用方法 UserTransaction.begin() 和 UserTransaction.commit() 。Container 管理的事物是由container 自动来完成的。
针对不同的事务类型,可以定义6种不同的事务属性。
事务属性告诉 Container 是否把EJB方法里面的工作放到用户的事务里面。还是针对这个方法重新启动一个新的事务. 或者执行这个方法而不包含在事务里面,等等。
在EJB3.0规范中,我们缺省的定义是容器管理的事务. 而且针对所有的Bean方法,应用Required Transaction属性,它的意思是如果调用这个方法的应用没有Transaction Environment,那么这个方法会自动创建一个新的。
开发人员可以使用标注的方式在针对整个EJB或者某个具体的方法指定他的Transaction Attribute.
首先我们看一个工资处理的EJB的例子, 这是一个标准的EJB 3.0 Stateless Session Bean。缺省情况下,每个方法是都是由container来管理Transaction的,缺省的事务属性是required
// Uses container-managed transction, REQUIRED attribute
@Stateless public PayrollBean implements Payroll {
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empId) {...}
public void setSalary(int empId, double salary) {...}
}
下面还是这个例子,我们知道对于有关重要数据的改动,总是非常敏感的。比如我们在更改某个用户的工资的时候同时修改他的所得税,我们希望这两个调用是在同一个Transaction里面发生的。
@Stateless public PayrollBean implements Payroll {
@TransactionAttribute(MANDATORY)
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empid) {...}
@TransactionAttribute(MANDATORY)
public void setSalary(int empId, double salary) {...}
}
那么调用用户的工资改动的方法就必须在一个已经存在的Transaction Environment
中,为此,我们用@transactionattribute(mandatory)标注 这个方法必须在一个客户端的transaction中,如果客户端没有这样的一个 Transaction Context, Container会扔出来一个 Javax.ejb.EjbTransactionRequiredException 的错误信息。
2.5 新的安全机制
EJB架构不鼓励开发人员用代码的方式实现安全机制,而是采用安全角色的方法,通过定义可以访问的安全角色,来限制对某个方法的访问权限。
缺省情况,在3.0里面所有的方法都是"unchecked"。也就是说缺省情况下对所有方法是不用安全控制策略的。如果调用某个方法的客户端具有某个用户角色(Role) ,我们可以制定是否这个方法也是沿用这个角色。缺省情况下的安全策略是"Caller Identity",也就是说被调用者的角色和调用者的角色应该是一致的
这是一个EJB 3.0的安全实现的例子。我们对于其他的方法都没有设定安全策略。但是对于设定某个员工的工资是多少,这样的安全要求比较高的方法设定了只有人事部门的管理员才能调用。我们采用了一个
@RolesAllowed("HR_PayrollAdministrator ")
// Security view
@Stateless public PayrollBean implements Payroll {
public void setBenefitsDeduction(int empId, double deduction) {...}
public double getBenefitsDeduction(int empId) {...}
public double getSalary(int empid) {...}
// salary setting is intended to be more restricted
@RolesAllowed( HR_PayrollAdministrator )
public void setSalary(int empId, double salary) {...}
}
2.6 事件的通知和检查
EJB 3.0中, 开发人员不需要实现那些不必要的callback methods。 他可以把任意方法指定为一个事件通知方法。通过标记一个通知标注,我们把一个方法标记为一个回调方法, 例如:
@Stateful public class AccountManagementBean
implements AccountManagement {
Socket cs;
@PostConstruct
@PostActivate
public void initRemoteConnectionToAccountSystem {
...
}
@PreDestroy
@PrePassivate
public void closeRemoteConnectionToAccountSystem {
...
}
...
}
在这个EJB 3.0 的例子当中, 我们定义了一个有状态的session Bean,把初始化的工作都交给init remote connection方法,同时标记他为一个回调方法,在construct ,和 activite之后调用
同时我们把清理的工作都交给close remote connection方法,同时标记他为一个回调方法,在destroy 和passivate之前调用
对于那些高级的用户,需要定制自己的事件检查和侦听机制。检查方法和所被检查的对象方法可以在同一个 Bean 中,也可以在不同的JAVA Class里面。 这种检查机制有下面的特点:
在方法周围进行检查
包装商务方法的整个调用过程
可以对方法调用的参数和结果进行处理
检查类的序列中,可以拿到上下文数据
多个检查类可以按照指定的顺序执行
可以用部署表述符来指定执行顺序
我们用 @Interceptors 标注指定一个外部的检查方法类, 用 @AroundInvoke制定内部的某个方法为检查方法。在检查方法里面,我们用proceed()来调用具体的商务方法。目前的检查方法是检查一个Bean里面的所有方法,专家组正在制定标准, 让它可以具体到检查制定的某个方法。
下面是一个EJB3.0的检查方法的例子:
我们制定了这个无状态session Bean检查方法类是这三个类.
accountaudit, metrics, customsecurity。那么实际执行的时候会按照这个制定的顺序来实行
检查.
@Interceptors({
com.acme.AccountAudit.class,
com.acme.Metrics.class,
com.acme.CustomSecurity.class
})
@Stateless
public class AccountManagementBean
implements AccountManagement {
public void createAccount(int accountId, Details details) {...}
public void deleteAccount(int accountId) {...}
public void activateAccount(int accountId) {...}
public void deactivateAccount(int accountId) {...}
...
}
2.7 部署描述文件的优先级
在EJB3.0中,我们可以用标注的方法来指定对环境对象的引用,也可以用部署描述符文件(Deployment Description)的方式来制定对环境对象的应用, 也可以两个同时使用. 如果我们在部署描述文件和代码标注中都制定了环境对象,那么部署描述文件中的那个引用有更高的优先级, 这样就给了应用的Deployer相对比较大的灵活性来控制.
3 持久化的魔力
3.1 最初的目标
EJB 3.0 专家组的另外一个目标是为实体Bean( Entity Bean ) 和对象/关系的映射,提供一个轻量级的模型。实际上,目前关于实体Bean的争论很多, 很多批评人士对它的架构感到不满,我们的目标是改善EJB 容器管理的持久化模型,从各地的一些优秀的开源软件中吸取灵感,从一些反模式(Anti-Pattern)中吸取经验, 从而让新的标准称为技术上的领跑者。 EJB3.0的持续化包括下面的一些特性:
简化实体bean的编程方式,减少不必要的开发接口
改善EJB的持久化, 为O/R映射提供继承和多态的信息
可以在EJB Container之外使用实体Bean
不需要Container 就可以对商务方法进行测试
不再需要数据传输对象DTO (Data Transfer Objects) 之类的设计模式(Design Pattern)
改善的EJB QL
为了解决这些意见,ejb 3.0 的专家组集中在一个经过简化的持久化模型,目前业界已经有类似的产品和模式,比如Hibernate和Toplink, 这是一个全新的方向,代表着轻量级的对象/关系(O/R)映射模型。
在EJB3.0中,实体Bean是普通的Java 类, 这是一些真正的类, 而不是抽象类。而且更为简化的是,开发人员不再需要实现任何接口,不论是商务借口或者是回调接口。
而且不再需要实现数据传输对象(DTO), 因为现在的Entity Bean本身就是一个简单的普通JAVA 对象POJO (Plain Old Java Object), 标记为序列化之后就可以在客户端和服务端进行传递,不再需要特殊的处理。
3.2 管理类的角色
在新的EJB3.0规范里面,我们看到了一个 EntityManager 类, 对于实体Bean而言,这是一个类似于调用工厂(Factory)或者统一的Home接口之类的角色。Entity manager自己的生命周期可以由Container或者应用程序都可以来管理。
Entity Manager 将负责跟踪数据库事务上下文中, 实体bean 对象的状态。对于开发人员来说, javax.persistence.EntityManager 成为对实体bean的统一访问点。 可以把它看作是对实体Bean操作的一个”home”. 我们要通过entity manager 调用实体bean的生命周期管理。 比如:Persist, Remove, Merge, Flush, Refresh, 等方法。
Entity Manager是所有Entity Bean的持久化管理接口,任何对Entity Bean的操作都必须通过它来进行。有的开发人员会对这个接口感到熟悉,因为它与Hibernate的Session接口和JDO的Persistence Manager非常相似。Entity Manager的接口主要方法如下:
package javax.ejb;
public interface EntityManager
public void create(Object entity);
public < T > T merge(T entity);
public void remove(Object entity);
public Object find(String entityName, Object primaryKey);
public < T > T find(Class < T > entityClass, Object primaryKey);
public void flush();
public Query createQuery(String ejbqlString);
public Query createNamedQuery(String name);
public Query createNativeQuery(String sqlString);
public void refresh(Object entity);
public void evict(Object entity);
public boolean contains(Object entity);
}
EntityManager 还大大方便了查找方法的实现,记得我们在EJB2.1里面是怎么做的吗?在EJB 3.0里面,可以直接调用EntityManager.find(String entityName, Object primaryKey),查找具有某个主键的实体 bean 实例。例如:
public OrderBean findByPrimaryKey(String orderId)
{ return (OrderBean)em.find("OrderBean" , orderId);
}
EntityManager作为Query对象的生产工厂, 可以用createQuery(String ejbQlString) 创建一个EJB QL 查询,也可以用createNamedQuery(String queryName)来创建一个 NamedQuery 查询。下面是一个例子:
public List findWithAddr(String addr) {
return em.createQuery(
"SELECT o FROM Orders o WHERE o.addr LIKE :orderAddress")
.setParameter("orderAddress", addr)
.setMaxResults(100)
.listResults();
}
3.3 O/R Mapping的标注
O/R Mappings 标注的元数据,使得用户可以修饰他们的EJB3.0 Entity Bean. 在 EJB 3.0缺省环境下,表的名字就是类的名字, 两者是一致的, public Java Bean getter方法假定为访问表中同样名字的属性值, 开发人员可以通过@Table, @column等各种不同的标注来修改这个缺省的定义. 例子:
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",
pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }
@Entity 说明这是一个Entity Bean, Table标记了对应的O/R Mapping的主表, 而 SecondaryTable标记了 Entity对应的辅助表,
持续化的API, 和查询语言,以及O/R Mapping标注, 都是EJB 3.0规范的一部分。
持久化API的设计目标是能够独立于Container的运行,只需要有一个Java SE环境就可以运行了。
4 小结
本文以描述了EJB 3.0规范的一些新特性。EJB 3.0将是EJB历史上最大的一次改动,它充分吸收了一些开源项目,比如Spring、Hibernate的经验,变得更加方便实用,体现了简化开发的设计目标。这篇文章希望能够给大家带来一点关于EJB 3.0的印象,目前EJB 3.0规范已经进入了Proposed Final Draft阶段,当然,将来这个规范的技术细节还可能发生变化。
2006年1月15日博客网新系统上线?
博客网新系统能否如方老总所言,在1月15日上线,期待中!
新系统上线后,博客网的问题是否能够得到解决,拭目以待!
稳定压倒一起,如果系统还是继续不稳定下去的话,估计离大家集体出逃的日子也不远了!
个人认为,博客网应该向博友们正式道歉,目前如此之不稳定的博客网让大家极度郁闷,严重打击了众博友的积极性!
另外,后台管理系统界面不友好、操作不便捷的问题也一直没有得到解决?博客网自己人就感觉不到么?而且,应该很容易调整的,怎地就不给改呢?
人生励志32佳句
01、大多数人想要改造这个世界,但却罕有人想改造自己。
02、积极的人在每一次忧患中都看到一个机会,而消极的人则在每个机会都看到某种忧患。
03、莫找借口失败,只找理由成功。(不为失败找理由,要为成功找方法)
04、伟人之所以伟大,是因为他与别人共处逆境时,别人失去了信心,他却下决心实现自己的目标。
05、世上没有绝望的处境,只有对处境绝望的人。
06、当你感到悲哀痛苦时,最好是去学些什么东西。学习会使你永远立于不败之地。
07、世界上那些最容易的事情中,拖延时间最不费力。
08、人之所以能,是相信能。
09、一个有信念者所开发出的力量,大于99个只有兴趣者。
10、每一发奋努力的背后,必有加倍的赏赐。
11、人生伟业的建立 ,不在能知,乃在能行。
12、任何的限制,都是从自己的内心开始的。
13、含泪播种的人一定能含笑收获。
14、欲望以提升热忱,毅力以磨平高山。
15、一个能从别人的观念来看事情,能了解别人心灵活动的人,永远不必为自己的前途担心。
16、一个人最大的破产是绝望,最大的资产是希望。
17、不要等待机会,而要创造机会。
18、如果寒暄只是打个招呼就了事的话,那与猴子的呼叫声有什么不同呢?事实上,正确的寒暄必须在短短一句话中明显地表露出你对他的关怀。
19、昨晚多几分钟的准备,今天少几小时的麻烦。
20、做对的事情比把事情做对重要。
21、人格的完善是本,财富的确立是末。
22、没有一种不通过蔑视、忍受和奋斗就可以征服的命运。
23、行动是治愈恐惧的良药,而犹豫、拖延将不断滋养恐惧。
24、没有天生的信心,只有不断培养的信心。
25、只有一条路不能选择——那就是放弃的路;只有一条路不能拒绝——那就是成长的路。
26、人性最可怜的就是:我们总是梦想着天边的一座奇妙的玫瑰园,而不去欣赏今天就开在我们窗口的玫瑰。
27、征服畏惧、建立自信的最快最确实的方法,就是去做你害怕的事,直到你获得成功的经验。
28、失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。
29、让我们将事前的忧虑,换为事前的思考和计划吧!
30、再长的路,一步步也能走完,再短的路,不迈开双脚也无法到达。
31、任何业绩的质变都来自于量变的积累。
32、成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。