﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-我的地盘我作主-文章分类-java</title><link>http://www.blogjava.net/leedo/category/7522.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 18:36:15 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 18:36:15 GMT</pubDate><ttl>60</ttl><item><title>Spring+Hibernate+Acegi 的初次体验(转)</title><link>http://www.blogjava.net/leedo/articles/68182.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Thu, 07 Sep 2006 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/68182.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/68182.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/68182.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/68182.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/68182.html</trackback:ping><description><![CDATA[
		<div class="posthead">
				<h2>
						<a class="singleposttitle" id="viewpost1_TitleUrl" href="/baputista/archive/2006/08/24/65465.html">Spring+Hibernate+Acegi 的初次体验</a>
				</h2>Posted on 2006-08-24 10:56 <a href="/baputista/">誰伴我闖蕩</a> 阅读(611) <a href="/baputista/archive/2006/08/31/65465.html#Post">评论(2)</a>  <a href="/baputista/admin/EditPosts.aspx?postid=65465">编辑</a> <a href="/baputista/AddToFavorite.aspx?id=65465">收藏</a><a title="功能强大的网络收藏夹，一秒钟操作就可以轻松实现保存带来的价值、分享带来的快乐" href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&amp;u='+escape(d.location.href)+'&amp;c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();">收藏至365Key</a><img height="1" src="/baputista/aggbug/65465.html?webview=1" width="1" /><!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description
rdf:about="http://www.blogjava.net/baputista/archive/2006/08/24/65465.html"
dc:identifier="http://www.blogjava.net/baputista/archive/2006/08/24/65465.html"
dc:title="Spring+Hibernate+Acegi 的初次体验"
trackback:ping="http://www.blogjava.net/baputista/services/trackbacks/65465.aspx" />
</rdf:RDF>
--></div>
		<div class="postbody">
				<p>到现在我也没有弄明白Acegi里面很多的功能,刚刚开始学的时候我就已经被它那繁琐的配置震慑住了，不过当我动起手来一步步实现的时候,才发现其实它远没有那么难,当然随着学习的深入,会渐渐再发现这一点吧,现在就让我们初学者一切体验Acegi的功能吧!<br /><br />还以我传统的例子为例:<br />毕业设计选题系统,三种角色:教师,学生,管理员,我想让他们的登陆都在一个界面下自动识别,而无需进行身份选择,登陆后,他们将分别到各自的admin.jsp,stu.jsp,teacher.jsp<br />在数据库中的表结构如下(很多属性略):<br />id--- user---password--type---about<br /><br />type是用来存储用户的类别,分别有a,t,s分别对应三种角色<br />about对应的是acegi里所需要的enable,用户是否可用</p>
				<p>在model里,我们采用了继承关系:<br /><br />父类user:<br />package subject.model;</p>
				<p>public abstract class User extends BaseObject<br />{<br /> private Integer id;<br /> private String user;<br /> private String password;<br /> private String name;<br /> private String telphone;</p>
				<p>//set and get method <br /> <br /> public abstract String getType(); //这个是用来反映用户角色的关键函数,在子类实现,从而实现多态<br />}<br /><br />子类的实现:<br />======================<br />package subject.model;</p>
				<p>import subject.Constants;</p>
				<p>public class Teacher extends User<br />{<br /> private String level;         //教师的职称<br /><br />//set and get method</p>
				<p> public String getType()<br /> {<br />  return Constants.TEACHER;<br /> }<br />}<br />================<br />package subject.model;</p>
				<p>import subject.Constants;</p>
				<p>public class Student extends User<br />{<br /> private static final long serialVersionUID = 1L;</p>
				<p> private SchoolClass schoolClass;         //学生的班级<br /> private String sn;             //学生的学号</p>
				<p>//set and get method<br /> <br /> public String getType()<br /> {<br />  return Constants.STUDENT;<br /> }<br />}<br />=================<br />package subject.model;</p>
				<p>import subject.Constants;</p>
				<p>public class Admin extends User<br />{<br /> private String grade;           //管理员的级别<br />//set and get method</p>
				<p> public String getType()<br /> {<br />  return Constants.ADMIN;<br /> }<br />}<br /><br />对于三者所共有的属性在数据库里,都存在一个字段,而依据不同的角色拥有不同的含义,学生的班级则存放在了about里,只要学生有班级,他就able,否则就enable了!而管理员和教师则默认为1!<br /><br />这种是属于一个继承树存放在一个表的情况,Hibernate的配置如下:<br />&lt;hibernate-mapping&gt;</p>
				<p> &lt;class name="subject.model.User" discriminator-value="not null"&gt;</p>
				<p>  &lt;id name="id"&gt;<br />   &lt;generator class="increment" /&gt;<br />  &lt;/id&gt;<br />  <br />  &lt;discriminator column="type" type="character" /&gt;<br />  <br />  &lt;property name="user" /&gt;<br />  &lt;property name="password" /&gt;<br />  &lt;property name="name" /&gt;<br />  &lt;property name="telphone" /&gt;</p>
				<p>  &lt;subclass name="subject.model.Admin" discriminator-value="a"&gt;<br />   &lt;property name="grade" column="sn" /&gt;<br />  &lt;/subclass&gt;<br />  <br />  &lt;subclass name="subject.model.Teacher" discriminator-value="t"&gt;<br />   &lt;property name="level" column="sn" /&gt;<br />  &lt;/subclass&gt;<br />  <br />  &lt;subclass name="subject.model.Student" discriminator-value="s"&gt;<br />   <br />   &lt;property name="sn" /&gt;<br />   <br />   &lt;many-to-one name="schoolClass" class="subject.model.SchoolClass" <br />    column="about" update="false" insert="false" /&gt;<br />    <br />  &lt;/subclass&gt;</p>
				<p> &lt;/class&gt;</p>
				<p>&lt;/hibernate-mapping&gt;<br /><br />=============================================<br />上面的这些都是模型的基础,下面再讲怎么样配合Spring和Acegi实现系统的安全与登陆<br />在Spring中Hibernate的配置只介绍不说明:<br />&lt;!-- 定义DBCP数据源 --&gt;<br /> &lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"&gt;<br />  &lt;property name="driverClassName" value="com.mysql.jdbc.Driver" /&gt;<br />  &lt;property name="url" value="jdbc:mysql://localhost/subject?useUnicode=true&amp;amp;characterEncoding=gbk" /&gt;<br />  &lt;property name="username" value="root" /&gt;<br />  &lt;property name="password" value="" /&gt;<br />  &lt;property name="maxActive" value="100" /&gt;<br />  &lt;property name="maxIdle" value="30" /&gt;<br />  &lt;property name="maxWait" value="1000" /&gt;<br />  &lt;property name="defaultAutoCommit" value="true" /&gt;<br />  &lt;property name="removeAbandoned" value="true" /&gt;<br />  &lt;property name="removeAbandonedTimeout" value="60" /&gt;<br /> &lt;/bean&gt;</p>
				<p> &lt;!-- Hibernate --&gt;<br /> &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;<br />  &lt;property name="dataSource" ref="dataSource" /&gt;<br />  &lt;property name="mappingResources"&gt;<br />   &lt;list&gt;<br />    &lt;value&gt;subject/model/User.hbm.xml&lt;/value&gt;<br />   &lt;/list&gt;<br />  &lt;/property&gt;<br />  &lt;property name="hibernateProperties"&gt;<br />   &lt;props&gt;<br />    &lt;prop key="hibernate.dialect"&gt;org.hibernate.dialect.MySQLInnoDBDialect&lt;/prop&gt;<br />   &lt;/props&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;</p>
				<p> &lt;bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt;<br />  &lt;property name="sessionFactory" ref="sessionFactory" /&gt;<br /> &lt;/bean&gt;<br /><br />&lt;!-- Dao对象 --&gt;<br />&lt;bean id="userDao" class="subject.dao.hibernate.UserDaoImpl"&gt;<br />  &lt;property name="sessionFactory" ref="sessionFactory" /&gt;<br /> &lt;/bean&gt;<br /><br />&lt;!-- 业务逻辑 --&gt;<br /> &lt;bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;<br />  &lt;property name="transactionManager" ref="transactionManager" /&gt;<br />  &lt;property name="transactionAttributes"&gt;<br />   &lt;props&gt;<br />    &lt;prop key="save*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br />    &lt;prop key="remove*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;<br />    &lt;prop key="get*"&gt;PROPAGATION_REQUIRED,readOnly&lt;/prop&gt;<br />   &lt;/props&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br /><br />&lt;bean id="userManager" parent="txProxyTemplate"&gt;<br />  &lt;property name="target"&gt;<br />   &lt;bean class="subject.service.impl.UserManagerImpl"&gt;<br />    &lt;property name="userDao" ref="userDao" /&gt;<br />   &lt;/bean&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br /><br />&lt;!-- Struts --&gt;<br /> &lt;bean name="/user" class="subject.web.action.UserAction" singleton="false"&gt;<br />  &lt;property name="userManager"&gt;<br />   &lt;ref bean="userManager" /&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br />==================<br />上面具体的不用了解,无非就是调用和数据库的操作,<br />下面就要对Acegi进行声明了:<br />我不用Ctrl+c和Ctrl+V的方式对Acegi进行介绍,没有意义,随便google就一大堆<br />我们想主要在这样的系统中需要的安全策略都有哪些?<br />1.用户的登陆<br />2.防止多个用户登陆一个帐号<br />3.用户的注销<br />4.防止非法用户的访问<br /><br />我这个程序所涉及到的只有这些,下面就进行说明:<br /><br />在web.xml的声明:<br />&lt;!-- Acegi安全控制 Filter 配置 --&gt;<br />    &lt;filter&gt;<br />        &lt;filter-name&gt;securityFilter&lt;/filter-name&gt;<br />        &lt;filter-class&gt;org.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br />        &lt;init-param&gt;<br />            &lt;param-name&gt;targetClass&lt;/param-name&gt;<br />            &lt;param-value&gt;org.acegisecurity.util.FilterChainProxy&lt;/param-value&gt;<br />        &lt;/init-param&gt;<br />    &lt;/filter&gt;<br />    <br />    &lt;filter-mapping&gt;<br />        &lt;filter-name&gt;securityFilter&lt;/filter-name&gt;<br />        &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />    &lt;/filter-mapping&gt;<br /><br />Acegi 通过实现了Filter接口的FilterToBeanProxy提供一种特殊的使用Servlet Filter的方式，它委托Spring中的Bean -- FilterChainProxy来完成过滤功能，这样就简化了web.xml的配置，并且利用Spring IOC的优势。FilterChainProxy包含了处理认证过程的filter列表，每个filter都有各自的功能。<br /><br />&lt;!-- ======================== FILTER CHAIN ======================= --&gt;<br /> &lt;bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"&gt;<br />  &lt;property name="filterInvocationDefinitionSource"&gt;<br />   &lt;value&gt;<br />    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON <br />    PATTERN_TYPE_APACHE_ANT<br />    <br />    /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,<br />         securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor<br />   &lt;/value&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br /><br />大体上先介绍一下:<br />httpSessionContextIntegrationFilter: 每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象，在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用，使之能跨越多个请求。<br />logoutFilter:用户的注销<br />authenticationProcessingFilter:处理登陆请求<br />exceptionTranslationFilter:异常转换过滤器<br />filterInvocationInterceptor:在访问前进行权限检查<br /><br />这些就犹如在web.xml声明一系列的过滤器,不过当把他们都声明在spring中就可以享受Spring给我们带来的方便了。<br /><br />下面就是对这些过滤器的具体声明：<br />只对有用的地方进行声明，别的地方几乎都是默许的<br />&lt;!-- ======================== FILTER ======================= --&gt;<br /> &lt;bean id="httpSessionContextIntegrationFilter" <br />  class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" /&gt;</p>
				<p> &lt;bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"&gt;<br />  &lt;constructor-arg value="/index.htm" /&gt;             离开后所转向的位置<br />  &lt;constructor-arg&gt;<br />            &lt;list&gt;<br />                &lt;bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/&gt;<br />            &lt;/list&gt;<br />        &lt;/constructor-arg&gt;<br />  &lt;property name="filterProcessesUrl" value="/logout.htm" /&gt;        定义用户注销的地址，<br /> &lt;/bean&gt;<br /><br />下面的这个过滤器，我们根据自己的需求有了自己的实现：</p>
				<p> &lt;bean id="authenticationProcessingFilter" class="subject.web.filter.UserAuthenticationProcessingFilter"&gt;<br />  &lt;property name="authenticationManager" ref="authenticationManager"/&gt;  下面会介绍的用来起到认证管理的作用<br />  &lt;property name="authenticationFailureUrl" value="/login.htm?error=wrong"/&gt;  登陆失败的地址<br />  &lt;property name="defaultTargetUrl" value="/login.htm"/&gt;       登陆成功的地址<br />  &lt;property name="filterProcessesUrl" value="/j_security_check"/&gt;      登陆请求的地址<br />  &lt;property name="userManager" ref="userManager"/&gt;        自己添加的属性，这样就可以访问到我们的业务逻辑<br />  &lt;property name="exceptionMappings"&gt;   出现异常所对应的地址<br />            &lt;value&gt;<br />                org.acegisecurity.AuthenticationException=/login.htm?error=fail     登陆失败                org.acegisecurity.concurrent.ConcurrentLoginException=/login.htm?error=too        已登陆了<br />            &lt;/value&gt;<br />        &lt;/property&gt;<br /> &lt;/bean&gt;<br /> <br /> &lt;bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/&gt;</p>
				<p> &lt;bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;<br />  &lt;property name="authenticationEntryPoint"&gt;<br />   &lt;bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;<br />    &lt;property name="loginFormUrl" value="/login.htm?error=please"/&gt;//如果用户没登陆就想访问，先到这里登陆吧<br />    &lt;property name="forceHttps" value="false"/&gt;<br />   &lt;/bean&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br /> <br /> &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />  &lt;property name="authenticationManager" ref="authenticationManager"/&gt;       认证服务<br />  &lt;property name="accessDecisionManager"&gt;<br />   &lt;bean class="org.acegisecurity.vote.AffirmativeBased"&gt;<br />    &lt;property name="allowIfAllAbstainDecisions" value="false"/&gt;<br />    &lt;property name="decisionVoters"&gt;<br />     &lt;list&gt;<br />      &lt;bean class="org.acegisecurity.vote.RoleVoter"&gt;<br />                    &lt;property name="rolePrefix" value=""/&gt;         //这里定义数据库中存放的角色和我们在这里声明的角色间是否需要加个前缀？我没加<br />                &lt;/bean&gt;<br />     &lt;/list&gt;<br />    &lt;/property&gt;<br />   &lt;/bean&gt;<br />  &lt;/property&gt;<br />  &lt;property name="objectDefinitionSource"&gt;<br />            &lt;value&gt;<br />                PATTERN_TYPE_APACHE_ANT<br />                <br />                /admin.htm*=a         这里就是数据库中对应的tyep a<br />                /student*=s           由于没有前缀和数据库里一样<br />                /teacher*=t<br />            &lt;/value&gt;<br />        &lt;/property&gt;<br /> &lt;/bean&gt;<br /> <br /> &lt;bean id="loggerListener"<br />          class="org.acegisecurity.event.authentication.LoggerListener"/&gt;       记录事件<br /><br />下面就要说明我们的认证服务了，其起到的关键作用就是用来保证用户登陆身份的验证：<br /><br />它将验证的功能委托给多个Provider，并通过遍历Providers, 以保证获取不同来源的身份认证，若某个Provider能成功确认当前用户的身份，authenticate()方法会返回一个完整的包含用户授权信息的 Authentication对象，否则会抛出一个AuthenticationException。<br /><br />先声明一个管理器吧，在上面的过滤器中都已经用到过了<br />&lt;!-- ======================== AUTHENTICATION ======================= --&gt;<br /> &lt;bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"&gt;<br />  &lt;property name="providers"&gt;<br />   &lt;list&gt;<br />    &lt;ref local="daoAuthenticationProvider" /&gt;   我仅仅用到 从数据库中读取用户信息验证身份<br />   &lt;/list&gt;<br />  &lt;/property&gt;<br />  &lt;property name="sessionController"&gt;<br />   &lt;bean id="concurrentSessionController" <br />    class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl"&gt;<br />    &lt;property name="maximumSessions"&gt;<br />     &lt;value&gt;1&lt;/value&gt;每个用户同时登陆一位<br />    &lt;/property&gt;<br />    &lt;property name="sessionRegistry"&gt;<br />     &lt;bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl" /&gt;<br />    &lt;/property&gt;<br />    &lt;property name="exceptionIfMaximumExceeded" value="true" /&gt;<br />   &lt;/bean&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br /> 来实现唯一的一个Provider，从数据库验证身份<br /> &lt;bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br />  &lt;property name="userDetailsService"&gt;<br />   &lt;bean id="jdbcDaoImpl"<br />            class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"&gt;<br />          &lt;property name="dataSource" ref="dataSource"/&gt;<br />          &lt;property name="usersByUsernameQuery"&gt;<br />              &lt;value&gt;<br />                  select user,password,about from user where user = ?        查找用户的查询语句，只需要把你数据库中的用户和密码以及enable相对应上就行<br />              &lt;/value&gt;<br />          &lt;/property&gt;<br />          &lt;property name="authoritiesByUsernameQuery"&gt;<br />              &lt;value&gt;<br />                  select user,type from user where user = ?           这里就是把用户和权限对应上，在appfuse中用的两个表，我都放一个表里了，所以就用这一个就行问题的关键是要让它能找到两个字段，构成一个对象<br />              &lt;/value&gt;<br />          &lt;/property&gt;<br />      &lt;/bean&gt;<br />  &lt;/property&gt;<br />  &lt;property name="userCache"&gt; 缓存都这么写：<br />   &lt;bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"&gt;<br />    &lt;property name="cache"&gt;<br />     &lt;bean class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />      &lt;property name="cacheManager"&gt;<br />       &lt;bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/&gt;<br />      &lt;/property&gt;<br />      &lt;property name="cacheName" value="userCache"/&gt;<br />     &lt;/bean&gt;<br />    &lt;/property&gt;<br />   &lt;/bean&gt;<br />  &lt;/property&gt;<br /> &lt;/bean&gt;<br /><br />==============<br />对于上面登陆请求的处理器我借鉴了springSide，实现的方法如下：<br />package subject.web.filter;</p>
				<p>import javax.servlet.http.HttpServletRequest;<br />import javax.servlet.http.HttpServletResponse;<br />import javax.servlet.http.HttpSession;</p>
				<p>import org.acegisecurity.Authentication;<br />import org.acegisecurity.context.SecurityContext;<br />import org.acegisecurity.context.SecurityContextHolder;<br />import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;<br />import org.acegisecurity.userdetails.UserDetails;</p>
				<p>import subject.Constants;<br />import subject.model.User;<br />import subject.service.UserManager;</p>
				<p>public class UserAuthenticationProcessingFilter extends<br />  AuthenticationProcessingFilter<br />{<br /> private UserManager userManager;</p>
				<p> public void setUserManager( UserManager userManager )<br /> {<br />  this.userManager = userManager;<br /> }</p>
				<p> protected boolean requiresAuthentication( HttpServletRequest request ,<br />   HttpServletResponse response )<br /> {<br />  boolean requiresAuth = super.requiresAuthentication( request, response );<br />  HttpSession httpSession = null;<br />  try<br />  {<br />   httpSession = request.getSession( false );<br />  }<br />  catch ( IllegalStateException ignored )<br />  {<br />  }<br />  if ( httpSession != null )<br />  {<br />   if ( httpSession.getAttribute( Constants.USER ) == null )<br />   {<br />    if ( !requiresAuth )<br />    {<br />     SecurityContext sc = SecurityContextHolder.getContext();<br />     Authentication auth = sc.getAuthentication();<br />     if ( auth != null<br />       &amp;&amp; auth.getPrincipal() instanceof UserDetails )<br />     {<br />      UserDetails ud = (UserDetails) auth.getPrincipal();//上面声明的sql无非就是要包装成这个对象<br />      User user = userManager.getUser( ud.getUsername() );从业务逻辑里找到用户，放到session里<br />      httpSession.setAttribute( Constants.USER, user );<br />     }<br />    }<br />   }<br />  }<br />  return requiresAuth;<br /> }<br />}<br /><br />在看看我的login.htm在登陆成功时是怎么工作的吧？<br />public class UserAction extends BaseAction<br />{<br /> private UserManager mgr;</p>
				<p> public void setUserManager( UserManager mgr )<br /> {<br />  this.mgr = mgr;<br /> }</p>
				<p> public ActionForward login( ActionMapping mapping , ActionForm form ,<br />   HttpServletRequest request , HttpServletResponse response )<br />   throws Exception<br /> {<br />  User user = (User) getSessionObject( request, Constants.USER );<br />  ActionMessages msg = new ActionMessages();<br />  if ( user != null )<br />  {<br />   return new ActionForward(  user.getType() + ".htm", true );成功就去type.htm<br />  }<br />  else<br />  {<br />   String error = getParameter( request, Constants.ERROR );<br />   if ( error != null )对于不同的错误，都加以提示<br />   {<br />    if ( error.equalsIgnoreCase( "wrong" ) )<br />     msg.add( "msg", new ActionMessage( "fail.login.wrong" ) );<br />    else if ( error.equalsIgnoreCase( "too" ) )<br />     msg.add( "msg", new ActionMessage( "fail.login.too" ) );<br />    else if ( error.equalsIgnoreCase( "fail" ) )<br />     msg.add( "msg", new ActionMessage( "fail.login.fail" ) );<br />    else<br />     msg.add( "msg", new ActionMessage( "fail.login.please" ) );<br />   }<br />   else<br />    msg.add( "msg", new ActionMessage( "fail.login.please" ) );<br />  }<br />  saveErrors( request, msg );<br />  return mapping.findForward( "fail" );<br /> }</p>
				<p>}<br /><br />当然，Acegi需要介绍的东西太多了，我只把我这次认为有必要解释的东西写在了上面让大家来参考，作为能google到的东西，比如对于认证的方式还有很多，我就没有详细的介绍，在学习Acegi过程中，把它自带的例子弄清楚很关键，希望大家一起学习一起共勉！</p>
		</div>
<img src ="http://www.blogjava.net/leedo/aggbug/68182.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-09-07 09:40 <a href="http://www.blogjava.net/leedo/articles/68182.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>三、Acegi安全系统扩展</title><link>http://www.blogjava.net/leedo/articles/68057.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Wed, 06 Sep 2006 08:38:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/68057.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/68057.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/68057.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/68057.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/68057.html</trackback:ping><description><![CDATA[
		<h1 class="block_title">
				<a id="viewpost1_TitleUrl" href="http://www.cnblogs.com/jackyChen/archive/2006/06/08/420677.html">三、Acegi安全系统扩展</a>
		</h1>
		<div class="post">
				<div class="postcontent">
						<p>转载于：<a href="http://www.springside.org.cn/docs/reference/Acegi4.htm">http://www.springside.org.cn/docs/reference/Acegi4.htm</a></p>
						<br />
						<h1>三 Acegi安全系统扩展 </h1>
						<p>      相信side对Acegi的扩展会给你耳目一新的感觉,提供完整的扩展功能,管理界面,中文注释和靠近企业的安全策略。side只对Acegi不符合企业应用需要的功能进行扩展,尽量不改动其余部分来实现全套权限管理功能,以求能更好地适应Acegi升级。</p>
						<p> </p>
						<h2>3.1 基于角色的权限控制(RBAC)</h2>
						<p>    Acegi 自带的 sample 表设计很简单: users表{username,password,enabled} authorities表{username,authority},这样简单的设计无法适应复杂的权限需求,故SpringSide选用RBAC模型对权限控制数据库表进行扩展。 RBAC引入了ROLE的概念,使User(用户)和Permission(权限)分离,一个用户拥有多个角色,一个角色拥有有多个相应的权限,从而减少了权限管理的复杂度,可更灵活地支持安全策略。 </p>
						<p>
								<img src="http://www.springside.org.cn/docs/reference/images/acegi/RBAC.jpg" />
						</p>
						<p>    同时,我们也引入了resource(资源)的概念,一个资源对应多个权限，资源分为ACL,URL,和FUNTION三种。注意，URL和FUNTION的权限命名需要以AUTH_开头才会有资格参加投票, 同样的ACL权限命名需要ACL_开头。</p>
						<br />
						<h2>3.2 管理和使用EhCache</h2>
						<h3>3.2.1 设立缓存</h3>
						<p>在SpringSide里的 Acegi 扩展使用 <a href="http://ehcache.sourceforge.net/"><font color="#002c99">EhCache</font></a><span style="FONT-FAMILY: 宋体"> 就作为一种缓存解决方案,以缓存用户和资源的信息和相对应的权限信息。</span></p>
						<p>
								<span style="FONT-FAMILY: 宋体">首先需要一个在classpath的<span style="FONT-FAMILY: Arial"><span lang="EN-US">ehcache.xml </span></span><span style="FONT-FAMILY: 宋体">文件，用于配置</span><span style="FONT-FAMILY: Arial"><span lang="EN-US">EhCache。</span></span></span>
						</p>
						<pre>
								<span style="FONT-FAMILY: 宋体">
										<span style="FONT-FAMILY: Arial">
												<span lang="EN-US">&lt;ehcache&gt;<br /></span>
										</span>
								</span>
								<span style="FONT-FAMILY: 宋体">
										<span style="FONT-FAMILY: Arial">
												<span lang="EN-US">   </span>
										</span>
								</span>
								<span style="FONT-FAMILY: 宋体">
										<span style="FONT-FAMILY: Arial">
												<span lang="EN-US">    &lt;defaultCache<br />            maxElementsInMemory="10000"<br />            eternal="false"<br />            overflowToDisk="true"<br />            timeToIdleSeconds="0"<br />            timeToLiveSeconds="0"<br />            diskPersistent="false"<br />            diskExpiryThreadIntervalSeconds= "120"/&gt; <br />    &lt;!-- acegi cache--&gt;<br />    &lt;cache name="<font color="#000080">userCache</font>"<br />           maxElementsInMemory="10000"<br />           eternal="true"<br />           overflowToDisk= "true"/&gt;<br />    &lt;!-- acegi cache--&gt;   <br />    &lt;cache name="<font color="#000080">resourceCache</font>"<br />           maxElementsInMemory="10000"<br />           eternal="true"<br />           overflowToDisk="true"/&gt;<br />&lt;/ehcache&gt;</span>
										</span>
								</span>
						</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">     maxElementsInMemory设定了允许在Cache中存放的数据数目，eternal设定Cache是否会过期， overflowToDisk设定内存不足的时候缓存到硬盘，timeToIdleSeconds和timeToLiveSeconds设定缓存游离时间和生存时间，diskExpiryThreadIntervalSeconds设定缓存在硬盘上的生存时间，注意当eternal="true"时， timeToIdleSeconds，timeToLiveSeconds和diskExpiryThreadIntervalSeconds都是无效的。</span>
						</p>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">&lt;defaultCache&gt;是除制定的Cache外其余所有Cache的设置，针对Acegi 的情况, 专门设置了userCache和resourceCache，都设为永不过期。在applicationContext-acegi- security.xml中相应的调用是</span>
						</p>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
								</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">&lt;bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />        &lt;property name="cacheManager" ref="cacheManager"/&gt;<br />        &lt;property name="<font color="#000080">cacheName</font>" value="<font color="#000080">  userCache</font>"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache" autowire="byName"&gt;<br />        &lt;property name="cache" ref="userCacheBackend"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="resourceCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />        &lt;property name="cacheManager" ref="cacheManager"/&gt;<br />        &lt;property name="<font color="#000080">cacheName</font>" value="<font color="#000080">  resourceCache</font>"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="resourceCache" class="org.springside.modules.security.service.acegi.cache.ResourceCache" autowire="byName"&gt;<br />        &lt;property name="cache" ref="resourceCacheBackend"/&gt;<br />    &lt;/bean&gt;<br />&lt;bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/&gt;<br /></span>
						</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">"cacheName" 就是设定在<span lang="EN-US">ehcache.xml </span>中相应Cache的名称。</span>
						</p>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">userCache使用的是Acegi 的EhCacheBasedUserCache(实现了UserCache接口), resourceCache是SpringSide的扩展类</span>
						</p>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">public interface <font color="#000080">UserCache</font>   {</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public UserDetails <font color="#800080">getUserFromCache</font>   (String username);</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public void <font color="#800080">putUserInCache</font>   (UserDetails user);</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public void <font color="#800080">removeUserFromCache</font>   (String username);<br />}</span>
						</pre>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">public class <font color="#000080">ResourceCache</font>   {<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    public ResourceDetails <font color="#800080">getAuthorityFromCache</font>   (String resString) {...</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">   }<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    public void <font color="#800080">putAuthorityInCache</font>   (ResourceDetails resourceDetails) {...  }<br /> </span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">   public void <font color="#800080">removeAuthorityFromCache</font>   (String resString) {... }<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    public List getUrlResStrings() {... }<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    public List getFunctions() {.. }</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />}</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
								</span>
						</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">UserCache 就是通过EhCache对UserDetails 进行缓存管理, 而ResourceCache 是对ResourceDetails 类进行缓存管理</span>
						</p>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">public interface <font color="#000080">UserDetails</font>   extends Serializable {<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    public boolean isAccountNonExpired();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public boolean isAccountNonLocked();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public GrantedAuthority[] <font color="#800080">getAuthorities</font>();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public boolean isCredentialsNonExpired();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public boolean isEnabled();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public String getPassword();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public String getUsername();<br />}</span>
						</pre>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">public interface <font color="#000080">ResourceDetails</font>   extends Serializable {</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public String getResString();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public String getResType();</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />    public GrantedAuthority[] <font color="#800080">getAuthorities</font>();<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">}</span>
						</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">UserDetails 包含用户信息和相应的权限，ResourceDetails 包含资源信息和相应的权限。</span>
						</p>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">public interface <font color="#000080">GrantedAuthority</font>     {<br />    public String <font color="#800080">getAuthority</font>   ();<br />}</span>
						</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>GrantedAuthority 就是权限信息，在Acegi 的 sample 里<span lang="EN-US" style="FONT-FAMILY: Arial">GrantedAuthority 的信息如</span>ROLE_USER, ROLE_SUPERVISOR, ACL_CONTACT_DELETE, ACL_CONTACT_ADMIN等等，网上也有很多例子把角色作为<span lang="EN-US" style="FONT-FAMILY: Arial">GrantedAuthority ，但事实上看看ACL 就知道， Acegi本身根本就没有角色这个概念，<span lang="EN-US" style="FONT-FAMILY: Arial">GrantedAuthority 包含的信息应该是权限，对于非ACL的权限用 AUTH_ 开头更为合理, 如SpringSide里的 AUTH_ADMIN_LOGIN, AUTH_BOOK_MANAGE 等等。</span></span></span>
						</p>
						<h3>3.2.2 管理缓存</h3>
						<p>     使用AcegiCacheManager对<span lang="EN-US" style="FONT-FAMILY: Arial">userCache和resourceCache进行统一缓存管理。<font face="Tahoma">当在后台对用户信息进行修改或赋权的时候, 在更新数据库同时就会调用acegiCacheManager相应方法, 从数据库中读取数据并替换cache中相应部分,使cache与数据库同步。</font></span></p>
						<pre>
								<span lang="EN-US" style="FONT-FAMILY: Arial">public class AcegiCacheManager extends BaseService {<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    private ResourceCache <font color="#800080">resourceCache</font>   ;<br /></span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">    private UserCache <font color="#800080">userCache</font>   ;</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<br />
										<font color="#800000">    /**<br />     * 修改User时更改userCache  <br />     */</font>
										<br />    public void <font color="#800080">modifyUserInCache</font>   (User user, String orgUsername) {...    }<br /><font color="#800000">    /**<br />     * 修改Resource时更改resourceCache  <br />     */</font><br />    public void <font color="#800080">modifyResourceInCache</font>   (Resource resource, String orgResourcename) {...    }<br /><font color="#800000">    /**<br />     *   修改权限时同时修改userCache和resourceCache<br />     */</font><br />    public void <font color="#800080">modifyPermiInCache</font>   (Permission permi, String orgPerminame) {...  }<br />    /**<br />     * <font color="#800000">User授予角色时更改userCache</font><br />     */<br />    public void <font color="#800080">authRoleInCache</font>   (User user) {...    }<br /><font color="#800000">    /**<br />     * Role授予权限时更改userCache和resourceCache  <br />     */</font><br />    public void <font color="#800080">authPermissionInCache</font>   (Role role) {...  }<br /> <font color="#800000">   /**<br />     * Permissioni授予资源时更改resourceCache  <br />     */</font><br />    public void <font color="#800080">authResourceInCache</font>   (Permission permi) {...  }<br /><font color="#800000">    /**<br />     *   初始化userCache<br />     */</font><br />    public void <font color="#800080">initUserCache</font>   () {...  }<br /><font color="#800000">    /**<br />     *   初始化resourceCache<br />     */</font><br />    public void <font color="#800080">initResourceCache</font>   () {... }<br /><font color="#800000">    /**<br />     *  获取所有的url资源<br />     */</font><br />    public List <font color="#800080">getUrlResStrings</font>   () {...  }<br /><font color="#800000">    /**<br />     * 获取所有的Funtion资源  <br />     */</font><br />    public List <font color="#800080">getFunctions</font>   () {...  }<br /><font color="#800000">    /**<br />     * 根据资源串获取资源  <br />     */</font><br />    public ResourceDetails <font color="#800080">getAuthorityFromCache</font>   (String resString) {...  }<br />    <br /> ......<br /><br /><br />}</span>
						</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
								</span>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
								</span> </p>
						<h2>3.3 资源权限定义扩展</h2>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>
								</span>Acegi给出的sample里,资源权限对照关系是配置在xml中的,试想一下如果你的企业安全应用有500个用户,100个角色权限的时候,维护这个xml将是个繁重无比的工作,如何动态更改用户权限更是个头痛的问题。</p>
						<pre>   &lt;bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;<br />      &lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;<br />      &lt;property name="accessDecisionManager"&gt;&lt;ref local="businessAccessDecisionManager"/&gt;&lt;/property&gt;<br />      &lt;property name="afterInvocationManager"&gt;&lt;ref local="afterInvocationManager"/&gt;&lt;/property&gt;<br />      &lt;property name="objectDefinitionSource"&gt;<br />         &lt;value&gt;<br />            sample.contact.ContactManager.create=ROLE_USER<br />            sample.contact.ContactManager.getAllRecipients=ROLE_USER<br />            sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ<br />            sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ<br />            sample.contact.ContactManager.delete=ACL_CONTACT_DELETE<br />            sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN<br />            sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN<br />         &lt;/value&gt;<br />      &lt;/property&gt;<br />   &lt;/bean&gt;</pre>
						<pre>  &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />      &lt;property name="authenticationManager"&gt;&lt;ref bean="authenticationManager"/&gt;&lt;/property&gt;<br />      &lt;property name="accessDecisionManager"&gt;&lt;ref local="httpRequestAccessDecisionManager"/&gt;&lt;/property&gt;<br />      &lt;property name="objectDefinitionSource"&gt;<br />         &lt;value&gt;<br />       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br />       PATTERN_TYPE_APACHE_ANT<br />       /index.jsp=ROLE_ANONYMOUS,ROLE_USER<br />       /hello.htm=ROLE_ANONYMOUS,ROLE_USER<br />       /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER<br />       /switchuser.jsp=ROLE_SUPERVISOR<br />       /j_acegi_switch_user=ROLE_SUPERVISOR<br />       /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER<br />       /**=ROLE_USER<br />         &lt;/value&gt;<br />      &lt;/property&gt;<br />   &lt;/bean&gt;</pre>
						<p> 对如此不Pragmatic的做法,SpringSide进行了扩展, 让Acegi 能动态读取数据库中的权限资源关系。</p>
						<h3>3.3.1 Aop Invocation Authorization</h3>
						<pre>    &lt;bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;<br />        &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />        &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br />        &lt;property name="objectDefinitionSource" ref="methodDefinitionSource"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="methodDefinitionSource" class="org.springside.security.service.acegi.DBMethodDefinitionSource"&gt;<br />        &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br />    &lt;/bean&gt;</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>
								</span>研究下Aceig的源码,<strong>ObjectDefinitionSource</strong>的实际作用是返回一个<strong>ConfigAttributeDefinition</strong>对象，而Acegi Sample 的方式是用<strong>MethodDefinitionSourceEditor</strong>把xml中的文本Function资源权限对应关系信息加载到<strong>MethodDefinitionMap </strong>( MethodDefinitionSource 的实现类 )中, 再组成ConfigAttributeDefinition，而我们的扩展目标是从缓存中读取信息来组成ConfigAttributeDefinition。</p>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>
								</span>
								<strong>MethodSecurityInterceptor</strong>是通过调用AbstractMethodDefinitionSource的<strong>lookupAttributes(method)</strong>方法获取ConfigAttributeDefinition。所以我们需要实现自己的O<span lang="EN-US" style="FONT-FAMILY: Arial"><font face="Tahoma">bjectDefinitionSource，</font></span>继承<strong>AbstractMethodDefinitionSource</strong>并实现其lookupAttributes方法,从缓存中读取资源权限对应关系组成并返回ConfigAttributeDefinition即可。SpringSide中的<strong>DBMethodDefinitionSource</strong>类的部分实现如下 :</p>
						<pre>public class DBMethodDefinitionSource <font color="#000080">extends</font><font color="#000080">AbstractMethodDefinitionSource</font> {<br />......<br />    protected <font color="#008080">ConfigAttributeDefinition</font><font color="#800080">lookupAttributes</font>(Method mi) {<br />        Assert.notNull(mi, "lookupAttrubutes in the DBMethodDefinitionSource is null");<br />        String methodString = mi.getDeclaringClass().getName() + "." + mi.getName();<br />        if (!acegiCacheManager.isCacheInitialized()) {<br />            <font color="#800000">//初始化Cache<br /></font>            acegiCacheManager.initResourceCache();<br />        }<br />        <font color="#800000">//获取所有的function<br /></font>        List methodStrings = acegiCacheManager.getFunctions();<br />        Set auths = new HashSet();<br />        <font color="#800000">//取权限的合集</font><br />        for (Iterator iter = methodStrings.iterator(); iter.hasNext();) {<br />            String mappedName = (String) iter.next();<br />            if (methodString.equals(mappedName)<br />                    || <font color="#800080">isMatch</font>(methodString, mappedName)) {<br />                ResourceDetails resourceDetails = acegiCacheManager.getAuthorityFromCache(mappedName);<br />                if (resourceDetails == null) {<br />                    break;<br />                }<br />                GrantedAuthority[] authorities = resourceDetails.getAuthorities();<br />                if (authorities == null || authorities.length == 0) {<br />                    break;<br />                }<br />                auths.addAll(Arrays.asList(authorities));<br />            }<br />        }<br />        if (auths.size() == 0)<br />            return null;<br />        ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();<br />        String authoritiesStr = " ";<br />        for (Iterator iter = auths.iterator(); iter.hasNext();) {<br />            GrantedAuthority authority = (GrantedAuthority) iter.next();<br />            authoritiesStr += authority.getAuthority() + ",";<br />        }<br />        String authStr = authoritiesStr.substring(0, authoritiesStr.length() - 1);<br />        configAttrEditor.setAsText(authStr);<br />       <font color="#800000">//组装并返回ConfigAttributeDefinition</font><br />        return (ConfigAttributeDefinition) configAttrEditor.getValue();<br />    }<br />    ......<br />}</pre>
						<p>要注意几点的是: <br />1) 初始化Cache是比较浪费资源的，所以SpringSide中除第一次访问外的Cache的更新是针对性更新。</p>
						<p>2) 因为method采用了匹配方式(详见 isMatch() 方法) , 即对于*Book和save*这两个资源来说，只要当前访问方法是Book结尾或以save开头都算匹配得上，所以应该取这些能匹配上的资源的相对应的权限的合集。</p>
						<p>3) 使用ConfigAttributeEditor 能更方便地组装ConfigAttributeDefinition。 </p>
						<h3>3.3.2 Filter Invocation Authorization</h3>
						<pre>    &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />        &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />        &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br />        &lt;property name="objectDefinitionSource" ref="filterDefinitionSource"/&gt;<br />    &lt;/bean&gt;<br /><br />    &lt;bean id="filterDefinitionSource" class="org.springside.security.service.acegi.DBFilterInvocationDefinitionSource"&gt;<br />        &lt;property name="convertUrlToLowercaseBeforeComparison" value="true"/&gt;<br />        &lt;property name="useAntPath" value="true"/&gt;<br />        &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br />    &lt;/bean&gt;</pre>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>
								</span>PathBasedFilterInvocationDefinitionMap和RegExpBasedFilterInvocationDefinitionMap都是 <strong>FilterInvocationDefinitionSource</strong>的实现类,当PATTERN_TYPE_APACHE_ANT字符串匹配上时时,<strong>FilterInvocationDefinitionSourceEditor </strong>选用PathBasedFilterInvocationDefinitionMap 把xml中的文本URL资源权限对应关系信息加载。</p>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>
								</span>
								<strong>FilterSecurityInterceptor</strong>通过FilterInvocationDefinitionSource的<strong>lookupAttributes(url)</strong>方法获取ConfigAttributeDefinition。 所以，我们可以通过继承FilterInvocationDefinitionSource的抽象类<strong>AbstractFilterInvocationDefinitionSource</strong>，并实现其lookupAttributes方法,从缓存中读取URL资源权限对应关系即可。SpringSide的<strong>DBFilterInvocationDefinitionSource</strong>类部分实现如下:</p>
						<pre>public class DBFilterInvocationDefinitionSource <font color="#000080">extends AbstractFilterInvocationDefinitionSource</font> {<br /><br />......<br />    public ConfigAttributeDefinition lookupAttributes(String url) {<br />        if (!acegiCacheManager.isCacheInitialized()) {<br />            acegiCacheManager.initResourceCache();<br />        }<br /><br />        if (isUseAntPath()) {<br />            // Strip anything after a question mark symbol, as per SEC-161.<br />            int firstQuestionMarkIndex = url.lastIndexOf("?");<br />            if (firstQuestionMarkIndex != -1) {<br />                url = url.substring(0, firstQuestionMarkIndex);<br />            }<br />        }<br />        List urls = acegiCacheManager.getUrlResStrings();<br />        <font color="#800000">//URL资源倒叙排序</font><br />        Collections.sort(urls);<br />        Collections.reverse(urls);<br /><font color="#800000">//是否先全部转为小写再比较</font><br />        if (convertUrlToLowercaseBeforeComparison) {<br />            url = url.toLowerCase();<br />        }<br />        GrantedAuthority[] authorities = new GrantedAuthority[0];<br />        for (Iterator iterator = urls.iterator(); iterator.hasNext();) {<br />            String resString = (String) iterator.next();<br />            boolean matched = false;<br /><font color="#800000">              //可选择使用AntPath和Perl5两种不同匹配模式  </font><br />            if (isUseAntPath()) {<br />                matched = pathMatcher.match(resString, url);<br />            } else {<br />                Pattern compiledPattern;<br />                Perl5Compiler compiler = new Perl5Compiler();<br />                try {<br />                    compiledPattern = compiler.compile(resString,<br />                            Perl5Compiler.READ_ONLY_MASK);<br />                } catch (MalformedPatternException mpe) {<br />                    throw new IllegalArgumentException(<br />                            "Malformed regular expression: " + resString);<br />                }<br />                matched = matcher.matches(url, compiledPattern);<br />            }<br />            if (matched) {<br />                ResourceDetails rd = acegiCacheManager.getAuthorityFromCache(resString);<br />                authorities = rd.getAuthorities();<br />                break;<br />            }<br />        }<br />        if (authorities.length &gt; 0) {<br />            String authoritiesStr = " ";<br />            for (int i = 0; i &lt; authorities.length; i++) {<br />                authoritiesStr += authorities[i].getAuthority() + ",";<br />            }<br />            String authStr = authoritiesStr.substring(0, authoritiesStr<br />                    .length() - 1);<br />            ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();<br />            configAttrEditor.setAsText(authStr);<br />            return (ConfigAttributeDefinition) configAttrEditor.getValue();<br />        }<br />        return null;<br />    }<br /><br />......<br /> }</pre>
						<p>继承<font color="#000000">AbstractFilterInvocationDefinitionSource</font>注意几点：<br />1)  需要先把获取回来的URL资源按倒序派序，以达到 a/b/c/d.* 在 a/.* 之前的效果(详见 Acegi sample 的applicationContext-acegi-security.xml 中的filterInvocationInterceptor的注释)，为的是更具体的URL可以先匹配上，而获取具体URL的权限，如 a/b/c/d.*权限AUTH_a, AUTH_b 才可查看,  a/.* 需要权限AUTH_a 才可查看，则如果当前用户只拥有权限AUTH_b,则他只可以查看a/b/c/d.jsp 而不能察看a/d.jsp。</p>
						<p>2) 基于上面的原因，故第一次匹配上的就是当前所需权限，而不是取权限的合集。</p>
						<p>3) 可以选用AntPath 或 Perl5 的资源匹配方式，感觉AntPath匹配方式基本足够。</p>
						<p>4) Filter 权限控制比较适合于较粗颗粒度的权限，如设定某个模块下的页面是否能访问等，对于具体某个操作如增删修改，是否能执行，用Method  Invocation 会更佳些，所以注意两个方面一起控制效果更好</p>
						<p> </p>
						<h2>3.4 授权操作</h2>
						<p>
								<span lang="EN-US" style="FONT-FAMILY: Arial">
										<font face="Tahoma">     </font>
								</span>RBAC模型中有不少多对多的关系，这些关系都能以一个中间表的形式来存放，而Hibernate中可以不建这中间表对应的hbm.xml , 以资源与权限的配置为例，如下:</p>
						<pre>&lt;hibernate-mapping package="org.springside.modules.security.domain"&gt;<br />    &lt;class name="Permission" table="PERMISSIONS" dynamic-insert="true" dynamic-update="true"&gt;<br />        &lt;cache usage="nonstrict-read-write"/&gt;<br />        &lt;id name="id" column="ID"&gt;<br />            &lt;generator class="native"/&gt;<br />        &lt;/id&gt;<br />        &lt;property name="name" column="NAME" not-null="true"/&gt;<br />        &lt;property name="descn" column="DESCN"/&gt;<br />        &lt;property name="operation" column="OPERATION"/&gt;<br />        &lt;property name="status" column="STATUS"/&gt;<br />        &lt;set name="roles" table="ROLE_PERMIS" lazy="true" inverse="true" cascade="save-update" batch-size="5"&gt;<br />            &lt;key&gt;<br />                &lt;column name="PERMIS_ID" not-null="true"/&gt;<br />            &lt;/key&gt;<br />            &lt;many-to-many class="Role" column="ROLE_ID" outer-join="auto"/&gt;<br />        &lt;/set&gt;<br />        &lt;set name="resources" table="PERMIS_RESC" lazy="true" inverse="false" cascade="save-update" batch-size="5"&gt;<br />            &lt;key&gt;<br />                &lt;column name="PERMIS_ID" not-null="true"/&gt;<br />            &lt;/key&gt;<br />            &lt;many-to-many class="Resource" column="RESC_ID"/&gt;<br />        &lt;/set&gt;<br />    &lt;/class&gt;<br />&lt;/hibernate-mapping&gt;</pre>
						<pre>&lt;hibernate-mapping package="org.springside.modules.security.domain"&gt;<br />    &lt;class name="Resource" table="RESOURCES" dynamic-insert="true" dynamic-update="true"&gt;<br />        &lt;cache usage="nonstrict-read-write"/&gt;<br />        &lt;id name="id" column="ID"&gt;<br />            &lt;generator class="native"/&gt;<br />        &lt;/id&gt;<br />        &lt;property name="name" column="NAME" not-null="true"/&gt;<br />        &lt;property name="resType" column="RES_TYPE" not-null="true"/&gt;<br />        &lt;property name="resString" column="RES_STRING" not-null="true"/&gt;<br />        &lt;property name="descn" column="DESCN"/&gt;<br />        &lt;set name="permissions" table="PERMIS_RESC" lazy="true" inverse="true" cascade="save-update" batch-size="5"&gt;<br />            &lt;key&gt;<br />                &lt;column name="RESC_ID" not-null="true"/&gt;<br />            &lt;/key&gt;<br />            &lt;many-to-many class="Permission" column="PERMIS_ID" outer-join="auto"/&gt;<br />        &lt;/set&gt;<br />    &lt;/class&gt;<br />&lt;/hibernate-mapping&gt;</pre>
						<p>配置时注意几点:</p>
						<p>1) 因为是分配某个权限的资源，所以权限是主控方，把inverse设为false，资源是被控方inverse设为true</p>
						<p>2) cascade是"save-update"，千万别配成delete</p>
						<p>3) 只需要 permission.getResources().add(resource)， permission.getResources()..remove(resource) 即可很方便地完成授权和取消授权操作</p>
				</div>
		</div>
<img src ="http://www.blogjava.net/leedo/aggbug/68057.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-09-06 16:38 <a href="http://www.blogjava.net/leedo/articles/68057.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一 Acegi安全系统介绍 (转载)</title><link>http://www.blogjava.net/leedo/articles/68055.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Wed, 06 Sep 2006 08:36:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/68055.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/68055.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/68055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/68055.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/68055.html</trackback:ping><description><![CDATA[ 转载于：<a href="http://www.springside.org.cn/docs/reference/Acegi2.htm">http://www.springside.org.cn/docs/reference/Acegi2.htm</a><a href="http://www.springside.org.cn/"></a><br /><br /><h1>一 Acegi安全系统介绍 </h1><p>    Author: <a href="http://www.cnblogs.com/jackyChen/archive/2006/06/08/420667.html">cac 差沙</a></p><p>    Acegi是Spring Framework 下最成熟的安全系统，它提供了强大灵活的企业级安全服务，如完善的认证和授权机制，Http资源访问控制，Method 调用访问控制，Access Control List (ACL) 基于对象实例的访问控制，Yale Central Authentication Service (CAS) 耶鲁单点登陆，X509 认证，当前所有流行容器的认证适配器，Channel Security频道安全管理等功能。</p><h2>1.1 Acegi 安全系统</h2><p>官方网站      <a href="http://acegisecurity.sourceforge.net/">http://acegisecurity.sourceforge.net</a><br />论坛            <a href="http://forum.springframework.org/forumdisplay.php?f=33">http://forum.springframework.org/forumdisplay.php?f=33</a><br />Jira              <a href="http://opensource.atlassian.com/projects/spring/browse/SEC">http://opensource.atlassian.com/projects/spring/browse/SEC</a></p><h2>1.2 多方面的安全控制粒度</h2><ol><li>URL 资源访问控制<br /> http://apps:8080/index.htm -&gt; for public<br /> http://apps:8080/user.htm -&gt; for authorized user </li><li>方法调用访问控制<br />public void getData() -&gt; all user<br />public void modifyData() -&gt; supervisor only </li><li>对象实例保护<br />order.getValue() &lt; $100 -&gt; all user<br />order.getValue() &gt; $100 -&gt; supervisor only</li></ol><h2>1.3 非入侵式安全架构</h2><ol><li>基于Servlet Filter和Spring aop,  使商业逻辑和安全逻辑分开，结构更清晰 </li><li>使用Spring 来代理对象，能方便地保护方法调用</li></ol><h2>1.4 其它安全架构</h2><p>    Acegi只是安全框架之一，其实还存在其它优秀的安全框架可供选择：</p><ul><li>JAAS ：<a href="http://java.sun.com/products/jaas/">http://java.sun.com/products/jaas/</a></li><li>Seraph: <a href="http://opensource.atlassian.com/seraph/">http://opensource.atlassian.com/seraph/</a></li><li> jSai - Servlet Security : <a href="http://oss.ipov.org/jsai/">http://oss.ipov.org/jsai/</a></li><li> Gabriel : <a href="http://gabriel.codehaus.org/">http://gabriel.codehaus.org/</a></li><li> JOSSO : <a href="http://www.josso.org/">http://www.josso.org/</a></li><li> Kasai: <a href="http://www.manentiasoftware.com/kasai/goToHome.action">http://www.manentiasoftware.com/kasai/goToHome.action</a></li><li> jPAM : <a href="http://jpam.sourceforge.net/">http://jpam.sourceforge.net/</a></li><li> OpenSAML : <a href="http://www.opensaml.org/">http://www.opensaml.org/</a></li></ul><img src ="http://www.blogjava.net/leedo/aggbug/68055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-09-06 16:36 <a href="http://www.blogjava.net/leedo/articles/68055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>二、Acegi安全系统的配置(转载)</title><link>http://www.blogjava.net/leedo/articles/68053.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Wed, 06 Sep 2006 08:33:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/68053.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/68053.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/68053.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/68053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/68053.html</trackback:ping><description><![CDATA[
		<h1 class="block_title">
				<a id="viewpost1_TitleUrl" href="http://www.cnblogs.com/jackyChen/archive/2006/06/08/420673.html">二、Acegi安全系统的配置</a>
		</h1>
		<div class="post">
				<div class="postcontent">转载于：<a href="http://www.springside.org.cn/docs/reference/Acegi3.htm">http://www.springside.org.cn/docs/reference/Acegi3.htm</a><br /><br /><h1>二 Acegi安全系统的配置 </h1><p>      Acegi 的配置看起来非常复杂,但事实上在实际项目的安全应用中我们并不需要那么多功能,清楚的了解Acegi配置中各项的功能，有助于我们灵活的运用Acegi于实践中。</p><h2>2.1 在Web.xml中的配置</h2><p>1)  <strong>FilterToBeanProxy</strong><br />　　Acegi通过实现了Filter接口的 FilterToBeanProxy提供一种特殊的使用Servlet Filter的方式，它委托Spring中的Bean -- FilterChainProxy来完成过滤功能，这好处是简化了web.xml的配置，并且充分利用了Spring IOC的优势。FilterChainProxy包含了处理认证过程的filter列表，每个filter都有各自的功能。</p><pre>    &lt;filter&gt;<br />        &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br />        &lt;filter-class&gt;org.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br />        &lt;init-param&gt;<br />            &lt;param-name&gt;targetClass&lt;/param-name&gt;<br />            &lt;param-value&gt;org.acegisecurity.util.FilterChainProxy&lt;/param-value&gt;<br />        &lt;/init-param&gt;<br />    &lt;/filter&gt;</pre><p>2) <strong>filter-mapping</strong><br />　　&lt;filter-mapping&gt;限定了FilterToBeanProxy的URL匹配模式,只有*.do和*.jsp和/j_acegi_security_check 的请求才会受到权限控制，对javascript,css等不限制。</p><pre>   &lt;filter-mapping&gt;<br />      &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br />      &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />    &lt;/filter-mapping&gt;<br />    <br />    &lt;filter-mapping&gt;<br />      &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br />      &lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<br />    &lt;/filter-mapping&gt;<br />    <br />    &lt;filter-mapping&gt;<br />      &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;<br />      &lt;url-pattern&gt;/j_acegi_security_check&lt;/url-pattern&gt;<br />    &lt;/filter-mapping&gt;</pre><p>3) <strong>HttpSessionEventPublisher</strong><br />　　&lt;listener&gt;的 HttpSessionEventPublisher用于发布HttpSessionApplicationEvents和 HttpSessionDestroyedEvent事件给spring的applicationcontext。</p><pre>    &lt;listener&gt;<br />        &lt;listener-class&gt;org.acegisecurity.ui.session.HttpSessionEventPublisher&lt;/listener-class&gt;<br />    &lt;/listener&gt;<br /></pre><h2><br />2.2 在applicationContext-acegi-security.xml中</h2><h3>2.2.1 FILTER CHAIN</h3><p>　　FilterChainProxy会按顺序来调用这些filter,使这些filter能享用Spring ioc的功能, CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写， PATTERN_TYPE_APACHE_ANT定义了使用Apache ant的匹配模式 </p><pre>    &lt;bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"&gt;<br />        &lt;property name="filterInvocationDefinitionSource"&gt;<br />            &lt;value&gt;<br />                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br />                PATTERN_TYPE_APACHE_ANT<br />               /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,<br />basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,<br /> exceptionTranslationFilter,filterInvocationInterceptor<br />            &lt;/value&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;</pre><h3>2.2.2 基础认证</h3><p>1) <strong>authenticationManager</strong><br />　　起到认证管理的作用，它将验证的功能委托给多个 Provider，并通过遍历Providers, 以保证获取不同来源的身份认证，若某个Provider能成功确认当前用户的身份，authenticate()方法会返回一个完整的包含用户授权信息的 Authentication对象，否则会抛出一个AuthenticationException。<br />Acegi提供了不同的AuthenticationProvider的实现,如：<br />        DaoAuthenticationProvider 从数据库中读取用户信息验证身份<br />        AnonymousAuthenticationProvider 匿名用户身份认证<br />        RememberMeAuthenticationProvider 已存cookie中的用户信息身份认证<br />        AuthByAdapterProvider 使用容器的适配器验证身份<br />        CasAuthenticationProvider 根据Yale中心认证服务验证身份, 用于实现单点登陆<br />        JaasAuthenticationProvider 从JASS登陆配置中获取用户信息验证身份<br />        RemoteAuthenticationProvider 根据远程服务验证用户身份<br />        RunAsImplAuthenticationProvider 对身份已被管理器替换的用户进行验证<br />        X509AuthenticationProvider 从X509认证中获取用户信息验证身份<br />        TestingAuthenticationProvider 单元测试时使用</p><p>        每个认证者会对自己指定的证明信息进行认证，如DaoAuthenticationProvider仅对UsernamePasswordAuthenticationToken这个证明信息进行认证。</p><pre>&lt;bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"&gt;<br />        &lt;property name="providers"&gt;<br />            &lt;list&gt;<br />                &lt;ref local="daoAuthenticationProvider"/&gt;<br />                &lt;ref local="anonymousAuthenticationProvider"/&gt;<br />                &lt;ref local="rememberMeAuthenticationProvider"/&gt;<br />            &lt;/list&gt;<br />        &lt;/property&gt;<br />&lt;/bean&gt;</pre><p><br />2) <strong>daoAuthenticationProvider</strong><br />　　进行简单的基于数据库的身份验证。DaoAuthenticationProvider获取数据库中的账号密码并进行匹配，若成功则在通过用户身份的同时返回一个包含授权信息的 Authentication对象，否则身份验证失败，抛出一个AuthenticatiionException。</p><pre>    &lt;bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br />        &lt;property name="userDetailsService" ref="jdbcDaoImpl"/&gt;<br />        &lt;property name="userCache" ref="userCache"/&gt;<br />        &lt;property name="passwordEncoder" ref="passwordEncoder"/&gt;<br />   &lt;/bean&gt;</pre><p><br />3) <strong>passwordEncoder</strong><br />　　使用加密器对用户输入的明文进行加密。Acegi提供了三种加密器:<br />PlaintextPasswordEncoder—默认，不加密，返回明文.<br />ShaPasswordEncoder—哈希算法(SHA)加密<br />Md5PasswordEncoder—消息摘要(MD5)加密</p><pre>&lt;bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/&gt;</pre><p><br />4) <strong>jdbcDaoImpl</strong><br />　　用于在数据中获取用户信息。 acegi提供了用户及授权的表结构，但是您也可以自己来实现。通过usersByUsernameQuery这个SQL得到你的(用户ID,密码,状态信息);通过authoritiesByUsernameQuery这个SQL得到你的(用户ID,授权信息)</p><pre>&lt;bean id="jdbcDaoImpl"
class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"&gt;<br />       
&lt;property name="dataSource"
ref="dataSource"/&gt;<br />        &lt;property
name="usersByUsernameQuery"&gt;<br />           
&lt;value&gt;select loginid,passwd,1 from users where loginid =
?&lt;/value&gt;<br />       
&lt;/property&gt;<br />        &lt;property
name="authoritiesByUsernameQuery"&gt;<br />           
&lt;value&gt;select u.loginid,p.name from users u,roles r,permissions
p,user_role ur,role_permis rp where u.id=ur.user_id and r.id=ur.role_id and
p.id=rp.permis_id
and<br />               
r.id=rp.role_id and p.status='1' and
u.loginid=?&lt;/value&gt;<br />       
&lt;/property&gt;<br />&lt;/bean&gt;</pre><p>5) <strong>userCache　&amp;  resourceCache</strong><br />　　缓存用户和资源相对应的权限信息。每当请求一个受保护资源时，daoAuthenticationProvider就会被调用以获取用户授权信息。如果每次都从数据库获取的话，那代价很高，对于不常改变的用户和资源信息来说，最好是把相关授权信息缓存起来。(详见 <a href="http://www.springside.org.cn/docs/reference/Acegi4.htm"><font color="#002c99">2.6.3 资源权限定义扩展</font></a> )<br />userCache提供了两种实现: NullUserCache和EhCacheBasedUserCache, NullUserCache实际上就是不进行任何缓存，EhCacheBasedUserCache是使用Ehcache来实现缓功能。</p><pre>    &lt;bean id="userCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />       
&lt;property name="cacheManager"
ref="cacheManager"/&gt;<br />       
&lt;property name="cacheName" value="userCache"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="userCache"
class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"
autowire="byName"&gt;<br />        &lt;property
name="cache" ref="userCacheBackend"/&gt;<br />     
&lt;/bean&gt;<br />    &lt;bean id="resourceCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;<br />       
&lt;property name="cacheManager"
ref="cacheManager"/&gt;<br />       
&lt;property name="cacheName" value="resourceCache"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="resourceCache"
class="org.springside.modules.security.service.acegi.cache.ResourceCache"
autowire="byName"&gt;<br />        &lt;property
name="cache" ref="resourceCacheBackend"/&gt;<br />    &lt;/bean&gt;</pre><p><br />6) <strong>basicProcessingFilter</strong><br />　　用于处理HTTP头的认证信息，如从 Spring远程协议(如Hessian和Burlap)或普通的浏览器如IE,Navigator的HTTP头中获取用户信息，将他们转交给通过 authenticationManager属性装配的认证管理器。如果认证成功，会将一个Authentication对象放到会话中，否则，如果认证失败，会将控制转交给认证入口点(通过authenticationEntryPoint属性装配)</p><pre>    &lt;bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter"&gt;<br />        &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />        &lt;property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/&gt;<br />    &lt;/bean&gt;</pre><p>7) <strong>basicProcessingFilterEntryPoint</strong><br />　　通过向浏览器发送一个HTTP401(未授权)消息，提示用户登录。<br />处理基于HTTP的授权过程， 在当验证过程出现异常后的"去向"，通常实现转向、在response里加入error信息等功能。</p><pre> &lt;bean
id="basicProcessingFilterEntryPoint"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint"&gt;<br />       
&lt;property name="realmName" value="SpringSide Realm"/&gt;<br />&lt;/bean&gt;</pre><p>8) <strong>authenticationProcessingFilterEntryPoint</strong><br />　　当抛出AccessDeniedException时，将用户重定向到登录界面。属性loginFormUrl配置了一个登录表单的URL,当需要用户登录时，authenticationProcessingFilterEntryPoint会将用户重定向到该URL</p><pre>&lt;bean id="authenticationProcessingFilterEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;<br />       
&lt;property
name="loginFormUrl"&gt;<br />           
&lt;value&gt;/security/login.jsp&lt;/value&gt;<br />       
&lt;/property&gt;<br />        &lt;property
name="forceHttps" value="false"/&gt;<br />&lt;/bean&gt;</pre><h2>2.2.3 HTTP安全请求</h2><p>1) <strong>httpSessionContextIntegrationFilter</strong><br />　　每次 request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象，在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用，使之能跨越多个请求。</p><pre>&lt;bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"&gt;&lt;/bean&gt;<br />    &lt;bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"&gt;<br />        &lt;property name="allowIfAllAbstainDecisions" value="false"/&gt;<br />        &lt;property name="decisionVoters"&gt;<br />            &lt;list&gt;<br />                &lt;ref bean="roleVoter"/&gt;<br />            &lt;/list&gt;<br />        &lt;/property&gt;<br />&lt;/bean&gt;</pre><p><br />2) <strong>httpRequestAccessDecisionManager</strong><br />　　经过投票机制来决定是否可以访问某一资源(URL或方法)。allowIfAllAbstainDecisions为false时如果有一个或以上的 decisionVoters投票通过,则授权通过。可选的决策机制有ConsensusBased和UnanimousBased</p><pre>    &lt;bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"&gt;<br />        &lt;property name="allowIfAllAbstainDecisions" value="false"/&gt;<br />        &lt;property name="decisionVoters"&gt;<br />            &lt;list&gt;<br />                &lt;ref bean="roleVoter"/&gt;<br />            &lt;/list&gt;<br />        &lt;/property&gt;<br />    &lt;/bean&gt;</pre><p><br />3) <strong>roleVoter</strong><br /> 　　必须是以rolePrefix设定的value开头的权限才能进行投票,如AUTH_ , ROLE_</p><pre>    &lt;bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter"&gt;<br />        &lt;property name="rolePrefix" value="AUTH_"/&gt;<br />   &lt;/bean&gt;</pre><p>4）<strong>exceptionTranslationFilter</strong><br />　　异常转换过滤器，主要是处理AccessDeniedException和AuthenticationException，将给每个异常找到合适的"去向" </p><pre>   &lt;bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;<br />        &lt;property name="authenticationEntryPoint" ref="authenticationProcessingFilterEntryPoint"/&gt;<br />    &lt;/bean&gt;</pre><p>5) <strong>authenticationProcessingFilter</strong><br />　　和servlet spec差不多,处理登陆请求.当身份验证成功时，AuthenticationProcessingFilter会在会话中放置一个Authentication对象，并且重定向到登录成功页面<br />         authenticationFailureUrl定义登陆失败时转向的页面<br />         defaultTargetUrl定义登陆成功时转向的页面<br />         filterProcessesUrl定义登陆请求的页面<br />         rememberMeServices用于在验证成功后添加cookie信息</p><pre>    &lt;bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;<br />        &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />        &lt;property name="authenticationFailureUrl"&gt;<br />            &lt;value&gt;/security/login.jsp?login_error=1&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="defaultTargetUrl"&gt;<br />            &lt;value&gt;/admin/index.jsp&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="filterProcessesUrl"&gt;<br />            &lt;value&gt;/j_acegi_security_check&lt;/value&gt;<br />        &lt;/property&gt;<br />        &lt;property name="rememberMeServices" ref="rememberMeServices"/&gt;<br />    &lt;/bean&gt;</pre><p>6) <strong>filterInvocationInterceptor</strong><br />　　在执行转向url前检查 objectDefinitionSource中设定的用户权限信息。首先，objectDefinitionSource中定义了访问URL需要的属性信息(这里的属性信息仅仅是标志，告诉accessDecisionManager要用哪些voter来投票)。然后， authenticationManager掉用自己的provider来对用户的认证信息进行校验。最后，有投票者根据用户持有认证和访问url需要的属性，调用自己的voter来投票，决定是否允许访问。</p><pre>    &lt;bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br />        &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />        &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br />        &lt;property name="objectDefinitionSource" ref="filterDefinitionSource"/&gt;<br />    &lt;/bean&gt;</pre><p><br />7) <strong>filterDefinitionSource </strong>(详见 <a href="http://www.springside.org.cn/docs/reference/Acegi4.htm"><font color="#002c99">2.6.3 资源权限定义扩展</font></a>)<br />　　自定义DBFilterInvocationDefinitionSource从数据库和cache中读取保护资源及其需要的访问权限信息 </p><pre>&lt;bean id="filterDefinitionSource" class="org.springside.modules.security.service.acegi.DBFilterInvocationDefinitionSource"&gt;<br />        &lt;property name="convertUrlToLowercaseBeforeComparison" value="true"/&gt;<br />        &lt;property name="useAntPath" value="true"/&gt;<br />        &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br />&lt;/bean&gt;</pre><h2>2.2.4 方法调用安全控制</h2><p>(详见 <a href="http://www.springside.org.cn/docs/reference/Acegi4.htm"><font color="#002c99">2.6.3 资源权限定义扩展</font></a>)</p><p>1) methodSecurityInterceptor<br />　　在执行方法前进行拦截，检查用户权限信息<br />2) methodDefinitionSource<br />　　自定义MethodDefinitionSource从cache中读取权限</p><pre>   &lt;bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;<br />        &lt;property name="authenticationManager" ref="authenticationManager"/&gt;<br />        &lt;property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/&gt;<br />        &lt;property name="objectDefinitionSource" ref="methodDefinitionSource"/&gt;<br />    &lt;/bean&gt;<br />    &lt;bean id="methodDefinitionSource" class="org.springside.modules.security.service.acegi.DBMethodDefinitionSource"&gt;<br />        &lt;property name="acegiCacheManager" ref="acegiCacheManager"/&gt;<br />    &lt;/bean&gt;</pre></div>
		</div>
<img src ="http://www.blogjava.net/leedo/aggbug/68053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-09-06 16:33 <a href="http://www.blogjava.net/leedo/articles/68053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>轻松使用线程： 不共享有时是最好的</title><link>http://www.blogjava.net/leedo/articles/66220.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Mon, 28 Aug 2006 09:02:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/66220.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/66220.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/66220.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/66220.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/66220.html</trackback:ping><description><![CDATA[
		<table class="main_tdbg_575" style="" align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr align="center" valign="middle">
								<td colspan="2" class="main_ArticleTitle" style="" background="/Skin/adv43/ad_dybg2.gif" height="48">轻松使用线程： 不共享有时是最好的</td>
						</tr>
						<tr align="center" bgcolor="#f8f8f8" valign="middle">
								<td colspan="2" style="" height="18">
										<table style="" align="center" bgcolor="#ffffff" border="0" cellpadding="5" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td style="line-height: 16px;" width="14%">
																		<strong>相关网站：</strong>
																</td>
																<td style="line-height: 16px;" width="86%">
																		<div align="left">
																				<a href="http://www.baidu.com/baidu?myselectvalue=0&amp;tn=netbei&amp;ct=&amp;lm=&amp;word=%E8%BD%BB%E6%9D%BE%E4%BD%BF%E7%94%A8%E7%BA%BF%E7%A8%8B%EF%BC%9A%C2%A0%E4%B8%8D%E5%85%B1%E4%BA%AB%E6%9C%89%E6%97%B6%E6%98%AF%E6%9C%80%E5%A5%BD%E7%9A%84" target="_blank" class="style1">
																						<font color="#003399">点击进入与“轻松使用线程： 不共享有时是最好的”相关的网站</font>
																				</a>
																		</div>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
						<tr align="center" bgcolor="#eeeeee">
								<td colspan="2" height="24">作者：Brian 文章来源：IBM : developerWorks 点击数：<script language="javascript" src="http://www.netbei.com/Article/GetHits.asp?ArticleID=80"></script>957 
            更新时间：2004-7-11 <a href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(vivi=window.open('http://vivi.sina.com.cn/collect/icollect.php?pid=http://www.netbei.com&amp;title='+escape(d.title)+'&amp;url='+escape(d.location.href)+'&amp;desc='+escape(t),'vivi','scrollbars=no,width=480,height=480,left=75,top=20,status=no,resizable=yes'));vivi.focus();" title="收藏的网页将被永久地保存到新浪ViVi收藏夹http://vivi.sina.com.cn"><img src="http://image2.sina.com.cn/pfp/iweb/vivi_coop.gif" border="0" /></a></td>
						</tr>
						<tr>
								<td colspan="2" background="/Skin/adv43/ad_bx1.gif" height="6">
										<br />
								</td>
						</tr>
						<tr>
								<td colspan="2">
										<br />
								</td>
						</tr>
						<tr>
								<td colspan="2" id="fontzoom" style="" height="600" valign="top">
										<!--内容页广告广告代码开始-->
										<script language="JavaScript" src="http://www.netbei.com/netbei/x/view_a1.js">
										</script>
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr align="center">
																<td height="60">
																		<iframe src="http://www.netbei.com/netbei/x/view_a1.htm" marginwidth="0" marginheight="0" hspace="0" vspace="0" frameborder="0" height="100%" scrolling="no" width="590">
																		</iframe>
																		<br />
																</td>
														</tr>
												</tbody>
										</table>
										<!--内容页广告广告代码开始-->
										<p>利用 ThreadLocal 提高可伸缩性 <br /><br />ThreadLocal 
类是悄悄地出现在 Java 平台版本 1.2 中的。虽然支持线程局部变量早就是许多线程工具（例如 Posix pthreads 工具）的一部分，
但 Java Threads API 的最初设计却没有这项有用的功能。而且，最初的实现也相当低效。由于这些原因，ThreadLocal 极少受到
关注，但对简化线程安全并发程序的开发来说，它却是很方便的。在轻松使用线程的第 3 部分，Java 软件顾问 Brian Goetz 研究了
 ThreadLocal 并提供了一些使用技巧。 <br />参加 Brian 的多线程 Java 编程讨论<a class="Channel_KeyLink" href="http://bbs.netbei.com/">论坛</a>以获得您工程中的线程和并发问题的帮助。<br /><br />编
写线程安全类是困难的。它不但要求仔细分析在什么条件可以对变量进行读写，而且要求仔细分析其它类能如何使用某个类。有时，要在不影响类的功能、易用性或
性能的情况下使类成为线程安全的是很困难的。有些类保留从一个方法调用到下一个方法调用的状态信息，要在实践中使这样的类成为线程安全的是困难的。<br /><br />管
理非线程安全类的使用比试图使类成为线程安全的要更容易些。非线程安全类通常可以安全地在多线程程序中使用，只要您能确保一个线程所用的类的实例不被其它
线程使用。例如，JDBC Connection 类是非线程安全的 — 两个线程不能在小粒度级上安全地共享一个 Connection — 但如果每
个线程都有它自己的 Connection，那么多个线程就可以同时安全地进行<a class="Channel_KeyLink" href="http://www.netbei.com/Article/db/Index.html">数据库</a>操作。<br /><br />不
使用 ThreadLocal 为每个线程维护一个单独的 JDBC 连接（或任何其它对象）当然是可能的；Thread API 给了我们把对象和线程
联系起来所需的所有工具。而 ThreadLocal 则使我们能更容易地把线程和它的每线程（per-thread）数据成功地联系起来。<br /><br />什么是线程局部变量（thread-local variable）？<br />线
程局部变量高效地为每个使用它的线程提供单独的线程局部变量值的副本。每个线程只能看到与自己相联系的值，而不知道别的线程可能正在使用或修改它们自己的
副本。一些编译器（例如 Microsoft Visual C++ 编译器或 IBM XL FORTRAN 编译器）用存储类别修饰符（像
 static 或 volatile）把对线程局部变量的支持集成到了其语言中。Java 编译器对线程局部变量不提供特别的语言支持；相反地，它用
 ThreadLocal 类实现这些支持，核心 Thread 类中有这个类的特别支持。<br /><br />因为线程局部变量是通过一个类来实现的，而不
是作为 Java 语言本身的一部分，所以 Java 语言线程局部变量的使用语法比内建线程局部变量语言的使用语法要笨拙一些。要创建一个线程局部变
量，请实例化类 ThreadLocal 的一个对象。 ThreadLocal 类的行为与 java.lang.ref 中的各种
 Reference 类的行为很相似；ThreadLocal 类充当存储或检索一个值时的间接句柄。清单 1 显示了 ThreadLocal 接
口。<br /><br />清单 1. ThreadLocal 接口<br />public class ThreadLocal { <br />  public Object get();<br />  public void set(Object newValue);<br />  public Object initialValue();<br />}<br /><br /> <br />get
() 访问器检索变量的当前线程的值；set() 访问器修改当前线程的值。initialValue() 方法是可选的，如果线程未使用过某个变量，那
么您可以用这个方法来设置这个变量的初始值；它允许延迟初始化。用一个示例实现来说明 ThreadLocal 的工作方式是最好的方法。清单 2 显示
了 ThreadLocal 的一个实现方式。它不是一个特别好的实现（虽然它与最初实现非常相似），所以很可能性能不佳，但它清楚地说明了
 ThreadLocal 的工作方式。<br /><br />清单 2. ThreadLocal 的糟糕实现<br /><br />public class ThreadLocal { <br />  private Map values = Collections.synchronizedMap(new HashMap());<br /><br />  public Object get() {<br />    Thread curThread = Thread.currentThread();<br />    Object o = values.get(curThread);<br />    if (o == null &amp;&amp; !values.containsKey(curThread)) {<br />      o = initialValue();<br />      values.put(curThread, o);<br />    }<br />    return o;<br />  }<br /><br />  public void set(Object newValue) {<br />    values.put(Thread.currentThread(), newValue);<br />  }<br /><br />  public Object initialValue() {<br />    return null;<br />  }<br />}<br /><br /> <br /><br />这
个实现的性能不会很好，因为每个 get() 和 set() 操作都需要 values 映射表上的同步，而且如果多个线程同时访问同一个
 ThreadLocal，那么将发生争用。此外，这个实现也是不切实际的，因为用 Thread 对象做 values 映射表中的关键字将导致无法在
线程退出后对 Thread 进行垃圾回收，而且也无法对死线程的 ThreadLocal 的特定于线程的值进行垃圾回收。<br /><br />用 ThreadLocal 实现每线程 Singleton<br />线程局部变量常被用来描绘有状态“单子”（Singleton） 或线程安全的共享对象，或者是通过把不安全的整个变量封装进 ThreadLocal，或者是通过把对象的特定于线程的状态封装进 ThreadLocal。例如，在与<a class="Channel_KeyLink" href="http://www.netbei.com/Article/db/Index.html">数据库</a>有紧密联系的应用程序中，程序的很多方法可能都需要访问<a class="Channel_KeyLink" href="http://www.netbei.com/Article/db/Index.html">数据库</a>。
在系统的每个方法中都包含一个 Connection 作为参数是不方便的 — 用“单子”来访问连接可能是一个虽然更粗糙，但却方便得多的技术。然而，
多个线程不能安全地共享一个 JDBC Connection。如清单 3 所示，通过使用“单子”中的 ThreadLocal，我们就能让我们的程序
中的任何类容易地获取每线程 Connection 的一个引用。这样，我们可以认为 ThreadLocal 允许我们创建每线程单子。<br /><br />清单 3. 把一个 JDBC 连接存储到一个每线程 Singleton 中<br />public class ConnectionDispenser { <br />  private static class ThreadLocalConnection extends ThreadLocal {<br />    public Object initialValue() {<br />      return DriverManager.getConnection(ConfigurationSingleton.getDbUrl());<br />    }<br />  }<br /><br />  private ThreadLocalConnection conn = new ThreadLocalConnection();<br /><br />  public static Connection getConnection() {<br />    return (Connection) conn.get();<br />  }<br />}<br /><br /><br /><br /><br />任
何创建的花费比使用的花费相对昂贵些的有状态或非线程安全的对象，例如 JDBC Connection 或正则表达式匹配器，都是可以使用每线程单子
（singleton）技术的好地方。当然，在类似这样的地方，您可以使用其它技术，例如用池，来安全地管理共享访问。然而，从可伸缩性角度看，即使是用
池也存在一些潜在缺陷。因为池实现必须使用同步，以维护池数据结构的完整性，如果所有线程使用同一个池，那么在有很多线程频繁地对池进行访问的系统中，程
序性能将因争用而降低。<br /><br />用 ThreadLocal 简化调试日志纪录<br />其它适合使用 ThreadLocal 但用池却不能成为
很好的替代技术的应用程序包括存储或累积每线程上下文信息以备稍后检索之用这样的应用程序。例如，假设您想创建一个用于管理多线程应用程序调试信息的工
具。您可以用如清单 4 所示的 DebugLogger 类作为线程局部容器来累积调试信息。在一个工作单元的开头，您清空容器，而当一个错误出现时，
您查询该容器以检索这个工作单元迄今为止生成的所有调试信息。<br /><br />清单 4. 用 ThreadLocal 管理每线程调试日志<br />public class DebugLogger {<br />  private static class ThreadLocalList extends ThreadLocal {<br />    public Object initialValue() {<br />      return new ArrayList();<br />    }<br /><br />    public List getList() { <br />      return (List) super.get(); <br />    }<br />  }<br /><br />  private ThreadLocalList list = new ThreadLocalList();<br />  private static String[] stringArray = new String[0];<br /><br />  public void clear() {<br />    list.getList().clear();<br />  }<br /><br />  public void put(String text) {<br />    list.getList().add(text);<br />  }<br /><br />  public String[] get() {<br />    return list.getList().toArray(stringArray);<br />  }<br />}<br /><br /> <br /><br /><br /><br />在
您的代码中，您可以调用 DebugLogger.put() 来保存您的程序正在做什么的信息，而且，稍后如果有必要（例如发生了一个错误），您能够容
易地检索与某个特定线程相关的调试信息。 与简单地把所有信息转储到一个日志文件，然后努力找出哪个日志记录来自哪个线程（还要担心线程争用日志纪录对
象）相比，这种技术简便得多，也有效得多。<br /><br />ThreadLocal 在基于 servlet 的应用程序或工作单元是一个整体请求的任何
多线程应用程序服务器中也是很有用的，因为在处理请求的整个过程中将要用到单个线程。您可以通过前面讲述的每线程单子技术用 ThreadLocal 变
量来存储各种每请求（per-request）上下文信息。<br /><br />ThreadLocal 的线程安全性稍差的堂兄弟，InheritableThreadLocal<br />ThreadLocal 
类有一个亲戚，InheritableThreadLocal，它以相似的方式工作，但适用于种类完全不同的应用程序。创建一个线程时如果保存了所有
 InheritableThreadLocal 对象的值，那么这些值也将自动传递给子线程。如果一个子线程调用
 InheritableThreadLocal 的 get()，那么它将与它的父线程看到同一个对象。为保护线程安全性，您应该只对不可变对象（一旦
创建，其状态就永远不会被改变的对象）使用 InheritableThreadLocal，因为对象被多个线程共享。
InheritableThreadLocal 很合适用于把数据从父线程传到子线程，例如用户标识（user id）或事务标识
（transaction id），但不能是有状态对象，例如 JDBC Connection。<br /><br />ThreadLocal 的性能<br />虽
然线程局部变量早已赫赫有名并被包括 Posix pthreads 规范在内的很多线程框架支持，但最初的 Java 线程设计中却省略了它，只是在
 Java 平台的版本 1.2 中才添加上去。在很多方面，ThreadLocal 仍在发展之中；在版本 1.3 中它被重写，版本 1.4 中又重
写了一次，两次都专门是为了性能问题。<br /><br />在 JDK 1.2 中，ThreadLocal 的实现方式与清单 2 中的方式非常相似，除了
用同步 WeakHashMap 代替 HashMap 来存储 values 之外。（以一些额外的性能开销为代价，使用 WeakHashMap 解
决了无法对 Thread 对象进行垃圾回收的问题。）不用说，ThreadLocal 的性能是相当差的。<br /><br />Java 平台版本
 1.3 提供的 ThreadLocal 版本已经尽量更好了；它不使用任何同步，从而不存在可伸缩性问题，而且它也不使用弱引用。相反地，人们通过给
 Thread 添加一个实例变量（该变量用于保存当前线程的从线程局部变量到它的值的映射的 HashMap）来修改 Thread 类以支持
 ThreadLocal。因为检索或设置一个线程局部变量的过程不涉及对可能被另一个线程读写的数据的读写操作，所以您可以不用任何同步就实现
 ThreadLocal.get() 和 set()。而且，因为每线程值的引用被存储在自已的 Thread 对象中，所以当对 Thread 进行
垃圾回收时，也能对该 Thread 的每线程值进行垃圾回收。<br /><br />不幸的是，即使有了这些改进，Java 1.3 中的
 ThreadLocal 的性能仍然出奇地慢。据我的粗略测量，在双处理器 Linux 系统上的 Sun 1.3 JDK 中进行
 ThreadLocal.get() 操作，所耗费的时间大约是无争用同步的两倍。性能这么差的原因是 Thread.currentThread()
 方法的花费非常大，占了 ThreadLocal.get() 运行时间的三分之二还多。虽然有这些缺点，
JDK 1.3 ThreadLocal.get() 仍然比争用同步快得多，所以如果在任何存在严重争用的地方（可能是有非常多的线程，或者同步块被频
繁地执行，或者同步块很大），ThreadLocal 可能仍然要高效得多。<br /><br />在 Java 平台的最新版本，即版本 1.4b2 中，
ThreadLocal 和 Thread.currentThread() 的性能都有了很大提高。有了这些提高，ThreadLocal 应该比其它
技术，如用池，更快。由于它比其它技术更简单，也更不易出错，人们最终将发现它是避免线程间出现不希望的交互的有效途径。<br /><br />ThreadLocal 的好处<br />ThreadLocal 
能带来很多好处。它常常是把有状态类描绘成线程安全的，或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用
 ThreadLocal 使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程，而且因为它不需要任何同步，所以也改善了可伸缩性。除简单
之外，用 ThreadLocal 存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处 — 通过使用 ThreadLocal，存储在
 ThreadLocal 中的对象都是不被线程共享的是清晰的，从而简化了判断一个类是否线程安全的工作。<br /><br />我希望您从这个系列中得到了乐趣，也学到了知识，我也鼓励您到我的讨论<a class="Channel_KeyLink" href="http://bbs.netbei.com/">论坛</a>中来深入研究多线程问题。<br /><br /><br /><br />关于作者<br />Brian Goetz 
是一名软件顾问，过去 15 年来一直是专业软件开发者。他是 Quiotix 的首席顾问，该公司从事软件开发和咨询业务，位于加利福尼亚的
 Los Altos。敬请查看 Brian 在流行的业界出版物中已发表和即将发表的论文列表。可通过 brian@quiotix.com 与
 Brian 联系。  </p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/leedo/aggbug/66220.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-08-28 17:02 <a href="http://www.blogjava.net/leedo/articles/66220.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现 Java 多线程并发控制框架</title><link>http://www.blogjava.net/leedo/articles/65322.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Wed, 23 Aug 2006 09:28:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/65322.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/65322.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/65322.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/65322.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/65322.html</trackback:ping><description><![CDATA[
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr valign="top">
								<td width="100%">
										<h1>实现 Java 多线程并发控制框架</h1>
										<img alt="" src="http://www.ibm.com/i/c.gif" class="display-img" height="6" width="1" />
								</td>
								<td class="no-print" width="192">
										<img alt="developerWorks" src="http://www-128.ibm.com/developerworks/cn/i/dw.gif" height="18" width="192" />
								</td>
						</tr>
				</tbody>
		</table>
		<table border="0" cellpadding="0" cellspacing="0" width="100%">
				<tbody>
						<tr valign="top">
								<td width="10">
										<img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10" />
								</td>
								<td width="100%">
										<table class="no-print" align="right" border="0" cellpadding="0" cellspacing="0" width="160">
												<tbody>
														<tr>
																<td width="10">
																		<img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10" />
																</td>
																<td>
																		<table border="0" cellpadding="0" cellspacing="0" width="150">
																				<tbody>
																						<tr>
																								<td class="v14-header-1-small">文档选项</td>
																						</tr>
																				</tbody>
																		</table>
																		<table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td class="no-padding" width="150">
																										<img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8" />
																										<input value="Java 多线程是提高程序效能的利器，本文并不是告诉您如何编写多线程 Java 程序，而着重于多线程的并发控制以及如何描述线程执行的过程。当您需要完全掌控 Java 多线程执行的过程时，本文将会对您有所帮助。" name="body" type="hidden" />
																										<input name="subject" value="实现 Java 多线程并发控制框架" type="hidden" />
																										<input name="lang" value="cn" type="hidden" />
																										<table border="0" cellpadding="0" cellspacing="0" width="143">
																												<form action="https://www.ibm.com/developerworks/secure/email-it.jsp" name="email">
																												</form>
																												<script language="JavaScript" type="text/javascript">
																														<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
																												</script>
																												<tbody>
																														<tr valign="top">
																																<td width="8">
																																		<img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8" />
																																</td>
																																<td width="16">
																																		<img src="http://www.ibm.com/i/v14/icons/em.gif" alt="将此页作为电子邮件发送" height="16" vspace="3" width="16" />
																																</td>
																																<td width="122">
																																		<p>
																																				<a class="smallplainlink" href="javascript:document.email.submit();">
																																						<b>将此页作为电子邮件发送</b>
																																				</a>
																																		</p>
																																</td>
																														</tr>
																														<noscript>
																																<tr valign="top">
																																		<td width="8">
																																				<img alt="" height="1" width="8" src="//www.ibm.com/i/c.gif" />
																																		</td>
																																		<td width="16">
																																				<img alt="" width="16" height="16" src="//www.ibm.com/i/c.gif" />
																																		</td>
																																		<td class="small" width="122">
																																				<p>
																																						<span class="ast">未显示需要 JavaScript
的文档选项</span>
																																				</p>
																																		</td>
																																</tr>
																														</noscript>
																												</tbody>
																										</table>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
																		<!-- 03/20/06 updated by gretchen -->
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0" width="150">
																				<tbody>
																						<tr>
																								<td class="v14-header-2-small">最新推荐</td>
																						</tr>
																				</tbody>
																		</table>
																		<table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td class="no-padding" width="150">
																										<table border="0" cellpadding="0" cellspacing="0" width="143">
																												<tbody>
																														<tr valign="top">
																																<td width="8">
																																		<img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8" />
																																</td>
																																<td>
																																		<img src="http://www.ibm.com/i/v14/icons/fw_bold.gif" alt="" border="0" height="16" vspace="3" width="16" />
																																</td>
																																<td width="125">
																																		<p>
																																				<a href="http://www-128.ibm.com/developerworks/cn/kickstart/" class="smallplainlink">Java 应用开发源动力 － 下载免费软件，快速启动开发</a>
																																		</p>
																																</td>
																														</tr>
																												</tbody>
																										</table>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
																		<br />
																</td>
														</tr>
												</tbody>
										</table>
										<p>级别: 中级</p>
										<p>
												<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#author">陈 威</a> (<a href="mailto:chenwbj@cn.ibm.com?subject=%E5%AE%9E%E7%8E%B0%20Java%20%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6%E6%A1%86%E6%9E%B6">chenwbj@cn.ibm.com</a>), 软件工程师, IBM CSDL<br /></p>
										<p>2006 年  8 月  14 日</p>
										<blockquote>Java
提供了语言级别的线程支持，所以在 Java 中使用多线程相对于 C，C++ 来说更简单便捷，但本文并不是介绍如何在 Java
中使用多线程来来解决诸如 Web services, Number crunching 或者 I/O processing
之类的问题。在本文中，我们将讨论如何实现一个 Java 多线程的运行框架以及我们是如何来控制线程的并发同步以及顺序执行的。</blockquote>
										<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
										<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
										<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
										<p>
												<a name="N1003C">
														<span class="atitle">所面临的问题</span>
												</a>
										</p>
										<br />
										<a name="N10044">
												<b>图 1. 线程场景</b>
										</a>
										<br />
										<img alt="图 1. 线程场景" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image001.jpg" border="0" />
										<br />
										<p>这幅图中节点代表一个 single Thread，边代表执行的步骤。</p>
										<p>整幅图代表的意思是，ROOT 线程执行完毕后执行 T1 线程，T1 执行完毕后并发的执行 T2 和 T3。而从 T2 和 T3 指向
T4 的两条边表示的是 T4 必须等 T2 和 T3 都执行完毕以后才能开始执行。剩下的步骤以此类推，直到 END
作为整个过程的结束。当然，这只是个简略的示意图，可能面对的一个线程场景会有上百个线程。还有，你可以观察到这整个场景只有一个入口点和一个出口点，这
意味着什么？在下文中为你解释。</p>
										<p>这其中涉及到了 Java 线程的同步互斥机制。例如如何让 T1 在 T2 和 T3 之前运行，如何让 T2 和 T3 都执行完毕之后开启 T4 线程。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="N1005B">
														<span class="atitle">模型的描述</span>
												</a>
										</p>
										<p>如何来描述图 1 中所示的场景呢？可以采用 XML 的格式来描述我们的模型。我定义一个“Thread” element 来表示线程。</p>
										<table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<code>
																		</code>
																		<pre class="section">&lt;ThreadList&gt;<br />&lt;Thread ID = "thread-id" PRETHREAD = "prethread1, prethread2…"&gt;&lt;/Thread&gt;<br />&lt;Thread ID = "thread-id" PRETHREAD = "prethread3, prethread4…"&gt;&lt;/Thread&gt;<br />&lt;/ThreadList&gt;<br /></pre>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<p>其中 ID 是线程的唯一标识符，PRETHREAD 便是该线程的直接先决线程的ID，每个线程 ID 之间用逗号隔开。</p>
										<p>在 Thread 这个 element 里面可以加入你想要该线程执行任务的具体信息。</p>
										<p>实际上模型的描述是解决问题非常重要的一个环节，整个线程场景可以用一种一致的形式来描述，作为 Java
多线程并发控制框架引擎的输入。也就是将线程运行的模式用 XML 来描述出来，这样只用改动 XML
配置文件就可以更改整个线程运行的模式，不用改动任何的源代码。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="N10071">
														<span class="atitle">两种实现机制</span>
												</a>
										</p>
										<p>对于 Java 多线程的运行框架来说，我们将采用“外”和“内”的两种模式来实现。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="N1007A">
														<span class="atitle">“外” - 主线程轮询</span>
												</a>
										</p>
										<br />
										<a name="N10082">
												<b>图 2. 静态类图</b>
										</a>
										<br />
										<img alt="图 2. 静态类图" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image002.gif" border="0" />
										<br />
										<p>Thread 是工作线程。ThreadEntry 是 Thread 的包装类，prerequisite 是一个 HashMap，它含有
Thread 的先决线程的状态。如图1中显示的那样，T4 的先决线程是 T2 和 T3，那么 prerequisite 中就包含 T2 和
T3 的状态。TestScenario 中的 threadEntryList 中包含所有的 ThreadEntry。</p>
										<br />
										<a name="N10095">
												<b>图 3. 线程执行场景</b>
										</a>
										<br />
										<img alt="图 3. 线程执行场景" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image003.gif" border="0" />
										<br />
										<p>TestScenario 作为主线程，作为一个“外”在的监控者，不断地轮询 threadEntryList 中所有
ThreadEntry 的状态，当 ThreadEntry 接受到 isReady 的查询后查询自己的
prerequisite，当其中所有的先决线程的状态为“正常结束时”，它便返回 ready，那么 TestScenario 便会调用
ThreadEntry 的 startThread() 方法授权该 ThreadEntry 运行线程，Thread 便通过 run()
方法来真正执行线程。并在正常执行完毕后调用 setPreRequisteState() 方法来更新整个
Scenario，threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有该 Thread
的状态信息为“正常结束”。</p>
										<br />
										<a name="N100A8">
												<b>图 4. 状态更改的过程</b>
										</a>
										<br />
										<img alt="图 4. 状态更改的过程" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image004.jpg" border="0" />
										<br />
										<p>如图 1 中所示的 T4 的先决线程为 T2 和 T3，T2 和 T3 并行执行。如图 4 所示，假设 T2 先执行完毕，它会调用
setPreRequisteState() 方法来更新整个 Scenario， threadEntryList 中所有 ThreadEntry
中 prerequisite 里面含有该 T2 的状态信息为“正常结束”。此时，T4 的 prerequisite 中 T2
的状态为“正常结束”，但是 T3 还没有执行完毕，所以其状态为“未完毕”。所以 T4 的 isReady 查询返回为 false，T4
不会执行。只有当 T3 执行完毕后更新状态为“正常结束”后，T4 的状态才为 ready，T4 才会开始运行。</p>
										<p>其余的节点也以此类推，它们正常执行完毕的时候会在整个的 scenario 中广播该线程正常结束的信息，由主线程不断地轮询各个 ThreadEntry 的状态来开启各个线程。</p>
										<p>这便是采用主控线程轮询状态表的方式来控制 Java 多线程运行框架的实现方式之一。</p>
										<p>
												<b>优点：</b>概念结构清晰明了，实现简单。避免采用 Java 的锁机制，减少产生死锁的几率。当发生异常导致其中某些线程不能正常执行完毕的时候，不会产生挂起的线程。</p>
										<p>
												<b>缺点：</b>采用主线程轮询机制，耗费 CPU 时间。当图中的节点太多的(n&gt;??? 而线程单个线程执行时间比较短的时候 t&lt;??? 需要进一步研究)时候会产生线程启动的些微延迟，也就是说实时性能在极端情况下不好，当然这可以另外写一篇文章来专门探讨。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="N100C9">
														<span class="atitle">“内” - wait&amp;notify</span>
												</a>
										</p>
										<p>相对于“外”-主线程轮询机制来说，“内”采用的是自我控制连锁触发机制。</p>
										<br />
										<a name="N100D4">
												<b>图 5. 锁机制的静态类图</b>
										</a>
										<br />
										<img alt="图 5. 锁机制的静态类图" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image005.gif" border="0" />
										<br />
										<p>Thread 中的 lock 为当前 Thread 的 lock，lockList 是一个 HashMap，持有其后继线程的 lock
的引用，getLock 和 setLock 可以对 lockList 中的 Lock 进行操作。其中很重要的一个成员是
waitForCount，这是一个引用计数。表明当前线程正在等待的先决线程的个数，例如图 1 中所示的
T4，在初始的情况下，他等待的先决线程是 T2 和 T3，那么它的 waitForCount 等于 2。</p>
										<br />
										<a name="N100E7">
												<b>图 6. 锁机制执行顺序图</b>
										</a>
										<br />
										<img alt="图 6. 锁机制执行顺序图" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image006.gif" border="0" />
										<br />
										<p>当整个过程开始运行的时候，我们将所有的线程 start，但是每个线程所持的 lock 都处于 wait 状态，线程都会处于
waiting 的状态。此时，我们将 root thread 所持有的自身的 lock notify，这样 root thread
就会运行起来。当 root 的 run 方法执行完毕以后。它会检查其后续线程的 waitForCount，并将其值减一。然后再次检查
waitForCount，如果 waitForCount 等于 0，表示该后续线程的所有先决线程都已经执行完毕，此时我们 notify
该线程的 lock，该后续线程便可以从 waiting 的状态转换成为 running
的状态。然后这个过程连锁递归的进行下去，整个过程便会执行完毕。</p>
										<p>我们还是以 T2，T3，T4 为例，当进行 initThreadLock 过程的时候，我们可以知道 T4 有两个直接先决线程 T2 和
T3，所以 T4 的 waitForCount 等于 2。我们假设 T3 先执行完毕，T2 仍然在 running
的状态，此时他会首先遍历其所有的直接后继线程，并将他们的 waitForCount 减去 1，此时他只有一个直接后继线程 T4，于是 T4 的
waitForCount 减去 1 以后值变为 1，不等于 0，此时不会将 T4 的 lock notify，T4 继续 waiting。当
T2 执行完毕之后，他会执行与 T3 相同的步骤，此时 T4 的 waitForCount 等于 0，T2 便 notify T4 的
lock，于是 T4 从 waiting 状态转换成为 running 状态。其他的节点也是相似的情况。</p>
										<p>当然，我们也可以将整个过程的信息放在另外的一个全局对象中，所有的线程都去查找该全局对象来获取各自所需的信息，而不是采取这种分布式存储的方式。</p>
										<p>
												<b>优点：</b>采用 wait&amp;notify 机制而不采用轮询的机制，不会浪费CPU资源。执行效率较高。而且相对于“外”-主线程轮询的机制来说实时性更好。</p>
										<p>
												<b>缺点：</b>采用 Java 线程 Object 的锁机制，实现起来较为复杂。而且采取一种连锁触发的方式，如果其中某些线程异常，会导致所有其后继线程的挂起而造成整个 scenario 的运行失败。为了防止这种情况的发生，我们还必须建立一套线程监控的机制来确保其正常运行。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="N10108">
														<span class="atitle">延伸</span>
												</a>
										</p>
										<p>下面的图所要表达的是这样一种递归迭代的概念。例如在图1 中展示的那样，T1 这个节点表示的是一个线程。现在，忘掉线程这样一个概念，将
T1 抽象为一个过程，想象它是一个银河系，深入到 T1 中去，它也是一个许多子过程的集合，这些子过程之间的关系模式就如图 1
所示那样，可以用一个图来表示。</p>
										<br />
										<a name="N10113">
												<b>图 7. 嵌套子过程</b>
										</a>
										<br />
										<img alt="图 7. 嵌套子过程" src="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/image007.jpg" border="0" />
										<br />
										<p>可以想象一下这是怎样的一个框架，具有无穷扩展性的过程框架，我们只用定义各个过程之间的关系，我们不用关心过程是怎样运行的。事实上，可以在最终
的节点上指定一个实际的工作，比如读一个文件，或者submit一个JCL job，或者执行一条sql statement。</p>
										<p>其实，按照某种遍历规则，完全可以将这种嵌套递归的结构转化成为一个一层扁平结构的图，而不是原来的分层的网状结构，但是我们不这样做的原因是基于以下的几点考虑：</p>
										<ol>
												<li>如果这样做，会导致图节点太多，边太多，令人眼花缭乱。</li>
												<li>不这样做更主要的原因是每一个场景，如图 7 中的 T1，T13，是状态聚集的一个单元，具有高复用性和可靠性。</li>
												<li>框架是高度抽象的，它实际的执行可以是分布式的，一个单元可以是一个系统，作为和其他系统的分界标志。</li>
										</ol>
										<p>实际上，这是一个状态聚集的层次控制框架，我们可以依赖此框架来执行自主运算。我们将在其它的文章中来讨论它的应用。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="N10136">
														<span class="atitle">总结</span>
												</a>
										</p>
										<p>本文介绍了一种 Java 多线程并发控制的框架，并给出了其两种实现的模型，它们有各自的优缺点，有各自的适用范围。当需要进行 Java 线程的并发控制的时候，可以作为参考。</p>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="resources">
														<span class="atitle">参考资料 </span>
												</a>
										</p>
										<ul>
												<li>developerWorks Java 专区 Peter Haggar 的文章：<a href="http://www.ibm.com/developerworks/java/library/j-spnotif.html?S_TACT=105AGX52&amp;S_CMP=cn-a-j" target="_blank">Apply the Specific Notification pattern to control the order of thread execution</a><br /><br /></li>
												<li>Doug Lea 的著名并发性图书：<a href="http://www.china-pub.com/computers/common/info.asp?id=16303" target="_blank"><i>Java 并发编程: 设计原则与模式. 第二版(Addison Wesley 1999)</i></a><br /><br /></li>
												<li>另一本关于并发性的图书：<a href="http://www-128.ibm.com/developerworks/cn/java/j-goetzbook.html"><i>Java Concurrency in Practice</i></a><br /><br /></li>
												<li>developerWorks Java 专区 Joseph Hartal，Ze'ev Bubis 的文章：<a href="http://www-128.ibm.com/developerworks/cn/java/j-prodcon/">使你轻松得进行多线程应用程序编程</a><br /><br /></li>
												<li>developerWorks Java 专区 Alex Roetter 的文章：<a href="http://www-128.ibm.com/developerworks/cn/java/j-thread/">编写多线程的Java应用程序</a><br /><br /></li>
												<li>developerWorks Java 专区 Neel V. Kumar 的文章：<a href="http://www-128.ibm.com/developerworks/cn/java/multithreading/">Java 程序中的多线程</a></li>
										</ul>
										<br />
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td>
																		<img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%" />
																		<br />
																		<img alt="" src="http://www.ibm.com/i/c.gif" border="0" height="6" width="8" />
																</td>
														</tr>
												</tbody>
										</table>
										<table class="no-print" align="right" cellpadding="0" cellspacing="0">
												<tbody>
														<tr align="right">
																<td>
																		<img src="http://www.ibm.com/i/c.gif" alt="" height="4" width="100%" />
																		<br />
																		<table border="0" cellpadding="0" cellspacing="0">
																				<tbody>
																						<tr>
																								<td valign="middle">
																										<img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16" />
																										<br />
																								</td>
																								<td align="right" valign="top">
																										<a href="http://www-128.ibm.com/developerworks/cn/java/j-lo-concurrent-frmk/index.html#main" class="fbox">
																												<b>回页首</b>
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
										<br />
										<br />
										<p>
												<a name="author">
														<span class="atitle">关于作者</span>
												</a>
										</p>
										<table border="0" cellpadding="0" cellspacing="0" width="100%">
												<tbody>
														<tr>
																<td colspan="3">
																		<img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="100%" />
																</td>
														</tr>
														<tr align="left" valign="top">
																<td>
																		<br />
																</td>
																<td>
																		<img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="4" />
																</td>
																<td width="100%">
																		<p>陈威，华中科技大学硕士，IBM CSDL Software Engineer，所在的 Team 是 DB2 for z/OS。联系方式：chenwbj@cn.ibm.com</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/leedo/aggbug/65322.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-08-23 17:28 <a href="http://www.blogjava.net/leedo/articles/65322.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我对Log4J的一点初认识(转载)</title><link>http://www.blogjava.net/leedo/articles/33496.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Fri, 03 Mar 2006 08:52:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/33496.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/33496.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/33496.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/33496.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/33496.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 我对Log4J的一点初认识- -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n...&nbsp;&nbsp;<a href='http://www.blogjava.net/leedo/articles/33496.html'>阅读全文</a><img src ="http://www.blogjava.net/leedo/aggbug/33496.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-03-03 16:52 <a href="http://www.blogjava.net/leedo/articles/33496.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Log4j配置</title><link>http://www.blogjava.net/leedo/articles/32868.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Tue, 28 Feb 2006 08:40:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/32868.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/32868.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/32868.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/32868.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/32868.html</trackback:ping><description><![CDATA[<div class="postText">
				<p align="left"><strong><u>Log4j配置<br></u></strong>所需文件：1、jar包 ： commons-logging.jar 、log4j-1.2.8.jar 、taglibs-log.jar<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
2、配置文件：commons-logging.properties 、log4j.properties<br>&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;3、标签文件：taglibs-log.tag<br><br><strong>commons-logging.properties</strong>文件内容：</p>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><img src="../../Images/OutliningIndicators/None.gif" align="top"><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;##set&nbsp;Log&nbsp;as&nbsp;Log4J<br><img src="../../Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org.apache.commons.logging.Log</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">org.apache.commons.logging.impl.Log4JCategoryLog<br><img src="../../Images/OutliningIndicators/None.gif" align="top"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;##&nbsp;set&nbsp;Log&nbsp;as&nbsp;SimpleLog<br><img src="../../Images/OutliningIndicators/None.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#org.apache.commons.logging.Log</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">org.apache.commons.logging.impl.SimpleLog</span></div>
<p align="left"><br><strong>log4j.properties</strong>文件内容：</p>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><img src="../../Images/OutliningIndicators/None.gif" align="top"><span style="color: rgb(0, 0, 0);">#ConversionPattern参数的格式含义<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#格式名&nbsp;含义<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">c&nbsp;输出日志信息所属的类的全名<br><img id="Codehighlighter1_99_119_Open_Image" onclick="this.style.display='none'; Codehighlighter1_99_119_Open_Text.style.display='none'; Codehighlighter1_99_119_Closed_Image.style.display='inline'; Codehighlighter1_99_119_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_99_119_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_99_119_Closed_Text.style.display='none'; Codehighlighter1_99_119_Open_Image.style.display='inline'; Codehighlighter1_99_119_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedBlock.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">d&nbsp;输出日志时间点的日期或时间，默认格式为ISO8601，也可以在其后指定格式，比如：</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">d</span><span id="Codehighlighter1_99_119_Closed_Text" style="border: 1px solid rgb(128, 128, 128); display: none; background-color: rgb(255, 255, 255);"></span><span id="Codehighlighter1_99_119_Open_Text"><span style="color: rgb(0, 0, 0);">{yyy</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">MM</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">dd&nbsp;HH:mm:ss&nbsp;}</span></span><span style="color: rgb(0, 0, 0);">，输出类似：</span><span style="color: rgb(0, 0, 0);">2002</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">10</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">18</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">22</span><span style="color: rgb(0, 0, 0);">：</span><span style="color: rgb(0, 0, 0);">10</span><span style="color: rgb(0, 0, 0);">：</span><span style="color: rgb(0, 0, 0);">28</span><span style="color: rgb(0, 0, 0);"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">f&nbsp;输出日志信息所属的类的类名<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">l&nbsp;输出日志事件的发生位置，即输出日志信息的语句处于它所在的类的第几行<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">m&nbsp;输出代码中指定的信息，如log(message)中的message<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">n&nbsp;输出一个回车换行符，Windows平台为“\r\n”，Unix平台为“\n”<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">p&nbsp;输出优先级，即DEBUG，INFO，WARN，ERROR，FATAL。如果是调用debug()输出的，则为DEBUG，依此类推<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">r&nbsp;输出自应用启动到输出该日志信息所耗费的毫秒数<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">t&nbsp;输出产生该日志事件的线程名<br><img src="../../Images/OutliningIndicators/None.gif" align="top"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">##&nbsp;LOGGERS&nbsp;##<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#define&nbsp;a&nbsp;logger&nbsp;named&nbsp;helloAppLogger<br><img src="../../Images/OutliningIndicators/None.gif" align="top">log4j.rootLogger</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">info,console,file<br><img src="../../Images/OutliningIndicators/None.gif" align="top"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">##&nbsp;APPENDERS&nbsp;##<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#&nbsp;define&nbsp;an&nbsp;appender&nbsp;named&nbsp;console,&nbsp;which&nbsp;is&nbsp;set&nbsp;to&nbsp;be&nbsp;a&nbsp;ConsoleAppender<br><img src="../../Images/OutliningIndicators/None.gif" align="top">log4j.appender.console</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">org.apache.log4j.ConsoleAppender<br><img src="../../Images/OutliningIndicators/None.gif" align="top"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">#&nbsp;define&nbsp;an&nbsp;appender&nbsp;named&nbsp;file,&nbsp;which&nbsp;is&nbsp;set&nbsp;to&nbsp;be&nbsp;a&nbsp;RollingFileAppender<br><img src="../../Images/OutliningIndicators/None.gif" align="top">log4j.appender.file</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">org.apache.log4j.RollingFileAppender<br><img src="../../Images/OutliningIndicators/None.gif" align="top">log4j.appender.file.File</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">dada.txt<br><img src="../../Images/OutliningIndicators/None.gif" align="top"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">##&nbsp;LAYOUTS&nbsp;##<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#&nbsp;assign&nbsp;a&nbsp;SimpleLayout&nbsp;to&nbsp;console&nbsp;appender<br><img src="../../Images/OutliningIndicators/None.gif" align="top">log4j.appender.console.layout</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">org.apache.log4j.SimpleLayout<br><img src="../../Images/OutliningIndicators/None.gif" align="top">#&nbsp;assign&nbsp;a&nbsp;PatternLayout&nbsp;to&nbsp;file&nbsp;appender<br><img src="../../Images/OutliningIndicators/None.gif" align="top"><br><img src="../../Images/OutliningIndicators/None.gif" align="top">log4j.appender.file.layout</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">org.apache.log4j.PatternLayout<br><img id="Codehighlighter1_1067_1087_Open_Image" onclick="this.style.display='none'; Codehighlighter1_1067_1087_Open_Text.style.display='none'; Codehighlighter1_1067_1087_Closed_Image.style.display='inline'; Codehighlighter1_1067_1087_Closed_Text.style.display='inline';" src="../../Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img id="Codehighlighter1_1067_1087_Closed_Image" style="display: none;" onclick="this.style.display='none'; Codehighlighter1_1067_1087_Closed_Text.style.display='none'; Codehighlighter1_1067_1087_Open_Image.style.display='inline'; Codehighlighter1_1067_1087_Open_Text.style.display='inline';" src="../../Images/OutliningIndicators/ContractedBlock.gif" align="top">log4j.appender.file.layout.ConversionPattern</span><span style="color: rgb(0, 0, 0);">=%</span><span style="color: rgb(0, 0, 0);">d</span><span id="Codehighlighter1_1067_1087_Closed_Text" style="border: 1px solid rgb(128, 128, 128); display: none; background-color: rgb(255, 255, 255);"></span><span id="Codehighlighter1_1067_1087_Open_Text"><span style="color: rgb(0, 0, 0);">{yyyy</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">MM</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">dd&nbsp;hh:mm:ss}</span></span><span style="color: rgb(0, 0, 0);">&nbsp;[</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">c]:</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">p&nbsp;</span><span style="color: rgb(0, 0, 0);">-%</span><span style="color: rgb(0, 0, 0);">c</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">m</span><span style="color: rgb(0, 0, 0);">%</span><span style="color: rgb(0, 0, 0);">n</span></div><br><strong>新建log4j配置文件载入servlet类:<br></strong>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;javax.servlet.</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">;<br></span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;javax.servlet.http.</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">;<br></span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.io.</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">;<br></span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;java.util.</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">;<br></span><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;org.apache.log4j.PropertyConfigurator;<br><br></span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;Log4j&nbsp;</span><span style="color: rgb(0, 0, 255);">extends</span><span style="color: rgb(0, 0, 0);">&nbsp;HttpServlet&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);">&nbsp;String&nbsp;CONTENT_TYPE&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">text/html;&nbsp;charset=GBK</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">Initialize&nbsp;global&nbsp;variables</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;init()&nbsp;</span><span style="color: rgb(0, 0, 255);">throws</span><span style="color: rgb(0, 0, 0);">&nbsp;ServletException&nbsp;{<br>&nbsp;&nbsp;&nbsp;String&nbsp;prefix&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.getServletContext().getRealPath(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">/</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);<br>&nbsp;&nbsp;&nbsp;String&nbsp;file&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.getInitParameter(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">log4j</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);<br>&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">载入log4j配置文件</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">prefix</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">file);<br>&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">(file</span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PropertyConfigurator.configure(prefix</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">file);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">Clean&nbsp;up&nbsp;resources</span><span style="color: rgb(0, 128, 0);"><br></span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;destroy()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</span></div><strong>web.xml</strong>文件:<br>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">name</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">log4j</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">name</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">derek.log4j.Log4j</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">init</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">param</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">name</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">log4j</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">param</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">name</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">param</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">value</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">WEB</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">INF\classes\log4j.properties</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">param</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">value</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">init</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">param</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">on</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">startup</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">on</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">startup</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">mapping</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">name</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">log4j</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">name</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">url</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">pattern</span><span style="color: rgb(0, 0, 0);">&gt;/</span><span style="color: rgb(0, 0, 0);">log4j</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">url</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">pattern</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);"><br>&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;/</span><span style="color: rgb(0, 0, 0);">servlet</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">mapping</span><span style="color: rgb(0, 0, 0);">&gt;</span></div>测试代码：<br>
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; font-size: 13px; width: 98%; background-color: rgb(238, 238, 238);"><span style="color: rgb(0, 0, 255);">import</span><span style="color: rgb(0, 0, 0);">&nbsp;org.apache.log4j.Logger;<br><br></span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);">&nbsp;Log4jTest&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;Log4jTest()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Logger&nbsp;log&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;Logger.getLogger(</span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.getClass().getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.info(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">log4jDemo</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);">&nbsp;main(String[]&nbsp;args)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log4jTest&nbsp;log4jtest&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;Log4jTest();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</span></div>
			</div>
<img src ="http://www.blogjava.net/leedo/aggbug/32868.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-02-28 16:40 <a href="http://www.blogjava.net/leedo/articles/32868.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate中的过滤集合类Filtering collections（转）</title><link>http://www.blogjava.net/leedo/articles/31898.html</link><dc:creator>阳光</dc:creator><author>阳光</author><pubDate>Wed, 22 Feb 2006 01:05:00 GMT</pubDate><guid>http://www.blogjava.net/leedo/articles/31898.html</guid><wfw:comment>http://www.blogjava.net/leedo/comments/31898.html</wfw:comment><comments>http://www.blogjava.net/leedo/articles/31898.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/leedo/comments/commentRss/31898.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/leedo/services/trackbacks/31898.html</trackback:ping><description><![CDATA[<p><font style="background-color: rgb(255, 254, 255);"><a href="http://dev.21tx.com/java/struts/" target="_blank">Hibernate</a>的文档中写道:集合<span class="emphasis"><em>filter</em></span>是一种特殊的查询，用于一个持久化集合或者数组。查询字符串可以引用<tt class="literal">this</tt>,意为当前的数组元素<br><br>我觉得这样理解起来有些费劲.其实他的作用就是把你不需要的数据过滤掉,然后把结果集返回给你.现在举个例子说明一下:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font color="#0000ff">String hql =&nbsp; "select p from Picgroup p join p.images t where p.id=" + groupid;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Query query = session.createQuery(hql);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List list = query.list();</font></font></p>

<p><font style="background-color: rgb(255, 254, 255);" color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Picgroup group = (Picgroup) list.get(0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; view.setName(group.getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; view.setId(groupid);</font></p>

<font style="background-color: rgb(255, 254, 255);"><font color="#0000ff">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
Collection image=session.filter(group.getImages(),"select this where
this.state='C'");//这里只取出state等于C的数据<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Iterator ite = image.iterator();</font><br><br>看,就这么简单.一般在一对多或者多对多关系中从一方取另外一方数据时使用.</font><img src ="http://www.blogjava.net/leedo/aggbug/31898.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/leedo/" target="_blank">阳光</a> 2006-02-22 09:05 <a href="http://www.blogjava.net/leedo/articles/31898.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>