posts - 40,  comments - 7,  trackbacks - 0
 
The Grammar of the Java Programming Language
Identifier:
        IDENTIFIER

QualifiedIdentifier:
        Identifier { . Identifier }

Literal:
        IntegerLiteral
        FloatingPointLiteral
        CharacterLiteral
        StringLiteral
        BooleanLiteral
        NullLiteral

Expression:
        Expression1 [AssignmentOperator Expression1]]

AssignmentOperator:

        =
        +=
        -=
        *=
        /=
        &=
        |=
        ^=
        %=
        <<=
        >>=
        >>>=

Type:
        Identifier [TypeArguments]{   .   Identifier [TypeArguments]} {
[]}
        BasicType

TypeArguments:
        < TypeArgument {, TypeArgument} >

TypeArgument:
        Type
       
? [( extends |super ) Type]

StatementExpression:
        Expression

ConstantExpression:
        Expression

Expression1:
        Expression2 [Expression1Rest]

Expression1Rest:
        ?   Expression  :   Expression1

Expression2 :
        Expression3 [Expression2Rest]

Expression2Rest:
        {InfixOp Expression3}
        Expression3 instanceof Type

InfixOp:

        ||
        &&
        |
        ^
        &
        ==
        !=
        <
        >
        <=
        >=
        <<
        >>
        >>>
        +
        -
        *
        /
        %

Expression3:
        PrefixOp Expression3
       
(   Expression | Type  )   Expression3
        Primary {Selector} {PostfixOp}

Primary:
        ParExpression
        NonWildcardTypeArguments (ExplicitGenericInvocationSuffix
| this
Arguments)

  this[Arguments]
  superSuperSuffix
        Literal

  newCreator
        Identifier { . Identifier }[ IdentifierSuffix]
        BasicType {
[]} .class
   void.class

IdentifierSuffix:
        [ ( ] {[]} .   class | Expression ])
        Arguments
        .   (
class | ExplicitGenericInvocation | this | superArguments | new
[NonWildcardTypeArguments] InnerCreator )

ExplicitGenericInvocation:
        NonWildcardTypeArguments ExplicitGenericInvocationSuffix

NonWildcardTypeArguments:
        < TypeList >


ExplicitGenericInvocationSuffix:
     super SuperSuffix
        Identifier Arguments


PrefixOp:

        ++
        --
        !
        ~
        +
        -

PostfixOp:

        ++
        --

Selector: Selector:
        . Identifier [Arguments]
        . ExplicitGenericInvocation
        .
this
   .super SuperSuffix
        . new [NonWildcardTypeArguments] InnerCreator
        [ Expression ]

SuperSuffix:
        Arguments
        . Identifier [Arguments]

BasicType:

  byte
  short
  char
  int
  long
  float
  double
  boolean

Arguments:
        ( [Expression { , Expression }] )

Creator:
        [NonWildcardTypeArguments] CreatedName ( ArrayCreatorRest  |
ClassCreatorRest )

CreatedName:
        Identifier [NonWildcardTypeArguments] {. Identifier
[NonWildcardTypeArguments]}

InnerCreator:
        Identifier ClassCreatorRest

ArrayCreatorRest:
       
[ ( ] {[]} ArrayInitializer | Expression ] {[ Expression ]} {[]} )

ClassCreatorRest:
         Arguments [ClassBody]

ArrayInitializer:
        { [VariableInitializer {, VariableInitializer} [,]] }

VariableInitializer:
        ArrayInitializer
        Expression

ParExpression:
        ( Expression )

Block:
        { BlockStatements }

BlockStatements:
        { BlockStatement }

BlockStatement :
        LocalVariableDeclarationStatement
        ClassOrInterfaceDeclaration
        [Identifier :] Statement

LocalVariableDeclarationStatement:
        [
final] Type VariableDeclarators   ;

Statement:
        Block
       
assert Expression [ : Expression] ;
     if ParExpression Statement [else Statement]
     for ( ForControl ) Statement
     while ParExpression Statement
     do Statement while ParExpression   ;
     try Block ( Catches | [Catches] finally Block )
     switch ParExpression { SwitchBlockStatementGroups }
     synchronized ParExpression Block
     return [Expression] ;
     throw Expression  ;
     break [Identifier]
     continue [Identifier]
       
;
        StatementExpression
;
        Identifier  
:   Statement

Catches:
        CatchClause {CatchClause}

CatchClause:
     catch ( FormalParameter ) Block

SwitchBlockStatementGroups:
        { SwitchBlockStatementGroup }

SwitchBlockStatementGroup:
        SwitchLabel BlockStatements

SwitchLabel:
     case ConstantExpression   :
       
case EnumConstantName :
        default   :

MoreStatementExpressions:
        { , StatementExpression }

ForControl:
        ForVarControl
        ForInit;   [Expression]   ; [ForUpdate]

ForVarControl
        [
final] [Annotations] Type Identifier ForVarControlRest

Annotations:
        Annotation [Annotations]

Annotation:
       
@ TypeName [( [Identifier =] ElementValue)]

ElementValue:
        ConditionalExpression
        Annotation
        ElementValueArrayInitializer

ConditionalExpression:
        Expression2 Expression1Rest

    ElementValueArrayInitializer:
       
{ [ElementValues] [,] }

    ElementValues:
        ElementValue [ElementValues]

ForVarControlRest:
        VariableDeclaratorsRest;   [Expression]   ;   [ForUpdate]
        : Expression

ForInit:
        StatementExpression Expressions

Modifier:
  Annotation

  public
  protected
  private
  static
  abstract
  final
  native
  synchronized
  transient
  volatile
        strictfp

VariableDeclarators:
        VariableDeclarator { ,   VariableDeclarator }

VariableDeclaratorsRest:
        VariableDeclaratorRest { ,   VariableDeclarator }

ConstantDeclaratorsRest:
        ConstantDeclaratorRest { ,   ConstantDeclarator }

VariableDeclarator:
        Identifier VariableDeclaratorRest

ConstantDeclarator:
        Identifier ConstantDeclaratorRest

VariableDeclaratorRest:
        {
[]} [  =   VariableInitializer]

ConstantDeclaratorRest:
        {
[]} =   VariableInitializer

VariableDeclaratorId:
        Identifier {
[]}

CompilationUnit:
        [[Annotations]
package QualifiedIdentifier   ;  ] {ImportDeclaration}
{TypeDeclaration}

ImportDeclaration:
     import [ static] Identifier {   .   Identifier } [   .     *   ] ;

TypeDeclaration:
        ClassOrInterfaceDeclaration
        ;

ClassOrInterfaceDeclaration:
        {Modifier} (ClassDeclaration
| InterfaceDeclaration)

ClassDeclaration:
        NormalClassDeclaration
        EnumDeclaration

NormalClassDeclaration:
     class Identifier [TypeParameters] [extends Type] [implements TypeList]
ClassBody

TypeParameters:
        < TypeParameter {, TypeParameter} >

TypeParameter:
        Identifier [
extends Bound]

Bound:
         Type {& Type}


EnumDeclaration:
       
enum Identifier [implements TypeList] EnumBody

EnumBody:
        { [EnumConstants] [,] [EnumBodyDeclarations] }

EnumConstants:
        EnumConstant
        EnumConstants , EnumConstant

EnumConstant:
        Annotations Identifier [Arguments] [ClassBody]

EnumBodyDeclarations:
        ; {ClassBodyDeclaration}

InterfaceDeclaration:
        NormalInterfaceDeclaration
        AnnotationTypeDeclaration

NormalInterfaceDeclaration:
     interface Identifier [ TypeParameters] [extends TypeList] InterfaceBody

TypeList:
        Type {  ,   Type}

AnnotationTypeDeclaration:
       
@ interface Identifier AnnotationTypeBody

    AnnotationTypeBody:
        { [AnnotationTypeElementDeclarations] }

    AnnotationTypeElementDeclarations:
        AnnotationTypeElementDeclaration
        AnnotationTypeElementDeclarations AnnotationTypeElementDeclaration

AnnotationTypeElementDeclaration:
        {Modifier} AnnotationTypeElementRest

AnnotationTypeElementRest:
         Type Identifier AnnotationMethodOrConstantRest;
        ClassDeclaration
        InterfaceDeclaration
        EnumDeclaration
        AnnotationTypeDeclaration

        AnnotationMethodOrConstantRest:
        AnnotationMethodRest
        AnnotationConstantRest

AnnotationMethodRest:
        ( ) [DefaultValue]

AnnotationConstantRest:
        VariableDeclarators


    DefaultValue:
       
default ElementValue

ClassBody:
       
{ {ClassBodyDeclaration} }

InterfaceBody:
       
{ {InterfaceBodyDeclaration} }

ClassBodyDeclaration:
       
;
        [
static] Block
        {Modifier} MemberDecl

MemberDecl:
        GenericMethodOrConstructorDecl
        MethodOrFieldDecl
       
void Identifier VoidMethodDeclaratorRest
        Identifier ConstructorDeclaratorRest
        InterfaceDeclaration
        ClassDeclaration

GenericMethodOrConstructorDecl:
        TypeParameters GenericMethodOrConstructorRest

GenericMethodOrConstructorRest:
        (Type
| void) Identifier MethodDeclaratorRest
        Identifier ConstructorDeclaratorRest

MethodOrFieldDecl:
        Type Identifier MethodOrFieldRest

MethodOrFieldRest:
        VariableDeclaratorRest
        MethodDeclaratorRest

InterfaceBodyDeclaration:
       
;
        {Modifier} InterfaceMemberDecl

InterfaceMemberDecl:
        InterfaceMethodOrFieldDecl
        InterfaceGenericMethodDecl
       
void Identifier VoidInterfaceMethodDeclaratorRest
        InterfaceDeclaration
        ClassDeclaration

InterfaceMethodOrFieldDecl:
        Type Identifier InterfaceMethodOrFieldRest

InterfaceMethodOrFieldRest:
        ConstantDeclaratorsRest ;
        InterfaceMethodDeclaratorRest

MethodDeclaratorRest:
        FormalParameters {
[]} [throwsQualifiedIdentifierList] ( MethodBody |   ;
)

VoidMethodDeclaratorRest:
        FormalParameters [
throws QualifiedIdentifierList] ( MethodBody |   ;  )

InterfaceMethodDeclaratorRest:
        FormalParameters {
[]} [throwsQualifiedIdentifierList]  ;

InterfaceGenericMethodDecl:
        TypeParameters (Type
| void) Identifier InterfaceMethodDeclaratorRest

VoidInterfaceMethodDeclaratorRest:
        FormalParameters [
throws QualifiedIdentifierList]   ;

ConstructorDeclaratorRest:
        FormalParameters [
throws QualifiedIdentifierList] MethodBody

QualifiedIdentifierList:
        QualifiedIdentifier {  ,   QualifiedIdentifier}

FormalParameters:
        ( [FormalParameterDecls] )

FormalParameterDecls:
        [
final] [Annotations] Type FormalParameterDeclsRest]

FormalParameterDeclsRest:
        VariableDeclaratorId [ , FormalParameterDecls]
        ... VariableDeclaratorId

MethodBody:
        Block

EnumConstantName:
        Identifier
posted @ 2006-08-18 09:00 Lansing 阅读(372) | 评论 (0)编辑 收藏
Java Web 应用

1. Java介绍 
1.1 什么是Java 
Java有两方面的含义:Java语言、Java平台 

作为一种语言,Java是一种跨平台
开发语言 ,能开发出跨平台的应用对象和应用程序。例如: Oracle  8i的安装程序就是用Java开发的。Java语言具有以下特点:简单、面向对象、分布式、解释执行、安全、 跨平台、高性能、多 线程 等特点。 
作为一种平台,Java平台包括两部分内容:Java虚拟机和Java API。 
1.2 Java之最 
最好的集成开发工具 
No1. Visualage For Java (www.ibm.com) 

No2. 
JBuilder  3 (www.inprise.com) 

No3. JDeveloper (www.oracle.com) 

No4. VisualCafe 

我推荐使用emacs 

最好的
应用服务器  
No1. WebObjects (www.apple.com) 

No2. WebSphere Application Server Ent
ERP rise Edition v3.0 (www.ibm.com) 

No3. Apache JServ (java.apache.org) 

No4. WebLogic Server 

我推荐使用tomcat (jakarta.apache.org), Enhydra (www.enhydra.org) 

最好的Java类库 
No1. The Java Collections (www.sun.com) 

No2. IBM Host 
Access  Library API For Java (www.ibm.com) 

No3. JClass Enterprise (www.klgroup.com) 

No4. JGL 

最好的Java中间件 
No1. 
Sybase  Enterprise Application Server (www.sybase.com) 

No2. WebSphere Host On-Demand v4 (www.ibm.com) 

No3. Progress Sonicmq (www.sonicmq.com) 

No4. Visiobroker 

我推荐使用cocoom (
XML .apache.org),  Struts  (jakarta.apache.org), turbine (java.apache.org) 

最好的Java组件 
No1. Bea Jumpstart eBusiness Smart Components (www.bea.com) 

No2. JClass Enterprise Suite (www.klgroup.com) 

No3. LingoGUI (www.slangsoft.com) 

No4. Stdioj 

最好的
数据库 产品 
No1. Oracle 8i (www.oracle.com) 

No2. Sybase Adaptive Server Anywhere (www.sybase.com) 

No3. Cloudscape (www.informix.com) 

No4. JDataStore 

我推荐使用PostgreSQL 

最好的Java虚拟机 
No1. Java HotSpot Performance Engine (java.sun.com) 

No2. ChaiVM (www.hewlett-packard.com) 

No3. JSCP (www.nsicom.com) 

No4. Jeode Platform 

最好的消息工具 
No1. Java Message Queue (www.sun.com) 

No2. SonicMQ (www.sonicmq.com) 

No3. FioranoMQ (www.fiorano.com) 

No4. IBus 

以上排名来自JDJ Nov 2000统计数据。 

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

2. ANT 介绍 
Ant是一个基于java的build工具。大家都知道,现在已经有了许多的build工具,例如make、gnumake、nmake、jam等,而且这些工具都非常优秀。那我为什么还要给大家介绍Ant这个新工具呢? 因为Ant是一个跨平台的Build工具。之所以Ant能跨平台,是因为Ant不再需要你编写shell命令,Ant的配置文件是基于XML的任务树,能让你运行各种各样的任务,任务的运行是由实现了特定任务接口的对象来完成的。 

2.1 获得Ant 
Binary版:最新的稳定版的
下载 地址是:http://jakarta.apache.org/builds/ant/release/v1.1/bin。 

Source版:最新的
源码 稳定版下载地址是:http://jakarta.apache.org/builds/ant/release/v1.1/src/。如果你想获得最新的源码,地址是:http://jakarta.apache.org/from-cvs/jakarta-ant/ 

2.2 系统要求 
需要在CLASSPATH中包含与JAXP兼容的XML parser,才能编译和使用Ant。 

我给大家推荐一个XML parser:xerces,下载地址:http://xml.apache.org/xerces 

当然,
JDK 肯定是必需的,并且是1.1或之后版本。 

2.3 编译Ant 
1. 进入jakarta-ant目录 

2. 将JDK加入到你的PATH环境变量中 

3. 设置JAVA_HOME环境变量,指向你的JDK安装目录 

4. 运行bootstrap.sh角本文件 

5. 运行以下命令创建Ant的Binary版: 

           build.sh -Dant.dist.dir=<安装Ant的目录> dist


2.4 安装Ant 
1. 设置ANT_HOME环境变量,指向你的Ant目录 

2. 设置JAVA_HOME环境变量,指向你的JSK目录 

3. 将ANT_HOME/bin加入PATH环境变量中 

4. 将ant.jar和xerces.jar加入到CLASSPATH环境变量中 

假设Ant安装在/usr/local/ant目录,可通过以下方法进行设置: 

           export ANT_HOME=/usr/local/ant
           export JAVA_HOME=/usr/local/jdk-1.2.2
           export PATH=${ANT_HOME}/bin:${PATH}
           export CLASSPATH=${ANT_HOME}/lib/ant.jar:/lib/xerces.jar:${CLASSPATH}


2.5 运行ant 
运行Ant非常简单,如果你按照上面描述的方法安装了ant,只需在命令行键入ant就行了。 

当你不带任何参数运行ant时,Ant会在当前目录找一个名叫build.xml的文件。如果找到了,就将该文件作为build配置文件。如果没找到,它会自动地查找上级目录,一直找到根目录。也可以通过命令行参数 -buildfile  来指定其他配置文件,其中,是你要采用的配置文件名。 

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

3. 
JSP 介绍 
JSP是JavaServer Pages的简写。JSP技术能让Web开发员和网页设计员快速地开发容易维护的动态Web主页。 

用JSP开发的Web应用是跨平台的,即能在
Linux 下运行,也能在其他 操作系统 上运行。 

JSP技术使用Java编程语言编写类XML的tags和scriptlets,来封装产生动态网页的处理逻辑。网页还能通过tags和scriptlets访问存在于服务端的资源(例如JavaBesns)的应用逻辑。JSP将网页逻辑与网页设计和显示分离,支持可重用的基于组件的设计,使基于Web的应用程序的开发变得迅速和容易。 

JSP技术是
Servlet 技术的扩展。Servlet是平台无关的,100%纯Java的Java服务端组件。 


3.1 JSP与jakarta 
正是因为Jakarta项目组的努力,才使Servlet/JSP据有了前所未有的动力。 

从Servlet2.2开始,Sun公司已放弃了对Servlet的控制,全权交由Jakarta项目组进行开发和维护。Sun公司不再提供Servlet/JSP的开发包,而是将软件下载全部链接到Jakarta站点,并全力支持Jakarta项目组的开发工作(Jakarta项目组中有部分骨干力量是Sun公司员工)。这是Sun公司的英明决策,也是开源软件的重大成果。 

JSP技术是jakarta所提供的两大模板技术(JSP和Velocity)之一,这两个都是非常好的模板技术。jakarta的
Framework (例如struts、slide)对JSP提供了很好的支持,java.apache的framework(turbine)对Velocity提供了很好的支持。 


3.2 运行自己的JSP文件 

有些网友不知道怎么才能运行自己的JSP文件,我在这里简单介绍一下,给大家提供一点参考:
1. 下载并安装tomcat。下载地址:http://jakarta.apache.org/tomcat
2. 编写自己的JSP网页和Java对象。
3. 配置自己的Web应用。配置方法:
    在TOMCAT_HOME/conf/server.xml文件中加入一行:

    其中,TOMCAT_HOME是tomcat的主目录,appName是你的Web应用的名称。
4. 将你的jsp文件、html文件、image文件拷贝到TOMCAT_HOME/webapps/appName目录下。
5. 编译你的java文件。
6. 将编译好的class文件拷贝到TOMCAT_HOME/webapps/WEB-INF/classes目录下。也可将class文件打包成jar文件放到TOMCAT_HOME/webapps/WEB-INF/lib目录下。
7. ALL IS OK! 你可以在你的浏览器上看到你的成果了:
    http://localhost:8080/appName/youjsp.jsp
    其中,appName是你配的Web应用名称,youjsp.jsp是你编写的jsp文件名。

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

4. Tomcat介绍 
在我写了一些有关Struts和Framework的贴子后,有人问我什么是tomcat,什么是jakarta。我才发现我应该先写一些更基本的东西。这篇是介绍tomcat的文章,我还准备写一篇介绍jakarta的文章。 


4.1 什么是Tomcat 
Tomcat是Java Servlet 2.2和JavaServer Pages 1.1技术的标准实现,是基于Apache许可证下开发的自由软件。 


4.2 Tomcat下载 
下载地址:http://jakarta.apache.org/downloads/binindex.html 


4.3 Tomcat和JServ的区别 
JServ是由Apache开发并使用的Servlet API 2.0兼容的Servlet容器。Tomcat是完全重写的Servlet API 2.2和JSP 1.1兼容的Servlet/JSP容器。Tomcat使用了JServ的一些代码,特别是Apache服务适配器。 


4.4 什么是servlets,什么是JSPs 
Servlet为Web开发员提供了一个简单、一致的机制,来扩展Web
服务器 的功能,并且和已有的业务系统交互。可以把Servlet看作在服务端运行的 Applet 。 

JSP技术是servlet技术的扩展,对HTML和XML的页面创作提供支持。它网页设计员能非常容易地将固定或静态的模板数据与动态内容进行组合。 

--------------------------------------------------------------------------------
我把近一段时间我在论坛中所发的贴子进行了一下整理,形成这篇文档,以便大家参考,欢迎提出宝贵意见。 我在写作本文原搞时采用的是sgml格式,通过SGML-Tools转成你所看到的格式。SGML-Tools是一组文本格式化工具,能将简单的sgml文档转变为格式丰富的各种文件,包括HTML、TeX、DVI、PostScript、plain text、groff等。感谢SGML-Tools的所有开发员所作出的贡献。 本文版权归 中文Linux论坛所有。 
--------------------------------------------------------------------------------
5. servlet/jsp/xml Frameworks介绍
5.1 Cocoon - 基于XML的Web内容发布 
5.2 Xang - 快速开发动态网页 
5.3 Slide - 内容管理框架 
5.4 Struts - 基于M
VC 设计模式 的JSP 
5.5 Jetspeed - 基于Web的组件 
5.6 Turbine - 基于Servlet的Web应用开发 
5.7 各种Framework比较 

6. JSP Framework - Struts介绍
6.1 1、什么是MVC模式。 
6.2 2、是否所有JSP应用都该采用MVC模式? 
6.3 3、Struts中能做XML吗,与Cocoon相比有何优点? 

7. Struts的安装
7.1 安装Struts需要的软件 
7.2 通过源码构造Structs 
7.3 通过Struts的二进制发布包安装Structs 
--------------------------------------------------------------------------------

5. servlet/jsp/xml Frameworks介绍 
现在,已经有众多的基于Java的开源Web Framework,让我们能更加容易构造Web应用。我在这里给大家作一个简单的介绍。 


5.1 Cocoon - 基于XML的Web内容发布 

Cocoon是采用100%纯Java编写的一个内容发布框架。Cocoon让你能采用W3C的最新技术(DOM、XML、XSL)来提供Web内容。
新的Cocoon模式能将文档内容、样式、处理逻辑进行完全的分离,允许这三层能独立地设计、创建和管理,从而减少了管理开销,加强了工作的重用性,减少了开发时间。
下载地址:http://xml.apache.org/cocoon



5.2 Xang - 快速开发动态网页 
Xang能整合不同的数据源,让你能快速地开发数据
驱动 的、跨平台的Web应用。Xang体系结构能将数据、逻辑和表示完全划清。Xang基于开放的工业标准,例如HTTP、XML、XSL、DOM、ECMAScript(JavaScrip)。 

下载地址:http://xml.apache.org/xang 


5.3 Slide - 内容管理框架 
Slide是一个内容管理和集成系统,是一个内容管理底层框架。Slide提供了一个分级的结构,能将内容存储到任意的、分布式的
数据仓库 。出此之外,Slide还集成了安全、锁定、内容版本和其他一些服务。 

下载地址:http://jakarta.apache.org/slide 


5.4 Struts - 基于MVC设计模式的JSP 

Struts是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。
采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。
Struts有如下的主要功能:
1. 包含一个controller servlet,能将用户的请求发送到相应的Action对象。
2. JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。
3. 提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。
下载地址:http://jakarta.apache.org/struts



5.5 Jetspeed - 基于Web的组件 
Jetspeed是实现了Enterprise Information Portal的开源软件。Jetspeed能从Internet的纵多资源中提取信息,来帮助用户管理大量的数据。这些信息能来自不同的内容类型,从XML到XMTP,到iCalendar这些新协议。 

下载地址:http://java.apache.org/jetspeed 


5.6 Turbine - 基于Servlet的Web应用开发 

Turbine是基于servlet的framework,使有经验的Java开发员能快速地构建web应用。
使用Turbine,可以通过创建使用特定服务来处理模板的Screen,来集成现有的模板技术(例如Velocity、Webmacro、Java Server Pages(JSP)、FreeMarker、Cocoon)。
下载地址:http://java.apache.org/turbine



5.7 各种Framework比较 

在这些framework中,我觉得Cocoon、Struts和Turbine比较好。这三者各有所长,Cocoon是最好的XML Framework,Struts是最好的JSP Framework,Turbine是最好的Servlet Framework。


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

6. JSP Framework - Struts介绍 
Struts是采用Java Servlet/JavaServer Pages技术,开发Web应用程序的开放源码的framework。 

采用Struts能开发出基于MVC(Model-View-Controller)设计模式的应用构架。 

Struts有如下的主要功能: 

1. 包含一个controller servlet,能将用户的请求发送到相应的Action对象。 

2. JSP自由tag库,并且在controller servlet中提供关联支持,帮助开发员创建交互式表单应用。 

3. 提供了一系列实用对象:XML处理、通过Java reflection APIs自动处理JavaBeans属性、国际化的提示和消息。 

Struts是Jakarta项目的一部分,主页在http://jakarta.apache.org/struts. 

Version 0.5的下载地址:http://jakarta.apache.org/builds/jakarta-struts/release/v0.5 


6.1 1、什么是MVC模式。 
MVC(Model/View/Controller)模式是国外用得比较多的一种设计模式,好象最早是在Smaltalk中出现。MVC包括三类对象。Model是应用对象,View是它在屏幕上的表示,Controller定义用户界面对用户输入的响应方式。 

6.2 2、是否所有JSP应用都该采用MVC模式? 
不一定所有的JSP应用都该采用MVC模式。但对于大型应用来说,我认为还是该采用MVC模式。不使用MVC模式,用户界面界面设计往往将这些对象混在一起,而MVC则将它们分离以提高灵活性和复用性。 

6.3 3、Struts中能做XML吗,与Cocoon相比有何优点? 
Struts把主要精力放在JSP上了。Cocoon才是专业级的XML Framework。 


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

7. Struts的安装 
7.1 安装Struts需要的软件 

Java Development Kit - 你需要download和install 1.2(或之后)版本的JDK。下载地址:http://java.sun.com/
J2SE
Servlet Container - 通常的选择是下载Tomcat(至少是3.1版,推荐使用3.2版)。下载地址:http://jakarta.apache.org/tomcat
Ant Build System - 如果你通过Struts源码发布包安装,你必须下载1.1或之后版本的ant build system。在你通过Struts来开发你自己的Web应用程序是,我也推荐使用ant来build你的应用。下载地址:http://jakarta.apache.org/ant
Servlet API Classes - 为了编译Structs自己,或应用程序使用Struts,你需要一个包含Servlet和JSP API对象的servlet.jar包。大多数Servlet container(例如Tomcat)已经自带了这个文件。否则,你必需下载:http://jakarta.apache.org/builds/jakarta-servletapi
XML Parser - Structs需要一个与Java API for XML Parsing(JAXP)规格兼容的XML处理器。我推荐使用Xerces。下载地址:http://xml.apache.org/xerces-j
Xalan XSLT Processor - 如果你通过Structs源码发布版来构造你的Structs系统,你必须下载和安装1_2_D01或之后版本的Xalan XSLT处理器(Xerces中已自带Xalan)。这个处理器用于将基于XML的Structs文档转换为Html文档。



7.2 通过源码构造Structs 

1.下载Structs的源码发布包。
2.设置ANT_HOME环境变量,指向你的Ant目录。
3.设置JAVA_HOME环境变量,指向你的JDK目录。
4.设置SERVLETAPI_HOME环境变量,指向你的Servlet API目录(如果你的CLASSPATH已经包含了servlet.jar,就不需要指定该目录)
5.将Structs的源码发布包进行解包。
6.进入Structs目录,运行以下命令:
   ./build.sh dist
该命令将创建Struts的二进制发布包,目录在../dist/structs(相对于你的编译目录)。



7.3 通过Struts的二进制发布包安装Structs 

1.下载Struts的二进制发布版。
2.将Struts的二进制发布版进行解包。(如果你是通过Struts源码构造Struts,build的结果就已经是已解包的Struts)。解包后的Struts包含以下内容:
   lib/struts.jar - 这个文件包含了Struts的所有Java对象。你需要把它拷贝到你的Web应用的WEB-INF/lib目录。
   lib/structs.tld - 这是一个"tag library descriptor"文件,它描述了Struts库的自由tag。需要将它拷贝到你的Web应用的WEB-INF目录。
   webapps/struts-documentation.war - 这是一个"web application archive"文件,包含了所有的Struts文档。你可以将它安装到支持Servlet API 2.2或之后版本的servlet container(推荐使用tomcat)中。
   webapps/struts-example.war - 这是一个web应用实例,它广泛地演示了Struts的许多功能。你可以将它安装到兼容Servlet2.2或之后版本以及JSP1.1或之后版本规范的servlet容器中(推荐使用tomcat)。
   webapps/struts-test.war - 这个web应用包含了Struts支持的许多tag的测试网页,可以作为使用Struts tag的例子。

可通过以下的步骤在你自己的应用程序中使用Struts:
1.将Struts目录的lib/struts.jar拷贝到web应用的WEB-INF/lib目录。
2.将Struts目录的lib/struts*.tld拷贝到web应用的WEB-INF目录。
3.修改Web应用目录的WEB-INF/web.xml文件,增加一个元素来定义controller servlet,增加一个元素来建立URI请求与servlet的对应关系。可以参照Struts例子中的WEB-INF/web.xml文件来了解详细的语法要求。
4.修改Web应用目录的WEB-INF/web.xml文件,使之包含以下的tag库定义:

           /WEB-INF/struts.tld
           /WEB-INF/struts.tld


           /WEB-INF/struts-bean.tld
           /WEB-INF/struts-bean.tld


           /WEB-INF/struts-logic.tld
           /WEB-INF/struts-logic.tld

5.创建一个WEB-INF/action.xml文件来定义你的web应用的action映射关系。可以参照Struts例子中的action.xml文件来了解详细的语法要求。
6.在使用Struts tag库的JSP网页中加入以下tag库定义:
         <@ taglib uri="/WEB-INF/struts.tld" prefix="struts" %>
         <@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
         <@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
7.最后,在编译你的web应用的java程序时,不要忘了在CLASSPATH中包含struts.jar文件哟。

posted @ 2006-08-17 19:58 Lansing 阅读(259) | 评论 (0)编辑 收藏

一.技术概观

在表现形式上,J2EE是一组规范,而.NET更象是一组产品。但它们的目的都是为了企业应用提供分布式的,高可靠性的解决方案.它们在架构上有着很多的相似之处,下表是一个简单对照:

J2EE .NET

通信协议 Remote Method Invocation over Internet InterOrb Protocol (RMI/IIOP),XML

编程语言 Java C#,VB.NET,COBOL

运行时环境 Java Virtual Machine (JVM) Common Language Runtime (CLR)

胖客户端 Java Swing Windows Forms

目录服务 Java Naming and Directory Interface (JNDI) Active Directory Services Interface (ADSI)

数据访问 Java Database Connection (JDBC) ,Java Connectors ADO.NET

异步消息处理 Java Message Service (JMS) Microsoft Message Queue

表示层技术 Servlets, Java Server Page(JSP) ASP.NET

中间层组件模型 EJB,JavaBean COM+,COM

安全访问 JAAS COM+ Security

Call Context

事物处理 Java Transaction Server (JTS) Microsoft Distributed Transaction Coordinator (MS-DTC)

开发工具 WebGain Visual Café

Borland JBuilder

IBM VisualAge 等

(第三方提供,规范本身没有定义) Visual Studio.NET

J2EE平台的构成

EJB - J2EE 中间层,完成商业逻辑;

JAAS - J2EE 处理认证和授权的API;

Java Connectors - J2EE 用于连接异种数据源的API,对上层来讲是透明的;

JSP, Java Servlets - J2EE的表示层技术,用于生成用户界面;

Java Virtual Machine - Java 语言运行环境;

JDBC - J2EE数据库访问;

JMS - J2EE的异步消息队列;

JNDI - J2EE的名字查找API,独立于目录服务器;

JTS - J2EE用于处理交易的API;

RMI/IIOP - J2EE的分布式对象的通讯API,提供了和CORBA交互的能力。

.NET平台构成

.NET Framework - .NET应用运行的基础;

IL (Intermediary Language) - 所有的.NET语言首先被编译成该中间语言,然后在CLR中运行;

SOAP - 用于服务访问的工业标准;

DCOM - 组件间通信协议;

MS-DTC - 用来在.NET平台上使用两阶段提交协议来处理分布式交易;

CLR - .NET应用的运行时环境;

COM+ - .NET的中间层模型,用于构建商务逻辑;

ADO.NET - .NET 对数据访问的API。

此外.NET平台还包括其他一些产品象Application Center Server,BizTalk Server ,NLBS (Network Load Balancing Service),Commerce Server,Enterprise Servers,HIS (Host Integration Server),ISAS (Internet Security and Acceleration Server)用来提供象防火墙,安全访问,B2B交易,负载平衡等服务.J2EE规范本身没有定义这些服务,但可通过选择第三方产品来满足类似的要求。

二.技术比较

1.一 vs 多

一种语言vs多种语言,一个平台vs多个平台.这似乎是大家最喜于津津乐道的话题,也似乎是所有问题的焦点。

两种平台主流的开发语言Java和C#在架构上有着惊人的相似:虚拟机技术,基于沙箱的安全模型,分层的命名空间,垃圾回收等。所以从第一眼看上去,C#简直就是Java的克隆。但微软并不这样认为,微软的说明是:“它集成了C++, Java,Modula 2,C和Smalltalk等多种语言的精华,对它们共同的核心思想象深度面向对象(deep object-orientation),对象简化 (object-simplification)等都一一做了参考。”一方面,C#的大多数关键字来源于C++,使它在书写上有别于Java。但另一方面,C#的严格的类型转换等概念却明显来自于Java(当然,它的原始类型的定义更严格,并且据微软声称没有影响到效率.),使其在内涵上有克隆之嫌.但即是Java,其有些特性也和Smalltalk颇有渊源.所以评价一种开发语言的优劣不仅是看其外在的表现形式,更重要的是其实实在在的功效.作为一种新语言,C#加入了基于XML的标记,可以被编译器用来直接生成文档,C#的另一个特点:一站式软件(one-stop-shopping software)强调了自解释( self-describing) 的编码方式,即头文件,IDL(Interface Definition Language),GUID和其他复杂的接口无需再被引用.也即是C#,VB.NET等代码片断可以任意的被加入到其他语言中.这无疑在多种语言混合编程的模式中是一次飞跃,但是,其难维护性也是不言而喻的。

微软的.NET的平台提供了象C#,VB.NET,COBOL等多种开发语言,C#是新的,而其他的每一种语言都是在原有的基础上改造而来.这是微软煞费苦心并且也是不得以的要为习惯于这些语言的程序员铺一条便捷之路.但是,这些语言的改造与其说是整容到不如说是一次开膛破肚的大手术.首先是观念变了,Basic,Cobol等语言先天的缺少面向对象的内涵,现在却变成了面向对象的语言,这就不是要求其传统的程序员仅仅熟悉一些额外的关键字那么简单的问题了.基于面向对象的软件分析设计开发测试是完全不同于基于传统过程性语言的质变,所以这一过程的转变对传统程序员来讲也是一个痛苦和漫长的过程.在传统程序员面前,微软看似提供了丰富多采的解决方法,但对于实际问题而言,却怕是有些力不从心.所以一个简单的办法是:直接使用C#.对于独立软件开发商来讲,其转换成本不容忽视.其次,在一个软件项目中使用多种语言,开发商必须同时拥有多种语言专家和多个独立的难以互相支援的开发小组,无疑的,这也使其软件的维护的成本已非线性的曲线增长.多样性是双韧剑,实施时需仔细斟酌.

跨平台是J2EE的最大卖点,也是至今为止还绊住微软的栅栏.当开发商完成了符合J2EE规范的软件时,其客户可以依据其喜好和实力来选择不同应用服务器.从基于open source的免费软件到高端满足B2B需求的商业套件来搭建自己的平台.但是由于J2EE的规范还不完善,各个J2EE服务器的提供商为了使其提供其各自理解的完整的功能,不得不添加一些额外的特性.这就使得使用了这些特别功能的应用软件,绑定到了特定的应用服务器上.随着J2EE规范的发展,这种差别会逐渐减小.

微软的跨平台解决方案是Web services,它解决的是异种平台上不同应用之间的连通性问题.从技术角度讲,它除了以XML为介质之外没有什么新意.但它的重要意义在于:它是微软这样一个重量级选手所推出的,前景不容小视.构造和使用 Web services 的过程较为简单:

服务提供者用他所选择的语言构造服务;

服务提供者用WSDL(the Web Services Description Language)来定义该服务;

服务提供者在UDDI (Universal Description, Discovery, and Integration )中注册该服务;

使用者的应用程序从 UDDI中查找已注册服务;

使用者的应用程序通过 SOAP (the Simple Object Access Protocol )来调用服务.(SOAP使用HTTP来传递基于XML为表现形式的参数)

正如我们所讨论的: Web services解决的是异构平台上服务连通性的问题,但在现实中所更迫切需要的是如何在异构的平台上构造具有可扩展性,高可靠性,高可用性,故障冗余,错误恢复能力的企业应用.缺少这一点,从结构上讲,.NET平台还远未完善.

2.中间层

基于组件的软件开发技术可以在较高的级别上实现软件复用,加快企业软件开发的进程.在J2EE构架中, JavaBean和EJB(Enterprise JavaBeans) 被用来完成事物逻辑.其中EJB和 JavaBean 有着类似的模型,但它被用来创建分布式的企业应用.它定义服务器端组件的模型,具有以下一些特性:

生存期模型;

访问模型;

安全模型;

事物处理模型;

会话处理模型;

数据封装模型;

部署模型

根据这些模型,简单的编码就可完成复杂的功能。

在微软的.NET平台中,旧的COM 和 COM+的组件模型被新的组件模型所代替。增加了象基于沙箱的安全模型和垃圾回收等功能.并且实现了多重接口继承,扩展的元数据和新的代理模型等.旧有的COM和COM+组件也可被映射到新的运行环境中。

综上所述,两众架构在基于组件的中间层的设计上各有千秋,对于创建分布式的,复杂的,高效的,高可靠性的的应用程序都有着足够的能力。

3.表示层

两种架构都同时支持胖客户端和瘦客户端.即C/S模式和B/S模式.对于C/S模式,J2EE提供了替代Java AWT的Java Swing,同时作为可视化组件的JavaBean也可用来构造系统。对于B/S结构的表示层,J2EE使用 servlet ,JSP(Java Server Page) ,HMTL,WML,XML等工具来实现。

微软的胖客户端技术则由 Windows Forms代替了MFC.它们起的作用相同,在结构上 Windows Forms 被插入到.NET的运行时框架(runtime framework)和组件模型 (component model)中.在瘦客户模型中, ASP.NET代替了旧有的ASP和 HMTL, WML ,XML作为表示层。在 ASP.NET 中,C#,VB.NET等语言的代码片断可被自由引用.ASP.NET 页面被首先转换成中介语言( Intermediary Language),然后再被 中介语言及时编译器(just-in-time IL compiler)编译,最后运行于公共语言运行环境中,并且 ASP.NET 提供了页面的缓冲,所以,其运行速度要远远快于ASP。

大体上,两种架构所使用的表示层的技术非常类似,虽在细节上各有所长,但总体功能当在伯仲之间。

4.数据访问

J2EE 和 .Net 已不同的形式支持数据的访问。JDBC和ADO一样和所连接的数据库无关,并且通过连接,命令语句和结果集来对数据进行操作.所以属于中间层次的 API.更高一级的数据封装和数据管理是通过实体EJB (entity EJB)来完成的.基于容器管理的实体EJB使开发更快捷,管理更方便.事实上,由于实体EJB的load()和store()方法的同步机制,将大大缓解因并发而使数据库产生的瓶颈.也可以采用不属于J2EE规范的第三方数据访问工具,象WebGain的 TopLink。

而微软的.NET的数据访问工具则由基于XML的ADO.NET代替了基于COM组件的ADO.任何以XML为输出的数据源都可以作为 ADO.NET 的数据源.相应的结果集升级为数据集 (DataSets),命令语句则升级为数据集命令(DataSetCommands).从形式来看,微软的ADO.NET更新潮和时髦一些,基于XML的特性使其可以处理极其丰富的数据源,并且,因其构架在HTTP协议之上,易于穿透防火墙,使沟通更为便利.但由于XML本身的基于标记的特性,很明显限制了在有超大数据量和有网络瓶颈的应用中的使用.而J2EE的数据访问规则则显得略有单薄,但同时却更简单,更有效.并且通过对应用程序有效的层次的设计,对于数据库和基于XML的数据源的访问,也是可以无缝的整合的。

三.整体评价

在微软还没有足以和Java平台相对抗的产品的时候,微软所乐于做是大声的宣传:"write once, debug everywhere"。而它的对手则更乐于这样评价它:"微软开始也喜欢Java,他们喜欢它的方式是让它死去,他们当然也憎恨它,他们甚至憎恨每一个以J开头的单词。"但是现在,形式不同了,微软有了足以自豪的.NET他们可以已他们自己所喜好的方式来对J2EE和.NET来做各种比较。最热闹的应该算是微软出示的第三方对.NET Pet Shop和J2EE的 Pet Store的综合比较了.有兴趣的读者可以到MSDN,www.onjava.com,IBM开发者原地等网站看到相关评论。

J2EE .NET

易用性 ** ***

扩展能力 *** **

多平台支持 **** *

多语言支持 * ****

可靠性 *** ***

性能 *** ***

可管理性 *** ***

重用性 **** **

负载平衡 *** ***

开放标准 ***** *

就企业而言,内部众多系统的整合、系统的延展性、安全性是更需要注意的议题,而这些都是J2EE的优势,也是微软的不足处。 在效率方面,J2EE阵营主张通过硬件的效能增加来弥补软件的不足.开放标准,功能强大,易于移植这些都是J2EE的卖点。但让人奇怪的是IBM的WebSphere和BEA的WebLogic在J2EE市场占了大半壁江山,而作为规则制定者的SUN却在做壁上观。

微软确实提供了从桌面的办公软件,开发工具,到后台服务器数据库的全方位的产品。 但统一平台的使用者可能要牺牲跨平台的好处,并也有可能由此就被无穷无尽的锁定在微软的许可证的汪洋中.更简单,更快捷,更高效是微软的目标,随着时代的发展,我们也许会看到更完美的技术解决方案。

posted @ 2006-08-17 19:45 Lansing 阅读(317) | 评论 (0)编辑 收藏

创建Web应用的配置文件

对于Struts应用,它的配置文件web.xml应该对ActionServlet类进行配置,此外,还应该声明Web应用所使用的Struts标签库,本例中声明使用了三个标签库: Struts Bean、Struts HTML和Struts Logic标签库。例程1为web.xml的源代码。

例程1 web.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

< web-app >

< display-name >
HelloApp Struts Application
</display-name >

<!-- Standard Action Servlet Configuration -->

< servlet >

< servlet-name > action </servlet-name >

< servlet-class >
org.apache.struts.action.ActionServlet
< /servlet-class >

< init-param >
< param-name> config< /param-name >
< param-value>
/WEB-INF/struts-config.xml
</param-value >
</init-param >

< load-on-startup>2</load-on-startup >

</servlet>

<!-- Standard Action Servlet Mapping -->

< servlet-mapping>

< servlet-name > action</servlet-name>

< url-pattern>*.do</url-pattern>

</servlet-mapping>

<!-- The Usual Welcome File List -->

< welcome-file-list>

< welcome-file > hello.jsp</welcome-file>

</welcome-file-list>

<!-- Struts Tag Library Descriptors -->

< taglib >

< taglib-uri >
/WEB-INF/struts-bean.tld
</taglib-uri>

< taglib-location>
/WEB-INF/struts-bean.tld
</taglib-location>

</taglib>

< taglib >

< taglib-uri >
/WEB-INF/struts-html.tld
</taglib-uri >

< taglib-location >
/WEB-INF/struts-html.tld
</taglib-location >

</taglib >

< taglib >

< taglib-uri >
/WEB-INF/struts-logic.tld
</taglib-uri >

< taglib-location >
/WEB-INF/struts-logic.tld
</taglib-location >

</taglib >

</web-app >

创建Struts框架的配置文件

正如前面提及的,Struts框架允许把应用划分成多个组件,提高开发速度。而Struts框架的配置文件struts-config.xml可以把这些组件组装起来,决定如何使用它们。例程2是helloapp应用的struts-config.xml文件的源代码。

例程2  struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config
PUBLIC"-//Apache Software Foundation
//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<!--This is the Struts configuration file
for the "Hello!" sample application-->
< struts-config>
<!-- ===== Form Bean Definitions ====== -->
< form-beans>
< form-bean name="HelloForm" type="hello.HelloForm"/>
</form-beans>
<!-- ====== Action Mapping Definitions ====== -->
< action-mappings>
<!-- Say Hello! -->
< action path="/HelloWorld"
type="hello.HelloAction"
name="HelloForm"
scope="request"
validate="true"
input="/hello.jsp"
>
< forward name="SayHello" path="/hello.jsp" />
</action >
</action-mappings >
<!-- ===== Message Resources Definitions ===== -->
< message-resources parameter="hello.application"/>
</struts-config >


以上代码对helloapp应用的HelloForm、HelloAction和消息资源文件进行了配置,首先通过元素配置了一个ActionForm Bean,名叫HelloForm,它对应的类为hello.HelloForm:



接着通过元素配置了一个Action组件: 
< action
path = "/HelloWorld"
type ="hello.HelloAction"
name = "HelloForm"
scope = "request"
validate = "true"
input = "/hello.jsp"
>
< forward name="SayHello" path="/hello.jsp" />
</action >

元素的path属性指定请求访问Action的路径,type属性指定Action的完整类名,name属性指定需要传递给Action的ActionForm Bean,scope属性指定ActionForm Bean的存放范围,validate属性指定是否执行表单验证,input属性指定当表单验证失败时的转发路径。元素还包含一个子元素,它定义了一个请求转发路径。

本例中的 元素配置了HelloAction组件,对应的类为hello.HelloAction,请求访问路径为"HelloWorld",当Action类被调用时,Struts框架应该把已经包含表单数据的HelloForm Bean传给它。HelloForm Bean存放在request范围内,并且在调用Action类之前,应该进行表单验证。如果表单验证失败,请求将被转发到接收用户输入的网页hello.jsp,让用户纠正错误。

struts-config.xml文件最后通过元素定义了一个Resource Bundle:元素的parameter属性指定Resource Bundle使用的消息资源文件。本例中parameter属性为"hello.application",表明消息资源文件名为"application.properties",它的存放路径为WEB-INF/classes/hello/application.properties。

posted @ 2006-08-17 19:43 Lansing 阅读(329) | 评论 (0)编辑 收藏

某些网站允许软件开发社团通过发布开发者指南、白皮书、FAQs【常见问题解答】和源代码以实现信息的共享。随着信息量的增长,和几个开发者贡献出自己的知识库,于是网站提供搜索引擎来搜索站点上现有的所有信息。虽然这些搜索引擎对文本文件的搜索可以做的很好,但对开发者搜索源代码做了比较严格的限制。搜索引擎认为源代码就是纯文本文件,因此,在这一点上,与成熟的可以处理大量源文件的工具――grep相比没有什么不同。

在这篇文章中,我推荐使用Lucene,它是基于Java的开源搜索引擎,通过提取和索引相关的源码元素来搜索源代码。这里,我仅限定搜索Java源代码。然而,Lucene同样可以做到对其他编程语言的源代码的搜索。

文章给出了在Lucene环境下搜索引擎重点方面的简短概述。要了解更多细节信息,参考Resources部分。

版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接
作者:Renuka;Knightchen(作者的blog:http://blog.matrix.org.cn/page/Knightchen)
原文:http://www.matrix.org.cn/resource/article/44/44362_Lucene+Java.html
关键字:Lucene;Java

概述
Lucene是最流行的开源搜索引擎库之一。它由能文本索引和搜索的核心API组成。Lucene能够对给出一组文本文件创建索引并且允许你用复杂的查询来搜索这些索引,例如:+title:Lucene -content:Search、search AND Lucene、+search +code。在进入搜索细节之前,先让我来介绍一下Lucene的一些功能。

在Lucene中索引文本

搜索引擎对所有需要被搜索的数据进行扫描并将其存储到能有效获取的一个结构里。这个最有名的结构被称为倒排索引。例如,现在考虑对一组会议记录进行索引。首先,每个会议记录的文件被分为几个独立的部分或者域:如标题、作者、email、摘要和内容。其次,每一域的内容被标记化并且提取出关键字或者术语。这样就可以建立如下表所示会议记录的倒排索引。

        ....                 

对于域中的每一术语而言,上图存储了两方面的内容:该术语在文件中出现的数量(即频率【DF】)以及包含该术语的每一文件的ID。对于每个术语保存的其它细节:例如术语在每个文件中出现的次数以及出现的位置也被保存起来。无论如何,对于我们非常重要的一点是要知道:利用Lucene检索文件意味着将其保存为一种特定格式,该格式允许高效率查询及获取。

分析被索引的文本

Lucene使用分析器来处理被索引的文本。在将其存入索引之前,分析器用于将文本标记化、摘录有关的单词、丢弃共有的单词、处理派生词(把派生词还原到词根形式,意思是把bowling、bowler和bowls还原为bowl)和完成其它要做的处理。Lucene提供的通用分析器是:
&#61548;        SimpleAnalyzer:用字符串标记一组单词并且转化为小写字母。
&#61548;        StandardAnalyzer:用字符串标记一组单词,可识别缩写词、email地址、主机名称等等。并丢弃基于英语的stop words (a, an, the, to)等、处理派生词。

检索(搜索索引)
索引结构建立后,可以通过指定被搜索的字段和术语构造复杂的查询来对索引进行检索。例如,用户查询abstract:system AND email:abc@mit.edu得到的结果是所有在摘要中包含system、在email地址中有abc@mit.edu的文件。也就是说,如果在前面倒排索引表的基础上搜索就返回Doc15。与查询匹配的文件是按照术语在文件中出现的次数以及包含该术语的文档的数量进行排列的。Lucene执行一种顺序排列机制并且提供了给我们更改它的弹性。

源代码搜索引擎

现在我们知道了关于搜索引擎的基本要点,下面让我们看一看用于搜索源代码的搜索引擎应如何实现。下文中展示在搜索Java示例代码时,开发者主要关注以下Java类:
继承一个具体类或实现一个接口。
调用特定的方法。
使用特定的Java类。

综合使用上述部分的组合可以满足开发者获取他们正在寻找相关代码的需要。因此搜索引擎应该允许开发者对这些方面进行单个或组合查询。IDEs【集成开发环境】有另一个局限性:大部分可使用的工具仅仅基于上述标准之一来支持搜索源代码。在搜索中,缺乏组合这些标准进行查询的灵活性。

现在我们开始建立一个支持这些要求的源代码搜索引擎。

编写源代码分析器
第一步先写一个分析器,用来提取或去除源代码元素,确保建立最佳的索引并且仅包含相关方面的代码。在Java语言中的关键字--public,null,for,if等等,在每个.java文件中它们都出现了,这些关键字类似于英语中的普通单词(the,a,an,of)。因而,分析器必须把这些关键字从索引中去掉。

我们通过继承Lucene的抽象类Analyzer来建立一个Java源代码分析器。下面列出了JavaSourceCodeAnalyzer类的源代码,它实现了tokenStream(String,Reader)方法。这个类定义了一组【stop words】,它们能够在索引过程中,使用Lucene提供的StopFilter类来被去除。tokenStream方法用于检查被索引的字段。如果该字段是“comment”,首先要利用LowerCaseTokenizer类将输入项标记化并转换成小写字母,然后利用StopFilter类除去英语中的【stop words】(有限的一组英语【stop words】),再利用PorterStemFilter移除通用的语形学以及词尾后缀。如果被索引的内容不是“comment”,那么分析器就利用LowerCaseTokenizer类将输入项标记化并转换成小写字母,并且利用StopFilter类除去Java关键字。

package com.infosys.lucene.code JavaSourceCodeAnalyzer.;

import java.io.Reader;
import java.util.Set;
import org.apache.lucene.analysis.*;

public class JavaSourceCodeAnalyzer extends Analyzer {
      private Set javaStopSet;
      private Set englishStopSet;
      private static final String[] JAVA_STOP_WORDS = {
         "public","private","protected","interface",
            "abstract","implements","extends","null""new",
           "switch","case", "default" ,"synchronized" ,
            "do", "if", "else", "break","continue","this",
           "assert" ,"for","instanceof", "transient",
            "final", "static" ,"void","catch","try",
            "throws","throw","class", "finally","return",
            "const" , "native", "super","while", "import",
            "package" ,"true", "false" };
     private static final String[] ENGLISH_STOP_WORDS ={
            "a", "an", "and", "are","as","at","be" "but",
            "by", "for", "if", "in", "into", "is", "it",
            "no", "not", "of", "on", "or", "s", "such",
            "that", "the", "their", "then", "there","these",
            "they", "this", "to", "was", "will", "with" };
     public SourceCodeAnalyzer(){
            super();
            javaStopSet = StopFilter.makeStopSet(JAVA_STOP_WORDS);
            englishStopSet = StopFilter.makeStopSet(ENGLISH_STOP_WORDS);
     }
     public TokenStream tokenStream(String fieldName, Reader reader) {
            if (fieldName.equals("comment"))
                     return   new PorterStemFilter(new StopFilter(
                        new LowerCaseTokenizer(reader),englishStopSet));
            else
                     return   new StopFilter(
                   new LowerCaseTokenizer(reader),javaStopSet);
     }
}



编写类JavaSourceCodeIndexer
第二步生成索引。用来建立索引的非常重要的类有IndexWriter、Analyzer、Document和Field。对每一个源代码文件建立Lucene的一个Document实例。解析源代码文件并且摘录出与代码相关的语法元素,主要包括:导入声明、类名称、所继承的类、实现的接口、实现的方法、方法使用的参数和每个方法的代码等。然后把这些句法元素添加到Document实例中每个独立的Field实例中。然后使用存储索引的IndexWriter实例将Document实例添加到索引中。

下面列出了JavaSourceCodeIndexer类的源代码。该类使用了JavaParser类解析Java文件和摘录语法元素,也可以使用Eclipse3.0 ASTParser。这里就不探究JavaParser类的细节了,因为其它解析器也可以用于提取相关源码元素。在源代码文件提取元素的过程中,创建Filed实例并添加到Document实例中。

import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import com.infosys.lucene.code.JavaParser.*;

public class JavaSourceCodeIndexer {
    private static JavaParser parser = new JavaParser();
        private static final String IMPLEMENTS = "implements";
        private static final String IMPORT = "import";
        ...
        public static void main(String[] args) {
                File indexDir = new File("C:\\Lucene\\Java");
                File dataDir = new File("C:\\JavaSourceCode ");
                IndexWriter writer = new IndexWriter(indexDir,
                    new JavaSourceCodeAnalyzer(), true);
                indexDirectory(writer, dataDir);
                writer.close();
        }
        public static void indexDirectory(IndexWriter writer, File dir){
            File[] files = dir.listFiles();
            for (int i = 0; i < files.length; i++) {
                    File f = files[i];
                // Create a Lucene Document
                Document doc = new Document();
                //  Use JavaParser to parse file
                parser.setSource(f);
                addImportDeclarations(doc, parser);
                        addComments(doc, parser);
                 // Extract Class elements Using Parser
                JClass cls = parser.getDeclaredClass();
                addClass(doc, cls);
                 // Add field to the Lucene Document
                       doc.add(Field.UnIndexed(FILENAME, f.getName()));
                writer.addDocument(doc);
                    }
        }
        private static void addClass(Document doc, JClass cls) {
                   //For each class add Class Name field
            doc.add(Field.Text(CLASS, cls.className));
            String superCls = cls.superClass;
            if (superCls != null)
                   //Add the class it extends as extends field
        doc.add(Field.Text(EXTENDS, superCls));
            // Add interfaces it implements
            ArrayList interfaces = cls.interfaces;
            for (int i = 0; i < interfaces.size(); i++)
                doc.add(Field.Text(IMPLEMENTS, (String) interfaces.get(i)));
                    //Add details  on methods declared
            addMethods(cls, doc);
            ArrayList innerCls = cls.innerClasses;
                   for (int i = 0; i < innerCls.size(); i++)
                addClass(doc, (JClass) innerCls.get(i));
        }
        private static void addMethods(JClass cls, Document doc) {
            ArrayList methods = cls.methodDeclarations;
            for (int i = 0; i < methods.size(); i++) {
                       JMethod method = (JMethod) methods.get(i);
                // Add method name field
                doc.add(Field.Text(METHOD, method.methodName));
                // Add return type field
                doc.add(Field.Text(RETURN, method.returnType));
                ArrayList params = method.parameters;
                for (int k = 0; k < params.size(); k++)
                // For each method add parameter types
                    doc.add(Field.Text(PARAMETER, (String)params.get(k)));
                String code = method.codeBlock;
                if (code != null)
                //add the method code block
                    doc.add(Field.UnStored(CODE, code));
            }
        }
        private static void addImportDeclarations(Document doc, JavaParser parser) {
                   ArrayList imports = parser.getImportDeclarations();
            if (imports == null)     return;
            for (int i = 0; i < imports.size(); i++)
                    //add import declarations as keyword
                doc.add(Field.Keyword(IMPORT, (String) imports.get(i)));
        }
}



Lucene有四种不同的字段类型:Keyword,UnIndexed,UnStored和Text,用于指定建立最佳索引。
&#61548;        Keyword字段是指不需要分析器解析但需要被编入索引并保存到索引中的部分。JavaSourceCodeIndexer类使用该字段来保存导入类的声明。
&#61548;        UnIndexed字段是既不被分析也不被索引,但是要被逐字逐句的将其值保存到索引中。由于我们一般要存储文件的位置但又很少用文件名作为关键字来搜索,所以用该字段来索引Java文件名。
&#61548;        UnStored字段和UnIndexed字段相反。该类型的Field要被分析并编入索引,但其值不会被保存到索引中。由于存储方法的全部源代码需要大量的空间。所以用UnStored字段来存储被索引的方法源代码。可以直接从Java源文件中取出方法的源代码,这样作可以控制我们的索引的大小。
&#61548;        Text字段在索引过程中是要被分析、索引并保存的。类名是作为Text字段来保存。下表展示了JavaSourceCodeIndexer类使用Field字段的一般情况。



1.
   用Lucene建立的索引可以用Luke预览和修改,Luke是用于理解索引很有用的一个开源工具。图1中是Luke工具的一张截图,它显示了JavaSourceCodeIndexer类建立的索引。


图1:在Luke中索引截图

如图所见,导入类的声明没有标记化或分析就被保存了。类名和方法名被转换为小写字母后,才保存的。

查询Java源代码
建立多字段索引后,可以使用Lucene来查询这些索引。它提供了这两个重要类分别是IndexSearcher和QueryParser,用于搜索文件。QueryParser类则用于解析由用户输入的查询表达式,同时IndexSearcher类在文件中搜索满足查询条件的结果。下列表格显示了一些可能发生的查询及它的含义:


用户通过索引不同的语法元素组成有效的查询条件并搜索代码。下面列出了用于搜索的示例代码。

public class JavaCodeSearch {
public static void main(String[] args) throws Exception{
    File indexDir = new File(args[0]);
    String q =  args[1]; //parameter:JGraph code:insert
    Directory fsDir = FSDirectory.getDirectory(indexDir,false);
    IndexSearcher is = new IndexSearcher(fsDir);

    PerFieldAnalyzerWrapper analyzer = new
        PerFieldAnalyzerWrapper( new
                JavaSourceCodeAnalyzer());

    analyzer.addAnalyzer("import", new KeywordAnalyzer());
    Query query = QueryParser.parse(q, "code", analyzer);
    long start = System.currentTimeMillis();
    Hits hits = is.search(query);
    long end = System.currentTimeMillis();
    System.err.println("Found " + hits.length() +
                " docs in " + (end-start) + " millisec");
    for(int i = 0; i < hits.length(); i++){
    Document doc = hits.doc(i);
        System.out.println(doc.get("filename")
                + " with a score of " + hits.score(i));
    }
    is.close();
}
}



IndexSearcher实例用FSDirectory来打开包含索引的目录。然后使用Analyzer实例分析搜索用的查询字符串,以确保它与索引(还原词根,转换小写字母,过滤掉,等等)具有同样的形式。为了避免在查询时将Field作为一个关键字索引,Lucene做了一些限制。Lucene用Analyzer分析在QueryParser实例里传给它的所有字段。为了解决这个问题,可以用Lucene提供的PerFieldAnalyzerWrapper类为查询中的每个字段指定必要的分析。因此,查询字符串import:org.w3c.* AND code:Document将用KeywordAnalyzer来解析字符串org.w3c.*并且用JavaSourceCodeAnalyzer来解析Document。QueryParser实例如果查询没有与之相符的字段,就使用默认的字段:code,使用PerFieldAnalyzerWrapper来分析查询字符串,并返回分析后的Query实例。IndexSearcher实例使用Query实例并返回一个Hits实例,它包含了满足查询条件的文件。

结束语

这篇文章介绍了Lucene——文本搜索引擎,其可以通过加载分析器及多字段索引来实现源代码搜索。文章只介绍了代码搜索引擎的基本功能,同时在源码检索中使用愈加完善的分析器可以提高检索性能并获得更好的查询结果。这种搜索引擎可以允许用户在软件开发社区搜索和共享源代码。

资源
这篇文章的示例Sample code
Matrix:http://www.matrix.org.cn
Onjava:http://www.onjava.com/
Lucene home page
"Introduction to Text Indexing with Apache Jakarta Lucene:" 这是篇简要介绍使用Lucene的文章。
Lucene in Action: 在使用Lucene方面进行了深入地讲解。

Renuka Sindhgatta 是一位资深的构架师,现在在印度班加罗尔市【 in the Software Engineering and Technology labs of Infosys Technologies Limited 】工作。

posted @ 2006-08-17 19:42 Lansing 阅读(641) | 评论 (0)编辑 收藏

J2EE简介



J2EE是一个开放的、基于标准的平台,可以开发、部署和管理N层结构的、面向Web的、以服务器为中心的企业级应用,它是利用Java 2 平台来简化与多级企业解决方案的开发、部署和管理相关的诸多复杂问题的应用体系结构。

J2EE平台采用一个多层次分布式的应用模式。这意味着应用逻辑根据功能被划分成组件,组成J2EE应用的不同应用组件安装在不同的服务器上,这种划分是根据应用组件属于多层次J2EE环境中的哪一个层次来决定的。如图1所示,J2EE应用可以由三或四个层次组成,J2EE多层次应用一般被认为是三层应用,因为它们是被分布在三个不同的地点:客户端机器、J2EE服务器和数据库或后端的传统系统服务器。三层架构应用是对标准的客户端/服务器应用架构的一种扩展, 即在客户端应用和后台存储之间增加一个多线程应用服务器。



J2EE体系包括JSP、Servlet、EJB、WEB SERVICE等多项技术。这些技术的出现给电子商务时代的WEB应用开发提供了一个非常有竞争力的选择。怎样把这些技术组合起来,形成一个适应项目需要的稳定架构是项目开发过程中一个非常重要的步骤。

一个成功的软件需要有一个成功的架构,但软件架构的建立是一个复杂而又持续改进的过程,软件开发者们不可能对每个不同的项目做不同的架构,而总是尽量重用以前的架构,或开发出尽量通用的架构方案,Struts就是流行的基于J2EE的架构方案之一,其他常用的基于J2EE的架构方案还有Turbine、RealMothods等。本文主要探讨Struts框架技术的应用。

J2EE应用程序架构的发展

在J2EE应用程序架构的发展路程中,主要经历了两个大的阶段:

1、Model 1

在JSP页面中结合业务逻辑、服务器端处理程序和HTML,在JSP页面中同时实现显示、业务逻辑和流程控制,从而快速的完成Web应用开发。这种模型的不足之处:1)不利于应用扩展和更新。2)业务逻辑和表示逻辑混合在JSP页面中没有进行抽象和分离,不利于应用系统业务的重用和改动。

2、Model 2

表示的是基于MVC模式的框架。根据Model 2,servlet 处理数据存取和导航流, JSP处理表现。Model 2 使Java 工程师和HTML设计者分别工作于它们所擅长和负责的部分。Model 2应用的一部分发生改变并不强求其他部分也跟着发生改变。HTML 开发人员可以改变程序的外观和感觉,并不需要改变后端servlet的工作方式。把应用逻辑、处理过程和显示逻辑分成不同的组件实现。弥补了Model1的不足。

Struts框架技术

Struts 框架就是基于Model 2 的架构,也就是基于MVC模式的框架技术。它是一个免费的开源的WEB层的应用框架,具有很高的可配置性,和有一个不断增长的特性列表。一个前端控制组件,一系列动作类,动作映射,处理XML的实用工具类,服务器端java bean 的自动填充,支持验证的WEB 表单,国际化支持,生成HTML,实现表现逻辑和模板组成了struts的灵魂。图2显示了Struts组件是如何一起工作的。



Struts 的ActionServlet 控制导航流。其他Struts 类,比如Action, 用来访问业务逻辑类。当 ActionServlet 从容器接收到一个请求,它使用URI (或者路径“path”) 来决定哪个Action 将用来处理请求。一个 Action可以校验输入,并且访问业务层以从数据库或其他数据服务中检索信息。

为校验输入或者使用输入来更新数据库, Action 需要知道什么被提交上来。并不是强制每个Action 从请求中抓取这些值,而是由 ActionServlet 将输入绑定到JavaBean中。输入 bean是Struts ActionForm c类的子类。ActionServlet 通过查找请求的路径可以决定使用哪个ActionForm,Action 也是通过同样的方法选取的。每个Action都必须以HTTP 响应进行应答。 通常, Struts Action 并不自行加工响应信息,而是将请求转发到其他资源,比如JSP 页面。Struts 提供一个ActionForward 类,用来将一个页面的路径存储为逻辑名称。当完成业务逻辑后,Action 选择并向Servlet返回一个ActionForward。Servlet 然后使用存储在ActionForward 对象中的路径来调用页面完成响应。

Struts 将这些细节都绑定在一个ActionMapping 对象中。每个ActionMapping 相对于一个特定的路径。当某个路径被请求时,Servlet 就查询ActionMapping 对象。ActionMapping对象告诉servlet哪个Actions、 ActionForms 和 ActionForwards 将被使用。

所有这些细节,关于Action, ActionForm, ActionForward, ActionMapping,以及其他一些东西,都在struts-config.xml 文件中定义。 ActionServlet 在启动时读取这个配置文件,并创建一个配置对象数据库。在运行时,Struts 应用根据的是文件创建的配置对象,而不是文件本身。

基于Struts框架的应用设计实例

本文以“面向铸造行业的网络化制造ASP平台开发”项目中的软件租用模块为例,来说明如何设计基于Struts框架的Web应用。在该模块中,用户合法登陆网站后,可以根据需要选择所要租用的软件类型及软件中的功能模块,确认信息提交服务器后,用户将收到系统给予的登陆密码,用户即可登陆网站,在线使用租用软件,实行业务托管。

根据项目需求分析,确定该系统必须具备的性能有:1)良好的交互性:工作内容中有相当大的部分是人机交流,这就要求系统的交互性要强。2)较好的可扩展性:工作的内容和形式具有多变性,要求系统具有良好的可扩展性。3)良好的可维护性:系统投入使用后,主要是由管理员承担系统维护的工作,维护人员不定期变动,这就要求系统的可维护性强。4)具有较好的跨平台性:用户可能使用各种不同的操作系统,而且为了适应今后可能的变化,系统应具有较好的跨平台性。基于以上四点,在开发软件租用模块时,采用J2EE编程环境,并相应采用了专为J2EE定制的Struts框架。

做基于Struts框架的项目开发,关键是要有一个好的整体模型,计划好系统中包括哪几个模块,每个模块各需要什么样的FormBean、JavaBean,各种处理结果都通过哪些JSP页面来展现,同时配置好struts-config.xml文件。本系统的设计模型如图3所示。



ActionServlet接受所有的HTTP请求,然后根据配置文件的内容,决定将请求映射到哪一个Action对象,本系统中有两个Action对象,分别对应着登陆远程软件(LogonAction)和系统反馈密码(MailAction)。

LogonAction首先会验证用户是否已经登录,如果没有登录则重定向到登录页面(Logon.jsp),验证通过后根据请求参数决定下一步的处理,如果用户还没有选择租用软件,则转到软件介绍租用界面(Query.jsp),选择需要租用的软件或软件的某些模块,提交信息后,MailAction使服务器向用户提交密码,用户接收到密码后,登陆运行软件。

如果用户登陆软件成功,则通过配置文件struts-config.xml中的ActionForward,通过GetInfo对象把该用户租用的软件信息读取道FormBean中,然后调用JSP页面显示Bean里的数据。如果是保存数据信息,则调SaveInfo对象将FormBean里保持的信息存入数据库;如果是修改信息,则调ModifyInfo对象将FormBean里保持的修改后的信息存入数据库;如果是删除数据信息,则调用DeleteInfo对象将FormBean里保持的信息从数据库中删除。

经过这样设计的系统,用户界面和数据处理已经完全分离,再加上在JSP页面中使用了自定义标记,使页面中没有了Java的脚本代码,这样Web界面的设计和后端程序的编写就有了清晰的界线,便于开发团队的分工,并且维护起来也很方便。

结束语

Struts是一种非常优秀的基于J2EE的MVC应用框架,虽然从正式发布到现在也只有两年多的时间,但它已经越来越多地运用于企业平台之上,许多大型网站已成功地应用了Struts框架。本文在总结了Struts框架技术及其工作原理的基础上,结合“面向铸造行业的网络化制造ASP平台开发”项目,提出了在线租用模块的设计思路,这为今后更好的应用采用Struts框架提供了参考。

posted @ 2006-08-17 19:36 Lansing 阅读(1217) | 评论 (1)编辑 收藏

构建高性能的J2EE应用不但需要了解常用的实施技巧。下面介绍最常用的10种有效方法,可帮助架构设计师们快速成为这方面的专家。

Java性能的基础—内存管理

任何Java应用,单机的或J2EE的性能基础都可归结到你的应用是如何管理内存的问题。Java的内存管理包括两个重要任务:内存的分配和内存的回收。在内存的分配中,目标是要减少需要创建的对象。

内存回收是导致性能下降的普遍原因。也就是说,内存中的对象越多,垃圾回收越困难。所以我们对创建对象的态度应该越保守越好。

在J2EE应用中常见的两个内存有关的问题是:游离的对象(也被称为内存泄露)和对象循环(指大量频繁创建和删除-在Java中体现为解除引用---对象)。

我们应注意确保所有可到达的对象实际是活的,即这些对象不但在内存中,而且也要在执行的代码中是存在的。当对象在应用中已经没有用了,而我们却忘记了删除对该对象的引用时,游离的对象就出现了。

我们知道垃圾回收会占用CPU时间。短期对象的大量创建增加了垃圾回收的频率会造成性能下降。

不要在Servlet中实现业务逻辑

在构建J2EE应用时,架构工程师通常会使用到J2EE的基本部分——Servlet。如果架构师不使用Session Beans, Entity Beans, 或 Message Beans, 那么改进性能的方法就很少。只能采用增加CPU或更多的物理服务器等方法。EJB使用了缓存(cache)和资源池等方法可以提高性能和扩展性。

尽可能使用本地接口访问EJB

在早期的J2EE (遵循EJB1.X规范)应用中,访问EJB是`通过RMI使用远程接口实现的。随着EJB2.0的出现,可以通过本地接口访问EJB,不再使用RMI,在同一个JVM中使用远程方法已经少多了。但是现在还是有一些使用EJB1.X实现的应用和不知道使用本地接口的一些EJB新手。为说明这点,我们作个比较:

1、客户端应用调用本地Stub

2、该Stub装配参数

3、该Stub传到skeleton

4、该skeleton分解参数

5、该skeleton调用EJB对象

6、EJB对象执行容器服务

7、EJB对象调用企业BEAN实例

8、企业BEA执行操作

9、执行组装/分解步骤然后返回

与远程接口处理相比较,本地接口的EJB方法是:

1、客户端调用本地对象

2、本地对象执行容器服务

3、本地对象调用企业Bean实例

4、企业Bean实例执行操作

5、没有其他返回步骤!

如果你不需要从远程的客户端访问一个特殊EJB,就应该使用本地方法。

在实现Session Bean的服务中封装对实体EJB的访问

从Servlet访问实体EJB不但效率低而且难于维护。使用Session Facade(会话外观)模式可把对实体EJB的访问封装在会话EJB中,在该会话EJB中通过使用本地接口访问实体EJB而避免过多的远程调用。

这项技术会有额外的性能和扩展方面的好处,这是因为会话和实体EJB可以使用缓存和资源池技术来进行改进。另外,由于负载的需要,会话和实体EJB可被扩展部署到其他硬件设备上,这比将Servlet层复制扩展到其他硬件设备上要简单的多。

尽量粗粒度访问远程EJB

当访问远程EJB时,调用set/get方法将产生过多的网络请求,同时也导致远程接口处理的过载。为避免这种情况,可考虑将数据属性集中在一个对象中,这样通过一次对远程EJB的调用就可以传递所有数据。这项技术就是数据传输对象(Data Transfer Object)模式。

优化SQL

J2EE的架构设计工程师和开发人员通常不是SQL专家或经验丰富的数据库管理员。首先应该确保SQL使用了数据库提供的索引支持。在某些情况下,将数据库的索引和数据分开存放会提高性能。但要知道,增加额外的索引可以提高SELECT性能但也会降低INSERT的性能。对于某些数据库,关联表之间的排序会严重影响性能。可以多向数据库管理员咨询。

避免在实体EJB中过多执行SQL

有时候,通过实体EJB访问数据会执行多个SQL语句。根据J2EE 规范,第一步,将调用实体Bean的find(发现)方法;第二步,在第一次调用实体EJB的业务方法时,容器会调用ejbLoad()从数据库中获得信息。

很多CMP(容器管理持久性)在调用发现方法时就缓存了实体数据,所以在调用ejbLoad()时就不再访问数据库了。应该避免使用BMP(Bean管理的持久性)或者自己实现缓存算法避免二次访问数据库。

使用Fast Lane Reader 模式访问只读数据

J2EE应用经常要以只读方式访问大量长时间不变的数据,而不是访问单个实体,例如浏览在线产品目录。在这种只读情况下,使用实体EJB访问数据会导致严重过载并且实现很麻烦。实体EJB 适合于对单个实体的粗粒度访问,访问大量的列表只读数据时效率不高。不管是使用CMP还是BMP,一定需要编写代码操作多个实体EJB及其关联。这将导致访问多个数据库并存在大量的也是不必要的事务开销。

利用Java Messaging Servce(消息服务)

J2EE规范在JMS中提供了内置的异步处理服务。当涉及到系统需求时,应该了解在什么情况下应该采用JMS进行异步处理的设计。一旦确定要执行一些异步处理,那么同步处理的任务就应该越少越好,将数据库密集的操作安排在稍后的异步处理中完成。

缓存JNDI Lookup查找

很多操作在进行JNDI查找时要消耗大量资源。通常应该缓存JNDI资源避免网络调用和某些处理的过载。可以缓存的JNDI查找包括:

EJB Home Interfaces

Data Sources

JMS Connection Factories

MS Destinations/Topics

一些JNDI包实现了缓存功能。但是调用对EJB主接口的narrow方法时,这种功能作用有限。缓存查找的设计应该使用共享的IntialContext实例,尽管构建它很麻烦。这是因为需要访问多种数据源,包括应用资源文件JNDI.properties,系统属性的各项参数,传入到构造函数的各项参数。

posted @ 2006-08-17 19:35 Lansing 阅读(239) | 评论 (0)编辑 收藏

Java语言内置了synchronized关键字用于对多线程进行同步,大大方便了Java中多线程程序的编写。但是仅仅使用synchronized关键字还不能满足对多线程进行同步的所有需要。大家知道,synchronized仅仅能够对方法或者代码块进行同步,如果我们一个应用需要跨越多个方法进行同步,synchroinzed就不能胜任了。在C++中有很多同步机制,比如信号量、互斥体、临届区等。在Java中也可以在synchronized语言特性的基础上,在更高层次构建这样的同步工具,以方便我们的使用。
    当前,广为使用的是由Doug Lea编写的一个Java中同步的工具包,可以在这儿了解更多这个包的详细情况:
    http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html
    该工具包已经作为JSR166正处于JCP的控制下,即将作为JDK1.5的正式组成部分。本文并不打算详细剖析这个工具包,而是对多种同步机制的一个介绍,同时给出这类同步机制的实例实现,这并不是工业级的实现。但其中会参考Doug Lea的这个同步包中的工业级实现的一些代码片断。
    本例中还沿用上篇中的Account类,不过我们这儿编写一个新的ATM类来模拟自动提款机,通过一个ATMTester的类,生成10个ATM线程,同时对John账户进行查询、提款和存款操作。Account类做了一些改动,以便适应本篇的需要:

  1. import  java.util.HashMap;
  2. import  java.util.Map;
  3. class  Account {
  4.     String name;
  5.     //float amount;
  6.     
  7.     //使用一个Map模拟持久存储
  8.     static Map storage = new HashMap();
  9.     static {
  10.         storage.put("John"new Float(1000.0f));
  11.         storage.put("Mike"new Float(800.0f));
  12.     }    
  13.     
  14.     
  15.     public Account(String name) {
  16.         //System.out.println("new account:" + name);
  17.         this.name = name;
  18.         //this.amount = ((Float)storage.get(name)).floatValue();
  19.     }
  20.     public synchronized void deposit(float amt) {
  21.         float amount = ((Float)storage.get(name)).floatValue();
  22.         storage.put(name, new Float(amount + amt));
  23.     }
  24.     public synchronized void withdraw(float amt) throws InsufficientBalanceException {
  25.         float amount = ((Float)storage.get(name)).floatValue();
  26.         if (amount >= amt)
  27.             amount -= amt;
  28.         else 
  29.             throw new InsufficientBalanceException();
  30.                 
  31.         storage.put(name, new Float(amount));
  32.     }
  33.     public float getBalance() {
  34.         float amount = ((Float)storage.get(name)).floatValue();
  35.         return amount;
  36.     }
  37. }



在新的Account类中,我们采用一个HashMap来存储账户信息。Account由ATM类通过login登录后使用:

  1. public  class ATM {
  2.     Account acc;
  3.     
  4.     //作为演示,省略了密码验证
  5.     public boolean login(String name) {
  6.         if (acc != null)
  7.             throw new IllegalArgumentException("Already logged in!");
  8.         acc = new Account(name);
  9.         return true;
  10.     }
  11.     
  12.     public void deposit(float amt) {
  13.         acc.deposit(amt);
  14.     }
  15.     
  16.     public void withdraw(float amt) throws InsufficientBalanceException  {
  17.             acc.withdraw(amt);
  18.     }
  19.     
  20.     public float getBalance() {
  21.         return acc.getBalance();
  22.     }
  23.     
  24.     public void logout () {
  25.         acc = null;
  26.     }
  27.     
  28. }
  29. 下面是ATMTester,在ATMTester中首先生成了10个ATM实例,然后启动10个线程,同时登录John的账户,先查询余额,然后,再提取余额的80%,然后再存入等额的款(以维持最终的余额的不变)。按照我们的预想,应该不会发生金额不足的问题。首先看代码:

    1. public  class ATMTester {
    2.     private static final int NUM_OF_ATM = 10;
    3.     public static void main(String[] args) {
    4.         ATMTester tester = new ATMTester();
    5.         
    6.         final Thread thread[] = new Thread[NUM_OF_ATM];
    7.         final ATM atm[] = new ATM[NUM_OF_ATM];
    8.         for (int i=0; i
    9.             atm[i] = new ATM();
    10.             thread[i] = new Thread(tester.new Runner(atm[i]));
    11.             thread[i].start();
    12.         }    
    13.         
    14.     }
    15.     
    16.     class Runner implements Runnable {
    17.         ATM atm;
    18.         
    19.         Runner(ATM atm) {
    20.             this.atm = atm;
    21.         }
    22.         
    23.         public void run() {
    24.             atm.login("John");
    25.             //查询余额
    26.             float bal = atm.getBalance();
    27.             try {
    28.                 Thread.sleep(1); //模拟人从查询到取款之间的间隔
    29.             } catch (InterruptedException e) {
    30.                 // ignore it
    31.             } 
    32.             
    33.             try {
    34.                 System.out.println("Your balance is:" + bal);
    35.                 System.out.println("withdraw:" + bal * 0.8f);
    36.                 atm.withdraw(bal * 0.8f);
    37.                 System.out.println("deposit:" + bal * 0.8f);
    38.                 atm.deposit(bal * 0.8f);
    39.             } catch (InsufficientBalanceException e1) {
    40.                 System.out.println("余额不足!");
    41.             } finally {
    42.                                     atm.logout();
    43.                            }
    44.         }
    45.     }
    46. }


    运行ATMTester,结果如下(每次运行结果都有所差异):

    Your balance is:1000.0
    withdraw:800.0
    deposit:800.0
    Your balance is:1000.0
    Your balance is:1000.0
    withdraw:800.0
    withdraw:800.0
    余额不足!
    Your balance is:200.0
    Your balance is:200.0
    Your balance is:200.0
    余额不足!
    Your balance is:200.0
    Your balance is:200.0
    Your balance is:200.0
    Your balance is:200.0
    withdraw:160.0
    withdraw:160.0
    withdraw:160.0
    withdraw:160.0
    withdraw:160.0
    withdraw:160.0
    withdraw:160.0
    deposit:160.0
    余额不足!
    余额不足!
    余额不足!
    余额不足!
    余额不足!
    余额不足!

    为什么会出现这样的情况?因为我们这儿是多个ATM同时对同一账户进行操作,比如一个ATM查询出了余额为1000,第二个ATM也查询出了余额1000,然后两者都期望提取出800,那么只有第1个用户能够成功提出,因为在第1个提出800后,账户真实的余额就只有200了,而第二个用户仍认为余额为1000。这个问题是由于多个ATM同时对同一个账户进行操作所不可避免产生的后果。要解决这个问题,就必须限制同一个账户在某一时刻,只能由一个ATM进行操作。如何才能做到这一点?直接通过synchronized关键字可以吗?非常遗憾!因为我们现在需要对整个Account的多个方法进行同步,这是跨越多个方法的,而synchronized仅能对方法或者代码块进行同步。在下一篇我们将通过编写一个锁对象达到这个目的。

我们首先开发一个BusyFlag的类,类似于C++中的Simaphore。

  1. public  class BusyFlag {
  2.     protected Thread busyflag = null;
  3.     protected int busycount = 0;
  4.     
  5.     public synchronized void getBusyFlag() {
  6.         while (tryGetBusyFlag() == false) {
  7.             try {
  8.                 wait();
  9.             } catch (Exception e) {}            
  10.         }
  11.     }
  12.     
  13.     private synchronized boolean tryGetBusyFlag() {
  14.         if (busyflag == null) {
  15.             busyflag = Thread.currentThread();
  16.             busycount = 1;
  17.             return true;
  18.         }
  19.         
  20.         if (busyflag == Thread.currentThread()) {
  21.             busycount++;
  22.             return true;
  23.         }
  24.         return false;        
  25.     }
  26.     
  27.     public synchronized void freeBusyFlag() {
  28.         if(getOwner()== Thread.currentThread()) {
  29.             busycount--;
  30.             if(busycount==0) {
  31.                 busyflag = null;
  32.                                      notify();
  33.                             }
  34.         }
  35.     }
  36.     
  37.     public synchronized Thread getOwner() {
  38.         return busyflag;
  39.     }
  40. }


注:参考Scott Oaks & Henry Wong《Java Thread》

BusyFlag有3个公开方法:getBusyFlag, freeBusyFlag, getOwner,分别用于获取忙标志、释放忙标志和获取当前占用忙标志的线程。使用这个BusyFlag也非常地简单,只需要在需要锁定的地方,调用BusyFlag的getBusyFlag(),在对锁定的资源使用完毕时,再调用改BusyFlag的freeBusyFlag()即可。下面我们开始改造上篇中的Account和ATM类,并应用BusyFlag工具类使得同时只有一个线程能够访问同一个账户的目标得以实现。首先,要改造Account类,在Account中内置了一个BusyFlag对象,并通过此标志对象对Account进行锁定和解锁:

  1. import  java.util.Collections;
  2. import  java.util.HashMap;
  3. import  java.util.Map;
  4. class  Account {
  5.     String name;
  6.     //float amount;
  7.     
  8.     BusyFlag flag = new BusyFlag();
  9.     
  10.     //使用一个Map模拟持久存储
  11.     static Map storage = new HashMap();
  12.     static {
  13.         storage.put("John"new Float(1000.0f));
  14.         storage.put("Mike"new Float(800.0f));
  15.     }
  16.     
  17.     static Map accounts = Collections.synchronizedMap(new HashMap());    
  18.     
  19.     
  20.     private Account(String name) {
  21.         this.name = name;
  22.         //this.amount = ((Float)storage.get(name)).floatValue();
  23.     }
  24.     
  25.     public synchronized static Account getAccount (String name) {
  26.         if (accounts.get(name) == null)
  27.             accounts.put(name, new Account(name));
  28.         return (Account) accounts.get(name);
  29.     }
  30.     public synchronized void deposit(float amt) {
  31.         float amount = ((Float)storage.get(name)).floatValue();
  32.         storage.put(name, new Float(amount + amt));
  33.     }
  34.     public synchronized void withdraw(float amt) throws InsufficientBalanceException {
  35.         float amount = ((Float)storage.get(name)).floatValue();
  36.         if (amount >= amt)
  37.             amount -= amt;
  38.         else 
  39.             throw new InsufficientBalanceException();
  40.                 
  41.         storage.put(name, new Float(amount));
  42.     }
  43.     public float getBalance() {
  44.         float amount = ((Float)storage.get(name)).floatValue();
  45.         return amount;
  46.     }
  47.     
  48.     public void lock() {
  49.         flag.getBusyFlag();
  50.     }
  51.     
  52.     public void unlock() {
  53.         flag.freeBusyFlag();
  54.     }
  55. }

新的Account提供了两个用于锁定的方法:lock()和unlock(),供Account对象的客户端在需要时锁定Account和解锁Account,Account通过委托给BusyFlag来提供这个机制。另外,大家也发现了,新的Account中提供了对Account对象的缓存,同时去除了public的构造方法,改为使用一个静态工厂方法供用户获取Account的实例,这样做也是有必要的,因为我们希望所有的ATM机同时只能有一个能够对同一个Account进行操作,我们在Account上的锁定是对一个特定Account对象进行加锁,如果多个ATM同时实例化多个同一个user的Account对象,那么仍然可以同时操作同一个账户。所以,要使用这种机制就必须保证Account对象在系统中的唯一性,所以,这儿使用一个Account的缓存,并将Account的构造方法变为私有的。你也可以说,通过在Account类锁上进行同步,即将Account中的BusyFlag对象声明为static的,但这样就使同时只能有一台ATM机进行操作了。这样,在一台ATM机在操作时,全市其它的所有的ATM机都必须等待。
另外必须注意的一点是:Account中的getAccount()方法必须同步,否则,将有可能生成多个Account对象,因为可能多个线程同时到达这个方法,并监测到accounts中没有“John”的Account实例,从而实例化多个John的Account实例。s

ATM类只需作少量改动,在login方法中锁定Account,在logout方法中解锁:

  1. public  class ATM {
  2.     Account acc;
  3.     
  4.     //作为演示,省略了密码验证
  5.     public synchronized boolean login(String name) {
  6.         if (acc != null)
  7.             throw new IllegalArgumentException("Already logged in!");
  8.         acc = Account.getAccount(name);
  9.         acc.lock();
  10.         return true;
  11.     }
  12.     
  13.     public void deposit(float amt) {
  14.         acc.deposit(amt);
  15.     }
  16.     
  17.     public void withdraw(float amt) throws InsufficientBalanceException  {
  18.             acc.withdraw(amt);
  19.     }
  20.     
  21.     public float getBalance() {
  22.         return acc.getBalance();
  23.     }
  24.     
  25.     public synchronized void logout () {
  26.         acc.unlock();
  27.         acc = null;
  28.     }
  29.     
  30. }



ATMTester类不需要做任何修改即可同样运行,同时保证同一个Account同时只能由一个ATM进行操作。解决了上篇提到的多个ATM同时对同一个Account进行操作造成的问题。

在最新的Doug Lea的util.concurrent工具包中(现处于JSR166)提供了类似的并发实用类:ReentrantLock,它实现了java .util.concurrent.locks.Lock接口(将在JDK1.5中发布),它的作用也类似于我们这儿的BusyFlag,实现机制、使用方法也相似。但这是一个工业强度的可重入锁的实现类。在ReentrantLock的API文档中有它的使用示例:

  1.      Lock l = ...; 
  2.      l.lock();
  3.      try {
  4.          // access the resource protected by this lock
  5.      } finally {
  6.          l.unlock();
  7.      }
posted @ 2006-08-17 19:30 Lansing 阅读(484) | 评论 (0)编辑 收藏
对象关系映射(Object Relative Mapping)简称ORM,是面向对象开发的一个热点,用来解决JDBC开发中手动进行OR映射的繁杂与不便。EJB中的实体Bean在这个领域是很著名的——既因为它的先进而著名,也因为它的低效而著名。有过实体Bean开发经验的人可能都会为实现远程接口造成的效率低下而头痛,在很多不大不小的项目中,使用实体Bean是否得不偿失,争论很大。一个轻量级的持久化方案也许能够解决一些问题,Hibernate应此而生。

   Hibernate是一个中间层,它的目的是把数据库中的关系通过一定的规则映射成为对象,让Java开发人员不用太多的考虑底层数据库的问题,只需要像通常情况下管理对象一样的管理数据。在关系数据库仍将持续占据市场的情况下,它很可观。在数据持久化领域,即便是轻量级的方案也会是复杂饶舌的,也许如同周杰伦的音乐一样不知所云。在学习它之前,最好先回想一下以前进行数据库开发中遇到的问题和不便,想想为什么需要一个持久化层,才能知道很多操作的目的是什么,以及为什么要这么干,在这个问题上我不想做更多的叙述,因为“长久以来……”这样的句式通常long(不好意思,打不出来)长,会对我的键盘和热情造成很大的磨损。如果让我写一本书,那么我会乐意去叙述什么是数据持久化,它有什么好处等等。废话少说,来了。

   首先需要配置环境,下载Hibernate 2.1(www.hibernate.org),把lib下的*.jar添加到classpath,你的数据库JDBC驱动程序也应该在classpath中。打开hibernate.properties,针对你使用的数据库,配置相应的信息,比如我使用的是MS SQL Server,配置如下:

 ## MS SQL Server

 hibernate.dialect net.sf.hibernate.dialect.SQLServerDialect
 hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver
 hibernate.connection.url jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=zizz
 hibernate.connection.username sa
 hibernate.connection.password

   其中很大部分是已经写好的,只需要取掉注释即可,我自己只是修改了数据库名称、帐号、密码。建立一个名为zizz的数据库备用。

   然后把这个文件拷贝到你的应用的根目录下。

   我们谈论了很多次映射,应该首先来看看这个映射是如何完成的。假设一个最简单的应用,写一个功能最单一的留言板,设计的数据有留言的编号、留言者名称、留言内容,还有留言时间。足够简单吧,换做是你打算怎么干?我猜你要首先建立一个数据库表格,名字也许叫做guestbook。No,这不是面向对象的方式,不妨首先从对象的角度来考虑。我们当然希望每一条留言都以对象的方式呈现,每个对象应该具有的属性有:id、author、content、time。偷个懒,没有画UML。下面这个类应该是很容易理解的:

 //GuestBook.java
 package org.bromon.zizz;

 import java.util.*;

 public class GuestBook
 {
 private int id;
 private String author;
 private String content;
 private Calendar time;

 private void setId(int id)
 {
  this.id=id;
 }
 public int getId()
 {
  return(id);
 }

 public void setAuthor(String author)
 {
  this.author=author;
 }
 public String getAuthro()
 {
  return(author);
 }

 public void setContent(String content)
 {
  this.content=content;
 }
 public String getContent()
 {
  return(content);
 }

 public void setTime(Calendar time)
 {
  this.time=time;
 }
 public Calendar getTime()
 {
  return(time);
 }
 }


   基本上是最简单的Bean了,如果觉得困难的话,请你先回火星等我。

   需要注意的是setId方法被指定为private,这是因为我希望用这个字段做主键,它最好由系统自动生成,所以不应该由用户来指定,这个方法专为Hibernate准备,所以是私有的。

   如何把这个类与数据库映射起来?看看Hibernate的魔法,使用一个XML文件来描述,它应该被命名为GuestBook.hbm.xml:

 &lt ?xml version="1.0"? &gt
 &lt !DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
        "' target=_blank &gthttp://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" &gt

 &lt hibernate-mapping package="org.bromon.zizz" &gt
    &lt class name="GuestBook" table=”guestbook" lazy="true" &gt
        &lt id name="id" type="integer" unsaved-value="null" &gt
   &lt column name="id" sql-type="int" not-null="true"/ &gt
   &lt generator class="identity"/ &gt
  &lt /id &gt
       
  &lt property name="author" column="author" not-null="true" unique="false"/ &gt
 &lt property name="content" column="content" not-null="true"/ &gt
  &lt property name="time" column="time" not-null="true"/ &gt
    &lt /class &gt
 &lt /hibernate-mapping &gt

 虽然有点陌生,但是很易读,仔细琢磨一下。

 下面来编写我们的应用,它的功能是插入数据:

 //Operate.java
 package org.bromon.zizz;
 import net.sf.hibernate.*;
 import net.sf.hibernate.cfg.*;
 import net.sf.hibernate.tool.hbm2ddl.*;
 import java.util.*;

 public class Operate
 {
 public static void main(String args[])
 {
  try
  {
   Configuration cfg=new Configuration().addClass(GuestBook.class);
   SessionFactory sessions=cfg.buildSessionFactory();
   new SchemaExport(cfg).create(true,true);
   Session session=sessions.openSession();
  
   GuestBook gb=new GuestBook();
   gb.setAuthor(“Bromon”);
   gb.setContent(“留言的内容”);
   gb.setTime(Calendar.getInstance());
  
   Transaction ts=session.beginTransaction();
   session.save(gb);
   ts.commit();
   session.close();
  }catch(Exception e)
  {
   System.out.println(e);
  }
 }
 }
   编译吧:javac –d . *.java
   执行一下:java org.bromon.zizz.Operate

 到数据库里面看看,表格已经建立好了,并且数据也已经保存。如果把

 new SchemaExport().create(true,true);

 注释掉,那么系统不会创建表格,而只是在已有的表格中添加新的记录,当然,如果表格不存在的话,会产生异常。

 你已经看到了Hibernate神奇魔法的5%,它足够的复杂强大,可以让你应付复杂的应用。

one-to-one关系
 在绝大多数系统当中不可能只存在一个数据表格,否则就违背了关系数据库的初衷。表与表的关系比较复杂,可以分为几种情况:

 ● 一对一关联(one to one)
 ● 一对多关联(one to many)
 ● 多对一关联(many to one)
 ● 多对多关联(many to many)

 按顺序来讲。假设一个一对一关联的例子是:
 表格:person
 id 编号(主键)
 name 姓名
 email email地址

 表格:spouse
 id 编号(外键)
 name 姓名

 person这个表保存用户信息,而spouse保存用户配偶的信息。在一般情况下一个人只有一个配偶,这很适合我们一对一的情况。如果你对婚外恋感兴趣的话,我们可以在一对多和多对一的关联中讨论这个问题,也许还可以在多对多中^_^(禽兽!)。

 OK,下面设计POJO:
 Person这个类非常简单:

 /*
 * Created on 2004-4-19
 */
 package org.bromon.zizz;

 /**
 * @author Bromon
 */
 public class Person
 {
 private int id;
 private String name;
 private String email;

 public void setId(int id)
 {
  this.id=id;
 }
 public int getId()
 {
  return(id);
 }

 public void setName(String name)
 {
  this.name=name;
 }
 public String getName()
 {
  return(name);
 }

 public void setEmail(String email)
 {
  this.email=email;
 }
 public String getEmail()
 {
  return(email);
 }
 }


 然后编写它的映射规则,这个应该能够理解了:
 &lt ?xml version="1.0"? &gt
 &lt !DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
 "' target=_blank &gthttp://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" &gt 

 &lt hibernate-mapping package="org.bromon.zizz" &gt
 &lt class name="Person" table="person" lazy="true" &gt
 &lt id name="id" type="integer" unsaved-value="null" &gt
 &lt column name="id" sql-type="int" not-null="true"/ &gt
 &lt generator class="identity"/ &gt
 &lt /id &gt

 &lt property name="name" column="name" not-null="true" unique="false"/ &gt
 &lt property name="email" column="email" not-null="false"/ &gt
 &lt /class &gt
 &lt /hibernate-mapping &gt

 so easy是不是?一切都按部就班。下面是Souse类:

 /*
 * Created on 2004-4-20
 */
 package org.bromon.zizz;

 /**
 * @author Bromon
 */
 public class Spouse
 {
 private int id;
 private String name;
 private Person person;

 public void setId(int id)
 {
  this.id=id;
 }
 public int getId()
 {
  return(id);
 }

 public void setName(String name)
 {
  this.name=name;
 }
 public String getName()
 {
  return(name);
 }

 public void setPerson(Person person)
 {
  this.person=person;
 }
 public Person getPerson()
 {
  return(person);
 }
 }


 注意里面的域person。它的映射文件:

 &lt ?xml version="1.0"? &gt
 &lt !DOCTYPE hibernate-mapping PUBLIC
 "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
 "' target=_blank &gthttp://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" &gt 

 &lt hibernate-mapping package="org.bromon.zizz" &gt
 &lt class name="Spouse" table="spouse" lazy="true" &gt
 &lt id name="id" type="integer" unsaved-value="null" &gt
 &lt column name="id" sql-type="int" not-null="true"/ &gt
 &lt generator class="foreign" &gt
 &lt param name="property" &gtperson&lt /param &gt
 &lt /generator &gt
 &lt /id &gt

 &lt property name="name" column="name" not-null="true" unique="false"/ &gt
 &lt one-to-one name="person" class="Person" cascade="all" constrained="true" / &gt
 &lt /class &gt
 &lt /hibernate-mapping &gt

 这里指明了id的generator是一个外键,和person相关联。然后指定一个one-to-one关系,不难理解是不是?Hibernate的确很符合我们的思维习惯。需要提醒的是,这种关联关系是单向的,Person并不需要去指定Spouse。

 下面来操作这两个类:

 /*
 * Created on 2004-4-20
 */
 package org.bromon.zizz;
 import net.sf.hibernate.*;
 import net.sf.hibernate.cfg.*;
 import net.sf.hibernate.tool.hbm2ddl.*;
 /**
 * @author Bromon
 */
 public class OperateSpouse
 {
 public static void main(String args[])
 {
  try
  {
   Configuration cfg=new Configuration().addClass(Spouse.class);
   cfg.addClass(Person.class);
   SessionFactory factory=cfg.buildSessionFactory();
   new SchemaExport(cfg).create(true,true);
   Session session=factory.openSession();
 
   Person person=new Person();
   person.setName("bromon");
   person.setEmail("bromon@163.com");
 
   Spouse spouse=new Spouse();
   spouse.setName("who");
   spouse.setPerson(person);
  
   Transaction ts=session.beginTransaction();
   session.save(person);
   session.save(spouse);
   ts.commit();
   session.close();
  }catch(Exception e)
  {
   System.out.println(e);
  }
 }
 }


 这个例子和第一篇中的例子非常相似。OK,执行一下,然后看看zizz数据库,搞掂。

Many-to-One关系

 很明显一对多或者多对一关系是关系数据库中非常常见的现象,下面通过父亲-儿子的例子来演示一对多关系,多对一关系是类似的,不过在我们的这个例子中不宜采用,否则会带来伦理学上的问题。

 首先定义Child类:
 /*
  * Created on 2004-5-8
  */
 package org.bromon.zizz;

 /**
  * @author Bromon
  */
 public class Child
 {
  private int id;
  private String name;
  private int fatherId;
  private Person father;

  public Child(){}
 
  /**
   * @return
   */
  public Person getFather()
  {
   return father;
  }

  /**
   * @return
   */
  public int getFatherId()
  {
   return fatherId;
  }

  /**
   * @return
   */
  public int getId()
  {
   return id;
  }

  /**
   * @return
   */
  public String getName()
  {
   return name;
  }

  /**
   * @param person
   */
  public void setFather(Person p)
  {
   father = p;
  }

  /**
   * @param i
   */
  public void setFatherId(int i)
  {
   fatherId = i;
  }

  /**
   * @param i
   */
  public void setId(int i)
  {
   id = i;
  }

  /**
   * @param string
   */
  public void setName(String string)
  {
   name = string;
  }

 }

 这里的fatherId是外键,关联person表的id字段。

 下面是映射文件Child.hbm.xml:
 &lt ?xml version="1.0"? &gt
 &lt !DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
        "' target=_blank &gthttp://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" &gt 

 &lt hibernate-mapping package="org.bromon.zizz" &gt
  &lt class name="Child" table="child" lazy="true" &gt
   &lt id name="id" type="integer" unsaved-value="null" &gt
    &lt column name="id" sql-type="int" not-null="true"/ &gt
    &lt generator class="identity"/ &gt
   &lt /id &gt
       
   &lt property name="name" column="name" not-null="true" unique="false"/ &gt
   &lt many-to-one name="father" column="fatherid" / &gt
      
  &lt /class &gt
 &lt /hibernate-mapping &gt
 需要注意的是fatherId并没有做为一个property被映射,而是在many-to-one声明中使用。

 需要对Person..java做修改,添加以下代码:

 import java.util.*;

 private Set children=new HashSet();
 /**
 * @return
 */
 public Set getChildren()
 {
  return children;
 }
 /**
 * @param set
 */
 public void setChildren(Set set)
 {
  children = set;
 }

 然后修改Person.hbm.xml,对添加的代码做映射:

 &lt set name="books" lazy="true" inverse="true" cascade="all"  &gt
  &lt key column="fatherid"/ &gt
  &lt one-to-many class="Child" / &gt
 &lt /set &gt

 这里的key column是child表的外键,inverse需要指定为true。

 下面做操作一下,功能是查询person表中id=1的记录,作为小孩的父亲,然后给child表添加一条新记录。

 /*
  * Created on 2004-5-8
  */
 package org.bromon.zizz;
 import net.sf.hibernate.*;
 import net.sf.hibernate.cfg.*;
 import net.sf.hibernate.tool.hbm2ddl.*;
 /**
  * @author Bromon
  */
 public class OperateChild
 {
  /**
   * @param args
   */
  public static void main(String args[])
  {
   try
   {
    Configuration cfg = new Configuration().addClass(Person.class);
    cfg.addClass(Child.class);
    SessionFactory sessions = cfg.buildSessionFactory();
    new SchemaExport(cfg).create(true, true);
    Session session = sessions.openSession();
   
    Child c=new Child();
   
    /*Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
    Person p=(Person)q.list().get(0);*/
   
    Person p=(Person)session.find("from org.bromon.zizz.Person as p where p.id=?",new Integer(1),Hibernate.INTEGER).get(0);
    System.out.println(p.getName());
    c.setName("andy");
    c.setFather(p);
   
    Transaction ts = session.beginTransaction();
    session.save(c);
    ts.commit();
    session.close();
   } catch (Exception e)
   {
    System.out.println(e);
   }
  }
 }

 被注释掉的部分是HQL的另外一种查询方法。在这个例子中可以看出对象的查询非常容易,不需要自己再去封装数据,修改和删除对象也很容易:

 //得到一个对象
 Query q=session.createQuery("from org.bromon.zizz.Person as p where p.id=1");
 Person p=(Person)q.list().get(0);

 //修改数据
 p.setName(“Mr Smith”);

 //保存数据
 session.save(p);
 session.flush();

 //删除数据
 session.delete(p);
 session.flush();

posted @ 2006-08-17 19:26 Lansing 阅读(297) | 评论 (0)编辑 收藏

Alt+左箭头,右箭头        以在编辑窗口切换标签
Alt+上下箭头,                以自动选择鼠标所在行,并将其上下移动
Ctrl+f6                            可以弹出菜单,上面列出可以切换的编辑窗口,这样不用鼠标也可切换
Ctrl+f7                            可以在视图之间切换 ,如编辑视图,输出视图,工程视图
Ctrl+f8                            可以在不同的观察视图中切换,就是在java视图,调试视图,等之间切换
Ctrl+m                            可以在最大化当前窗口和还原当前窗口之间切换
Ctrl+e                              弹出输入窗口,可以输入你想要编辑的代码窗口,和Ctrl+f6的功能相同,只不过一个是选择的方式,一个是输入的方式,切换窗口
Ctrl+T                              可以直接显示光标所在内容的类图,可以直接输入,并跳到输入内容部分
按住Ctrl键,然后鼠标指向变量名,方法名,类名       在源代码中快速跳转 
Ctrl + F11                      快速执行程序
Ctrl+Shift+F                   程序代码自动排版
Ctrl+Shift+O                 自动加入引用。说明: 假设我们没有Import任何类别时,当我们在程序里打入: ResourceAttirbute ra =new ResourceAttribute();  Eclipse会提示说没有引用类别,这时我们只要按下Ctrl+Shift+O ,它就会自动帮我们Import这个类别。 非常方便
Ctrl+/                            将选取的块注释起来:在Debug时很方便。 
Alt + /                           就是大家都应该最常用的代码辅助了
Ctrl+h                           搜索,打开搜索对话框
Ctrl+Shift+Space          参数提示,如果此时位于方法体中,就会出现方法的参数提示,当前光标所在位置的参数会用粗体显示

作用域 功能 快捷键 
全局 查找并替换 Ctrl+F 
文本编辑器 查找上一个 Ctrl+Shift+K 
文本编辑器 查找下一个 Ctrl+K 
全局 撤销 Ctrl+Z 
全局 复制 Ctrl+C 
全局 恢复上一个选择 Alt+Shift+↓ 
全局 剪切 Ctrl+X 
全局 快速修正 Ctrl1+1 
全局 内容辅助 Alt+/ 
全局 全部选中 Ctrl+A 
全局 删除 Delete 
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space 
Java编辑器 显示工具提示描述 F2 
Java编辑器 选择封装元素 Alt+Shift+↑ 
Java编辑器 选择上一个元素 Alt+Shift+← 
Java编辑器 选择下一个元素 Alt+Shift+→ 
文本编辑器 增量查找 Ctrl+J 
文本编辑器 增量逆向查找 Ctrl+Shift+J 
全局 粘贴 Ctrl+V 
全局 重做 Ctrl+Y 


查看
作用域 功能 快捷键 
全局 放大 Ctrl+= 
全局 缩小 Ctrl+- 


窗口
作用域 功能 快捷键 
全局 激活编辑器 F12 
全局 切换编辑器 Ctrl+Shift+W 
全局 上一个编辑器 Ctrl+Shift+F6 
全局 上一个视图 Ctrl+Shift+F7 
全局 上一个透视图 Ctrl+Shift+F8 
全局 下一个编辑器 Ctrl+F6 
全局 下一个视图 Ctrl+F7 
全局 下一个透视图 Ctrl+F8 
文本编辑器 显示标尺上下文菜单 Ctrl+W 
全局 显示视图菜单 Ctrl+F10 
全局 显示系统菜单 Alt+- 


导航
作用域 功能 快捷键 
Java编辑器 打开结构 Ctrl+F3 
全局 打开类型 Ctrl+Shift+T 
全局 打开类型层次结构 F4 
全局 打开声明 F3 
全局 打开外部javadoc Shift+F2 
全局 打开资源 Ctrl+Shift+R 
全局 后退历史记录 Alt+← 
全局 前进历史记录 Alt+→ 
全局 上一个 Ctrl+, 
全局 下一个 Ctrl+. 
Java编辑器 显示大纲 Ctrl+O 
全局 在层次结构中打开类型 Ctrl+Shift+H 
全局 转至匹配的括号 Ctrl+Shift+P 
全局 转至上一个编辑位置 Ctrl+Q 
Java编辑器 转至上一个成员 Ctrl+Shift+↑ 
Java编辑器 转至下一个成员 Ctrl+Shift+↓ 
文本编辑器 转至行 Ctrl+L 


搜索
作用域 功能 快捷键 
全局 出现在文件中 Ctrl+Shift+U 
全局 打开搜索对话框 Ctrl+H 
全局 工作区中的声明 Ctrl+G 
全局 工作区中的引用 Ctrl+Shift+G 


文本编辑
作用域 功能 快捷键 
文本编辑器 改写切换 Insert 
文本编辑器 上滚行 Ctrl+↑ 
文本编辑器 下滚行 Ctrl+↓ 


文件
作用域 功能 快捷键 
全局 保存 Ctrl+X 
Ctrl+S 
全局 打印 Ctrl+P 
全局 关闭 Ctrl+F4 
全局 全部保存 Ctrl+Shift+S 
全局 全部关闭 Ctrl+Shift+F4 
全局 属性 Alt+Enter 
全局 新建 Ctrl+N 


项目
作用域 功能 快捷键 
全局 全部构建 Ctrl+B 


源代码
作用域 功能 快捷键 
Java编辑器 格式化 Ctrl+Shift+F 
Java编辑器 取消注释 Ctrl+\ 
Java编辑器 注释 Ctrl+/ 
Java编辑器 添加导入 Ctrl+Shift+M 
Java编辑器 组织导入 Ctrl+Shift+O 
Java编辑器 使用try/catch块来包围 未设置,太常用了,所以在这里列出,建议自己设置。
也可以使用Ctrl+1自动修正。 


运行
作用域 功能 快捷键 
全局 单步返回 F7 
全局 单步跳过 F6 
全局 单步跳入 F5 
全局 单步跳入选择 Ctrl+F5 
全局 调试上次启动 F11 
全局 继续 F8 
全局 使用过滤器单步执行 Shift+F5 
全局 添加/去除断点 Ctrl+Shift+B 
全局 显示 Ctrl+D 
全局 运行上次启动 Ctrl+F11 
全局 运行至行 Ctrl+R 
全局 执行 Ctrl+U 


重构
作用域 功能 快捷键 
全局 撤销重构 Alt+Shift+Z 
全局 抽取方法 Alt+Shift+M 
全局 抽取局部变量 Alt+Shift+L 
全局 内联 Alt+Shift+I 
全局 移动 Alt+Shift+V 
全局 重命名 Alt+Shift+R 
全局 重做 Alt+Shift+Y 

热键篇:Template:Alt + /修改处:窗口->喜好设定->工作台->按键->编辑->内容辅助。个人习惯:Shift+SPACE(空白)。简易说明:编辑程序代码时,打sysout +Template启动键,就会自动出现:System.out.println(); 。设定Template的格式:窗口->喜好设定->Java->编辑器->模板。程序代码自动排版:Ctrl+Shift+F修改处:窗口->喜好设定->工作台->按键->程序代码->格式。个人习惯:Alt+Z。自动排版设定:窗口->喜好设定->Java->程序代码格式制作程序。样式页面->将插入tab(而非空格键)以内缩,该选项取消勾选,下面空格数目填4,这样在自动编排时会以空格4作缩排。快速执行程序:Ctrl + F11个人习惯:ALT+X修改处:窗口->喜好设定->工作台->按键->执行->启动前一次的启动作业。简易说明:第一次执行时,它会询问您执行模式,设置好后,以后只要按这个热键,它就会快速执行。
<ALT+Z(排版完)、ATL+X(执行)>..我觉得很顺手^___^自动汇入所需要的类别:Ctrl+Shift+O简易说明:假设我们没有Import任何类别时,当我们在程序里打入:

BufferedReader buf =
new BufferedReader(new InputStreamReader(System.in));

此时Eclipse会警示说没有汇入类别,这时我们只要按下Ctrl+Shift+O,它就会自动帮我们Import类别。查看使用类别的原始码:Ctrl+鼠标左键点击简易说明:可以看到您所使用类别的原始码。将选取的文字批注起来:Ctrl+/简易说明:Debug时很方便。修改处:窗口->喜好设定->工作台->按键->程序代码->批注视景切换:Ctrl+F8个人习惯:Alt+S。修改处:窗口->喜好设定->工作台->按键->窗口->下一个视景。简易说明:可以方便我们快速切换编辑、除错等视景。密技篇:一套Eclipse可同时切换,英文、繁体、简体显示:
1.首先要先安装完中文化包。
2.在桌面的快捷方式后面加上参数即可,英文-> -nl "zh_US"繁体-> -nl "zh_TW"简体-> -nl "zh_CN"。
(其它语系以此类推)像我2.1.2中文化后,我在我桌面的Eclipse快捷方式加入参数-n1 "zh_US"。
"C:\Program Files\eclipse\eclipse.exe" -n "zh_US"接口就会变回英文语系噜。利用Eclipse,在Word编辑文书时可不必将程序代码重新编排:将Eclipse程序编辑区的程序代码整个复制下来(Ctrl+C),直接贴(Ctrl+V)到
Word或WordPad上,您将会发现在Word里的程序代码格式,跟Eclipse所设定的完全一样,包括字型、缩排、关键词颜色。我曾试过JBuilder、GEL、NetBeans...使用复制贴上时,只有缩排格式一样,字型、颜色等都不会改变。外挂篇:外挂安装:将外挂包下载回来后,将其解压缩后,您会发现features、
plugins这2个数据夹,将里面的东西都复制或移动到Eclipse的features、plugins数据夹内后,重新启动Eclipse即可。让Eclipse可以像JBuilderX一样使用拖拉方式建构GUI的外挂:
1.Jigloo SWT/Swing GUI Builder :http://cloudgarden.com/jigloo/index.html下载此版本:Jigloo plugin for Eclipse (using Java 1.4 or 1.5)安装后即可由档案->新建->其它->GUI Form选取要建构的GUI类型。

2.Eclipse Visual Editor Project:http://www.eclipse.org/vep/点选下方Download Page,再点选Latest Release 0.5.0进入下载。除了VE-runtime-0.5.0.zip要下载外,以下这2个也要:
EMF build 1.1.1: (build page) (download zip) 
GEF Build 2.1.2: (build page) (download zip) 

3.0 M8版本,请下载:
EMF build I200403250631
GEF Build I20040330
VE-runtime-1.0M1安装成功后,便可由File->New->Visual Class开始UI设计。安装成功后,即可由新建->Java->AWT与Swing里选择所要建构的GUI类型开始进行设计。VE必须配合着对应版本,才能正常使用,否则即使安装成功,使用上仍会有问题。使用Eclipse来开发JSP程序:外挂名称:lomboz(下载页面)http://forge.objectweb.org/project/showfiles.php?group_id=97请选择适合自己版本的lomboz下载,lomboz.212.p1.zip表示2.1.2版,
lomboz.3m7.zip表示M7版本....以此类推。
lomboz安装以及设置教学:Eclipse开发JSP-教学文件

Java转exe篇:实现方式:Eclipse搭配JSmooth(免费)。
1.先由Eclipse制作包含Manifest的JAR。制作教学
2.使用JSmooth将做好的JAR包装成EXE。
JSmooth下载页面:http://jsmooth.sourceforge.net/index.php
3.制作完成的exe文件,可在有装置JRE的Windows上执行。

Eclipse-Java编辑器最佳设定:编辑器字型设定:工作台->字型->Java编辑器文字字型。
(建议设定Courier New -regular 10)编辑器相关设定:窗口->喜好设定->Java->编辑器外观:显示行号、强调对称显示的方括号、强调显示现行行、显示打印边距,将其勾选,Tab宽度设4,打印编距字段设80。程序代码协助:采预设即可。语法:可设定关键词、字符串等等的显示颜色。附注:采预设即可。输入:全部字段都勾选。浮动说明:采预设即可。导览:采预设即可。使自动排版排出来的效果,最符合Java设计惯例的设定:自动排版设定:窗口->喜好设定->Java->程序代码制作格式。换行:全部不勾选。分行:行长度上限设:80。样式:只将强制转型后插入空白勾选。内缩空格数目:设为4。

1. Control-Shift-T: 打开类型(Open type)。如果你不是有意磨洋工,还是忘记通过源码树(source tree)打开的方式吧。

2. Control-Shift-R: 打开资源(不只是用来寻找Java文件)。小提示:利用Navigator视图的黄色双向箭头按钮让你的编辑窗口和导航器相关联。这会让你打开的文件对应显示在导航器的层级结构中,这样便于组织信息。如果这影响了速度,就关掉它。

3. F3: 打开申明(Open declaration)。或者,利用Declaration Tab(在Java视图模式下,选择Windows --> Show View -- > Declaration)。当你选中代码中的一个方法,然后按这个按键,它会把整个方法在申明方框里显示出来。

4. Alt-left arrow: 在导航历史记录(Navigation History)中后退。就像Web浏览器的后退按钮一样,在利用F3跳转之后,特别有用。(用来返回原先编译的地方)

5. Alt-right arrow: 导航历史记录中向前。

6. Control-Q: 回到最后依次编辑的地方。这个快捷键也是当你在代码中跳转后用的。特别是当你钻的过深,忘记你最初在做什么的时候。

7. Control-Shift-G: 在workspace中搜索引用(reference)。这是重构的前提。对于方法,这个热键的作用和F3恰好相反。它使你在方法的栈中,向上找出一个方法的所有调用者。一个与此相关的功能是开启“标记”功能(occurrence marking) 。选择Windows->Preferences->Java-> Editor-> Mark Occurrences,勾选选项。这时,当你单击一个元素的时候,代码中所有该元素存在的地方都会被高亮显示。我个人只使用“标记本地变量”(Mark Local Variables)。注意:太多的高亮显示会拖慢Eclipse。

8. Control-Shift-F: 根据代码风格设定重新格式化代码。我们的团队有统一的代码格式,我们把它放在我们的wiki上。要这么做,我们打开Eclipse,选择WindowPreferencesJavaCode Style,然后设置Code Formatter,Code Style和Organize Imports。利用导出(Export)功能来生成配置文件。我们把这些配置文件放在wiki上,然后团队里的每个人都导入到自己的Eclipse中。

9. Control-O: 快速概要(quick outline)。通过这个快捷键,你可以迅速的跳到一个方法或者属性,只需要输入名字的头几个字母。 

10. Control-/: 对一行注释或取消注释。对于多行也同样适用。

11. Control-Alt-down arrow: 复制高亮显示的一行或多行。

12. Alt-down arrow: 将一行或多行向下移动。Alt-up arrow会向上移动。

其他的热键在菜单里有。你可以通过按下Control-Shift-L(从3.1版本开始),看到所有快捷键的列表。按下Control-Shift-L两次,会显示热键对话框(Keys Preferences dialog),你可以在这里自己设置热键。我欢迎你在Talkback部分发表你的Eclipse提示。


Ctrl+1 快速修复(最经典的快捷键,就不用多说了)

Ctrl+D: 删除当前行

Ctrl+Alt+↓ 复制当前行到下一行(复制增加)

Ctrl+Alt+↑ 复制当前行到上一行(复制增加)

Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)

Alt+↑ 当前行和上面一行交互位置(同上)

Alt+← 前一个编辑的页面

Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)

Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性

Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)

Shift+Ctrl+Enter 在当前行插入空行(原理同上条)

Ctrl+Q 定位到最后编辑的地方

Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)

Ctrl+M 最大化当前的Edit或View (再按则反之)

Ctrl+/ 注释当前行,再按则取消注释

Ctrl+O 快速显示 OutLine

Ctrl+T 快速显示当前类的继承结构

Ctrl+W 关闭当前Editer

Ctrl+K 参照选中的Word快速定位到下一个

Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)

Ctrl+/(小键盘) 折叠当前类中的所有代码

Ctrl+×(小键盘) 展开当前类中的所有代码

Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)

Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)

Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)

Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)

Ctrl+Shift+F4 关闭所有打开的Editer

Ctrl+Shift+X 把当前选中的文本全部变味小写

Ctrl+Shift+Y 把当前选中的文本全部变为小写

Ctrl+Shift+F 格式化当前代码

Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)

下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开头的了)

Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)

Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)

Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)

Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)

Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)

Alt+Shift+I 合并变量(可能这样说有点不妥Inline)

Alt+Shift+V 移动函数和变量(不怎么常用)

Alt+Shift+Z 重构的后悔药(Undo)


经常用到的Eclipse快捷键
存盘 Ctrl+s(一定记住)
注释代码 Ctrl+/
取消注释 Ctrl+\(Eclipse3已经都合并到Ctrl+/了)
代码辅助 Alt+/
快速修复 Ctrl+1
代码格式化 Ctrl+Shift+f
整理导入 Ctrl+Shift+o
切换窗口 Ctrl+f6<可改为ctrl+tab方便>
ctrl+shift+M 导入未引用的包
ctrl+w 关闭单个窗口
F3 跳转到类、变量的声明
F11 运行上次程序
Ctrl + F11 调试上次程序
Alt + 回下一个编辑点
ctrl+shift+T 查找工程中的类

posted @ 2006-08-17 14:17 Lansing 阅读(3674) | 评论 (0)编辑 收藏
仅列出标题
共4页: 上一页 1 2 3 4 下一页 
<2025年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

欢迎探讨,努力学习Java哈

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

文章档案

Lansing's Download

Lansing's Link

我的博客

搜索

  •  

最新评论

阅读排行榜

评论排行榜