Tuesday, December 22, 2009

maven2|的本地配置了解

maven2的本地配置了解_快马扬鞭须努力!

maven2的本地配置了解
2009-11-12 11:30

maven2的本地配置了解

其实需求很简单,我本机安装了一个私服nexus,有时也用artifactory,同时公司也有一个私服nexus。
也就是说我平时能用的私服有:
本机
http://localhost:8081/nexus
公司1
http://10.206.19.59:81/nexus
公司2
http://10.206.19.252:8081/artifactory

各个服务有时并不一定都保持开机的,所以我想我的maven2是一个一个去试,连得上哪个用哪个。最近修改了本地的settings.xml配置后,基本实现了这个功能,使用了一段时间后发现没有什么问题,现在将settings.xml配置文件记录一下:
<settings xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                               http://maven.apache.org/xsd/settings-1.0.0.xsd">
<profiles>
  
   <profile>
      <id>nexus_local</id>
      <repositories>
        <repository>
            <id>nexus_local</id>
            <name>local private nexus</name>
            <url>http://localhost:8081/nexus/content/groups/public</url>
            <releases><enabled>true</enabled></releases>
            <snapshots><enabled>false</enabled></snapshots>
        </repository>
        <repository>
            <id>nexus_local</id>
            <name>local private nexus</name>
            <url>http://localhost:8081/nexus/content/groups/public-snapshots</url>
            <releases><enabled>false</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
        <repository>
            <id>nexus_company</id>
            <name>local private nexus</name>
            <url>http://10.206.19.59:81/nexus/content/groups/public</url>
            <releases><enabled>true</enabled></releases>
            <snapshots><enabled>false</enabled></snapshots>
        </repository>
        <repository>
            <id>nexus_company</id>
            <name>local private nexus</name>
            <url>http://10.206.19.59:81/nexus/content/groups/public-snapshots</url>
            <releases><enabled>false</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
            <id>nexus_local</id>
            <name>local private nexus</name>
            <url>http://localhost:8081/nexus/content/groups/public</url>
            <releases><enabled>true</enabled></releases>
            <snapshots><enabled>false</enabled></snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>nexus_local</id>
            <name>local private nexus</name>
            <url>http://localhost:8081/nexus/content/groups/public-snapshots</url>
            <releases><enabled>false</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>nexus_company</id>
            <name>local private nexus</name>
            <url>http://10.206.19.59:81/nexus/content/groups/public</url>
            <releases><enabled>true</enabled></releases>
            <snapshots><enabled>false</enabled></snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>nexus_company</id>
            <name>local private nexus</name>
            <url>http://10.206.19.59:81/nexus/content/groups/public-snapshots</url>
            <releases><enabled>false</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
       </pluginRepositories>
    </profile>
</profiles>
<activeProfiles>
   <activeProfile>nexus_local</activeProfile>
</activeProfiles>
</settings>

我的示例中只给出了nexus的两个服务的配置,其实按照这个思路,再多加几个artifactory或者nexus也无妨。

Maven2|安装总结

Maven2安装总结_zhaoyang blog

Maven2安装总结
2007-02-25 17:09

以前虽然使用过Maven1.0,当发现现在的Maven2.0的变化和1.0比起来变化很大,配置上倒还一样,但是使用上有很大不同,如果你还用1.0的命令,会发现许多不能使用。闲话少叙,进入正题。

首先当然去Apache网站下载Maven2了。下载地址:http://maven.apache.org/download.html 。我下载的版本是maven-2.0.4-bin.zip

然后我把maven-2.0.4-bin.zip解压到D:\maven-2.0.4

下面就是配置环境变量了,这和1.0的版本是一样的。(1)MAVEN_HOME:D:\maven-2.0.4 (2)在Path 中加入%MAVEN_HOME%\bin,如下图示:

这样配置就OK了。然后,我建立了如下目录:E:\app\mavenApp,当然这些可以随便建立,呵呵。

下面说一些Maven1.x和Maven2.x的不同(一些摘自一些网友文章)
------------------------------------
Maven2 把配置文件压缩成了pom.xml和settings.xml 两个文件,简洁了不少。POM是Maven的核心对象模型,在Maven2中POM已由project.xml转移到pom.xml中使用,版本也由3升级为4。对于项目,一般只需要pom.xml就行了。在Maven2中不需要也不提倡使用maven.xml

生命周期的引入
在Maven2中有了明确的生命周期概念,而且都提供与之对应的命令,使得项目构建更加清晰明了。主要的生命周期阶段:

validate,验证工程是否正确,所有需要的资源是否可用。
compile,编译项目的源代码。
test-compile,编译项目测试代码。
test,使用已编译的测试代码,测试已编译的源代码。
package,已发布的格式,如jar,将已编译的源代码打包。
integration-test,在集成测试可以运行的环境中处理和发布包。
verify,运行任何检查,验证包是否有效且达到质量标准。
install,把包安装在本地的repository中,可以被其他工程作为依赖来使用
deploy,在整合或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。
generate-sources,产生应用需要的任何额外的源代码,如xdoclet。

新增Dependency Scope
在POM 4中,<dependency>中还引入了<scope>,它主要管理依赖的部署。目前<scope>可以使用5个值:

compile,缺省值,适用于所有阶段,会随着项目一起发布。
provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。
runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。
test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。
system,类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。
------------------------------------

下面说些基本设置,打开D:\maven-2.0.4\conf\settings.xml
1. 改变<localRepository>E:/app/m2/repository</localRepository> 目的:改变本地的数据仓库目录(存放从远程下载的jar包),如果不改变也可以,如不改变,缺省目录为:C:\Documents and Settings\主机名\.m2\repository
2. 配置代理,主要目的是为了能从远程下载jar包,根据实际的网路情况进行配置吧。因为我现在单位是有代理服务器的,如果不配置,就不能从远程下载jar包了。我的配置如下(注意:如果你的网络没有代理服务器,就不用配置这个步骤)
      <proxy>
       <id>optional</id>
       <active>true</active>
       <protocol>http</protocol>
       <host>192.168.17.100</host>
       <port>8889</port>
       <nonProxyHosts>192.168.*.*</nonProxyHosts>
     </proxy>



下面就是一些goal命令了,这里和Maven1有不少不同,常用的命令有:

  • 创建Maven的普通java项目:mvn archetype:create -DgroupId=com.codeline.commons -DartifactId=pjoName    
  • 创建Maven的Web项目:mvn archetype:create -DgroupId=com.mycompany.app
    -DartifactId=my-webapp
    -DarchetypeArtifactId=maven-archetype-webapp    
  • 编译源代码:mvn compile (或者:mvn compiler:compile)   
  • 编译测试代码:mvn test-compile    
  • 运行测试:mvn test    
  • 产生site:mvn site    
  • 打包:mvn package    
  • 在本地Repository中安装jar:mvn install    
  • 清除产生的项目:mvn clean    
  • 生成eclipse项目:mvn eclipse:eclipse   
  • 生成idea项目:mvn idea:idea   
  • 组合使用goal命令,如只打包不测试:mvn -Dtest package    
  • 编译测试的内容:mvn test-compile   
  • 只打jar包: mvn jar:jar   
  • 只测试而不编译,也不测试编译:mvn test -skipping compile -skipping test-compile (这里要特别注意 -skipping 的灵活运用,当然也可以用于其他组合命令)   
  • 清除eclipse的一些系统设置:mvn eclipse:clean   

    =========================================================

    配置存储库

    要求项目的每个开发者必须在conf目录中配置存储库是不方便的,所以Maven可以同时查看多个存储库并且将它们全部配置在pom.xml文件中。让我们看看一个例子,它展示了如何在应用程序用使用多个存储库。在以下从pom.xml文件摘录的片断中,我们设置了两个存储库来让Maven寻找依赖项。Ibiblio一直是默认的存储库,我们又添加了Planet Mirror作为后援存储库。我们也可以让团队使用的本地web服务器作为第二个存储库。

    <repositories>
         <repository>
           <id>Ibiblio</id>
           <name>Ibiblio</name>
           <url>http://www.ibiblio.org/maven/</url>
         </repository>
         <repository>
           <id>PlanetMirror</id>
           <name>Planet Mirror</name>
            <url>http://public.planetmirror.com/pub/maven/</url>
         </repository>
       </repositories>

    ==================================================
    遇到的小问题解决。
    * 当建立一个工程后,执行 mvn eclipse:eclipse ,建立一个maven的工程,用eclipse打开后,发现报如下错误:Unbound classpath variable: 'M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar' in project my-webapp   ,这是因为在Eclipse 没有配置 M2_REPO 变量,配置步骤:window >> preferences >> Java >> Build Path >> Classpath Variables
    新建一个 M2_REPO 的变量,变量值指向你系统的Maven2的数据仓库位置。如图:


    ------------------------------------------
    * POM.XML 继承的写法
    <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
         <modelVersion>4.0.0</modelVersion>
         <groupId>org.apache.hivemind</groupId>
         <artifactId>hivemind-examples</artifactId>
         <packaging>jar</packaging>
         <version>2.0-alpha-1</version>
         <parent>
             <groupId>org.apache.hivemind</groupId>
             <artifactId>hivemind-project</artifactId>
             <version>2.0-alpha-1</version>
             <relativePath>../pom.xml</relativePath>
         </parent>

         <name>HiveMind Examples</name>
         <inceptionYear>2003</inceptionYear>
         <dependencies>
             <dependency>
                 <groupId>org.apache.hivemind</groupId>
                 <artifactId>hivemind-framework</artifactId>
                 <version>2.0-alpha-1</version>
             </dependency>

    * 如何安装Jar(或war)依赖到本地的数据仓库中?
    下面具一个如何把dwr.jar包上传到本地数据仓库的例子,一看例子,大家就明了了。
    - 下载dwr.jar包到本地,我把它放到了 C:/ (C盘根目录),如图示:


    - 进入“命令行提示符”,进入到C:/,然后执行 mvn install:install-file -Dfile=dwr.jar -DgroupId=dwr.mocha -DartifactId=dwrDart -Dversion=1.0 -Dpackaging=jar 这样就把dwr传到了本地仓库中(我的本地仓库地址为:E:\app\m2\repository\),上传到仓库中的jar包名为dwrDart-1.0.jar,目录结构为dwr/mocha/dwrDart/1.0/dwrDart-1.0.jar如图示:


      
  • Java Build|结合Maven2进行J2EE项目构建

    结合Maven2进行J2EE项目构建 - 技术论坛-天使之城 - 51CTO技术博客

    结合Maven2进行J2EE项目构建


    2008-08-17 02:17:57
     标签:J2ee 项目管理 maven   [推送到技术圈]

    版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://zhangmeng.blog.51cto.com/310854/93165
    一.背景
    Maven2的基本原理很简单,采用远程仓库和本地仓库以及  pom(project object model).xml  ,将  pom.xml  中定义的  jar  文件从远程仓库下载到本地仓库,各个应用使用同一个本地仓库的  jar  ,同一个版本的  jar  只需下载一次,而且避免每个应用都去拷贝  jar  。如图  1  。同时它采用了现在流行的插件体系架构,只保留最小的核心,其余功能都通过插件的形式提供,所以在执行  maven  任务时,才会自动下载需要的插件。这个特性也为客户系统的升级带来的很大的方便,客户每次升级的时候可以使用maven的远程部署功能自动下载最新的系统组件(jar),并重新打包部署,很大程度的减少的系统升级的工作量。
    理解Maven的原理,可以参考 Pear ――PHP扩展与应用库( the PHP Extension and Application Repository ) ,其原理非常类似,都有一个官方库,都是微内核,通过网络将需要的文件下载到本地,通过官方仓库将相应的类库进行统一管理。
         Maven2的基本安装方法网上很多,就到[url]http://maven.apache.org[/url]下载一个最新版,解压后即可,如果需要在命令行运行,还需要设置一些环境变量,网上的资料很多,这里就不多说了。总之,安装成功后当你在命令行下执行maven -version后正确显示当前maven的版本即可。
         我们在项目中结合maven的进行开发的主要思路:
       1.建立支持Maven2的开发框架,框架中结合了一些项目功能和工具类,并且此框架本身是一个eclipse工程,支持使用eclipse IDE的开发,并通过CVS可进行团队协作。
       2.在Maven2的pom.xml中制定开发框架的依赖包,并建立依赖包的团队管理本地服务器,使团队中的包依赖得到统一管理。
       3.每日下班后,在构建服务器上每日从cvs上下载各个团队开发人员的代码,统一进行集成构建和测试。由于是每日构建,所以发现的bug可及时反馈给开发人员进行修正,避免了一般开发过程中的bug长时间遗留的情况。

    二.实施过程

    为了实现上述思路,我们分几步实施:

    1.首先需要构建一个系统的开发框架,
        我们有两种方式构建,
        其一是从零开始构建全新的框架,进入commond line,cd 到一个目录 ,执行
     
     
    mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-webapp -DarchetypeArtifactId=maven-archetype-webapp

    执行完毕后接下来cd 到项目目录my-webapp下,执行
    mvn package
    mvn eclipse:eclipse


    之后,打开eclipse,到其目录下导入项目,并手动编辑pom.xml文件,设定指定的jar包,比如加入一个jwebunit的jar包,我们需要在pom中添加一段:
    <dependency>
                <groupId>jwebunit</groupId>
                <artifactId>jwebunit</artifactId>
                <version>1.2</version>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>rhino</groupId>
                        <artifactId>js</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>


    其中指定了包的名称,版本,使用的范围域等,pom.xml设置方式网上也是一堆一堆的,具体的可以自己搜搜。同时我们也可以使用maven2在 eclipse中的插件进行编辑,很方便,就不用记住那些该死的标签了。插件下载地址[url]http://m2eclipse.codehaus.org[/url] /update,将这个url填入到eclipse的Help-》Software Updates->find&install中新建一个插件下载地址的对话框中即可下载。
    这种方式是完全自定义一个全新的工程后再进行框架搭建,比较累,尤其是添加依赖包的时候,需要根据自己的项目需要一个一个添加,很烦人,所以我们使用的第二个方法就直接找了一个现成的,到Appfus 的网站[url]http://appfuse.org/[/url] 根据项目需要下载了一个项目框架作为原型,我们使用的是appfuse-light-webwork-ibatis- 1.8.2(webwork2.26,spring2.0,ibatis2.0),如果你使用的是其他的的技术,如 struts2,hibernate....直接到网站上下载一个相应的框架即可。appfuse框架使用maven2作为基本构建工具,其中自带的 pom.xml也替开发人员写好了,中所定义的依赖包可满足一般的开发需要,如需要自己指定的包,那么直接在其pom.xml中添加即可。要将这个框架作为eclipse工程使用,需要在解压后的框架目录下执行:

    mvn eclipse:eclipse -DdownloadSources=true


    这个命令会将工程将框架转换为eclipse工程,并从远程下载jar包到本地仓库(window下是(C:\Documents and Settings\${username}\.m2\repository),之后执行:

    mvn -Declipse.workspace=<path-to-eclipse-workspace> eclipse:add-maven-repo


    其中path-to-eclipse-workspace是本机的eclipse的worksapce的路径。执行后maven会在eclipse中建立一个M2_REPO环境变量,并将其中所有的jar包引入到工程中,完全自动化,十分方便。
         打开eclipse修改开发中的环境变量(我们项目中使用了Myeclipse插件),找到相应的工程,发现框架中已有一些代码,这是appfuse提供给开发人员的示例代码,我们可以按照自己以前项目的积累进行对框架进行完善,形成一套自己的开发框架,之后设置工程环境变量,在该项目中右键 ->Myeclipse->add web capabilities->指定该工作空间下的Src/main/webapps作为WEB工程的根路径,并指定修改JAVA Build Path中
    src/main/java
    src/main/resource
    src/test/java

    的三个soucrefolder的outputpath 为scr/main/webapp/WEB-Inf/class,这样设置的目的是便于开发人员在本地进行部署测试,否则按照appfuse原有的工程设置是不能进行顺利部署的。
    至此,我们已经将Maven2结合到项目中,一开始可能对目录结构有些不适应,毕竟这是maven提供的项目框架格式,可以修改为自己习惯的,但是不建议这样做。设置完成后,cd到项目路径下,运行
    mvn test
    mvn package
    mvn install


    三个命令,均成功后,可上传到cvs/svn上面去,共享给项目组人员,各开发人员可直接使用,但有可能M2_REPO环境设置路径不一样(C:\Documents and Settings\${username}\.m2\repository,毕竟不是所有人都把系统装在C盘),需要手动修改一下。

    2.建立开发团队内部仓库
              为了便于团队的依赖包管理,我们不能全部使用官网的仓库,毕竟上面不具备我们项目开发所需要的所有的依赖包,所以我们需要为自己的团队建立一个内部仓库,可以自己管理所需的依赖包,建立一个内部仓库也十分简单:

    首先需要一个 http server ,找台服务器装上 apache 就行。放一个空的 maven 目录到 htdocs 下,假设服务器 ip 为 192.168.0.1 ,确认能用 [url]http://192.168.0.1/maven[/url] 访问到。

    copy本地仓库的jar包到服务器:对于 windows xp 来说一般在 C:\Documents and Settings\ % username%\.m2 下,其中% username %为操作系统登录用户名。这时你可以看到 ${user.home}/.m2/ 下有个 repository 目录,里面有很多的项目相关 jar ,目录按 groupId/ artifactId/version 排好。把 repository 目录整个拷贝到 apache 服务器的 maven 目录下,如果需要官方缺少的 jar 或公司内部 jar ,仿照这个目录结构,做好 jar 放到 maven 目录下。或者把包copy到本地,运行:
    mvn install:install-file -Dfile=X:/path/mail-1.3.jar -DartifactId=javamail -Dversion=1.3.1 -Dpackaging=jar -DgroupId=javamail



    开发人员要使用内部仓库,只需修改本地工程pom.xml ,在 repository 配置后加上:

    <repository>
          <id>companyName</id>
          <url>http:// ${ip}/maven</url>
    </repository>

    3.每日构建
        为了保证项目质量,尽早的发现项目中的bug,我们需要每日对系统进行构建,这也是我们使用maven的初衷之一,maven的几个命令就可帮助我们完成这项任务,当然我们可以使用持续构建工具与maven结合实现定时自动构建。构建方式:
    mvn test
    mvn package
    mvn install

    maven会自动编译,测试,运行所有的testcase,这也要求我们的开发人员一定要按照规则编写单元测试代码,否则每日构建的意义就不大了。appfuse框架中提供了很好的单元测试代码,包括针对数据库层,业务逻辑层,web展示层等等,如果我们能很好的编写这些单元测试,那么对于系统后续的缺陷管理和控制是大有裨益的。

    构建完成后或构建时需要对最新版本的项目进行部署,便于次日安排测试人员进行测试,maven提供多多种部署方式,在pom.xml进行项目的部署配置,不同的部署方式根据协议的不同,配置方式也有所差异:

    以文件方式部署

      
    <project>
            [...]
            <distributionManagement>
                <repository>
                    <id>proficio-repository</id>
                    <name>Proficio Repository</name>
                    <url>file://${basedir}/target/deploy</url>
                </repository>
            </distributionManagement>
            [...]
        </project>

    以SSH2方式部署

      
    <project>
            [...]
            <distributionManagement>
                <repository>
                    <id>proficio-repository</id>
                    <name>Proficio Repository</name>
                    <url>scp://sshserver.yourcompany.com/deploy</url>
                </repository>
                </distributionManagement>
            [...]
        </project>

     以SFTP方式部署

     
       <project>
        [...]
        <distributionManagement>
            <repository>
                <id>proficio-repository</id>
                <name>Proficio Repository</name>
                <url>sftp://ftpserver.yourcompany.com/deploy</url>
            </repository>
        </distributionManagement>
        [...]
        </project>

    以扩展SSH方式部署

       
    目前为止上述3中方式已经被Maven包含,所以只要distributionManagement就可以了,但是使用扩展SSH命令部署的话你不仅需要配置distributionManagement还需要一个build extension,如下
        <project>
            [...]
            <distributionManagement>
                <repository>
                    <id>proficio-repository</id>
                    <name>Proficio Repository</name>
                    <url>scpexe://sshserver.yourcompany.com/deploy</url>
                </repository>
            </distributionManagement>
            <build>
                <extensions>
                    <extension>
                        <groupId>org.apache.maven.wagon</groupId>
                        <artifactId>wagon-ssh-external</artifactId>
                        <version>1.0-alpha-6</version>
                    </extension>
                </extensions>
            </build>
            [...]
        </project>
        The build extension specifies the use of the Wagon external SSH provider, which does the work of moving your files to the remote server. Wagon is the general purpose transport mechanism used throughout Maven.

    以FTP方式部署

      
    <project>
            [...]
            <distributionManagement>
            <repository>
                <id>proficio-repository</id>
                <name>Proficio Repository</name>
                <url>[url]ftp://ftpserver.yourcompany.com/deploy</url>[/url]
            </repository>
            </distributionManagement>
            <build>
                <extensions>
                    <extension>
                    <groupId>org.apache.maven.wagon</groupId>
                    <artifactId>wagon-ftp</artifactId>
                    <version>1.0-alpha-6</version>
                    </extension>
                </extensions>
            </build>
            [...]
        </project>

    一旦你配置好了相应的POM你可以执行下列命令来开始部署:
    mvn deploy

    同时也可通过执行一下命令生成此项目的站点报告,供项目参与人员使用。
    mvn site


    三. 结论

        maven的强大显而易见,有很多其他的特性本文没有提及,如对各类插件的支持,以及对项目模块划分和继承关系的管理,这些都是maven的特性,也是maven对项目生命周期的详尽诠释,有兴趣深入的TX可以下载我在附件中提供的教程《Better Builds With Maven2》.同时我也提供我根据appfuse建立的一套项目框架,可在myeclipse环境下使用,大家可以共同探讨完善。

    附1:使用Maven2团队开发建立内部开发仓库详解
    在真正使用Maven后是为团队进行定制,所以我们不应使用官网的开发库,应在本地建立一个内部开发库对团队的jar包进行管理,所以我们首先搭建一个内部库环境,除文章上面所述的搭建Apache服务器方法外,我们还可以使用artifactory(下载地址:[url]http://www.jfrog.org/sites/artifactory/latest/[/url]),一个很好的maven内部库的应用系统,下载后执行bin目录下的artifactory.bat命令即可。启动后可访问控制台http://内部库ip:8081/artifactory/验证服务是否成功启动。默认的用户名为admin,密码为password。artifactory最重要的是可配置第三方jar包,在deploy artifacts中加入并制定其groupId和artifactId即可
    (不要忘记更改本地的pom.xml文件引入新加的jar包)。
    在开发端我们需要更改全局配置文件setting.xml文件,将工程中setting.xml放入本地maven2->conf目录下,配置内部仓库的地址,只需要在setting.xml的mirrors元素中加入以下配置:
    <mirror>
          <id>emay local</id>
          <mirrorOf>central</mirrorOf>
          <name>emay local artifactory</name>
          <url>http://内部库ip:8081/artifactory/repo</url>
        </mirror>

    这里要注意的是,在加入这段代码后我使用的appfuse框架中自带的应用服务器tomcat6进行构建,不能正常运行,报tomcat出错,把这段去掉或者在pom.xml中将应用服务器改为tomcat5.5后运行正常。看来maven还是有不少bug需要改进。

    配置完成后再运行mvn install即可正常进行构建,maven会从本地内部库中寻找项目所依赖的jar包。运行mvn clean清除maven生成文件。



    附2:maven2命令大全


        validate,验证工程是否正确,所有需要的资源是否可用。
        compile,编译项目的源代码。
        test-compile,编译项目测试代码。
        test,使用已编译的测试代码,测试已编译的源代码。
        package,已发布的格式,如jar,将已编译的源代码打包。
        integration-test,在集成测试可以运行的环境中处理和发布包。
        verify,运行任何检查,验证包是否有效且达到质量标准。
        install,把包安装在本地的repository中,可以被其他工程作为依赖来使用
        deploy,在整合或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。 
        generate-sources,产生应用需要的任何额外的源代码,如xdoclet。

    Thursday, December 17, 2009

    Version|软件版本GA,RC,Demo,Build意思

    软件版本GA,RC,Demo,Build意思 - vavi - JavaEye技术网站

    软件版本GA,RC,Demo,Build意思

    关键字: 版本 ga rc
    转自 http://blog.chinaunix.net/u2/64804/showart_1751869.html
    Alpha:

    是内部测试版,一般不向外部发布,会有很多Bug.一般只有测试人员使用。


    Beta:

    也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出。


    RC:(Release Candidate)

    顾名思义么 ! 用在软件上就是候选版本。系统平台上就是发行候选版本。RC版不会再加入新的功能了,主要着重于除错。


    RTM:(Release to Manufacture)

    是给工厂大量压片的版本,内容跟正式版是一样的,不过RTM版也有出限制、评估版的。但是和正式版本的主要程序代码都是一样的。


    OEM:
    是给计算机厂商随着计算机贩卖的,也就是随机版。只能随机器出货,不能零售。只能全新安装,不能从旧有操作系统升级。包装不像零售版精美,通常只有一面CD和说明书(授权书)。


    RVL:
    号称是正式版,其实RVL根本不是版本的名称。它是中文版/英文版文档破解出来的。


    EVAL:
    而流通在网络上的EVAL版,与"评估版"类似,功能上和零售版没有区别。


    RTL:Retail(零售版)
    是真正的正式版,正式上架零售版。在安装盘的i386文件夹里有一个eula.txt,最后有一行EULAID,就是你的版本。比如简体中文正式版是EULAID:WX.4_PRO_RTL_CN,繁体中文正式版是WX.4_PRO_RTL_TW。其中:如果是WX.开头是正式版,WB.开头是测试版。_PRE,代表家庭版;_PRO,代表专业版。
    ----------------------------------------------------------------------------------------------------------------------------------------

    V(Version):即版本,通常用数字表示版本号。(如:EVEREST Ultimate v4.20.1188 Beta )
    Build:用数字或日期标示版本号的一种方式。(如:VeryCD eMule v0.48a Build 071112)
    SP:Service Pack,升级包。(如:Windows XP SP 2/Vista SP 1)

    授权和功能划分:
    Trial:试用版,通常都有时间限制,有些试用版软件还在功能上做了一定的限制。可注册或购买成为正式版
    Unregistered:未注册版,通常没有时间限制,在功能上相对于正式版做了一定的限制。可注册或购买成为正式版。
    Demo:演示版,仅仅集成了正式版中的几个功能,不能升级成正式版。
    Lite:精简版。
    Full version:完整版,属于正式版。

    语言划分:
    SC:Simplified Chinese简体中文版。
    CN : 简体中文版
    GBK:简体中文汉字内码扩展规范版。
    TC:Traditional Chinese繁体中文版。
    CHT : 繁体中文版
    BIG5:繁体中文大五码版。
    EN : 英文版
    Multilanguage : 多语言版
    UTF8:Unicode Transformation Format 8 bit,对现有的中文系统不是好的解决方案。



    开发阶段划分:
    α(Alpha)版:内测版,内部交流或者专业测试人员测试用。Bug较多,普通用户最好不要安装。
    β(Beta)版:公测版,专业爱好者大规模测试用,存在一些缺陷,该版本也不适合一般用户安装。
    γ(Gamma)版:相当成熟的测试版,与即将发行的正式版相差无几。
    RC版:Release Candidate。
    RC 版。是 Release Candidate 的缩写,意思是发布倒计时,候选版本,处于Gamma阶段,该版本已经完成全部功能并清除大部分的BUG。到了这个阶段只会除BUG,不会对软件做任何大的更改。从Alpha到Beta再到Gamma是改进的先后关系,但RC1、RC2往往是取舍关系。
    Final:正式版。

    其他版本
    Enhance :增强版或者加强版 属于正式版1
    Free :自由版
    Release :发行版 有时间限制
    Upgrade :升级版
    Retail  :零售版
    Cardware :属共享软件的一种,只要给作者回复一封电邮或明信片即可。(有的作者并由此提供注册码等),目前这种形式已不多见。/ S
    Plus :属增强版,不过这种大部分是在程序界面及多媒体功能上增强。
    Preview :预览版
    Corporation & Enterprise :企业版
    Standard :标准版
    Mini :迷你版也叫精简版只有最基本的功能
    Premium : 贵价版
    Professional : 专业版
    Express : 特别版
    Deluxe : 豪华版
    Regged : 已注册版

    Rip :是指从原版文件(一般是指光盘或光盘镜像文件)直接将有用的内容(核心内容)分离出来,剔除无用的文档,例如PDF说明文件啊,视频演示啊之类的东西,也可以算做是精简版吧…但主要内容功能是一点也不能缺少的!另:DVDrip是指将视频和音频直接从DVD光盘里以文件方式分离出来。



    RTM 版 :这基本就是最终的版本,英文是 Release To Manufactur,意思是发布到生产商。
    Original Equipment Manufacturer (OEM) 
    You may license products through an Original Equipment Manufacturer (OEM). These products, such as Windows operating systems, come installed when you purchase a new computer. 
    OEM软件是给电脑生产厂的版本,无需多说。 

    Full Packaged Product (FPP)/Retail 
    Physical, shrink-wrapped boxes of licensed product that can be purchased in a local retail store or any local software retailer. 
    FPP就是零售版(盒装软件),这种产品的光盘的卷标都带有"FPP"字样,比如英文WXP Pro的FPP版本的光盘卷标就是WXPFPP_EN,其中WX表示是Windows XP,P是Professional(H是Home),FPP表明是零售版本,EN是表明是英语。获得途径除了在商店购买之外,某些MSDN用户也可以得到。
    Volume Licensing for Organizations (VLO) 
    You may enjoy potentially significant savings by acquiring multiple product licenses. Depending on the size and type of your organization. 
    团体批量许可证(大量采购授权合约),这是为团体购买而制定的一种优惠方式。这种产品的光盘的卷标都带有"VOL"字样,取"Volume"前3个字母,以表明是批量,比如英文WXP Pro的VOL版本的光盘卷标就是WXPVOL_EN,其中WX表示是Windows XP,P是Professional(VOL没有Home版本),VOL表明是团体批量许可证版本,EN是表明是英语。获得途径主要是集团购买,某些MSDN用户也可以得到。
    ----------------------------------------------------------------------------------------------------------------------------------------

    在很多软件下载的时候,你会发觉标识为GA或者CRx等。比如MySQL和JBoss都采用这种标识。那什么是GA呢。GA是Generally Available的缩写,意思是开发团队认为该版本是稳定版(有的软件可能会标识为stable版或者production版,其意思和GA相同),可以在较为关键的场合使用。
      如果你是要用在生产中的软件,或者你是一个新手,那么你最好选用GA版本。这是测试最为充分,最为稳定的版本。

    Sunday, December 13, 2009

    javaweb|web.xml中listener的作用及使用

    web.xml中listener的作用及使用 - 退思园 - CSDN博客

    一.WebContextLoaderListener  监听类
      它能捕捉到服务器的启动和停止,在启动和停止触发里面的方法做相应的操作!
      它必须在web.xml 中配置才能使用,是配置监听类的

    二.下面是搜集的一些listener方面的知识
    简例一
    监听用户上线与退出,显示在线用户

    1、登陆页面 Login.jsp

    <%@page pageEncoding="gb2312" contentType="text/html; charset=gb2312" %>
    <%
    session=request.getSession(false);
    if(session!=null)session.invalidate();
    %>
    <html>
    <head><title></title></head>
    <body>
    <form action="isOnline.jsp" method="post">
    用户名:<input type="text" name="uName"/>
    <input type="submit" value="上线">
    </form>
    </body>
    </html>

    2、控制页面(只是为了说明监听器问题,所以简单了点...) isOnline.jsp

    <%@page pageEncoding="gb2312" contentType="text/html; charset=gb2312" %>
    <html>
    <head><title></title></head>
    <body>
    <%
    session=request.getSession();
    session.setAttribute("userName",request.getParameter("uName"));
    response.sendRedirect("showOnline.jsp");
    %>
    </body>
    </html>


    3、显示页面 showOnline.jsp

    <%@page pageEncoding="gb2312" contentType="text/html; charset=gb2312" import="java.util.ArrayList" %>
    <html>
    <head><title></title></head>
    <body>
    <%
    ArrayList showList=(ArrayList)(getServletContext().getAttribute("list"));
    out.print("在线人数 "+showList.size()+"<br>");
    for(int i=0;i<showList.size();i++){
    out.print(showList.get(i)+"在线"+"<br>");
    }
    %>
    <br>
    <a href="Login.jsp">退出</a>
    </body>
    </html>

    4、配置页面 web.xml

    <?xml version="1.0" encoding="gb2312"?>
    <!DOCTYPE web-app
        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
    <listener>
        <listener-class>org.xiosu.listener.onlineListener</listener-class>
    </listener>
    </web-app>

    5、监听器 onlineListener.java

    package org.xiosu.listener;

    import java.util.ArrayList;
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpSessionAttributeListener;
    import javax.servlet.http.HttpSessionBindingEvent;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;

    public class onlineListener implements HttpSessionListener,
      HttpSessionAttributeListener {
    // 参数
    ServletContext sc;
    ArrayList list = new ArrayList();
    // 新建一个session时触发此操作
    public void sessionCreated(HttpSessionEvent se) {
      sc=se.getSession().getServletContext();
      System.out.println("新建一个session");
    }
    // 销毁一个session时触发此操作
    public void sessionDestroyed(HttpSessionEvent se) {
      System.out.println("销毁一个session");
      if (!list.isEmpty()) {
       list.remove((String) se.getSession().getAttribute("userName"));
       sc.setAttribute("list", list);
      }
    }
    // 在session中添加对象时触发此操作,在list中添加一个对象
    public void attributeAdded(HttpSessionBindingEvent sbe) {
      list.add((String) sbe.getValue());
      sc.setAttribute("list", list);
    }
    // 修改、删除session中添加对象时触发此操作
    public void attributeRemoved(HttpSessionBindingEvent arg0) {
    }
    public void attributeReplaced(HttpSessionBindingEvent arg0) {
    }
    }


    说明:本例只为简单介绍监听器,并未进行安全方面设置。

    监听器也叫Listener,是Servlet的监听器,它可以监听客户端的请求、服务端的操作等。通过监听器,可以自动激发一些操作,比如监听在线的用户的数量。当增加一个HttpSession时,就激发sessionCreated(HttpSessionEvent   se)方法,这样
    就可以给在线人数加1。常用的监听接口有以下几个:
    ServletContextAttributeListener监听对ServletContext属性的操作,比如增加、删除、修改属性。
    ServletContextListener监听ServletContext。当创建ServletContext时,激发contextInitialized(ServletContextEvent   sce)方法;当销毁ServletContext时,激发contextDestroyed(ServletContextEvent   sce)方法。
    HttpSessionListener监听HttpSession的操作。当创建一个Session时,激发session   Created(HttpSessionEvent   se)方法;当销毁一个Session时,激发sessionDestroyed   (HttpSessionEvent   se)方法。
    HttpSessionAttributeListener监听HttpSession中的属性的操作。当在Session增加一个属性时,激发attributeAdded(HttpSessionBindingEvent   se)   方法;当在Session删除一个属性时,激发attributeRemoved(HttpSessionBindingEvent   se)方法;当在Session属性被重新设置时,激发attributeReplaced(HttpSessionBindingEvent   se)   方法。
    example:随服务器启动
    <web-app>

          com.tb.listener.CountStartListener


    package com.tb.listener;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.http.HttpServlet;
    import com.tb.timertask.DoCountTask;
    public class CountStartListener extends HttpServlet implements ServletContextListener
    {
    private static final long serialVersionUID = 1824920962239905170L;
    public CountStartListener()
    {
       // TODO Auto-generated constructor stub
    }
    public void contextDestroyed(ServletContextEvent arg0)
    {
       // TODO Auto-generated method stub
    }
       public void contextInitialized(ServletContextEvent arg0)
    {   
       DoCountTask.dotask();
    }
    }

    javaweb|web.xml的配置中的作用 - WEB开发 de 点滴

    (转)web.xml的配置中的作用 - WEB开发 de 点滴 - BlogJava

    1.启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件web.xml.读两个节点: <listener></listener> 和 <context-param></context-param> 

    2.紧接着,容器创建一个ServletContext(上下文),这个WEB项目所有部分都将共享这个上下文. 

    3.容器将<context-param></context-param>转化为键值对,并交给ServletContext. 

    4.容器创建<listener></listener>中的类实例,即创建监听. 

    5.在监听中会有contextInitialized(ServletContextEvent args)初始化方法,在这个方法中获得 
    ServletContext = ServletContextEvent.getServletContext(); 
    context-param的值 = ServletContext.getInitParameter("context-param的键"); 

    6.得到这个context-param的值之后,你就可以做一些操作了.注意,这个时候你的WEB项目还没有完全启动完成.这个动作会比所有的Servlet都要早. 
    换句话说,这个时候,你对<context-param>中的键值做的操作,将在你的WEB项目完全启动之前被执行. 

    7.举例.你可能想在项目启动之前就打开数据库. 
    那么这里就可以在<context-param>中设置数据库的连接方式,在监听类中初始化数据库的连接. 

    8.这个监听是自己写的一个类,除了初始化方法,它还有销毁方法.用于关闭应用前释放资源.比如说数据库连接的关闭. 

    9.... 以上资料来自<<Head First Servlet&JSP>>

    Saturday, December 12, 2009

    javaweb|对session何时生成的无用讨论

    第 16 章 Never End...

    第 16 章 Never End...

    学习到此为止告一段落

    对教程内容和jsp有什么建议的话,可以跟叮咚姐姐联系,随着技术的进步,我们也会同步更新教程的内容。

    16.1. 对session何时生成的无用讨论

    提出问题:“session是在什么时候生成的?”

    现象:

    1. 如果写一个servlet,在里边调用getSession(false),得到的是null。(这里的参数表示当session不存在时是否新建,返回null表示还没为当前用户生成对应的session)。

    2. 如果写一个jsp,在里边打印session,可以看到打印的信息,说明session已经创建了。

    对现象的感性认识:jsp会自动创建HttpSession对象,而servlet则不会,只有在servlet中显式调用getSession()方法时才创建session。

    对现象的理性说明

    1. 只有执行了getSession()才会生成session。(此处无参数,默认与getSession(true)功能相同)。

    2. jsp转换成servlet后,生成的servlet代码中包含getSession()。

      例子中见tomcat目录下,work\Catalina\localhost\99-01\org\apache\jsp\test1.jsp,其中包含了pageContext.getSession()。

    3. 我们可以在jsp中使用<%@ page session="false"%>禁止生成session的代码,这样即使访问此jsp页面,也不会创建session。

      例子中见tomcat目录下,work\Catalina\localhost\99-01\org\apache\jsp\test2.jsp,这里已经看不到getSession()的代码了。

    因为<%@ page session="false"%>不会生成默认的session,在这个jsp页面里就无法直接使用session变量了。下面的代码会导致jsp无法编译的错误。

    <%@ page session="false"%> <%     out.println(session); %> <hr />         

    访问test3.jsp会出现500服务器内部错误

    在使用过<%@ page session="false"%>的页面里只能使用getSession()手工获得session才能使用了。

    演示用代码放在99-01目录下,TestServlet映射到/请求路径,test1.jsp会自动创建session,test2.jsp禁用了session,test3.jsp直接使用session会出现错误。

    这个问题是典型的无用研究之一,实际工作中会有人使用getSession(false)吗?

    16.2. 对手工将jsp转换为servlet的无用讨论

    有同学提到了一个问题:让用户自己编写jsp上传到服务器,服务器接收后保存入数据库,在用户访问的时候从数据库中取出对应的jsp,运行后返回响应。

    这个效果从理论上讲是可以实现的,我们已经对jsp的运行机制非常熟悉了,第 6.2 节 “jsp与servlet的关系”,我们只需要将jsp转换为对应servlet的java文件,再编译为class,最后使用ClassLoader加载生成的class执行即可。

    好消息是tomcat为我们提供了批量转换jsp的工具,使用以下的ant脚本就可以将指定目录下的jsp转换成对应servlet的java代码。(ant的使用方法已经超出了本文的讨论范围,如果有兴趣请自行参阅官方网站。http://ant.apache.org/)

    <project name="jspc" default="jspc" basedir=".">     <property name="CATALINA_HOME" location="../../"/>      <path id="build.lib">         <pathelement location="WEB-INF/classes"/>         <fileset dir="WEB-INF/lib">             <include name="*.jar"/>         </fileset>     </path>      <target name="jspc">         <taskdef classname="org.apache.jasper.JspC" name="jasper2">             <classpath>                 <pathelement location="${java.home}/../lib/tools.jar"/>                 <fileset dir="${CATALINA_HOME}/server/lib">                     <include name="*.jar"/>                 </fileset>                 <fileset dir="${CATALINA_HOME}/common/lib">                     <include name="*.jar"/>                 </fileset>                 <path refid="build.lib"/>             </classpath>         </taskdef>          <jasper2 verbose="1"             package="org.apache.jsp"             uriroot="."             webXmlFragment="WEB-INF/generated_web.xml"             outputDir="./WEB-INF/src/" />     </target> </project>           

    为了顺利完成转换工作,我们需要把一系列jar文件放入classpath中:

    1. WEB-INF/lib/*.jar(星号代表所有)

    2. ${java.home}/../lib/tools.jar(${java.home}表示jdk安装目录)

    3. ${CATALINA_HOME}/server/lib/*.jar(${CATALINA_HOME}表示tomcat的安装目录)

    4. ${CATALINA_HOME}/common/lib/*.jar

    简要介绍一下jasper2的配置参数。

    1. verbose="1"表示打印进度信息,如果verbose="0"则不打印信息。

    2. package="org.apache.jsp"表示生成servlet中的包名(package)。

    3. uriroot="."表示会把当前目录下的所有jsp都转换成servlet。

    4. webXmlFragment="WEB-INF/generated_web.xml"会在WEB-INF目录下生成servlet的默认配置。

      比如我们转换的test.jsp将生成如下部分。

      <servlet>     <servlet-name>org.apache.jsp.test_jsp</servlet-name>     <servlet-class>org.apache.jsp.test_jsp</servlet-class> </servlet>  <servlet-mapping>     <servlet-name>org.apache.jsp.test_jsp</servlet-name>     <url-pattern>/test.jsp</url-pattern> </servlet-mapping>                 

      将这部分复制到你项目中的web.xml中就可以通过/test.jsp请求来访问生成的servlet了。

    5. outputDir="./WEB-INF/src/"表示生成的servlet文件放在WEB-INF/src目录下,tomcat会自动生成package对应的目录,比如test.jsp生成的最终路径是WEB-INF/src/org/apache/jsp/test_jsp.java。

    接着把生成的servlet编译成class,这次除了common/lib/servlet-api.jar以外我们还需要common/lib/jasper-runtime.jar加入classpath中,编译脚本参考WEB-INF/src/compile.bat。

    现在可以删除test.jsp了,web.xml中已经将/test.jsp请求转发至对应的servlet处理,我们甚至不需要修改任何链接。

    jspc的主要功能在于预先编译jsp发现其中的语法错误,有些公司也使用这种方式进行加密(毫无意义的做法,把jsp唯一的灵活性都浪费了)。

    演示程序在99-02目录下,需要安装ant之后才可能执行run.bat将jsp转换成servlet。

    在了解如何手工转换jsp之后,我们可以来讨论在数据库中保存jsp的问题了。

    将jsp从数据库中提取出来,将这些数据保存成本地文件,使用jspc转换成servlet再编译为class,最后使用自定义的ClassLoader读取到jvm中执行。

    难点在于生成文件要保证互不影响和自定义ClassLoader如何加载管理这些生成的class。

    这个问题是典型的无用研究之一,任何想获得灵活模板功能的同学都应该亦然决然的抛弃jsp,jsp这种先解释编译再执行的机制不仅没有帮助我们提升效率,反而大大增加技术难度并降低响应效率。需要自定义模板的同志务必考虑velocity, freemarker此类模板引擎或者groovy一类脚本语言,这时使用jsp无异于给自己带上一副沉重的枷锁。

    16.3. 胡乱解释一下session

    会话都保存在服务器端。

    每个用户打开浏览器就服务器就会给它生成一个sessionId,浏览器或者把这个sessionId放到cookie里,或者每次请求都带在url后边(自动的),然后服务器就拿到这个sessionid,在内存里翻啊翻啊,翻出对应的session来,就这么对应上的。

    ServletContext和session就没多大关系了,虽然ServletContext也是在服务器端,每个web应用发布的时候,就要创建这么一块空间放置ServletContext,这个web应用中的所有的servlet, jsp, filter, listener都可以访问这块空间。你可以把他看作是一个全局变量,所有共享数据都放到里边。

    javaweb|生命周期

    第 14 章 生命周期

    第 14 章 生命周期

    注意

    讲一下servlet的生命周期与运行时的线程模型,对了解servlet的运行原理有所帮助,这样才能避免一些有冲突的设计。

    如果你不满足以下任一条件,请继续阅读,否则请跳过此后的部分,进入下一章:第 15 章 分页。

    1. 了解servlet的生命周期。

    2. 了解servlet运行时的线程模型,及设计程序时需要注意的部分。

    14.1. 生命周期

    我们之前使用的都是javax.servlet.http.HttpServlet,这个类实现了javax.servlet.Servlet接口,而这个接口中定义的三个方法是所有servlet都必须实现的。

    package javax.servlet;  public interface Servlet {      void init(ServletConfig config);      void service(ServletRequest request, ServletResponse response);      void destroy();  }         

    如图所示,tomcat之类的服务器首先根据web.xml中的定义实例化servlet,然后调用它的init()方法进行初始化,init()方法的ServletConfig参数是服务器传递进servlet的,其中包含web.xml配置的初始化信息和ServletContext对象等共享内容。

    初始化后的servlet实例便进入等待请求的状态,当有与servlet-mapping匹配的请求进入时,服务器会调用servlet实例的service方法,传入ServletRequest与ServletResponse两个参数等待servlet处理完毕。

    注意一点,对于每个web应用,内存中只存在一个servlet实例,所有请求都是调用这个servlet实例,所以我们说servlet不是线程安全的,所有操作都要限制在service()方法中进行,不要在servlet中定义类变量。(doGet()和doPost()是HttpServlet覆盖service()方法后分支出来的辅助方法,实际上服务器调用的还是service()。)

    当web应用卸载时,服务器会调用每个已经初始化的servlet的destroy(),然后销毁这些servlet实例,如果你需要在servlet销毁时释放什么资源的话,可以写在destory()方法中。

    那么servlet是在什么时候进行初始化的呢?我们可以通过web.xml中的load-on-startup标签。

    <servlet>     <servlet-name>TestServlet</servlet-name>     <servlet-class>anni.TestServlet</servlet-class>     <load-on-startup>1</load-on-startup> </servlet>         

    load-on-startup的值是一个整数,当它大于等于零的时候服务器会在web发布的时候初始化servlet。当它小于零或者我们没有设置load-on-startup的时候,服务器会在用户第一次访问servlet的时候才去初始化servlet。

    或许你对load-on-startup为什么是一个整数存有疑问,为什么不是true和false呢?这是因为如果我们在web.xml中设置了多个servlet的时候,可以使用load-on-startup来指定servlet的加载顺序,服务器会根据load-on-startup的大小依次对servlet进行初始化。不过即使我们将load-on-startup设置重复也不会出现异常,服务器会自己决定初始化顺序。

    回头看看javax.servlet.Filter中也有init()和destroy()方法,它的声明周期与servlet基本一致,服务器使用init()对Filter初始化,销毁Filter的时候调用destroy()方法,只是过滤器就不在有load-on-startup设置了,它总是会在服务器启动的时候进行初始化,然后按照web.xml定义的顺序依次执行。

    14.2. 线程模型

    我们做一个试验,以此来证明某些编写servlet的方法是绝对错误的。

    第一步,我们打开浏览器,浏览14-02的index.jsp页面,输入“叮咚”。

    第二步,我们再打开一个14-02/index.jsp页面,输入“lingirl”。

    第三步,点击第一个页面的提交按钮,然后在10秒之内点击另一个页面的提交按钮,等两个页面都提交成功后,我们会看到如下页面。

    url上有乱码这个就是提交“叮咚”的页面,会惊讶吧?本来这时应该显示“叮咚”的。

    这个页面对应提交“lingirl”的页面,它似乎是显示正常的。

    到底是哪里出错了,为什么第一个页面提交了数据,却得到第二个页面提交的结果,首先让我们看一下TestServlet的代码。

    package anni;  import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  public class TestServlet extends HttpServlet {      private String username;      public void doGet(HttpServletRequest request,                     HttpServletResponse response)             throws ServletException, IOException {         this.username = request.getParameter("username");          try {             Thread.sleep(10000);         } catch(InterruptedException ex) {         }          response.getWriter().write(this.username);     }  }         

    doGet()方法中从request中获得username参数,然后赋给this.username,这是一个类变量。然后暂停10秒,这10秒我们假设正在进行一些很费时间的计算,这样我们就有十秒钟去点两个页面的提交按钮了。最后将this.username写入response。

    你也许在想:“这没有问题啊,第一个页面提交了数据,等待10秒返回,第二个页面再提交数据,等待10秒返回,两者并不冲突啊。”可实际上在多线程模型中不会有这种队列让请求一个一个执行,所有请求都是蜂拥而至。

    在这个例子里,第一个请求过来将“叮咚”赋值给this.username后进行等待,10秒之内我们的第二个请求又调用了doGet()方法,并把this.username修改为“lingirl”,等到10秒后第一个请求结束等待后,获得的this.username已经是“lingirl”了。

    this.username这种写法在servlet中是绝对禁用的,如果有什么信息需要保存,可以考虑放到session或ServletContext中。

    14.3. 在jsp中定义类变量

    写在<%%>之间的代码,在转换成servlet之后都会service()方法内运行,所以我们不必担心出现上边this.username的问题。

    但是我们可以用<%!%>(注意多出来的感叹号)定义类变量或类方法,把上一个罪大恶极的servlet改造成jsp的话,就像这样。

    <%@ page contentType="text/html; charset=gb2312"%> <%!     String username; %> <%     this.username = request.getParameter("username");     try {         Thread.sleep(10000);     } catch(InterruptedException ex) {     }     out.write(this.username); %>         

    注意

    使用14-03下的例子可以测试jsp出错的效果,记得要在10秒之内点击两次。

    <%!%>似乎是一个巨大的陷阱,如果我们使用它定义类变量就一定会出现多线程错误。

    不过凡事都有正反两面,当我们需要在jsp中定义一个通用方法时,就需要借助<%!%>的力量了,假设我们需要一个方法,根据用户的性别显示不同的html内容,如果sex = 0就输出红色的“男”,如果sex = 1就输出绿色的“女”。为实现这个功能,我们可以定义一个sexRenderer()方法。

    14-04/index.jsp页面显示效果如下:

    index.jsp中的代码分两部分。

    第一部分定义sexRenderer()方法和

    <%!     public String sexRenderer(int sex) {         if (sex == 0) {             return "<span style='color:red;'>男</span>";         } else if (sex == 1) {             return "<span style='color:green;'>女</span>";         } else {             return "";         }     } %>         

    第二部分循环显示保存了性别信息的数组,显示的时候将会调用sexRenderer()方法。

    <%     int[] people = {0, 1, 1, 0};     for (int i = 0; i < this.people.length; i++) { %>                 <tr>                     <td><%=this.sexRenderer(this.people[i])%></td>                 </tr> <%     } %>         

    好的,现在我们知道可以在<%!%>中定义方法和变量了。但是同时也要了解的是<%!%>已经脱离了service()方法,这就导致不能在它里边使用request,response这些默认变量了,如果想要调用request只能写成void doSomething(HttpServletRequest request)的形式了,稍微注意一下即可。

    14.4. jsp九大默认对象

    分别是request, response, out, pageContext, session, application, page, config, exception。

    让我们看看它们与servlet中变量的对应关系。

    首先要明确的是,这九个变量都只在<%%>中有效,<%!%>中是无法调用这九个对象的。实际上<%%>最后会成为service()方法中的代码,我们这里就看看如何在service()方法中获得这些对象吧。

    1. request

      public void service(ServletRequest req, ServletResponse res) {     HttpServletRequest request  = (HttpServletRequest) req; }                  

      jsp中的request就是service()中传入的req参数,因为service中定义的是ServletRequest类型,我们还需要转换成HttpServletRequest类型。

    2. response

      public void service(ServletRequest req, ServletResponse res) {     HttpServletResponse response  = (HttpServletResponse) res; }                  

      与上例相同,response也是service()中传入的res参数。

    3. out

      Writer out = response.getWriter();                 

      out对应着从response中取出的writer对象,负责向响应中输出数据。不过jsp和servlet中的out还是有一点区别,虽然它们都实现了java.io.Writer接口,但servlet中实际类型是java.io.PrintWriter,而jsp中实际类型是javax.servlet.jsp.JspWriter。

    4. pageContext

      这是jsp独有的,servlet里没有page的概念。

    5. session

      HttpSession session = request.getSession();                 

      直接从request中获得会话。

    6. application

      ServletConext application = getServletConfig().getServletContext();                 

      可以通过servletConfig获得ServletContext,这是整个web应用共享的一个对象。

    7. page

      Object page = this;                 

      page就代表当前jsp对象,也可以直接使用this引用。

    8. config

      ServletConfig config = getServletConfig();                 

      这是在servlet初始化时由服务器传入的对象,可以通过它获得web.xml中定义的初始化参数。

    9. exception

      想在jsp中使用这个对象需要满足一些条件了。

      首先我们要在14-05/index.jsp中故意抛出一个异常。

      <%@ page contentType="text/html; charset=gb2312" errorPage="error.jsp"%> <%     String str = null;     str.length(); %>                 

      str值是null,直接在null上调用length()方法会引发NullPointerException,然后我们可以看到页面第一行使用jsp指令(directive)设置了errorPage="error.jsp",这样在出现异常的时候就会自动forward到error.jsp中。现在看看error.jsp中有些什么。

      <%@ page contentType="text/html; charset=gb2312" isErrorPage="true"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html>     <head>         <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />         <title>index</title>     </head>     <body>         <%=exception%>     </body> </html>                 

      最主要的是在jsp指令(directive)中设置isErrorPage="true",这样我们就可以在jsp中使用exception对象了,实际上这个异常是从request中取出来的。

    到此为止,jsp九大默认对象已经讲解完毕,其中常用的还是四个作用域对应的对象,其他的了解即可。

    javaweb|剖析el表达式

    第 13 章 剖析el表达式

    第 13 章 剖析el表达式

    注意

    这里详细讲解如何在jsp中使用el表达式。

    如果你不满足以下任一条件,请继续阅读,否则请跳过此后的部分,进入下一章:第 14 章 生命周期。

    1. 了解如何在jsp中使用及禁用el表达式。

    2. 了解el表达式的取值方式。

    13.1. 再谈el(Expression Language)

    我们已经知道el是jsp-2.0规范的一部分,tomcat-5.x版本以上都已经能够支持jsp-2.0规范,但在更低版本的tomcat和webphere,weblogic中还是无法使用这一便捷方式。

    其实我们也可以选择在jsp中禁止使用el表达式,使用jsp指令(directive)可以对禁用某一个jsp中的el表达式。

    禁用之后的el表达式会以原样显示出来,如下图所示。

    为了对照,我们还在13-01下放了一个可以正常使用el表达式的例子,运行效果如下图显示。

    在13-01/index.jsp中禁用el表达式,是使用了isELIgnore="true"这样一条jsp指令(directive),请注意大小写。

    <%@ page isELIgnored="true" %> <%     pageContext.setAttribute("hello", "Hello World"); %>  ${hello}         

    还有一种批量禁用el的方法,我们可以在WEB-INF/web.xml中使用jsp-property-group标签批量禁用el,我们在13-02/WEB-INF/web.xml中进行如下配置。

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"     version="2.4">      <jsp-config>         <jsp-property-group>             <url-pattern>*.jsp</url-pattern>             <el-ignored>true</el-ignored>         </jsp-property-group>     </jsp-config>  </web-app>         

    这样就会禁用所有以.jsp后缀的请求中的el表达式,使用这种方式需要注意两点。

    1. jsp-property-group标签是jsp-2.0中新增功能,如果你使用低版本的web.xml(2.3或以下)就不能使用这个标签了。

    2. 设置jsp-config会影响jsp生成servlet的过程,如果程序修改时已经有jsp转换成servlet并缓存在work目录下,那么修改后需要先清除缓存,才能看到效果。

    实际上还有第三种方法可以禁用掉所有jsp中的el表达式,那就是把web.xml定义为2.3版。

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"     "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> </web-app>         

    这个就是13-03/WEB-INF/web.xml的定义,定义了web-app的版本号是2.3,这样一来所有的jsp都无法使用el表达式了,因为el表达式是2.4版才开始支持的功能。

    注意

    说了以上三种禁用el表达式的方法,不是希望大家抛弃el表达式,相反是希望大家在自己编写el时出现问题时,先去考虑是否有人在这三个地方做了设置造成el表达式的失效。建议大家多多使用el表达式,少用一些jsp中的scriptlet代码。

    13.2. 作用域

    使用el的时候,默认会以一定顺序搜索四个作用域,将最先找到的变量值显示出来。

    如果我们有${username}这样一个正则表达式,它回去依次调用pageContext.getAttribute("username") -> request.getAttribute("username") -> session.getAttribute("username") -> application.getAttribute("username"),只要找到某一个不为空的值就立刻返回。

    这样的确方便我们的操作,但是随之也出现了另外一个问题,如果pageContext和request中有同名变量,但是我想取得request中的变量该如何是好呢?这就需要为el表达式引入作用域的概念了。

    ${pageScope.username}  ${requestScope.username}         

    我们可以直接访问13-04这个应用,看看el表达式支持的所有对象。

    下面我们分别对每个作用域对象进行讲解。

    表 13.1. el中的作用域

    el中的作用域 对应关系
    pageContext1 当前页的pageContext对象
    pageScope把page作用域中的数据映射为一个map对象
    requestScope2 把request作用域中的数据映射为一个map对象
    sessionScope把session作用域中的数据映射为一个map对象
    applicationScope把application作用域中的数据映射为一个map对象
    param 对应request.getParameter()
    paramValues3 对应request.getParameterValues()
    header4 对应request.getHeader()
    headerValues对应request.getHeaderValues()
    cookie5 对应request.getCookies()
    initParam6 对应ServletContext.getInitParamter()

    1

    例子中的${pageContext.request.contextPath}返回的是request.getContextPath()的值,在此例中就是/13-04,我们经常使用这个来拼接jsp中的绝对路径。

    这里的${pageContext.request.contextPath}是一种特殊用法,不能使用${request.contextPath}的形式替代。

    2

    pageScope, requestScope, sessionScope, appliationScope都可以看作是Map型变量,调用其中的数据可以使用${pageScope.name}或${pageScope["name"]}的形式,这两种写法是等价的。

    在某些情况下只能使用${pageScope["content-type"]},这里不能写成${pageScope.content-type},jsp无法解析连字符(-)会出现错误。

    3

    需要注意的是${paramValues.name}得到的是一个字符串数组,如果需要获得其中某个值,还需要使用${paramValues.name[0]}指定数组中的索引。

    这与下面的${headerValues.name}是相似的。

    4

    ${header.name}会取得http请求中的header参数,现实工作中很少用到这里的数据。

    例子中使用Host是指请求访问的主机地址,包括ip和端口号。而Referer比较有趣,如果用户通过超链接跳转过来的,Referer会保存上次访问页面的地址,我们就可以通过它来统计哪些用户是从哪里转来的了。

    5

    ${cookie.name}将获得对应cookie的对象,比如我们用jsp将一段cookie发送给客户端。

    Cookie cookie = new Cookie("username", "Username in cookie"); response.addCookie(cookie);                 

    创建一个名称为username,值为"Username in cookie"的Cookie对象,然后发送给客户端。

    然后我们就可以使用${cookie.username}获得这个cookie了,${cookie.username.name}获得cookie名称,${cookie.username.value}获得cookie值。

    6

    ServletContext.getInitParamter()指的应用的初始变量,这些变量都是定义在web.xml中的。

    <context-param>     <param-name>username</param-name>     <param-value>username with context param</param-value> </context-param>                 

    ${initParam.username}就会得到这里的变量值。

    以上都是死记硬背的东西,建议实际用到的时候翻看一下就好了,演示代码都放在13-04下,为了获得param和cookie还要点击一下最下边的连接才可以。

    13.3. 运算符

    el表达式中支持java中所有的操作符,并且还有一些扩展,下面我们简要做一下对照。

    表 13.2. 加减乘除四则运算

    符号说明
    +加
    - 减
    *乘
    /或div 除
    %或mod求余

    表 13.3. 比较运算

    符号说明
    ==或eq相等(equals)
    !=或ne 不相等(not equals)
    <或lt小于(less than)
    >或gt 大于(greater than)
    <=或le小于等于(less than or equals)
    >=或ge大于等于(greater than or equals)

    表 13.4. 逻辑运算

    符号说明
    &&或and逻辑和
    ||或or 逻辑或
    !或not取反

    表 13.5. 特殊运算

    符号说明
    empty是否为null或空字符串
    ? : 三元运算符

    下面上所有运算符的显示结果,顺便说一下如果想在jsp中显示${name}而不让jsp把它当作el计算出来,可以写成\${name},这样最后显示的结果就是${name}了。