Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » 技术文章库  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
flat modethreaded modego to previous topicgo to next topicgo to back
作者 克服J2SE 1.3 ~ 1.4不兼容问题 (转贴)
donnan





发贴: 40
积分: 30
于 2003-10-06 22:02 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
标题 克服J2SE 1.3 ~ 1.4不兼容问题 hk2000c(翻译)

from csdn
http://www.csdn.net/develop/article/21/21351.shtm

关键字 版本兼容问题,Ant,Reflection

出处 http://www.javaworld.com/javaworld/jw-09-2003/jw-0926-overcome.html?

克服J2SE 1.3 ~ 1.4不兼容问题
--从反射API和ANT获得帮助
--------------------------------------------------------------------------------

概要
如果你要实现JavaAPI中的一个,那么可能是件比较痛苦的事情。你经常会需要实现许多交叉依赖的接口。对新特性的需求促成了升级现有的JavaAPI,这就造成了提供这些API的供应商对他们的相关实现不断的升级以维持相关功能。随着这些API的升级更改越来越频繁,API代码的不兼容使你不得不分别维护新旧版本的代码库。这直接到导致了你维护成本和难度的增加。本文演示了解决此问题的技术,揭示了如何仅使用一个代码库编译不同JavaAPI版本的代码。

--------------------------------------------------------------------------------

现在非常多的API被加入到到Java的标准库中,比如JDBC。这样做的好处是,Java可选包在部署时不必被绑定到相关的部署应用中去。这些API由专门的专业开发小组实现,在实际的使用当中这些API变得越来越受欢迎,使用的深度及广度也在不断的增加。但是有时候对一些API升级会变得使一些类及方法不可用。开发小组宁愿让这些API包成为可选组件而不是作为Java标准支持库的形式来发布。但是一旦加入标准库中的API包,就像是和用户签定了终生契约,想再成为可选包是不可能的。所以作为用户的你,可能会突然发现你一下子自己的代码库变成了不兼容的2个代码库,一个是使用新API的代码库,另一个是使用旧API的代码库。你可能会以为情况不像你想象的那样糟糕。我这里举一个简单的例子。J2SE1.4中由于对JDBC中的一些API的升级使的java.sql.Connection 不能同时被1.3 及 1.4 版本编译通过。你可能会遇到我这样的困境:我可能需要实现java.sql.Connection这个接口,但是我的代码需要同时通过1.3 及1.4 得编译。但是我不想同时维护2个版本的代码库。所以我开始寻找更好的解决方法。
如果你依赖于javac来编译你的应用的话,那么很不幸,Java著名的一次编写,到处运行(WORA)并不包括WOCA(一次编写,到处编译^_^;)。
不过别太沮丧,编码的反射技巧以及编译的Ant技巧是你能够安然过关。我能够仅仅使用一组Java文件以及Ant工具,就能使一个版本同时编译
在1.3 和1.4 版本下面。别急,在我结识解决办法之前,让我先详细的解释一下问题的描述。

可怜人的连接池(PS:Poor man's connection pool ,很有意思的一句话)
两年前,我的公司需要一个连接池,但是又不肯出钱买一个。当时并没有什么免费的东东可以使用,所以我们自己写了一个连接池。为了能更好的跟踪在整个应用中连接的情况,我们写了一个com.icentris.sql.ConnectionWrapper类,它实现了java.sql.Connection 接口以及其他的一些包装类(实现了另外的一些的java.sql 接口)。这些包装类仅仅是跟踪我们应用中的数据库使用,以及通过方法调用真正的
数据库资源。
当J2SE1.4来的时候,我们自然而然的想到升级我们提供给客户的应用,使这些应用的性能得到很多提升。当然,我们也需要保留1.3版本,因为有些客户根本不需要升级到1.4。我们气恼的发现,如果我们不修改,我们的ConnectionWrapper 以及其他JDBC封装类根本通不过J2SE1.4的编译。
为了文章的简明,我通过使用ConnectionWrapper 这个类来演示我对所有其他不能够通过J2SE1.4的类所使用的技术。如果我按照新的API标准,那么我不得不添加几个方法到ConnectionWrapper中去,接下来2个大问题摆在了面前:
1.因为我的包装类需要经历方法调用,我将不得不调用在J2SE1.3 sql类中并不存在的方法。
2.因为一些新的方法涉及到一些新出现的类,我将不得不在编译中面对那些在J2SE1.3中并不存在的类。

反射提供了援助
一些代码可以很方便的解释第一个问题。但是我的ConnectionWrapper 封装了java.sql.Connection , 所有的我的例子
依赖于在构造方法中的变量 realConnection :

private java.sql.Connection realConnection = null;

public ConnectionWrapper(java.sql.Connection connection) {
realConnection = connection;
}

为了看清楚我怎么做到解决版本不兼容问题,让我们仔细看一下setHoldability(int)(这个在J2SE1.4被声明的新方法)
public void setHoldability(int holdability) throws SQLException {
realConnection.setHoldability( holdability );
}

很不幸,这个方法在J2SE1.3中显然通不过编译,这就陷入了2难的尴尬境地。为了解决这一情况,我假定setHoldability() 将只会在J2SE1.4
下面被调用,所以我使用了反射机制来调用该方法。

public void setHoldability(int holdability) throws SQLException {
Class[] argTypes = new Class[] { Integer.TYPE };
Object[] args = new Object[] {new Integer(holdability)};
callJava14Method("setHoldability", realConnection, argTypes, args);
}

public static Object callJava14Method(String methodName, Object instance,
Class[] argTypes, Object[] args)
throws SQLException
{
try {
Method method = instance.getClass().getMethod(methodName, argTypes);
return method.invoke(instance, args );
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new SQLException("Error Invoking method (" + methodName + "): "
+ e);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new SQLException("Error Invoking method (" + methodName + "): "
+ e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new SQLException("Error Invoking method (" + methodName + "): "
+ e);
}
}

现在我有了setHoldability() 方法,因此能顺利通过J2SE1.4的编译。原理是我并不直接调用J2SE1.3中间java.sql.Connection并不存在的方法,
而是转为通过让setHoldability调用callJava14Method这个通用方法来调用,然后在一个SQLException 里封装所有的异常。这样就达到我预期的效果。
现在所有的在J2SE1.4中新方法都工作的很好,在J2SE1.3的老版本下也能顺利编译而且工作正常。现在我来着手解决第二个问题。
就是如何在应用中能够找到一个方法能够使用J2SE1.3中并不存在的新的类。

Ant 是答案
在J2SE1.4中,java.sql.Connection 依赖于一个新的类java.sql.Savepoint。因为这个类在java.sql 包中,所以你不可能把它加入到J2SE1.3中去。Java不允许任何的第三方扩展包加入它的核心包(java.* 以及 javax.* )中去。 因此挑战来了,在J2SE1.4下调用这个新的java.sql.Savepoint 类,但同时需要代码能够在J2SE1.3下面得到编译以及能够运行。很简单,不是吗?所有回答"Yes"的人都会得到一个榛仁巧克力饼(PS:哈哈,我回答了,可是没有:P)。至少现在我找到了答案,使问题变得很简单了。
首先我插入了下面一条有条件的import语句
// Comment_next_line_to_compile_with_Java_1.3
import java.sql.Savepoint;

然后我找到了一个能够在J2SE1.3下面注释掉import的方法。非常简单,使用如下Ant 语句就可以了:
<replace>
<replacetoken>Comment_next_line_for_Java_1.3
</replacetoken>
<replacevalue>Comment_next_line_for_Java_1.3
//</replacevalue>
</replace>

这个Ant 的 replace 标签 有好几个标签选项,在以后我给出的全部例子里有很多。在这里面最重要的是使用<replacevalue>来替换<replacetoken> 。
在XML里面的意思是换行。在J2SE1.4下,没什么会发生, 但是在J2SE1.3下面一个import声明被注释掉了。
// Comment_next_line_to_compile_with_Java_1.3
//import java.sql.Savepoint;

但是我在代码中Savepoint仍在使用public Savepoint setSavepoint(String name) throws SQLException { . . .}。不过我只在J2SE1.4使用这些方法类,在J2SE1.3中只要能编译就可以了。我发现只要我有一个我自己的Savepoint 类在我的包中,我的代码就能够通过编译,而且不用任何的import包。但是我又要同时在这条import 语句不被注释的同时我自己的Savepoint类被忽略掉。因此我造了一个空的com.icentris.sql.Savepoint类,这个可能(除了JavaDoc)是最短的有效类:
package com.icentris.sql;

/** Dummy class to allow ConnectionWrapper to implement java.sql.Connection
* and still compile under J2SE 1.3 and J2SE 1.4. When compiled
* under J2SE 1.3, this class compiles as a placeholder instead of the
* missing java.sql.Savepoint (not in J2SE 1.3). When compiled
* under J2SE 1.4, this class is ignored and ConnectionWrapper uses the
* java.sql.Savepoint that is new in J2SE 1.4.
*/
public class Savepoint {}

在J2SE1.4下我能够正确的import java.sql.Savepoint类,而在J2SE1.3下面Ant注释了这条import语句。因此这个Savepoint就被替换成了我这个包里面写的一个空的Savepoint类。所以我现在就能加入任何引用到Savepoint类的方法,同样的在这些新方法中使用刚才所说的反射方法。
// Comment_next_line_to_compile_with_Java_1.3
import java.sql.Savepoint;

. . .
public Savepoint setSavepoint() throws SQLException {
Class[] argTypes = new Class[0];
Object[] args = new Object[0];
return (Savepoint) callJava14Method("setSavepoint", realConnection,
argTypes, args);
}

public Savepoint setSavepoint(String name) throws SQLException {
Class[] argTypes = new Class[] { String.class };
Object[] args = new Object[] { name };
return (Savepoint) callJava14Method("setSavepoint", realConnection,
argTypes, args);
}

public void rollback(Savepoint savepoint) throws SQLException {
Class[] argTypes = new Class[] { Savepoint.class };
Object[] args = new Object[] { savepoint };
callJava14Method("rollback", realConnection, argTypes, args);
}

public void releaseSavepoint(Savepoint savepoint) throws SQLException {
Class[] argTypes = new Class[] { Savepoint.class };
Object[] args = new Object[] { savepoint };
callJava14Method("releaseSavepoint", realConnection, argTypes, args);
}

现在我所要做的就是能够使Ant 识别 J2SE1.3版,然后能够使这条import 语句被注释掉。
<target name="compile">
<antcall target="undoJava13Tweaks" />
<antcall target="doJava13Tweaks" />
<javac srcdir="src" destdir="WEB-INF/classes" debug="on">
<classpath>
<fileset dir="WEB-INF/lib">
<include name="*.jar"/>
</fileset>
</classpath>
</javac>
<antcall target="undoJava13Tweaks" />
</target>

<target description="Find out if we're being compiled on Java 1.3"
name="isJava13">
<echo message="java.specification.version=[${java.specification.version}]"/>
<condition property="isJava13">
<equals arg1="${java.specification.version}" arg2="1.3" />
</condition>
</target>

<target description="There are a couple tweaks I have to do to compile under Java 1.3"
name="doJava13Tweaks" depends="isJava13" if="isJava13">
<echo message="This is Java 1.3, doing Tweaks!" />
<replace dir="src/com/icentris/" summary="true">
<include name="sql/ConnectionWrapper.java" />
<replacetoken>Comment_next_line_for_Java_1.3
</replacetoken>
<replacevalue>Comment_next_line_for_Java_1.3
//</replacevalue>
</replace>
</target>

<target description="Let's undo Java 1.3 tweaks" name="undoJava13Tweaks">
<replace dir="src/com/icentris/" summary="true">
<include name="sql/ConnectionWrapper.java" />
<replacetoken>Comment_next_line_for_Java_1.3
//</replacetoken>
<replacevalue>Comment_next_line_for_Java_1.3
</replacevalue>
</replace>
</target>

注意编译目标在调用doJava13Tweaks的前后都调用了undoJava13Tweaks。如果万一javac编译失败的话,我们可以恢复以前的编译版本。

你没有必要同时维护2个应用实现
对于Java来说,新的API升级所带来的新的方法以及新的类/接口并不是新鲜事。一般而言,加入的新方法以及新的类的同时,会考虑到向上兼容的问题来照顾老API用户。但是当升级的API属于Java核心包内时,就会很麻烦。因为Java不允许对这些核心包的任何的外在更改或者是增加。通常这会引起针对不同版本API而维护不同版本代码树的需要。但是,就像上面的例子所演示的那样,你只要维护一棵代码树就能够在不同的版本的API下,编译运行。这个反射的API允许你调用并不存在的方法,而Ant能通过识别不同的Java编译版本而对相应的import包进行调整。虽然上面的所举的例子仅仅是一个简单的演示,但是在实际工作当中,利用这些简单的技术,解决了许多J2SE1.4和J2SE1.3的版本问题。我相信通过这些技术,你可以在频繁的Java版本升级中不必为同时维护两棵代码库而烦恼。

--------------------------------------------------------------------------------

关于作者:
Sam Mefford是iCentris的首席架构设计师。对于系统的兼容性重视程度,Sam Mefford是放在第一位的。他带领的团队致力于使用一个代码库
向众多的客户公司提供应用发布方案。这些部署方案使用的应用服务器有Tomcat,Weblogic, Resin, Orion以及 Websphere;在数据库方面有
Oracle,PostgreSQL, MySQL,以及 Informix;以及多个Java运行期环境。

译者: SpikeWang (CSDN ID:hk2000c)

东华大学计算机系毕业,现在同济大学攻读软件工程硕士学位。致力于J2EE方面的企业级应用开发以及研究工作。

About Copyright:
原文章版权属于作者 Sam Mefford
译文版权属于译者及原文作者共同所有,欢迎转载,但要注上译者及原文作者。

--------------------------------------------------------------------------------

参考资源:
The API for java.sql.Connection (J2SE 1.3):
http://java.sun.com/j2se/1.3/docs/api/java/sql/Connection.html

The API for java.sql.Connection (J2SE 1.4):
http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Connection.html

The JDBC API:
http://java.sun.com/products/jdbc/

Java Core Reflection参考概要:
http://java.sun.com/j2se/1.3/docs/guide/reflection/spec/java-reflectionTOC.doc.html

the Reflection API指南:
http://java.sun.com/docs/books/tutorial/reflect/

The Javadoc (java.lang.reflect):
http://java.sun.com/j2se/1.3/docs/api/java/lang/reflect/package-summary.html


palatum edited on 2003-10-08 18:14

I'll.be.BACK!...

作者 Re:克服J2SE 1.3 ~ 1.4不兼容问题 (转贴) [Re:donnan]
zwed2000



发贴: 0
积分: 0
于 2003-10-30 10:43 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
Thanks



flat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923