<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>fangyong2006</title>
    <description>不谋万世者，不足谋一时；不谋大局者，不足谋一域！</description>
    <link>http://fangyong2006.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>serialization</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/188309" style="color:red;">http://fangyong2006.javaeye.com/blog/188309</a>&nbsp;
          发表时间: 2008年04月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>Object serialization的定义：Object serialization 允许你将实现了Serializable接口的对象转换为字节序列，这些字节序列可以被完全存储以备以后重新生成原来的对象</p>
<p>&nbsp;</p>
<p>
<table class="content" cellspacing="0" border="0" width="98%" cellpadding="0">
<tbody>
<tr>
<td class="content_title" align="left"><a href="javascript:void(0);">javaBean实现java.io.Serializable接口的作用</a></td>
</tr>
<tr>
<td class="content_admin" align="left">日期:2007-05-02 01:41:02</td>
</tr>
<tr>
<td class="content_text" align="left">所谓的Serializable,就是java提供的通用数据保存和读取的接口。<br />任何类型只要实现了Serializable接口，就可以被保存到文件中，或者作为数据流通过网络发送到别的地方。也可以用管道来传输到系统的其他程序中。<br />这样子极大的简化了类的设计。只要设计一个保存一个读取功能就能解决上面说得所有问题。</td>
</tr>
</tbody>
</table>
</p>
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/188309#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 30 Apr 2008 10:40:38 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/188309</link>
        <guid>http://fangyong2006.javaeye.com/blog/188309</guid>
      </item>
      <item>
        <title>CVS的常用命令速查手册</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/182475" style="color:red;">http://fangyong2006.javaeye.com/blog/182475</a>&nbsp;
          发表时间: 2008年04月14日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">概述：CVS是一个C/S系统，多个开发人员通过一个中心版本控制系统来记录文件版本，从而达到保证文件同步的目的。 
<br />
<br />
<br />CVS服务器（文件版本库） 
<br />/ | \ 
<br />（版 本 同 步） 
<br />/ | \ 
<br />开发者1 开发者2 开发者3 
<br />
<br />以下是本文主要内容：开发人员可以主要挑选2, 6看就可以了，CVS的管理员则更需要懂的更多一些 
<br />
<br />CVS环境初始化：CVS环境的搭建 管理员 
<br />CVS的日常使用：日常开发中最常用的CVS命令， 开发人员 管理员 
<br />CVS的分支开发：项目按照不同进度和目标并发进行 管理员 
<br />CVS的用户认证：通过SSH的远程用户认证，安全，简单 管理员 
<br />CVSWEB：CVS的WEB访问界面大大提高代码版本比较的效率 管理员 
<br />CVS TAG：将$Id$加入代码注释中，方便开发过程的跟踪 开发人员 
<br />CVS vs VSS: CVS和Virsual SourceSafe的比较 
<br />一个系统20%的功能往往能够满足80%的需求，CVS也不例外，以下是CVS最常用的功能，可能用到的还不到它全部命令选项的10%，更多的功能请在实际应用过程中体会，学习过程中应该是用多少，学多少，用到了再学也不迟。 
<br />
<br />
<br />CVS环境初始化 
<br />============ 
<br />
<br />环境设置：指定CVS库的路径CVSROOT 
<br />tcsh 
<br />setenv CVSROOT /path/to/cvsroot 
<br />bash 
<br />CVSROOT=/path/to/cvsroot ; export CVSROOT 
<br />
<br />后面还提到远程CVS服务器的设置： 
<br />CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH 
<br />
<br />初始化：CVS版本库的初始化。 
<br />cvs init 
<br />
<br />一个项目的首次导入 
<br />cvs import -m "write some comments here" project_name vendor_tag release_tag 
<br />执行后：会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下 
<br />vender_tag: 开发商标记 
<br />release_tag: 版本发布标记 
<br />
<br />项目导出：将代码从CVS库里导出 
<br />cvs checkout project_name 
<br />cvs 将创建project_name目录，并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check out不是一个概念，相对于Virvual SourceSafe的check out是cvs update， check in是cvs commit。 
<br />
<br />CVS的日常使用 
<br />============= 
<br />
<br />注意：第一次导出以后，就不是通过cvs checkout来同步文件了，而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步（添加，修改，删除）操作。 
<br />
<br />将文件同步到最新的版本： 
<br />cvs update 
<br />不制定文件名，cvs将同步所有子目录下的文件，也可以制定某个文件名/目录进行同步 
<br />cvs update file_name 
<br />最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次，并养成“先同步 后修改”的习惯，和Virvual SourceSafe不同，CVS里没有文件锁定的概念，所有的冲突是在commit之前解决，如果你修改过程中，有其他人修改并commit到了CVS库中，CVS会通知你文件冲突，并自动将冲突部分用 
<br />&gt;&gt;&gt;&gt;&gt;&gt; 
<br />content on cvs server 
<br />&lt;&lt;&lt;&lt;&lt;&lt; 
<br />content in your file 
<br />&gt;&gt;&gt;&gt;&gt;&gt; 
<br />标记出来，由你确认冲突内容的取舍。 
<br />版本冲突一般是在多个人修改一个文件造成的，但这种项目管理上的问题不应该指望由CVS来解决。 
<br />
<br />确认修改写入到CVS库里： 
<br />cvs commit -m "write some comments here" file_name 
<br />
<br />注意：CVS的很多动作都是通过cvs commit进行最后确认并修改的，最好每次只修改一个文件。在确认的前，还需要用户填写修改注释，以帮助其他开发人员了解修改的原因。如果不用写-m "comments"而直接确认`cvs commit file_name` 的话，cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。 
<br />注释的质量很重要：所以不仅必须要写，而且必须写一些比较有意义的内容：以方便其他开发人员能够很好的理解 
<br />不好的注释，很难让其他的开发人员快速的理解：比如： -m "bug fixed" 甚至 -m "" 
<br />好的注释，甚至可以用中文: -m "在用户注册过程中加入了Email地址校验" 
<br />
<br />修改某个版本注释：每次只确认一个文件到CVS库里是一个很好的习惯，但难免有时候忘了指定文件名，把多个文件以同样注释commit到CVS库里了，以下命令可以允许你修改某个文件某个版本的注释： 
<br />cvs admin -m 1.3:"write some comments here" file_name 
<br />
<br />添加文件 
<br />创建好新文件后，比如：touch new_file 
<br />cvs add new_file 
<br />注意：对于图片，Word文档等非纯文本的项目，需要使用cvs add -b选项，否则有可能出现文件被破坏的情况 
<br />比如：cvs add -kb new_file.gif 
<br />然后确认修改并注释 
<br />cvs ci -m "write some comments here" 
<br />
<br />删除文件： 
<br />将某个源文件物理删除后，比如：rm file_name 
<br />cvs rm file_name 
<br />然后确认修改并注释 
<br />cvs ci -m "write some comments here" 
<br />以上面前2步合并的方法为： 
<br />cvs rm -f file_name 
<br />cvs ci -m "why delete file" 
<br />
<br />
<br />注意：很多cvs命令都有缩写形式：commit=&gt;ci; update=&gt;up; checkout=&gt;co; remove=&gt;rm; 
<br />
<br />
<br />添加目录： 
<br />cvs add dir_name 
<br />
<br />查看修改历史：cvs log file_name 
<br />cvs history file_name 
<br />
<br />查看当前文件不同版本的区别 
<br />cvs diff -r1.3 -r1.5 file_name 
<br />查看当前文件（可能已经修改了）和库中相应文件的区别 
<br />cvs diff file_name 
<br />cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法，具体安装设置请看后面的cvsweb使用 
<br />
<br />正确的通过CVS恢复旧版本的方法： 
<br />如果用cvs update -r1.2 file.name 
<br />这个命令是给file.name加一个STICK TAG： "1.2" ，虽然你的本意只是想将它恢复到1.2版本 
<br />正确的恢复版本的方法是：cvs update -p -r1.2 file_name &gt;file_name 
<br />如果不小心已经加成STICK TAG的话：用cvs update -A 解决 
<br />
<br />移动文件：文件重命名 
<br />cvs里没有cvs move或cvs rename，因为这两个操作是先cvs remove old_file_name，然后cvs add new_file_name实现的。 
<br />
<br />删除，移动目录： 
<br />最方便的方法是让管理员直接移动，删除CVSROOT里相应目录（因为CVS一个项目下的子目录都是独立的，移动到$CVSROOT目录下都可以作为新的独立项目：好比一颗树，其实砍下任意一枝都能独立存活），对目录进行了修改后，要求其开发人员重新导出项目cvs checkout project_name 或者用cvs update -dP同步。 
<br />
<br />CVS Branch：项目多分支同步开发 
<br />============================= 
<br />
<br />确认版本里程碑：多个文件各自版本号不一样，项目到一定阶段，可以给所有文件统一指定一个阶段里程碑版本号，方便以后按照这个阶段里程碑版本号导出项目，同时也是项目的多个分支开发的基础。 
<br />cvs tag release_1_0 
<br />
<br />开始一个新的里程碑： 
<br />cvs commit -r 2 标记所有文件开始进入2.x的开发 
<br />
<br />注意：CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。 
<br />
<br />在开发项目的2.x版本的时候发现1.x有问题，但2.x又不敢用，则从先前标记的里程碑：release_1_0导出一个分支release_1_0_patch 
<br />cvs rtag -b -r release_1_0 release_1_0_patch proj_dir 
<br />
<br />一些人先在另外一个目录下导出release_1_0_patch这个分支：解决1.0中的紧急问题， 
<br />cvs checkout -r release_1_0_patch 
<br />而其他人员仍旧在项目的主干分支2.x上开发 
<br />
<br />在release_1_0_patch上修正错误后，标记一个1.0的错误修正版本号 
<br />cvs tag release_1_0_patch_1 
<br />
<br />如果2.0认为这些错误修改在2.0里也需要，也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中： 
<br />cvs update -j release_1_0_patch_1 
<br />
<br />CVS的远程认证：通过SSH远程访问CVS 
<br />================================ 
<br />
<br />使用cvs本身的远程认证很麻烦,需要定义服务器和用户组，用户名，设置密码等，而且不安全，因此和系统本地帐号认证并通过SSH传输是比较好的办法，通过在客户机的/etc/profile里设置一下内容： 
<br />CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH 
<br />所有客户机所有本地用户都可以映射到CVS服务器相应同名帐号了。 
<br />
<br />如果CVS所在服务器的SSH端口不在缺省的22，或者和客户端与CVS服务器端SSH缺省端口不一致，有时候设置了： 
<br />:ext:$USER@test.server.address#port:/path/to/cvsroot 
<br />
<br />仍然不行，比如有以下错误信息： 
<br />ssh: test.server.address#port: Name or service not known 
<br />cvs [checkout aborted]: end of file from server (consult above messages if any) 
<br />
<br />解决的方法是做一个脚本指定端口转向（不能使用alias，会出找不到文件错误）： 
<br />创建一个/usr/bin/ssh_cvs文件： 
<br />#!/usr/bin/sh 
<br />/path/to/ssh -p 34567 "$@" 
<br />然后：chmod +x /usr/bin/ssh_cvs 
<br />并CVS_RSH=ssh_cvs; export CVS_RSH 
<br />
<br />注意：port是指相应服务器SSH的端口，不是cvs pserver的端口 
<br />
<br />CVSWEB：提高程序员比较文件修改效率 
<br />================================ 
<br />
<br />CVSWEB就是CVS的WEB界面，可以大大提高程序员定位修改的效率: 
<br />使用的样例可以看：http://www.freebsd.org/cgi/cvsweb.cgi 
<br />
<br />CVSWEB的下载：CVSWEB从最初的版本已经演化出很多功能界面更丰富的版本，这个是个人感觉觉得安装设置比较方便的： 
<br />http://www.spaghetti-code.de/software/linux/cvsweb/ 
<br />
<br />下载解包： 
<br />tar zxf cvsweb.tgz 
<br />把配置文件cvsweb.conf放到安全的地方（比如和apache的配置放在同一个目录下）， 
<br />修改：cvsweb.cgi让CGI找到配置文件： 
<br />$config = $ENV{'CVSWEB_CONFIG'} || '/path/to/apache/conf/cvsweb.conf'; 
<br />
<br />转到/path/to/apache/conf下并修改cvsweb.conf： 
<br />
<br />修改CVSROOT路径设置： 
<br />%CVSROOT = ( 
<br />'Development' =&gt; '/path/to/cvsroot', #&lt;==修改指向本地的CVSROOT 
<br />); 
<br />缺省不显示已经删除的文档： 
<br />"hideattic" =&gt; "1",#&lt;==缺省不显示已经删除的文档 
<br />在配置文件cvsweb.conf中还可以定制页头的描述信息，你可以修改$long_intro成你需要的文字 
<br />CVSWEB可不能随便开放给所有用户，因此需要使用WEB用户认证： 
<br />先生成 passwd: 
<br />/path/to/apache/bin/htpasswd -c cvsweb.passwd user 
<br />
<br />修改httpd.conf: 增加 
<br />&lt;Directory "/path/to/apache/cgi-bin/cvsweb/"&gt; 
<br />AuthName "CVS Authorization" 
<br />AuthType Basic 
<br />AuthUserFile /path/to/cvsweb.passwd 
<br />require valid-user 
<br />&lt;/Directory&gt; 
<br />
<br />CVS TAGS: who? when? 
<br />==================== 
<br />
<br />将$Id$ 加在程序文件开头的注释里是一个很好的习惯，cvs能够自动解释更新其中的内容成：file_name version time user_name 的格式，比如：cvs_card.txt,v 1.1 2002/04/05 04:24:12 chedong Exp，可以这些信息了解文件的最后修改人和修改时间 
<br />
<br />几个常用的缺省文件： 
<br />default.php 
<br />&lt;?php 
<br />/* 
<br />* Copyright (c) 2002 Company Name. 
<br />* $Header$ 
<br />*/ 
<br />
<br />?&gt; 
<br />
<br />==================================== 
<br />Default.java: 注意文件头一般注释用 /* 开始 JAVADOC注释用 /** 开始的区别 
<br />/* 
<br />* Copyright (c) 2002 Company Name. 
<br />* $Header$ 
<br />*/ 
<br />
<br />package com.netease; 
<br />
<br />import java.io; 
<br />
<br />/** 
<br />* comments here 
<br />*/ 
<br />public class Default { 
<br />/** 
<br />* 
<br />* @param 
<br />* @return 
<br />*/ 
<br />public toString() { 
<br />
<br />} 
<br />} 
<br />
<br />==================================== 
<br />default.pl: 
<br />#!/usr/bin/perl -w 
<br /># Copyright (c) 2002 Company Name. 
<br /># $Header$ 
<br />
<br /># file comments here 
<br />
<br />use strict; 
<br />
<br />
<br />CVS vs VSS　 
<br />=========== 
<br />
<br />CVS没有文件锁定模式，VSS在check out同时，同时记录了文件被导出者锁定。 
<br />
<br />CVS是update commit， VSS是check out check in 
<br />
<br />在CVS中，标记自动更新功能缺省是打开的，这样也带来一个潜在的问题，就是不用-kb方式添加binary文件的话在cvs自动更新时可能会导致文件失效。 
<br />
<br />Virsual SourceSafe中这个功能称之为Keyword Explaination，缺省是关闭的，需要通过OPITION打开，并指定需要进行源文件关键词扫描的类型：*.txt,*.java,*.html... 
<br />
<br />对于Virsual SourceSafe和CVS都通用的TAG有： 
<br />$Header$ 
<br />$Author$ 
<br />$Date$ 
<br />$Revision$ 
<br />
<br />尽量使用通用的关键词保证代码在CVS和VSS都能方便的跟踪。 
<br />
<br />　 
<br />
<br />相关资源： 
<br />
<br />CVS HOME： 
<br />http://www.cvshome.org/ 
<br />
<br />CVS FAQ： 
<br />http://www.loria.fr/~molli/cvs-index.html 
<br />
<br />相关网站: 
<br />http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_Versions_System/ 
<br />
<br />CVS 免费书: 
<br />http://cvsbook.red-bean.com/ 
<br />
<br />CVS 命令的速查卡片： 
<br />http://www.refcards.com/about/cvs.html 
<br />
<br />
<br />
<br />
<br />
<br />Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=327935
<br />
<br />
<br />
<br /></pre>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/182475#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 14 Apr 2008 09:43:52 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/182475</link>
        <guid>http://fangyong2006.javaeye.com/blog/182475</guid>
      </item>
      <item>
        <title>war包</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/178084" style="color:red;">http://fangyong2006.javaeye.com/blog/178084</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          war包:是做好一个web应用后，通常是网站，打成包部署到容器中。 <br />jar包：通常是开发时要引用通用类，打成包便于存放管理。 <br />ear包：企业级应用，通常是EJB打成ear包。 <br />所有的包都是用jar打的，只不过目标文件的扩展名不一样。 <br />ear本身就是一个包，怎么打成war啊？ <br />你是说把要打成ear的包打成war吧？当然可以，不过应该不会被正确解开，即不能运行，具体情况没试过。。。
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/178084#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 15:55:29 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/178084</link>
        <guid>http://fangyong2006.javaeye.com/blog/178084</guid>
      </item>
      <item>
        <title>在Struts2中集成Spring详细讲解</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/178073" style="color:red;">http://fangyong2006.javaeye.com/blog/178073</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Spring的官方定义是：一个轻量级的IoC和Aop容器框架，它使用了一种叫做依赖注入的技术。<br /><br />所谓依赖注入，就是指将创建对象以及协议依赖对象之间合作的责任从对象自身中转移到“工厂”中去，这个“工厂”通常由IoC容器提供。<br /><br />Struts2与Spring的集成要用到Spring插件包，这个包是同Struts2一起发布的。Spring插件是通过覆盖(override)Struts2的ObjectFactory来增强核心框架对象的创建。当创建一个对象的时候，它会用Struts2配置文件中的class属性去和Spring配置文件中的id属性进行关联，如果能找到则由Spring创建，否则由Struts2框架自身创建，然后由Spring来装配。Spring插件具体有如下几个作用：<br /><br />1． 允许spring来创建Action、Interceptror和Result<br /><br />2． 由Struts创建的对象能够被Spring装配<br /><br />3． 提供了2个拦截器来自动装配action，如果没有使用Spring ObjectFactory的话<br /><br />这里要注意的是，我们不必在Spring中去注册action，尽管我们可以这么去做，通常Struts框架会自动的从action mapping中创建action对象。<br /><br />要实现与Struts与Spring的集成有如下几步<br /><br />1.将struts2-spring-plugin-2.0.8.jar文件包含到我们的应用中，放到WEB-INF/lib目录下面即可。在这个插件包中有个struts-plugin.xml文件，它的内容如下：<br /><br />这里它将框架常量struts.objectFactory覆盖了，设置为”spring”，其实这里是使用了缩写，我们可以写全称：org.apache.struts2.spring.StrutsSpringObjectFactory。这个缩写的”spring”是和bean配置中的name属性相对应的。默认情况下所有由框架创建的对象都是由ObjectFactory实例化的，ObjectFactory提供了与其它IoC容器如Spring、Pico等集成的方法。覆盖这个ObjectFactory的类必须继承ObjectFactory类或者它的任何子类，并且要带有一个不带参数的构造方法。在这里我们用org.apache.struts2.spring.StrutsSpringObjectFactory代替了默认的ObjectFactory。<br /><br />此外，上面我们说了，如果action不是使用Spring ObjectFactory创建的话，插件提供了两个拦截器来自动装配action，默认情况下框架使用的自动装配策略是name，也就是说框架会去Spring中寻找与action属性名字相同的bean，可选的装配策略还有：type、auto、constructor，我们可以通过常量struts.objectFactory.spring.autoWire来进行设置。<br /><br />2．配置Spring监听器，将Spring.jar包包含到应用中<br /><br />将下面一段话插入到web.xml文件中，同时将spring.jar文件放到WEB-INF/lib目录下面。<br />        org.springframework.web.context.ContextLoaderListener<br /><br />3．利用Spring配置文件来注册对象<br /><br /> 这之后的步骤就和我们使用单独使用Spring一样了，现在我们就已经完成了Struts与Spring的集成了。这里有一点要注意的是，我们还要指定Spring配置文件的地点，默认情况下，容器会到WEB-INF目录下面去寻找applicationContext.xml文件。如果我们想指定别的地方的配置文件或者指定多个配置文件，可以通过在web.xml文件中定义context-param元素来指定，如下所示：<br /><br />    contextConfigLocation<br />    <br />        /WEB-INF/applicationContext.xml,classpath:applicationContext-*.xml上面一段话表示WEB-INF下面的applicationContext.xml文件以及classpath下面的所有匹配applicationContext-*.xml模式的文件都会作为Spring配置文件被装载。
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/178073#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 15:27:20 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/178073</link>
        <guid>http://fangyong2006.javaeye.com/blog/178073</guid>
      </item>
      <item>
        <title>Struts2 概览 </title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/178056" style="color:red;">http://fangyong2006.javaeye.com/blog/178056</a>&nbsp;
          发表时间: 2008年03月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div id="content"><p><span style="font-weight: bold">Struts2 的特性有那些？</span><br /><br />上面提到，Struts 2 关注开发，部署和后期维护三个方面。我们就从这三个方面说明Struts 2 的新特性。<br /><br /><span style="font-size: x-small"><span style="font-weight: bold">构建</span></span><br />&nbsp;&nbsp;&nbsp; 上手非常容易，可以使用起步教程，模板工程或者Maven原型建立Struts 2工程。（起步简单）<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Struts 2 的官方站点上有很多关于Struts 2入门的资料，<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Struts 2 的下载目录中包含空白的模板工程（struts2-blank-2.x.war）和示例工程（struts2-showcase-2.x.war）可以参考。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Maven 2 中有Struts 2 的项目模板，可以使用Maven 2 直接建立Struts 2 工程<br /><br />&nbsp;&nbsp;&nbsp; 更好的设计，Struts 2 中不想要要在在处理和HTTP相关的操作，自需要使用框架的接口即可。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 在Strut 2 中不再会涉及到诸如 HttpServletRequest, HttpServletResponse， HttpSession等Http相关的Servlet接口类，取而代之的是Struts 2 的接口，例如RequestAware，SessionAware等。<br /><br />&nbsp;&nbsp;&nbsp; 强大的标签库，Struts 2 的标签基于 CSS，标签可以提供自己需要的HTML支持。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Struts 2 的标签利用了CSS和模板，使用起来会非常方便，在Struts 1.x中我们需要使用Table来组织表单，但是在Struts 2中所有的标签自带了Table内容，可以方便的处理格式。例如 标签自动添加了 <td>&nbsp;</td>&nbsp; 等标签。<br /><br />&nbsp;&nbsp;&nbsp; 有状态的Checkbox，可以以一种统一的方式记录checkbox状态的变化。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 在Struts 2中即使没有被选中的checkbox其内容仍然存在于Struts 2 框架中，不必像在Struts 1.x中那样需要做特殊的存在性判断。<br /><br />&nbsp;&nbsp;&nbsp; 灵活的取消按钮，在取消按钮点击的时候可以指向一个不同的action。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; @TODO<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 可以在制定Form的action的同时，制定cancel按钮的action，当点击submit和cancel的时候出现完全按不同的功能。<br /><br />&nbsp;&nbsp;&nbsp; 第一等级的AJAX的支持，在普通struts 标签的基础上，使用AJAX增加了交互性和灵活性。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Struts 2 的标签内置了Ajax的支持。Struts 2 的标签使用了Dojotoolkit Ajax框架，不但能够使用Ajax特性，而且能够使用非常丰富和强大的浏览器小控件，例如日期选择控件<br /><br />&nbsp;&nbsp;&nbsp; 见到那集成Spring框架，非常简单的使用Spring框架提供的依赖注入功能。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 可以方便的使用Spring管理Struts 2 的action的创建，通过使用Spring可以充分的利用Spring的依赖诸如功能，并且能够很好的集成其他的框架，例如Hibernate，iBatis等。<br /><br />&nbsp;&nbsp;&nbsp; 更多的返回形式，除了JSP还支持，JasperReports,JFreeChart, Action链，文件下载等。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 除了支持JSP的表现形式，还支持JasperResports报表， JFreechart图标，Action链，文件下载等。<br /><br />&nbsp;&nbsp;&nbsp; POJO表单，不再需要ActionForms，使用Javabean获得客户的收入或者将属性表示出来，<br />&nbsp;&nbsp;&nbsp; 完全消除了ActionForm组建，可以使用任意合适的类型来接受页面传来的数据或者将数据表现出来。ActionFrom可以使用POJO的 JavaBean来替代，JavaBean中的属性可以使用String，也可以使用具体的类型，例如Date，Int等。 <br /><br />&nbsp;&nbsp;&nbsp; POJO Action，使用任意的类作为Action类，甚至可以使用接口。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 任何类都可以作为Action类，只要接口满足一些简单的定义，不需要在使用Action类似的基类，你可以完全自由的发挥。<br /><br />部署<br />&nbsp;&nbsp;&nbsp; 插件结构，使用jar文件扩展框架功能，不需要在做手动的配置，内置了JavaServer Faces， JasperResports, JFreeChart, Tiles等插件。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 扩展一个功能只需要添加一个插件，插件甚至可以热插拔，在你的应用不停止的情况下追加新的功能。<br /><br />&nbsp;&nbsp;&nbsp; 集成了分析功能，可以方便的找到程序性能的问题点。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 可以不借助外力发现程序的热点，找到问题的所在，<br /><br />&nbsp;&nbsp;&nbsp; 准确的报告错误，可以非常准确的指出程序的问题点。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 准确的报告运行时的错误，方便解决问题。<br /><br />维护<br />&nbsp;&nbsp;&nbsp; Action容易测试，直接测试Struts 2的Action，不需要使用Mock Http对象来测试。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Action是普通的类，不需要特殊的环境，所以Struts 2 的Action 特别容易测试。<br /><br />&nbsp;&nbsp;&nbsp; 聪明的默认值，不需要配置不必要的配置，大部分的框架配置元素的都有非常合适的默认值，基本上你不需要在做任何配置。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Struts 2 有很多的配置项，但是每一个都有默认值，基本额上不需要更改默认的选项即可保证最佳<br /><br />&nbsp;&nbsp;&nbsp; 容易定制的控制器，可以定制每一个Action的处理过程。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 可以使用Intercepter来过滤每一个Action，在Action执行前后追加自定义的操作。<br /><br />&nbsp;&nbsp;&nbsp; 集成了Debugging，可以使用内容之的debugging工具找到问题。<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp; 灵活的标签库，可以通过修改FreeMarker模板来定制标签的输出，不需要在操作像天书异样的JSP Taglib API，模板语言支持，Freemarker和Velocity &nbsp;&nbsp; &nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 可以自定义模板库，或者修改已有模板的内容来定制页面的显示。<br /><br />Struts 2 中使用的模式<br />Command<br />Chain of responsibility<br /><br />Struts2 处理流程概要</p><p><img src="http://www.easyjf.com/upload/newsDocPicture/11930424.GIF" border="0" height="750" alt="" width="640" /></p><p>上图来源于Struts2官方站点，是Struts 2 的整体结构。<br />一个请求在Struts2框架中的处理大概分为以下几个步骤<br />1 客户端初始化一个指向Servlet容器（例如Tomcat）的请求<br />2 这个请求经过一系列的过滤器（Filter）（这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器，这个过滤器对于Struts2和其他框架的集成很有帮助，例如：SiteMesh Plugin）<br />3 接着FilterDispatcher被调用，FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action<br />4 如果ActionMapper决定需要调用某个Action，FilterDispatcher把请求的处理交给ActionProxy<br />5 ActionProxy通过Configuration Manager询问框架的配置文件，找到需要调用的Action类<br />6 ActionProxy创建一个ActionInvocation的实例。<br />7 ActionInvocation实例使用命名模式来调用，在调用Action的过程前后，涉及到相关拦截器（Intercepter）的调用。<br />8 一旦Action执行完毕，ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是（但不总是，也可能是另外的一个Action链）一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper<br />&nbsp;<br />在上述过程中所有的对象（Action，Results，Interceptors，等）都是通过ObjectFactory来创建的。</p></div>
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/178056#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 31 Mar 2008 14:51:20 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/178056</link>
        <guid>http://fangyong2006.javaeye.com/blog/178056</guid>
      </item>
      <item>
        <title>spring @aspectj 无法控制spring mvc?(转)</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/177810" style="color:red;">http://fangyong2006.javaeye.com/blog/177810</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="postbody clearfix"><div id="related_topics" style="position: relative"><ul></ul><ul>原来采用纯粹的<span class="hilite1"><span class="hilite1">aspectj</span></span>进行的开发，是可以控制spring mvc层的 <br />现在用spring2.5来实现代码和测试都通过了可是就是实际部署运行的时候没有反应，不知道大家遇到没，请大家指点,谢谢~！ <br /><br />用同样的方法控制其它的相关类都是可以的，代码如下： <br /></ul></div><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/151658#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>CAspect.java里： &nbsp;&nbsp;</span></span></li><li><span class="annotation"><span style="color: #646464">@Pointcut</span></span><span>(</span><span class="string"><span style="color: #0000ff">&quot;execution(*&nbsp;com..action.*Controller.*(..))&nbsp;&amp;&amp;&nbsp;args(request,response)&quot;</span></span><span>)&nbsp; &nbsp;&nbsp;</span></li><li><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">void</span></strong></span><span>&nbsp;pointcutMVC(HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response){ &nbsp;&nbsp;</span></li><li><span>} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li><span class="annotation"><span style="color: #646464">@Around</span></span><span>(</span><span class="string"><span style="color: #0000ff">&quot;pointcutMVC(request,response)&quot;</span></span><span>) &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;Object&nbsp;aroundMvc(ProceedingJoinPoint&nbsp;jp,HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response){ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff">&quot;mvc&nbsp;aop&nbsp;arounding...&quot;</span></span><span>);&nbsp; &nbsp;&nbsp;</span></li><li><span>..&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li><span>spring配置文件： &nbsp;&nbsp;</span></li><li><span>&lt;aop:&lt;SPAN&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>=hilite1&gt;&lt;SPAN&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>=hilite1&gt;aspectj&lt;/SPAN&gt;&lt;/SPAN&gt;-autoproxy&nbsp;&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;aop:include&nbsp;name=</span><span class="string"><span style="color: #0000ff">&quot;CAspect&quot;</span></span><span>/&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/aop:&lt;SPAN&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>=hilite1&gt;&lt;SPAN&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>=hilite1&gt;aspectj&lt;/SPAN&gt;&lt;/SPAN&gt;-autoproxy&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&lt;bean&nbsp;id=</span><span class="string"><span style="color: #0000ff">&quot;CAspect&quot;</span></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>=</span><span class="string"><span style="color: #0000ff">&quot;com.axt.aop.CAspect&quot;</span></span><span>&nbsp;/&gt;&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">CAspect.java里：
@Pointcut(&quot;execution(* com..action.*Controller.*(..)) &amp;&amp; args(request,response)&quot;) 
public void pointcutMVC(HttpServletRequest request, HttpServletResponse response){
}

@Around(&quot;pointcutMVC(request,response)&quot;)
	public Object aroundMvc(ProceedingJoinPoint jp,HttpServletRequest request, HttpServletResponse response){
	System.out.println(&quot;mvc aop arounding...&quot;);	
..	}

spring配置文件：
&lt;aop:<span class="hilite1"><span class="hilite1">aspectj</span></span>-autoproxy &gt;
	&lt;aop:include name=&quot;CAspect&quot;/&gt;
&lt;/aop:<span class="hilite1"><span class="hilite1">aspectj</span></span>-autoproxy&gt;      
&lt;bean id=&quot;CAspect&quot; class=&quot;com.axt.aop.CAspect&quot; /&gt;
</pre></div><div id="topic_copyright">(转)http://www.javaeye.com/topic/151658</div>
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/177810#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 17:56:17 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/177810</link>
        <guid>http://fangyong2006.javaeye.com/blog/177810</guid>
      </item>
      <item>
        <title> AspectJ1(转载)</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/177809" style="color:red;">http://fangyong2006.javaeye.com/blog/177809</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&nbsp;</p><p>(转载)http://www.javaeye.com/topic/31023</p><p>软件环境 ： Eclipse3.1.1 , AJDT 1.3 , JDK1.4.2 , <span class="hilite1"><span class="hilite1">AspectJ</span></span>1.5, Struts1.1 ， Jetty5.1.6 <br /><br />场景介绍 ： workspace中已存在一个java项目，并已部署与Jetty服务器上，这里我采用了一个简单的struts项目，我将演示如何在不改变原有项目的任何代码和配置的基础上，在runtime对struts项目中的一些代码进行拦截。 <br /><br />首先，让我们来新建已个<span class="hilite1"><span class="hilite1">AspectJ</span></span>项目，名字为<span class="hilite1"><span class="hilite1">AspectJ</span></span>Demo。 <br /><img src="http://lenson.javaeye.com/upload/picture/pic/219/771a377b-0256-4dad-af14-4858b440f01b.gif" alt="" /> <br />在新建project的build path中加入<span class="hilite1"><span class="hilite1">aspectj</span></span>rt.jar <br /><img src="http://lenson.javaeye.com/upload/picture/pic/220/8a47cbe7-1c92-4f7f-8084-aa2e6dda86b3.gif" alt="" /> <br />在<span class="hilite1"><span class="hilite1">Aspectj</span></span> InPath中加入<span class="hilite1"><span class="hilite1">aspectj</span></span>代码中需要用到的类及jar，这边我加入了struts的所有jar及StrutsDemo的类编译路径 <br /><img src="http://lenson.javaeye.com/upload/picture/pic/221/43ad10f7-2d00-4316-9140-2110b9f6643c.gif" alt="" /> <br />好，接下来说一下我要拦截的代码片断，在原项目中有一个名叫UserLoginForm的FormBean，希望在runtime的时候，当struts调用这个form中的setUsername方法时拦截代码，相关的struts代码如下： </p><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>&nbsp;UserLoginForm&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">extends</span></strong></span><span>&nbsp;ActionForm&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">private</span></strong></span><span>&nbsp;String&nbsp;username; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">private</span></strong></span><span>&nbsp;String&nbsp;password; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;String&nbsp;getPassword()&nbsp;{ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">return</span></strong></span><span>&nbsp;password; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">void</span></strong></span><span>&nbsp;setPassword(String&nbsp;password)&nbsp;{ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">this</span></strong></span><span>.password&nbsp;=&nbsp;password; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;String&nbsp;getUsername()&nbsp;{ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">return</span></strong></span><span>&nbsp;username; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">void</span></strong></span><span>&nbsp;setUsername(String&nbsp;username)&nbsp;{ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">this</span></strong></span><span>.username&nbsp;=&nbsp;username; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">public class UserLoginForm extends ActionForm {
	private String username;
	private String password;
	
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}	
}
</pre><br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>&nbsp;UserLoginAction&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">extends</span></strong></span><span>&nbsp;Action&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">private</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">final</span></strong></span><span>&nbsp;String&nbsp;LOGIN_FAILURE&nbsp;=&nbsp;</span><span class="string"><span style="color: #0000ff">&quot;failure&quot;</span></span><span>; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">private</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">final</span></strong></span><span>&nbsp;String&nbsp;LOGIN_SUCCESS&nbsp;=&nbsp;</span><span class="string"><span style="color: #0000ff">&quot;success&quot;</span></span><span>; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;ActionForward&nbsp;execute(ActionMapping&nbsp;mapping,&nbsp;ActionForm&nbsp;form,HttpServletRequest&nbsp;request,&nbsp;HttpServletResponse&nbsp;response)&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">throws</span></strong></span><span>&nbsp;Exception&nbsp;{ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;UserLoginForm&nbsp;ulf&nbsp;=&nbsp;(UserLoginForm)form; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;LOG.info(</span><span class="string"><span style="color: #0000ff">&quot;Username:&quot;</span></span><span>+ulf.getUsername()+</span><span class="string"><span style="color: #0000ff">&quot;+Password:&quot;</span></span><span>+ulf.getPassword()); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">return</span></strong></span><span>&nbsp;mapping.findForward(LOGIN_FAILURE); &nbsp;&nbsp;</span></li><li><span>} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">public class UserLoginAction extends Action {
	private final String LOGIN_FAILURE = &quot;failure&quot;;
	private final String LOGIN_SUCCESS = &quot;success&quot;;
	
	public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) throws Exception {
	UserLoginForm ulf = (UserLoginForm)form;
	LOG.info(&quot;Username:&quot;+ulf.getUsername()+&quot;+Password:&quot;+ulf.getPassword());
		
	return mapping.findForward(LOGIN_FAILURE);
}
}</pre><br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>&lt;html:form&nbsp;action=</span><span class="string"><span style="color: #0000ff">&quot;/login&quot;</span></span><span>&nbsp;focus=</span><span class="string"><span style="color: #0000ff">&quot;username&quot;</span></span><span>&nbsp;onsubmit=</span><span class="string"><span style="color: #0000ff">&quot;&quot;</span></span><span>&gt; &nbsp;&nbsp;</span></span></li><li><span>&lt;table&nbsp;border=</span><span class="string"><span style="color: #0000ff">&quot;0&quot;</span></span><span>&nbsp;width=</span><span class="string"><span style="color: #0000ff">&quot;100%&quot;</span></span><span>&gt; &nbsp;&nbsp;</span></li><li><span>&lt;tr&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;th&nbsp;align=</span><span class="string"><span style="color: #0000ff">&quot;right&quot;</span></span><span>&gt;Username:&lt;/th&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&nbsp;align=</span><span class="string"><span style="color: #0000ff">&quot;left&quot;</span></span><span>&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;html:text&nbsp;property=</span><span class="string"><span style="color: #0000ff">&quot;username&quot;</span></span><span>&nbsp;size=</span><span class="string"><span style="color: #0000ff">&quot;16&quot;</span></span><span>&nbsp;maxlength=</span><span class="string"><span style="color: #0000ff">&quot;18&quot;</span></span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/td&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/tr&gt; &nbsp;&nbsp;</span></li><li><span>&lt;tr&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;th&nbsp;align=</span><span class="string"><span style="color: #0000ff">&quot;right&quot;</span></span><span>&gt;Password:&lt;/th&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&nbsp;align=</span><span class="string"><span style="color: #0000ff">&quot;left&quot;</span></span><span>&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;html:password&nbsp;property=</span><span class="string"><span style="color: #0000ff">&quot;password&quot;</span></span><span>&nbsp;size=</span><span class="string"><span style="color: #0000ff">&quot;16&quot;</span></span><span>&nbsp;maxlength=</span><span class="string"><span style="color: #0000ff">&quot;18&quot;</span></span><span>&nbsp;redisplay=</span><span class="string"><span style="color: #0000ff">&quot;false&quot;</span></span><span>&nbsp;/&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/td&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/tr&gt; &nbsp;&nbsp;</span></li><li><span>&lt;tr&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&nbsp;align=</span><span class="string"><span style="color: #0000ff">&quot;right&quot;</span></span><span>&gt;&lt;html:submit&nbsp;value=</span><span class="string"><span style="color: #0000ff">&quot;Submit&quot;</span></span><span>&nbsp;/&gt;&lt;/td&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;td&nbsp;align=</span><span class="string"><span style="color: #0000ff">&quot;left&quot;</span></span><span>&gt;&lt;html:reset&nbsp;/&gt;&lt;/td&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/tr&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/table&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/html:form&gt;&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">&lt;html:form action=&quot;/login&quot; focus=&quot;username&quot; onsubmit=&quot;&quot;&gt;
&lt;table border=&quot;0&quot; width=&quot;100%&quot;&gt;
&lt;tr&gt;
	&lt;th align=&quot;right&quot;&gt;Username:&lt;/th&gt;
	&lt;td align=&quot;left&quot;&gt;
	&lt;html:text property=&quot;username&quot; size=&quot;16&quot; maxlength=&quot;18&quot; /&gt;
	&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
	&lt;th align=&quot;right&quot;&gt;Password:&lt;/th&gt;
	&lt;td align=&quot;left&quot;&gt;
	&lt;html:password property=&quot;password&quot; size=&quot;16&quot; maxlength=&quot;18&quot; redisplay=&quot;false&quot; /&gt;
	&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
	&lt;td align=&quot;right&quot;&gt;&lt;html:submit value=&quot;Submit&quot; /&gt;&lt;/td&gt;
	&lt;td align=&quot;left&quot;&gt;&lt;html:reset /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/html:form&gt;
</pre><br />OK,现在让我们在<span class="hilite1"><span class="hilite1">AspectJ</span></span>Demo中建一个aspect吧 <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;aspect&nbsp;LoginAspect&nbsp;{ &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment"><span style="color: #008200">//建立一个pointcut，这个pointcut对所有方法签名为&nbsp; </span></span><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment"><span style="color: #008200">//public&nbsp;void&nbsp;setUsername(String)&nbsp;的方法被执行的时候有效 </span></span><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;pointcut&nbsp;setUsername()&nbsp;: &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;execution&nbsp;(</span><span class="keyword"><strong><span style="color: #7f0055">public</span></strong></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">void</span></strong></span><span>&nbsp;setUsername(String)); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment"><span style="color: #008200">//设定拦截位置，在setUsername这个pointcut执行前打印出一段message </span></span><span>&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;before()&nbsp;:&nbsp;setUsername()&nbsp;{ &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string"><span style="color: #0000ff">&quot;setUsername&nbsp;method&nbsp;has&nbsp;been&nbsp;invoked!!!&quot;</span></span><span>); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span></li><li><span>}&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">public aspect LoginAspect {
    //建立一个pointcut，这个pointcut对所有方法签名为 
     //public void setUsername(String) 的方法被执行的时候有效
    public pointcut setUsername() :
    	execution (public void setUsername(String));
    //设定拦截位置，在setUsername这个pointcut执行前打印出一段message
    before() : setUsername() {
    	System.out.println(&quot;setUsername method has been invoked!!!&quot;);
    }
}
</pre><br />接下来，我们需要一些配置文件，首先在StrutsDemo下我们新建一个Jetty5.xml的jetty配置文件，当然这个文件可以拷贝jetty_home/ect/jetty.xml，但必须定义你项目的context <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>&lt;Call&nbsp;name=</span><span class="string"><span style="color: #0000ff">&quot;addWebApplications&quot;</span></span><span>&gt; &nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Arg&gt;&lt;/Arg&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Arg&gt;./webapp/&lt;/Arg&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Arg&gt;&lt;SystemProperty&nbsp;name=</span><span class="string"><span style="color: #0000ff">&quot;jetty.home&quot;</span></span><span>&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">default</span></strong></span><span>=</span><span class="string"><span style="color: #0000ff">&quot;.&quot;</span></span><span>/&gt;/etc/webdefault.xml&lt;/Arg&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Arg&nbsp;type=</span><span class="string"><span style="color: #0000ff">&quot;boolean&quot;</span></span><span>&gt;</span><span class="keyword"><strong><span style="color: #7f0055">true</span></strong></span><span>&lt;/Arg&gt;&lt;!--extract&nbsp;WARs--&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Arg&nbsp;type=</span><span class="string"><span style="color: #0000ff">&quot;boolean&quot;</span></span><span>&gt;</span><span class="keyword"><strong><span style="color: #7f0055">false</span></strong></span><span>&lt;/Arg&gt;&lt;!--&nbsp;java&nbsp;</span><span class="number"><span style="color: #c00000">2</span></span><span>&nbsp;compliante&nbsp;</span><span class="keyword"><strong><span style="color: #7f0055">class</span></strong></span><span>&nbsp;loader&nbsp;--&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&lt;/Call&gt;&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">&lt;Call name=&quot;addWebApplications&quot;&gt;
    &lt;Arg&gt;&lt;/Arg&gt;
    &lt;Arg&gt;./webapp/&lt;/Arg&gt;
    &lt;Arg&gt;&lt;SystemProperty name=&quot;jetty.home&quot; default=&quot;.&quot;/&gt;/etc/webdefault.xml&lt;/Arg&gt;
    &lt;Arg type=&quot;boolean&quot;&gt;true&lt;/Arg&gt;&lt;!--extract WARs--&gt;
    &lt;Arg type=&quot;boolean&quot;&gt;false&lt;/Arg&gt;&lt;!-- java 2 compliante class loader --&gt;
  &lt;/Call&gt;</pre><br />然后在<span class="hilite1"><span class="hilite1">AspectJ</span></span>Demo/src/META-INF下建立aop.xml，定义我们的aspect列表 <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>&lt;?xml&nbsp;version=</span><span class="string"><span style="color: #0000ff">&quot;1.0&quot;</span></span><span>&nbsp;encoding=</span><span class="string"><span style="color: #0000ff">&quot;UTF-8&quot;</span></span><span>?&gt; &nbsp;&nbsp;</span></span></li><li><span>&lt;<span class="hilite1"><span class="hilite1">aspectj</span></span>&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;aspects&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;aspect&nbsp;name=</span><span class="string"><span style="color: #0000ff">&quot;lenson.presentation.LoginAspect&quot;</span></span><span>/&gt; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/aspects&gt; &nbsp;&nbsp;</span></li><li><span>&lt;/<span class="hilite1"><span class="hilite1">aspectj</span></span>&gt;&nbsp;&nbsp;</span></li></ol></div><pre name="code" class="java">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;<span class="hilite1"><span class="hilite1">aspectj</span></span>&gt;
	&lt;aspects&gt;
		&lt;aspect name=&quot;lenson.presentation.LoginAspect&quot;/&gt;
	&lt;/aspects&gt;
&lt;/<span class="hilite1"><span class="hilite1">aspectj</span></span>&gt;</pre><br />代码都写完了，我们怎么让我们的setUsername方法在runtime时被拦截呢？我们需要对Jetty服务器启动方式进行一些改变，让我们来新建一个<span class="hilite1"><span class="hilite1">AspectJ</span></span> Load-Time Weaving Application，非常类似于Java Application <br /><img src="http://lenson.javaeye.com/upload/picture/pic/222/422399cd-73a5-4fbc-b0fb-bdb8c91362c9.gif" alt="" /> <br />选中StrutsDemo项目，当然我们需要在classpath中加入jetty运行时所需要的jar <br /><img src="http://lenson.javaeye.com/upload/picture/pic/223/35837bb4-32ce-4aa6-aa6f-cee01a7e47cd.gif" alt="" /> <br />然后，我们需要把我们的<span class="hilite1"><span class="hilite1">AspectJ</span></span>Demo放到LTW Aspectpath中 <br /><img src="http://lenson.javaeye.com/upload/picture/pic/224/16215de7-0d07-4fda-8a69-b59cbaac94e5.gif" alt="" /> <br />最后，我们需要设定Jetty启动时所需要的arguments <br /><img src="http://lenson.javaeye.com/upload/picture/pic/225/d1429f91-f05b-4678-adde-081e9b9ee974.gif" alt="" /> <br />Jetty Home Path <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>-Djetty.home=</span><span class="string"><span style="color: #0000ff">&quot;D:\development\jetty-5.1.6&quot;</span></span><span>&nbsp;&nbsp;</span></span></li></ol></div><pre name="code" class="java">-Djetty.home=&quot;D:\development\jetty-5.1.6&quot;</pre><br />jetty.xml Path （注意我的working directory都在StrutsDemo下） <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>./jetty5.xml&nbsp;&nbsp;</span></span></li></ol></div><pre name="code" class="java">./jetty5.xml</pre><br />大功告成，点run运行一下我们的项目吧。访问jsp，提交你的表单 <br /><div class="dp-highlighter"><div class="bar"><div class="tools">Java代码 <a href="http://www.javaeye.com/topic/31023#" title="复制代码" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;"><img src="http://www.javaeye.com/images/icon_copy.gif" alt="复制代码" /></a></div></div><ol class="dp-j"><li><span><span>setUsername&nbsp;method&nbsp;has&nbsp;been&nbsp;invoked!!! &nbsp;&nbsp;</span></span></li><li><span class="number"><span style="color: #c00000">21</span></span><span>:</span><span class="number"><span style="color: #c00000">51</span></span><span>:</span><span class="number"><span style="color: #c00000">59</span></span><span>,</span><span class="number"><span style="color: #c00000">211</span></span><span>&nbsp;&nbsp;INFO&nbsp;UserLoginAction:</span><span class="number"><span style="color: #c00000">22</span></span><span>&nbsp;-&nbsp;Username:test+Password:test&nbsp;&nbsp;</span></li></ol></div>
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/177809#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 17:50:34 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/177809</link>
        <guid>http://fangyong2006.javaeye.com/blog/177809</guid>
      </item>
      <item>
        <title>AspectJ</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/177806" style="color:red;">http://fangyong2006.javaeye.com/blog/177806</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">  AspectJ是一个面向切面的框架，它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。


一、AspectJ概述



图1 ：FigureEditor例子的UML图

      AspectJ（也就是AOP）的动机是发现那些使用传统的编程方法无法很好处理的问题。考虑一个要在某些应用中实施安全策略的问题。安全性是贯穿于系统所有模块间的问题，每个模块都需要应用安全机制才能保证整个系统的安全性，很明显这里的安全策略的实施问题就是一个横切关注点，使用传统的编程解决此问题非常的困难而且容易产生差错，这就正是AOP发挥作用的时候了。

      传统的面向对象编程中，每个单元就是一个类，而类似于安全性这方面的问题，它们通常不能集中在一个类中处理因为它们横跨多个类，这就导致了代码无法重用，可维护性差而且产生了大量代码冗余，这是我们不愿意看到的。

      面向方面编程的出现正好给处于黑暗中的我们带来了光明，它针对于这些横切关注点进行处理，就好象面向对象编程处理一般的关注点一样。而作为AOP的具体实现之一的AspectJ，它向Java中加入了连接点（Join Point）这个新概念，其实它也只是现存的一个Java概念的名称而已。它向Java语言中加入少许新结构：切点(pointcut)、通知(Advice)、类型间声明(Inter-type declaration)和方面(Aspect)。切点和通知动态地影响程序流程，类型间声明则是静态的影响程序的类等级结构，而方面则是对所有这些新结构的封装。

      一个连接点是程序流中指定的一点。切点收集特定的连接点集合和在这些点中的值。一个通知是当一个连接点到达时执行的代码，这些都是AspectJ的动态部分。其实连接点就好比是程序中的一条一条的语句，而切点就是特定一条语句处设置的一个断点，它收集了断点处程序栈的信息，而通知就是在这个断点前后想要加入的程序代码。AspectJ中也有许多不同种类的类型间声明，这就允许程序员修改程序的静态结构、名称、类的成员以及类之间的关系。AspectJ中的方面是横切关注点的模块单元。它们的行为与Java语言中的类很象，但是方面还封装了切点、通知以及类型间声明。

动态连接点模型

      任何面向方面编程的关键元素就是连接点模型。AspectJ提供了许多种类的连接点集合，但是本篇只介绍它们中的一个：方法调用连接点集(method call join points)。一个方法调用连接点捕捉对象的方法调用。每一个运行时方法调用都是一个不同的连接点，许多其他的连接点集合可能在方法调用连接点执行时运，包括方法执行时的所有连接点集合以及在方法中其他方法的调用。我们说这些连接点集合在原来调用的连接点的动态环境中执行。

 

切点

       在AspectJ中，切点捕捉程序流中特定的连接点集合。例如，切点

              call(void Point.setX(int))

捕捉每一个签名为void Point.setX(int)的方法调用的连接点，也就是说，调用Point对象的有一个整型参数的void setX方法。切点能与其他切点通过或(||)、与(&amp;&amp;)以及非(!)操作符联合。例如 call(void Point.setX(int)) || call(void Point.setY(int)) 捕捉setX或setY调用的连接点。切点还可以捕捉不同类型的连接点集合，换句话说，它们能横切类型。例如

       call(void FigureElement.setXY(int,int)) || call(void Point.setX(int))

       || call(void Point.setY(int) || call(void Line.setP1(Point))

       || call(void Line.setP2(Point));

捕捉上述五个方法调用的任意一个的连接点集合。它在本文的例子中捕捉当FigureElement移动时的所有连接点集合。AspectJ使程序员可以命名一个切点集合，以便通知的使用。例如可以为上面的那些切点命名

pointcut move():

call(void FigureElement.setXY(int,int)) || call(void Point.setX(int))

|| call(void Point.setY(int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point));

无论什么时候，程序员都可以使用move()代替捕捉这些复杂的切点。

       前面所说的切点都是基于显示的方法签名，它们称为基于名字(name-based)横切。AspectJ还提供了另一种横切，称为基于属性(property-based)的横切。它们可以使用通配符描述方法签名，例如 call(void Figure.make*(..)) 捕捉Figure对象中以make开头的参数列表任意的方法调用的连接点。而 call(public &amp; Figure.*(..)) 则捕捉Figure对象中的任何公共方法调用的连接点。但是通配符不是AspectJ支持的唯一属性，AspectJ中还有许多其他的属性可供程序员使用。例如cflow，它根据连接点集合是否在其他连接点集合的动态环境中发生标识连接点集合。例如 cflow(move()) 捕捉被move()捕捉到的连接点集合的动态环境中发生的连接点。

 

通知

       虽然切点用来捕捉连接点集合，但是它们没有做任何事。要真正实现横切行为，我们需要使用通知机制。通知包含了切点和要在每个连连接点处执行的代码段。AspectJ有几种通知。

&middot;前通知(Before Advice)      当到达一个连接点但是在程序进程运行之前执行。例如，前通知在方法实际调用之前运行，刚刚在方法的参数被分析之后。

       Before() : move(){ System.out.println(&ldquo;物体将移动了&rdquo;);}

&middot;后通知(After Advice) 当特定连接点处的程序进程执行之后运行。例如，一个方法调用的后通知在方法体运行之后，刚好在控制返回调用者之前执行。因为Java程序有两种退出连接点的形式，正常的和抛出异常。相对的就有三种后通知：返回后通知(after returning)、抛出异常后通知(after throwing)和清楚的后通知(after)，所谓清楚后通知就是指无论是正常还是异常都执行的后通知，就像Java中的finally语句。

       After() returning : move(){    System.out.println(&ldquo;物体刚刚成功的移动了&rdquo;);}

&middot;在周围通知(Around Advice)    在连接点到达后，显示的控制程序进程是否执行（暂不讨论）

 

暴露切点环境

       切点不仅仅捕捉连接点，它还能暴露连接点处的部分执行环境。切点中暴露的值可以在通知体中声明以后使用。通知声明有一个参数列表（和方法相同）用来描述它所使用的环境的名称。例如后通知

       after(FigureElement fe,int x,int y) returning : somePointcuts {  someCodes  }

使用了三个暴露的环境，一个名为fe的FigureElement对象，两个整型变量x,y。通知体可以像使用方法的参数那样使用这些变量，例如

       after(FigureElement fe,int x,int y) returning : somePointcuts {

              System.out.println(fe+&rdquo;移动到(&rdquo;+x+&rdquo;,&rdquo;+y+&rdquo;)&rdquo;);

}

通知的切点发布了通知参数的值，三个原生切点this、target和args被用来发布这些值/所以上述例子的完整代码为

       after(FigureElement fe,int x,int y) returning : call(void FigureElement.setXY(int,int)

&amp;&amp; target(fe) &amp;&amp; args(x,y) {

              System.out.println(fe+&rdquo;移动到(&rdquo;+x+&rdquo;,&rdquo;+y+&rdquo;)&rdquo;);

}

目标对象是FigureElement所以fe是after的第一个参数，调用的方法包含两个整型参数所以x和y为after的第二和第三个参数。所以通知打印出方法setXY调用返回后对象移动到的点x和y。当然还可以使用命名切点完成同样的工作，例如

       pointcut setXY(FigureElement fe,int x,int y):call(void FigureElement.setXY(int,int)

              &amp;&amp; target(fe) &amp;&amp; args(x,y);

       after(FigureElement fe,int x,int y) returning : setXY(fe,x,y){

       System.out.println(fe+&rdquo;移动到(&rdquo;+x+&rdquo;,&rdquo;+y+&rdquo;)&rdquo;);

       }

 

类型间声明

       AspectJ的类型间声明指的是那些跨越类和它们的等级结构的声明。这些可能是横跨多个类的成员声明或者是类之间继承关系的改变。不像通知是动态地操作，类型间声明编译时的静态操作。考虑一下，Java语言中如何向一个一些的类中加入新方法，这需要实现一个特定接口，所有类都必须在各自内部实现接口声明的方法，而使用AspectJ则可以将这些工作利用类型间声明放在一个方面中。这个方面声明方法和字段，然后将它们与需要的类联系。

假设我们想有一个Sreen对象观察Point对象的变化，当Point是一个存在的类。我们可以通过书写一个方面，由这个方面声明Point对象有一个实例字段observers,用来保存所有观察Point对象的Screen对象的引用，从而实现这个功能。

       Aspect PointObserving{

              Private    Collection Point.observers=new ArrayList();

&hellip;&hellip;

}

observers字段是私有字段，只有PointObserving能使用。因此，要在aspect中加入方法管理observers聚集。

       Aspect PointObserving{

              Private Collection Point.observers=new ArrayList();

              Public static void addObserver(Point p,Screen s){

                     p.observers.add(s);

              }

              public static void removeObserver(Point p,Screen s){

                     p.observers.remove(s);

              }

              &hellip;&hellip;

}

然后我们可以定义一个切点stateChanges决定我们想要观察什么并且提供一个after通知定义当观察到变化时我们想要做什么。

       Aspect PointObserving{

              Private Collection Point.observers=new ArrayList();

              Public static void addObserver(Point p,Screen s){

                     p.observers.add(s);

              }

              public static void removeObserver(Point p,Screen s){

                     p.observers.remove(s);

              }

              pointcut stateChanges(Point p) : target(p) &amp;&amp; call(void Point.set*(int));

              after(Point p) : stateChanges(p){

                     Iterator it=p.observers.iterator();

                     While(it.hasNext()){

                            UpdateObserver(p,(Screen)it.next()));

                     }

              }

              private static void updateObserver(Point p,Screen s){

                     s.display(p);

              }

}

注意无论是Sreen还是Point的代码都没有被修改，所有的新功能的加入都在方面中实现了，很酷吧！

 

方面

       方面以横切模块单元的形式包装了所有的切点、通知和类型间声明。这非常像Java语言的类。实际上，方面也可以定义自己的方法，字段和初始化方法。像类一样一个方面也可以用abstrace关键字声明为抽象方面，可以被子方面继承。在AspectJ中方面的设计实际上使用了单例模式，缺省情况下，它不能使用new构造，但是可以使用一个方法实例化例如方法aspectOf()可以获得方面的实例。所以在方面的通知中可以使用非静态的成员字段。

例如

       aspect Tracing {

              OutputStream trace=System.out;

              After() : move(){    trace.println(&ldquo;物体成功移动&rdquo;); }


二、AspectJ应用范围

       如前所述，AspectJ可以用于应用开发的不同阶段。下面讨论不同阶段的AspectJ的具体应用情况。

开发型方面(Development Aspects)

       开发方面可以很容易的从真正的产品中删除。而产品方面则被可用于开发过程和生产过程，但是仅仅影响某几个类。

       这一部分将通过几个例子说明方面在Java应用的开发阶段是如何使用的。这些方面包括调试、测试和性能检测等工作。方面定义的行为范围包括简单的代码跟踪、测试应用的内在联系等等。使用AspectJ不仅使得模块化这些功能变为可能，同时也使得根据需要打开和关闭这些功能变成可能。

 

代码跟踪(Tracing)
       首先让我们看看如何增加一个程序内部工作的可视性。我们定义一个简单的方面用于代码跟踪并且在每个方法调用时输出一些信息。在前一篇的图形编辑例子中，这样的方面可能仅仅简单的跟踪什么时候画一个点。

aspect SimpleTracing {
    pointcut tracedCall():
        call(void FigureElement.draw(GraphicsContext));
 
    before(): tracedCall() {
        System.out.println(&quot;Entering: &quot; + thisJoinPoint);
    }
}
代码利用了thisJoinPoint变量。在所有的通知体内，这个变量将与描述当前连接点的对象绑定。所以上述代码在每次一个FigureElement对象接受到draw方法时输出如下信息：

Entering: call(void FigureElement.draw(GraphicsContext))

通常我们在调式程序时，会在特定的地方放置几条输出语句，而当调试结束时还需要找到这些代码段将它们删除，这样做不但使我们的代码很难看而且很费时间。而使用AspectJ我们可以克服以上的两个问题，我们可以通过定义切点捕捉任何想要观察的代码段，利用通知可以在方面内部书写输出语句，而不需要修改源代码，当不在需要跟踪语句的时候还可以很轻松的将方面从应用中删除并重新编译代码即可。

 

前提条件和后续条件(Pre-and Post-Conditions)
       许多的程序员使用按契约编程(Design by Contract)的形式。这种形式的编程需要显式的前提条件测试以保证方法调用是否合适，还需要显式的后续条件测试保证方法是否工作正常。AspectJ使得可以模块化地实现这两种条件测试。例如下面的代码

aspect PointBoundsChecking {

    pointcut setX(int x):

        (call(void FigureElement.setXY(int, int)) &amp;&amp; args(x, *))

        || (call(void Point.setX(int)) &amp;&amp; args(x));

 

    pointcut setY(int y):

        (call(void FigureElement.setXY(int, int)) &amp;&amp; args(*, y))

        || (call(void Point.setY(int)) &amp;&amp; args(y));

 

    before(int x): setX(x) {

        if ( x &lt; MIN_X || x &gt; MAX_X )

            throw new IllegalArgumentException(&quot;x is out of bounds.&quot;);

    }

 

    before(int y): setY(y) {

        if ( y &lt; MIN_Y || y &gt; MAX_Y )

            throw new IllegalArgumentException(&quot;y is out of bounds.&quot;);

    }

}

它实现了边界检测功能。当FigureElement对象移动时，如果x或y的值超过了定义的边界，程序将会抛出IllegalArgumentException异常。

 

合同实施(Contract Enforcement)
       基于属性的横切机制在定义更加复杂的合同实施上非常有用。一个十分强大的功能是它可以强制特定的方法调用只出现在对应的程序中，而在其他程序中不出现。例如，下面的方面实施了一个限制，使得只有在知名的工厂方法中才能向注册并添加FigureElement对象。实施这个限制的目的是为了确保没有任何一个FigureElement对象被注册多次。

static aspect RegistrationProtection {

    pointcut register(): call(void Registry.register(FigureElement));

    pointcut canRegister(): withincode(static * FigureElement.make*(..));

 

    before(): register() &amp;&amp; !canRegister() {

        throw new IllegalAccessException(&quot;Illegal call &quot; + thisJoinPoint);

    }

}

这个方面使用了withincode初始切点，它表示在FigureElement对象的工厂方法(以make开始的方法)体内出现的所有连接点。在before通知中声明一个异常，该通知用于捕捉任何不在工厂方法代码内部产生的register方法的调用。该通知在特定连接点处抛出一个运行时异常，但是AspectJ能做地更好。使用declare error的形式，我们可以声明一个编译时的错误。

static aspect RegistrationProtection {

    pointcut register(): call(void Registry.register(FigureElement));

    pointcut canRegister(): withincode(static * FigureElement.make*(..));

 

    declare error: register() &amp;&amp; !canRegister(): &quot;Illegal call&quot;

}

当使用这个方面后，如果代码中存在定义的这些非法调用我们将无法通过编译。这种情况只出现在我们只需要静态信息的时候，如果我们需要动态信息，像上面提到的前提条件实施时，就可以利用在通知中抛出带参数的异常来实现。

 

配置管理(Configuration Management)
       AspectJ的配置管理可以使用类似于make-file等技术进行处理。程序员可以简单的包括他们想要的方面进行编译。不想要任何方面出现在产品阶段的开发者也可以通过配置他们的make-file使用传统的Java编译器编译整个应用。

 

产品型方面(Production Aspects)

       这一部分的方面例子将描述方面用于生产阶段的应用。产品方面将向应用中加入功能而不仅仅为程序的内部工作增加可视性。

改变监视(Change Monitoring)
       在第一个例子，方面的角色是用于维护一位数据标志，由它说明对象从最后一次显示刷新开始是否移动过。在方面中实现这样的功能是十分直接的，testAndClear方法被显示代码调用以便找到一个图形元素是否在最近移动过。这个方法返回标志的状态并将它设置为假。切点move捕捉所有能够是图形移动的方法调用。After通知截获move切点并设置标志位。

aspect MoveTracking {

private static boolean dirty = false;

 

    public static boolean testAndClear() {

        boolean result = dirty;

        dirty = false;

        return result;

    }

 

    pointcut move():

        call(void FigureElement.setXY(int, int)) ||

        call(void Line.setP1(Point)) ||

        call(void Line.setP2(Point)) ||

        call(void Point.setX(int)) ||

        call(void Point.setY(int));

 

    after() returning: move() {

        dirty = true;

    }

}
 

这个简单例子同样说明了在产品代码中使用AspectJ的一些好处。考虑使用普通的Java代码实现这个功能：将有可能需要包含标志位，testAndClear以及setFlag方法的辅助类。这些方法需要每个移动的图形元素包含一个对setFlag方法的调用。这些方法的调用就是这个例子中的横切关注点。

&middot;显示的捕捉了横切关注点的结构

&middot;功能容易拔插

&middot;实现更加稳定

 

传递上下文(Context Passing)
横切结构的上下文传递在Java程序中是十分复杂的一部分。考虑实现一个功能，它允许客户设置所创建的图形对象的颜色。这个需求需要从客户端传入一个颜色或颜色工厂。而要在大量的方法中加入一个参数，目的仅仅是为传递上下文信息这种不方便的情况是所有的程序员都十分熟悉的。

使用AspectJ，这种上下文的传递可以使用模块化的方式实现。下面代码中的after通知仅当一个图形对象的工厂方法在客户ColorControllingClient的某个方法控制流程中调用时才运行。

aspect ColorControl {

    pointcut CCClientCflow(ColorControllingClient client):

        cflow(call(* * (..)) &amp;&amp; target(client));

 

    pointcut make(): call(FigureElement Figure.make*(..));

 

    after (ColorControllingClient c) returning (FigureElement fe):

            make() &amp;&amp; CCClientCflow(c) {

        fe.setColor(c.colorFor(fe));

    }

}

这个方面仅仅影响一小部分的方法，但是注意该功能的非AOP实现可能 需要编辑更多的方法。

 

提供一致的行为(Providing Consistent Behavior)
接下来的例子说明了基于属性的方面如何在很多操作中提供一致的处理功能。这个方面确保包com.bigboxco的所有公共方法记录由它们抛出的任何错误。PublicMethodCall切点捕捉包中的公共方法调用， after通知在任何一个这种调用抛出错误后运行并且记录下这个错误。

aspect PublicErrorLogging {

    Log log = new Log();

 

    pointcut publicMethodCall():

        call(public * com.bigboxco.*.*(..));

 

    after() throwing (Error e): publicMethodCall() {

        log.write(e);

    }

}

在一些情况中，这个方面可以记录一个异常两次。这在com.bigboxco包内部的代码自己调用本包中的公共方法时发生。为解决这个问题，我们可以使用cflow初始切点将这些内部调用排除：

after() throwing (Error e) : publicMethodCall() &amp;&amp; !cflow(publicMethodCall()) {

    log.write(e);

}

 

结论

       AspectJ是对Java语言的简单而且实际的面向方面的扩展。仅通过加入几个新结构，AspectJ提供了对模块化实现各种横切关注点的有力支持。向以有的Java开发项目中加入AspectJ是一个直接而且渐增的任务。一条路径就是通过从使用开发方面开始再到产品方面当拥有了AspectJ的经验后就使用开发可重用方面。当然可以选取其他的开发路径。例如，一些开发者将从使用产品方面马上得到好处，另外的人员可能马上编写可重用的方面。

       AspectJ可以使用基于名字和基于属性这两种横切点。使用基于名字横切点的方面仅影响少数几个类，虽然它们是小范围的，但是比起普通的Java实现来说它们能够减少大量的复杂度。使用基于属性横切点的方面可以有小范围或着大范围。使用AspectJ导致了横切关注点的干净、模块化的实现。当编写AspectJ方面时，横切关注点的结构变得十分明显和易懂。方面也是高度模块化的，使得开发可拔插的横切功能变成现实。

       AspectJ提供了比这两部分简短介绍更多的功能。本系列的下一章内容，The AspectJ Language，将介绍 AspectJ语言的更多细节和特征。系列的第三章，Examples将通过一些完整的例子说明如何使用AspectJ。建议大家在仔细阅读了接下来的两章后再决定是否在项目中加入AspectJ。


三、AspectJ的高级特性

（一）、The reflection API

说到高级特性，首先要说的就是AspectJ提供的一套reflection API，主要包括JoinPoint、JoinPoint.StaticPart和Signature三个主要的接口。你可以从aspectj.jar中的javadoc来了解它们的详细情况。那它们能提供什么功能呢？其实从字面上就能大致明白：通过这三个接口能访问到Join Points的信息。譬如，调用thisJoinPoint.getArgs()就可以得到方法的参数列表。

（二）、Aspect precedence

在AspectJ中，pointcut和advice都会包含在一个aspect中。在应用系统中，对同一个join point会有多种advice（logging，caching等），这就会引出一个问题：如果系统中有很多的aspect，而这些aspect很有可能会捕获同样的join points，那这些aspect的执行顺序是如何安排的呢？

AspectJ早已为我们考虑到了这个问题，它提供了一种设置aspect precedence的方法。对三种不同的advice来说：

1、before advice是先执行higher-precedence，后执行lower-precedence；

2、around advice是higher-precedence包含lower-precedence，当higher-precedence around advice没有调用proceed()方法时，lower-precedence不会被执行；

3、after advice与before advice正好相反，先执行执行lower-precedence，然后执行higher-precedence。

那应该如何来声明aspect precedence？非常简单，只要在aspect中使用如下的语法即可：

declare precedence : TypePattern1, TypePattern2, ..;

从左往右，排在前面的是higher-precedence advice，后面的是lower-precedence。

（三）、Aspect association

在Java中，为了节省对象每次构建的耗费，增加效率，很多人会考虑使用Singleton模式，让jvm中只有一个实例存在。AspectJ当然为我们考虑到这个问题，Aspect association实际上就是aspect与advised join point object的一种关联关系，这很类似于OO中association，譬如1:1，1:m等。Aspect association能让我们能更好地控制aspect的状态信息。

在AspectJ中可以把Aspect association大致分为三类：

1、Per virtual machine (default)

一个jvm中只有一个aspect instance，AspectJ默认association。

2、Per object

每一个advised join point object都会产生一个aspect instance，不过同一个object instance只会产生一个aspect instance。

3、Per control-flow association

这种association稍微复杂一些，它主要针对程序调用的控制流，譬如：A方法调用B方法，B方法又调用C方法，这就是control-flow。

在aspect中声明这三种association非常简单，它的主要语法如下：

aspect [()] {
... aspect body
}

Per virtual machine是aspectj的默认association，不需要你额外的声明，正常使用即可。

Per object主要有两种方式：perthis()和pertarget()。perthis()主要用于execution object，pertarget()主要用于target object，两者非常类似。

Per control-flow中也包含两种方式：percflow()和percflowbelow()。这两者也很类似，只是两者的control-flow不太一样而已。

维护aspect的状态信息还有一种方法，就是使用introduce。可以在aspect中introduce member fields，通过fields来保存状态信息。

 

四、AspectJ实例

 

使用方面的Tracing程序

       写一个具有跟踪能力的类是很简单的事情：一组方法，一个控制其开或关的布尔变量，一种可选的输出流，可能还有一些格式化输出能力。这些都是Trace类需要的东西。当然，如果程序需要的话，Trace类也可以实现的十分的复杂。开发这样的程序只是一方面，更重要的是如何在合适的时候调用它。在大型系统开发过程中，跟踪程序往往影响效率，而且在正式版本中去除这些功能十分麻烦，需要修改任何包含跟踪代码的源码。出于这些原因，开发人员常常使用脚本程序以便向源码中添加或删除跟踪代码。

       AspectJ可以更加方便的实现跟踪功能并克服这些缺点。Tracing可以看作是面向整个系统的关注点，因此，Tracing方面可以完全独立在系统之外并且在不影响系统基本功能的情况下嵌入系统。

 

应用实例

整个例子只有四个类。应用是关于Shape的。TwoShape类是Shape类等级的基类。

public abstract class TwoDShape {

    protected double x, y;

    protected TwoDShape(double x, double y) {

        this.x = x; this.y = y;

    }

    public double getX() { return x; }

    public double getY() { return y; }

    public double distance(TwoDShape s) {

        double dx = Math.abs(s.getX() - x);

        double dy = Math.abs(s.getY() - y);

        return Math.sqrt(dx*dx + dy*dy);

    }

    public abstract double perimeter();

    public abstract double area();

    public String toString() {

        return (&quot; @ (&quot; + String.valueOf(x) + &quot;, &quot; + String.valueOf(y) + &quot;) &quot;);

    }

}

TwoShape类有两个子类，Circle和Square   

public class Circle extends TwoDShape {

    protected double r;

    public Circle(double x, double y, double r) {

        super(x, y); this.r = r;

    }

    public Circle(double x, double y) { this(  x,   y, 1.0); }

    public Circle(double r)           { this(0.0, 0.0,   r); }

    public Circle()                   { this(0.0, 0.0, 1.0); }

    public double perimeter() {

        return 2 * Math.PI * r;

    }

    public double area() {

        return Math.PI * r*r;

    }

    public String toString() {

        return (&quot;Circle radius = &quot; + String.valueOf(r) + super.toString());

    }

}

public class Square extends TwoDShape {

    protected double s;    // side

    public Square(double x, double y, double s) {

        super(x, y); this.s = s;

    }

    public Square(double x, double y) { this(  x,   y, 1.0); }

    public Square(double s)           { this(0.0, 0.0,   s); }

    public Square()                   { this(0.0, 0.0, 1.0); }

    public double perimeter() {

        return 4 * s;

    }

    public double area() {

        return s*s;

    }

    public String toString() {

        return (&quot;Square side = &quot; + String.valueOf(s) + super.toString());

    }

}

 

Tracing版本一

首先我们直接实现一个Trace类并不使用方面。公共接口Trace.java

public class Trace {

    public static int TRACELEVEL = 0;

    public static void initStream(PrintStream s) {...}

    public static void traceEntry(String str) {...}

    public static void traceExit(String str) {...}

}

如果我们没有AspectJ，我们需要在所有需要跟踪的方法或构造子中直接调用traceEntry和traceExit方法并且初试化TRACELEVEL和输出流。以上面的例子来说，如果我们要跟踪所有的方法调用（包括构造子）则需要40次的方法调用并且还要时刻注意没有漏掉什么方法，但是使用方面我们可以一致而可靠的完成。TraceMyClasses.java

aspect TraceMyClasses {

    pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);

    pointcut myConstructor(): myClass() &amp;&amp; execution(new(..));

    pointcut myMethod(): myClass() &amp;&amp; execution(* *(..));

 

    before (): myConstructor() {

        Trace.traceEntry(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

    after(): myConstructor() {

        Trace.traceExit(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

 

    before (): myMethod() {

        Trace.traceEntry(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

    after(): myMethod() {

        Trace.traceExit(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

}

这个方面在合适的时候调用了跟踪方法。根据此方面，跟踪方法在Shape等级中每个方法或构造子的入口和出口处调用，输出的是各个方法的签名。因为方法签名是静态信息，我们可以利用thisJoinPointStaticPart对象获得。运行这个方面的main方法可以获得以下输出：

  --&gt; tracing.TwoDShape(double, double)

  &lt;-- tracing.TwoDShape(double, double)

  --&gt; tracing.Circle(double, double, double)

  &lt;-- tracing.Circle(double, double, double)

  --&gt; tracing.TwoDShape(double, double)

  &lt;-- tracing.TwoDShape(double, double)

  --&gt; tracing.Circle(double, double, double)

  &lt;-- tracing.Circle(double, double, double)

  --&gt; tracing.Circle(double)

  &lt;-- tracing.Circle(double)

  --&gt; tracing.TwoDShape(double, double)

  &lt;-- tracing.TwoDShape(double, double)

  --&gt; tracing.Square(double, double, double)

  &lt;-- tracing.Square(double, double, double)

  --&gt; tracing.Square(double, double)

  &lt;-- tracing.Square(double, double)

  --&gt; double tracing.Circle.perimeter()

  &lt;-- double tracing.Circle.perimeter()

c1.perimeter() = 12.566370614359172

  --&gt; double tracing.Circle.area()

  &lt;-- double tracing.Circle.area()

c1.area() = 12.566370614359172

  --&gt; double tracing.Square.perimeter()

  &lt;-- double tracing.Square.perimeter()

s1.perimeter() = 4.0

  --&gt; double tracing.Square.area()

  &lt;-- double tracing.Square.area()

s1.area() = 1.0

  --&gt; double tracing.TwoDShape.distance(TwoDShape)

    --&gt; double tracing.TwoDShape.getX()

    &lt;-- double tracing.TwoDShape.getX()

    --&gt; double tracing.TwoDShape.getY()

    &lt;-- double tracing.TwoDShape.getY()

  &lt;-- double tracing.TwoDShape.distance(TwoDShape)

c2.distance(c1) = 4.242640687119285

  --&gt; double tracing.TwoDShape.distance(TwoDShape)

    --&gt; double tracing.TwoDShape.getX()

    &lt;-- double tracing.TwoDShape.getX()

    --&gt; double tracing.TwoDShape.getY()

    &lt;-- double tracing.TwoDShape.getY()

  &lt;-- double tracing.TwoDShape.distance(TwoDShape)

s1.distance(c1) = 2.23606797749979

  --&gt; String tracing.Square.toString()

    --&gt; String tracing.TwoDShape.toString()

    &lt;-- String tracing.TwoDShape.toString()

  &lt;-- String tracing.Square.toString()

s1.toString(): Square side = 1.0 @ (1.0, 2.0)

 

Tracing版本二

       版本二实现了可重用的tracing方面，使其不仅仅用于Shape的例子。首先定义如下的抽象方面Trace.java

abstract aspect Trace {

 

    public static int TRACELEVEL = 2;

    public static void initStream(PrintStream s) {...}

    protected static void traceEntry(String str) {...}

    protected static void traceExit(String str) {...}

abstract pointcut myClass();

 

}

为了使用它，我们需要定义我们自己的子类。

public aspect TraceMyClasses extends Trace {

    pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);

 

    public static void main(String[] args) {

        Trace.TRACELEVEL = 2;

        Trace.initStream(System.err);

        ExampleMain.main(args);

    }

}

注意我们仅仅在类中声明了一个切点，它是超类中声明的抽象切点的具体实现。版本二的Trace类的完整实现如下

abstract aspect Trace {

 

    // implementation part

 

    public static int TRACELEVEL = 2;

    protected static PrintStream stream = System.err;

    protected static int callDepth = 0;

 

    public static void initStream(PrintStream s) {

        stream = s;

    }

    protected static void traceEntry(String str) {

        if (TRACELEVEL == 0) return;

        if (TRACELEVEL == 2) callDepth++;

        printEntering(str);

    }

    protected static void traceExit(String str) {

        if (TRACELEVEL == 0) return;

        printExiting(str);

        if (TRACELEVEL == 2) callDepth--;

    }

    private static void printEntering(String str) {

        printIndent();

        stream.println(&quot;--&gt; &quot; + str);

    }

    private static void printExiting(String str) {

        printIndent();

        stream.println(&quot;&lt;-- &quot; + str);

    }

    private static void printIndent() {

        for (int i = 0; i &lt; callDepth; i++)

            stream.print(&quot;  &quot;);

    }

 

    // protocol part

 

    abstract pointcut myClass();

 

    pointcut myConstructor(): myClass() &amp;&amp; execution(new(..));

    pointcut myMethod(): myClass() &amp;&amp; execution(* *(..));

 

    before(): myConstructor() {

        traceEntry(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

    after(): myConstructor() {

        traceExit(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

 

    before(): myMethod() {

        traceEntry(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

    after(): myMethod() {

        traceExit(&quot;&quot; + thisJoinPointStaticPart.getSignature());

    }

}

它与版本一的不同包括几个部分。首先在版本一中Trace用单独的类来实现而方面是针对特定应用实现的，而版本二则将Trace所需的方法和切点定义融合在一个抽象方面中。这样做的结果是traceEntry和traceExit方法不需要看作是公共方法，它们将由方面内部的通知调用，客户完全不需要知道它们的存在。这个方面的一个关键点是使用了抽象切点，它其实与抽象方法类似，它并不提供具体实现而是由子方面实现它。

 

Tracing版本三

       在前一版本中，我们将traceEntry和traceExit方法隐藏在方面内部，这样做的好处是我们可以方便的更改接口而不影响余下的代码。

       重新考虑不使用AspectJ的程序。假设，一段时间以后，tracing的需求变了，我们需要在输出中加入方法所属对象的信息。至少有两种方法实现，一是保持traceEntry和traceExit方法不变，那么调用者有责任处理显示对象的逻辑，代码可能如下

       Trace.traceEntry(&quot;Square.distance in &quot; + toString());

另一种方法是增强方法的功能，添加一个参数表示对象，例如

  public static void traceEntry(String str, Object obj);

  public static void traceExit(String str, Object obj);

然而客户仍然有责任传递正确的对象，调用代码如下

       Trace.traceEntry(&quot;Square.distance&quot;, this);

这两种方法都需要动态改变其余代码，每个对traceEntry和traceExit方法的调用都需要改变。

       这里体现了方面实现的另一个好处，在版本二的实现中，我们只需要改变Trace方面内部的一小部分代码，下面是版本三的Trace方面实现

abstract aspect Trace {

 

    public static int TRACELEVEL = 0;

    protected static PrintStream stream = null;

    protected static int callDepth = 0;

 

    public static void initStream(PrintStream s) {

        stream = s;

    }

 

    protected static void traceEntry(String str, Object o) {

        if (TRACELEVEL == 0) return;

        if (TRACELEVEL == 2) callDepth++;

        printEntering(str + &quot;: &quot; + o.toString());

    }

 

    protected static void traceExit(String str, Object o) {

        if (TRACELEVEL == 0) return;

        printExiting(str + &quot;: &quot; + o.toString());

        if (TRACELEVEL == 2) callDepth--;

    }

 

    private static void printEntering(String str) {

        printIndent();

        stream.println(&quot;Entering &quot; + str);

    }

 

    private static void printExiting(String str) {

        printIndent();

        stream.println(&quot;Exiting &quot; + str);

    }

 

    private static void printIndent() {

        for (int i = 0; i &lt; callDepth; i++)

            stream.print(&quot;  &quot;);

    }

 

    abstract pointcut myClass(Object obj);

 

    pointcut myConstructor(Object obj): myClass(obj) &amp;&amp; execution(new(..));

    pointcut myMethod(Object obj): myClass(obj) &amp;&amp;

        execution(* *(..)) &amp;&amp; !execution(String toString());

 

    before(Object obj): myConstructor(obj) {

        traceEntry(&quot;&quot; + thisJoinPointStaticPart.getSignature(), obj);

    }

    after(Object obj): myConstructor(obj) {

        traceExit(&quot;&quot; + thisJoinPointStaticPart.getSignature(), obj);

    }

 

    before(Object obj): myMethod(obj) {

        traceEntry(&quot;&quot; + thisJoinPointStaticPart.getSignature(), obj);

    }

    after(Object obj): myMethod(obj) {

        traceExit(&quot;&quot; + thisJoinPointStaticPart.getSignature(), obj);

    }

}

在此我们必须在methods切点排除toString方法的执行。问题是toString方法在通知内部调用，因此如果我们跟踪它，我们将陷入无限循环中。这一点不明显，所以必须在写通知时格外注意。如果通知回调对象，通常都回存在循环的可能性。

       事实上，简单的排除连接点的执行并不够，如果在这之中调用了其他跟踪方法，那么就必须提供以下限制

&amp;&amp; !cflow(execution(String toString()))

排除toString方法的执行以及在这之下的所有连接点。

       总之，为了实现需求的改变我们必须在Trace方面中做一些改变，包括切点说明。但是实现的改变只局限于Trace方面内部，而如果没有方面，则需要更改每个应用类的实现。

</pre>&nbsp;
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/177806#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 17:19:31 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/177806</link>
        <guid>http://fangyong2006.javaeye.com/blog/177806</guid>
      </item>
      <item>
        <title>Acegi配置信息讲解(转)</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/177790" style="color:red;">http://fangyong2006.javaeye.com/blog/177790</a>&nbsp;
          发表时间: 2008年03月30日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">1. 概述 
Acegi是一个专门为SpringFramework应用提供安全机制的开放源代码项目，全称为Acegi Security System for Spring，当前版本为 
0.8.3。它使用了Spring的方式提供了安全和认证安全服务，包括使用Bean Context，拦截器和面向接口的编程方式。通过精心 
配置Acegi安全系统能够轻松地适用于复杂的安全需求。它既能应用于WEB应用也能应用于非WEB应用。在 本文的示例程序 
里，我将演示如何将Acegi应用于WEB应用程序。通过这个例子详细介绍如何配置Acegi的各个组件，同时介绍如何扩展Acegi 
使其能够从数据库中读取配置信息。

2. 例子说明 
本文的例子是一个联系人管理程序，使用SpringFramework 1.2.4 和 Acegi0.8.3，数据库采用Mysql。程序的目录结构如下： 　 
acegi-sample 
│ contactadd.jsp//增加联系人页面 
│ contactedit.jsp//编辑联系人页面 
│ contactlist.jsp//联系人列表页面 
│ contactmainterance.jsp//联系人操作页面 
│ index.jsp//主页面 
│ login.jsp//登录页面 
│ logoff.jsp//登出页面 
│
├─WEB-INF 
│ │ web.xml 
│ │ applicationContext-basic.xml 
│ │ applicationContext-security-acegi.xml 
│ │ log4j.properties 
│ │ 
│ ├─src 
│ │ └─sample//java代码目录 
│ ├─classes 
│ └─lib//依赖包目录 
└─db//建表脚本目录 

3.配置文件说明 
接下来，我们将进入本文的重要内容，开始对Acegi应用程序所牵涉到的配置文件进行一一说明。 

3.1 web.xml 
首先声明SpringFramework的配置文件列表。为了便于管理，将业务方法相关的配置文件和Acegi安全配置相关的配置文件分 
开。 　 

  
contextConfigLocation 

/WEB-INF/applicationContext-basic.xml 
/WEB-INF/applicationContext-security-acegi.xml 


其次声明Acegi过滤器。 　 
&lt;!--Acegi Filter Chain Proxy --&gt;

Acegi Filter Chain Proxy 
net.sf.acegisecurity.util.FilterToBeanProxy 

targetClass 
net.sf.acegisecurity.util.FilterChainProxy 



Acegi Filter Chain Proxy 
/* 

说明：Acegi对WEB应用的支持主要是依靠servlet 过滤器（filter） 来实现的。每一个http request都将被这些过滤器层层拦截 并 
进行安全处理（包括认证和授权）。针对不同的安全处理，Acegi提供了不同的过滤器。过滤器的配置信息位于web.xml，但是 
我们又希望把Acegi的过滤器配置信息放在SpringFramework的配置文件里（applicationContext-security-acegi.xml），从而实现对 
这些过滤器的&ldquo;控制反转&rdquo;。解决这个问题的方法是采用Acegi提供的FilterToBeanProxy。FilterToBeanProxy顾名思义就是对 
Acegi过滤器Bean的代理，它的主要功能就是将http请求依次分派给对应的过滤器Bean。 
3.2 applicationContext-security-acegi.xml 
applicationContext-security-acegi.xml主要包括认证相关配置信息和WEB资源授权配置信息。首先是声明过滤器序列。 　 



CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 
PATTERN_TYPE_APACHE_ANT 
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter 



各个过滤器的作用如下： 
httpSessionContextIntegrationFilter 
根据session中存放的信息组装ContextHolder。ContextHolder主要用于存放SecureContext， 
包括用户的权限信息 
authenticationProcessingFilter 处理认证请求（通常是一个登录页面的表单请求） 

anonymousProcessingFilter 
匿名用户处理。如果用户尚未登录，将生成一个匿名用户的Authentication存放到 
ContextHolder中 
securityEnforcementFilter 强制安全验证过滤器。验证所请求的url是否在用户的权限范围内。 
3.2.1 httpSessionContextIntegrationFilter相关配置信息 
　

net.sf.acegisecurity.context.security.SecureContextImpl

说明： context属性指定context的实现类。 
3.2.2 authenticationProcessingFilter相关配置信息 
authenticationProcessingFilter的配置比较复杂，我们通过下图来看一下： 
authenticationProcessingFilter配置说明： 

authenticationManager 认证管理器 
authenticationFailureUrl 认证失败后，重定向的url 
defaultTargetUrl 认证成功后，重定向的url 
filterProcessesUrl 
该过滤器拦截的url，通常是/j_acegi_security_check，和登录页面（login.jsp）的登录表单的action相 
同 
authenticationManager（认证管理器）用于管理AuthenticationProvider（认证提供者）。它的作用是使你能够通过多个不同的认 
证管理源来对用户进行认证。认证管理器将依次调用认证提供者的认证方法，直到认证通过。本程序使用两种认证提供者。 
authenticationManager配置说明： 
daoAuthenticationProvider 基于数据库的认证提供者。 
anonymousAuthenticationProvider 用于认证匿名用户。 
daoAuthenticationProvider主要功能是从数据库取出用户名和密码，判断登录信息是否正确，如果是，则取出用户权限等用户 
信息，并且存放到cache中，以便以后再次使用。具体流程可以参考：DaoAuthenticationProvider的authenticate方法。 
daoAuthenticationProvider配置说明： 
authenticationDao 认证数据访问对象，用于获取用户信息，包括：用户名，用户密码，用户状态和用户权限。 
userCache 用户信息cache实现bean 
jdbcDaoImpl是认证数据访问对象，它能够从默认的数据库结构中获取用户信息，由于Acegi默认的数据库结构和本程序的不 
同，因此需要修改jdbcDaoImpl的默认sql。注意，采用这种方式能够使Acegi很好的兼容旧的应用程序，因为它对底层的数据结 
构并没有强制要求。jdbcDaoImpl配置说明： 
dataSource 数据源bean 
usersByUsernameQuery 用户信息查询sql 
authoritiesByUsernameQuery 用户权限查询sql 
userCache用于定义用户信息cache功能的提供者。userCache配置说明： 
cache 定义ehcache工厂bean 
本程序采用ehcache作为cache实现。由于认证管理器在每次对http请求进行认证之前都会查找用户信息，通过使用cache就可以 
避免每次都重复访问数据库。 
3.2.3 anonymousProcessingFilter相关配置信息： 
foobar
anonymousUser,AUTH_ANONYMOUS
说明：anonymousProcessingFilter的作用是判断ContextHolder中是否有Authentication对象，如果没有就创建一个Authentication对 
象，其中包含的用户名是anonymousUser，用户权限是AUTH_ANONYMOUS。这使得没有登录的匿名用户能够自动的获得匿 
名的用户名和权限。 
3.2.4 securityEnforcementFilter相关配置信息 

securityEnforcementFilter的配置比较复杂
securityEnforcementFilter配置说明： 
filterSecurityInterceptor 实现对URL资源进行授权访问。 
authenticationEntryPoint 配置登录界面信息。 
securityEnforcementFilter的作用主要是将http请求转发给filterSecurityInterceptor，由filterSecurityInterceptor来对HTTP请求的合法 
性进行判断。 
filterInvocationInterceptor配置说明： 
authenticationManager 认证管理器 
accessDecisionManager 投票通过策略管理器 
objectDefinitionSource 
URL的权限配置信息。用于指定不同的URL资源对应的权限。配置如下： 
/**/*.jpg=AUTH_ANONYMOUS,AUTH_USER 
/**/*.gif=AUTH_ANONYMOUS,AUTH_USER 
/**/*.png=AUTH_ANONYMOUS,AUTH_USER 
/login.jsp*=AUTH_ANONYMOUS,AUTH_USER 
/**=AUTH_USER 
以上配置指定AUTH_ANONYMOUS权限的用户（即匿名用户）只可以访问图片资源和登录页 
面，AUTH_USER权限的用户可以访问全部WEB资源。 
accessDecisionManager（访问决策管理器）首先通过authenticationManager判断用户是否通过认证（即是否已经登录），然后根 
据objectDefinitionSource的配置信息调用accessDecisionManager对用户权限进行投票。 
httpRequestAccessDecisionManager配置说明： 
allowIfAllAbstainDecisions 设定是否允许：&ldquo;没人反对就通过&rdquo;的投票策略 
decisionVoters 投票者 

httpRequestAccessDecisionManager（投票通过策略管理器）用于管理投票通过策略。Acegi提供三种投票通过策略的实现： 
AffirmativeBased（至少一个投票者同意方可通过），ConsensusBased（多数投票者同意方可通过），UnanimousBased（所有投 
票者同意方可通过）。本程序采用AffirmativeBased策略，并且禁止&ldquo;没人反对就通过&rdquo;的投票策略。 
roleVoter配置说明： 
rolePrefix 
该投票者支持的权限前缀，默认是&ldquo;ROLE_&rdquo;，本程序所有的权限字符串均以&ldquo;AUTH_&rdquo;开头， 
故设为&ldquo;AUTH_&rdquo; 
通过设定rolePrefix可以指定roleVoter所支持的权限范围。 
3.3 applicationContext-basic.xml 
applicationContext-basic.xml主要包括数据访问对象，业务方法，业务方法安全管理拦截器的配置信息。主要讲解业务方法安全管理拦截器（MethodSecurityInterceptor）的相关配置，其它的配置就不再赘述了，请参考相关文 
档。 
3.3.1 contactManager相关配置信息 
  
sample.service.IContactManager
contactManager的实现类是ProxyFactoryBean（代理工厂），它使用Spring AOP技术拦截代理接口里的方法，并依次执行拦截器 
列表里的拦截器对应的操作。contactManager有事务拦截器和业务方法安全拦截器。 
3.3.2 contactManagerSecurity相关配置信息 
authenticationManager 认证管理器 
accessDecisionManager 
投票通过策略管理器，和filterInvocationInterceptor采用相同的策略管理器，在实际项目中，如果有 
需要可以采用不同的策略管理器。 
objectDefinitionSource 业务方法的权限配置信息。用于指定不同的业务方法资源对应的权限。 
注意：contactManagerSecurity的实现类是MethodSecurityInterceptor，缺省情况下MethodSecurityInterceptor的 
objectDefinitionSource属性是通过net.sf.acegisecurity.intercept.method.MethodDefinitionSourceEditor来设置的。 
MethodDefinitionSourceEditor只支持属性配置文件的格式（同filterInvocationInterceptor的objectDefinitionSource属性），而实际 
情况中，由于业务方法比较多，显然配置信息存放在数据库中比较好。因此，我们通过注册一个CustomEditorConfigurer来修 
改MethodDefinitionSource类型属 性的对应读取类。配置如下： 　 
DataSourceMethodDefinitionSourceEditor根据以下SQL读取业务方法安全配置信息： 　 
select authority,PROTECTED_RES from authorities where AUTH_TYPE='FUNCTION' and authority like 
'AUTH_FUNC_ContactManager%' 

4.总结 
在本文的示例程序中我们只对业务对象（ContactManager）进行安全保护，对业务领域对象（Contact）的访问并没有作限 
制，这是由于在Acegi框架中采用ACL（访问控制列表）技术实现这个功能，这使得一旦我们的业务领域对象数量很多的话， 
效率将变得很低，因此我们将对业务领域对象访问控制的代码放在业务对象的业务方法中。 
将业务无关的代码从业务代码中剥离，使业务代码更干净，系统结构更合理是每个开发人员的梦想。随着AOP技术的日渐流 
行和日益发展，这个梦想已经离我们不远了。本文中的例子通过结合使用SpringFramework和Acegi两种开源框架，实现了将安 
全认证和授权代码和事务代码从业务代码中分离。  
</pre>&nbsp;
          <br/>
          <span style="color:red;">
            <a href="http://fangyong2006.javaeye.com/blog/177790#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 30 Mar 2008 16:03:24 +0800</pubDate>
        <link>http://fangyong2006.javaeye.com/blog/177790</link>
        <guid>http://fangyong2006.javaeye.com/blog/177790</guid>
      </item>
      <item>
        <title>CVS配置管理</title>
        <author>fangyong2006</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fangyong2006.javaeye.com">fangyong2006</a>&nbsp;
          链接：<a href="http://fangyong2006.javaeye.com/blog/169867" style="color:red;">http://fangyong2006.javaeye.com/blog/169867</a>&nbsp;
          发表时间: 2008年03月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Eclipse中的CVS配置管理<br />关键字: CVS配置管理 <br />成都朗沃信息技术有限公<br />- 0 -<br />CVS讲解纲要 V2.0<br />• 关于CVS的三个W<br />• CVS配置<br />• CVS维护使用<br />目 录<br />一、关于CVS的三个W.............................................................................1<br />①what：CVS是什么？.......................................................................1<br />②why：为什么要使用CVS................................................................1<br />③when：什么时候使用CVS..............................................................1<br />二、CVS的相关配置.................................................................................2<br />三、CVS的使用与维护.............................................................................7<br />1、上传文件：....................................................................................7<br />2、下载文件......................................................................................11<br />3、改动比较......................................................................................14<br />四、附件：...............................................................................................19<br /><br />二○○七年九月二十一日<br />一、关于CVS的三个W<br />①what：CVS是什么？<br />CVS是并发版本系统的缩写（Concurrent Versions System）是目前主流的软件源码版本控制系统。整个软件由客户机服务器两部分组成。其中客户端已集成在eclipse软件中，我们只需装服务器端软件即可。见下图<br />②why：为什么要使用CVS<br />现在开发软件控制版本的软件较多，比较著名的有CVS、SVN、VSS，其中CVS是一款比较优秀的在eclipse集成了客户端的软件。有了CVS我们可以轻松方便的整合管理程序员每天