刘文涛

Flex3,Struts2,Hibernate3,Spring2,UML,Oracle,mysql,tomcat,compass,lucene

   :: 首页 ::  ::  ::  :: 管理 ::

2006年8月31日 #

appfuse 的webapp包下有这么几个包




一 :org.appfuse.webapp.filter


这个包下定义了一些过滤器

首先是GZIPFilter继承实现了spring提供的抽象类OncePerRequestFilter(每一次请求执行一次的过滤器)的doFilterInternal方法。
这个类的作用是通过gzip来压缩输出流。

private boolean isGZIPSupported(HttpServletRequest)

通过request获得TableTagParameters.PARAMETER_EXPORTING的属性值。放到字符串exporting里。如果exporting不为null.说明此时是用displayTag以execel或xml或pdf的格式往外输出表格内容。此时不应该压缩输出流。所以返回false.

通过request获得http 头中的"accept-encoding"的属性值。如果此值中有gzip这个字符串,support就为true.其他情况为false
通过request获得http 头中的"user-agent"属性值。如果此值是以httpunit开头 返回false.(此时是进行页面测试,不需要压缩输出流)。
返回support.
doFilterInternal(HttpServletRequest request, HttpServletResponse response,  FilterChain chain) 调用前边的方法判断是否需要压缩输出流。
如果需要
 新建 一个GZIPResponseWrapper(HttpServletResponse).对象把response 做为参数传过去。 然后调用
  chain.doFilter (request, wrappedResponse);
   wrappedResponse.finishResponse();
否则
 chain.doFilter (request, response);
 
GZIPResponseStream继承于ServletOutputStream.是实现对response进行 gzip 压缩的最终实现类。



 GZIPResponseWrapper继承了HttpServletResponseWrapper.是对GZIPResponseStream的一个包装。



类LocaleRequestWrapper继承了HttpServletRequestWrapper。它有一个不可被继承的属性
preferredLocale。
在它的构造函数里把参数userLocale的值赋给preferredLocale.
重构了getLocale()方法。如果preferredLocale 不为空,返回它,否则调用父类的方法。
重构了父类的getLocales()方法。如果preferredLocale不为空,调用父类的方法取得Locales后放到一个
列表里
如果此列表中有preferredLocale.从列表中把它删除把preferredLocale添加到此列表的开头。返回.
否则调用父类的getLocales()方法。
LocaleFilter 继承实现了OncePerRequestFilter 的doFilterInternal方法。
在此方法中,先request获得locale对象赋给字符串locale.如果locale不为null,通过字符串locale生成
一个Locale赋给preferredLocale.
然后获取一个session对象 request.getSession(false);
 request.getSession (boolean)的参数有true和false两个值,true为如果如果没有相同的session则创
建。如果有则覆盖,
 false则没有则创建有则不创建。
如果preferredLocale为null,
 从Session范围取得属性Constants.PREFERRED_LOCALE_KEY的值赋予preferredLocale。
如果preferredLocale不为null
 在Session范围设置属性Constants.PREFERRED_LOCALE_KEY 的值为preferredLocale。
 通过jstl的的Config在session范围内设置fmt的local为preferredLocale.
                Config.set(session, Config.FMT_LOCALE, preferredLocale);
如果preferredLocale 不为null,request的类型是LocaleRequestWrapper。
给request赋值new LocaleRequestWrapper(request, preferredLocale)。
把spring的LocaleCOntextHolder的属性Locale设为preferredLocale.
然后是
 chain.doFilter(request, response);
 LocaleContextHolder.setLocaleContext(null);
MessageFilter实现了Filter接口。
实现了doFilter 方法。在此方法中,从session里取属性messages的值。如果取到值,把这个值放到
reques范围内messages里.
从session里删掉这条属性。
对session范围内的errors属性执行类似操作。然后调用  chain.doFilter(req, res);
重构init 和destroy方法,在这两个方法里不执行任何代码。
org.appfuse.webapp.listener 这个包下是和监听相关的类。
StartupListener 这个类继承于spring的ContextLoaderListener类,实现了ServletContextListener接
口。
方法setupContext
通过   WebApplicationContextUtils.getRequiredWebApplicationContext(context) 获得spring的上下
文句柄ctx。
通过ctx获得bean "lookupManager"赋予LookupManager 对象mgr.然后把mgr.getAllRoles()做为
Constants.AVAILABLE_ROLES的
属性值放到context里。
覆盖了父类的contextinitialized方法。
首先调用父类的对应方法。然后根据输入的参数ServletContextEvent获得ServletContext对象。然后从
它里边获取Constants.CONFIG
对应的属性值。放到Map  config里。通过
WebApplicationContextUtils.getRequiredWebApplicationContext(context);获得spring上下文
ctx. 通过ctx获得bean "authenticationManager".赋予ProviderManager对象实例provider.
然后循环判断provider 的属性providers.如果它包含一个类型为RememberMeAuthenticationProvider的
实例,就在config里放一条属性
"rememberMeEnabled",值为Boolean.TRUE.
判断是否在spring的配置文件里定义了名为"passwordEncoder"
如果有 把encryptPassword赋值true.然后在config里添加属性Constants.ENCRYPT_PASSWORD,值为
Boolean.TRUE.如果spring配置文件
中定义的"passwordEncoder" 的类型是Md5PasswordEncoder. algorithm 赋值MD5,其他情况下赋值SHA.
然后在config 里添加一条属性
Constants.ENC_ALGORITHM .值为algorithm.
然后把config做为Constants.CONFIG.的值放到context里。
最后前边介绍的setupContext(context)方法。
总之这个类的作用就是在servletContext 根据实际情况设置 Constants.CONFIG属性值。
UserCounterlistener 这个类实现了ServletContextListener 和HttpSessionAttributeListener接口。
它有几个不可被继承的属性 COUNT_KEY,USERS_KEY,EVENT_KEY.有两个被transient修饰的属性
log和ServletContext 修饰的属性.(被transient修饰的属性在对象序列化的过程中是不被包括的,比如
把对象序列化保存到硬盘中的
过程中,此属性是不被保存的)。
还有两个私有属性, counter(int 型数据)和users(Set 集合).
实现了HttpSessionAttributeListener接口的 attributeAdded方法. 在这个方法中,程序先判断参数
event(类型为HttpSessionBindingEvent)
的名字是否等于属性EVENT_KEY的值
(HttpSessionContextIntegrationFilter.ACEGI_SECURITY_CONTEXT_KEY).
如果相等,取event的值.然后赋给SecurityContext 对象securityContext.根据它取得authentication 属
性,把authentication对象中的principal
赋给 User对象user.然后调用addUsername方法添加此用户.
实现了HttpSessionAttributeListener的attributeRemoved方法,在这个方法中先判断event的名字是否为
EVENT_KEY.如果是把event的值
赋予SecurityContext对象securityContext.然后通过它取得储存在它的属性authentication中的
principal对象赋予User对象user.然后调用
removeUsername方法删除此对象.
实现了HttpSessionAttributeListener的attributeReplaced方法,但是在此方法中没有任何代码.
公共方法contextInitialized(ServletContextEvent sce) 此方法被synchronized关键字修饰,表示此方
法是线程互斥的.在
此方法中,先通过
输入参数ServletContextEvent,获得ServletContext对象.然后在ServletContext里添加属性COUNT_KEY,
值为counter的字符串形式表达.
公共方法contextDestroyed,此方法是被synchronized 关键字修饰的,在此方法中给属性
servletContext,users 赋值null,counter 赋值0.
friend 方法 incrementUserCounter() 此方法也是线程互斥的,它servletContext里把COUNT_KEY取出加
一后再放回。(不写范围表达的方法就是friend,范围是本包和子类)
friend 方法decrementUserCounter 它的作用是把servletContext里的COUNT_KEY减一后再放回,当然它
也是线程互斥的。
friend 方法addUsername  此方法的作用是把把用户添加到servletContext范围内与属性USERS_KEY对应
的HashMap里。然后调用incrementUserCounter方法。这个方法也是线程互斥的。
friend方法removeUsername 此方法的作用是把用户从servletContext范围内与属性USER_KEY对应的
HashMap中删掉。然后调用decrementUserCounter方法。
org.jsfdemo.webapp.taglib包下是appfuse自定义标签相对应的类.
首先看一下这个自定义标签的tld文件 appfuse.tld.(这个文件会根据你的项目名而定)
在这个文件里首先配置了两个监听类StartupListener 和UserCounterListener.
然后定义了一个constants的tag.与这个tag对应的<tag-class>是ConstantsTag,对应的<tei-class>是
ConstantsTei.并且定义了这个tag有三个属性className,scope,var.
其实这个标签的作用是把一个了类放到scope指定的范围里。
类ConstantsTag继承了TagSupport类
它有clazz ,scope,var 三个属性以及对应的set和get方法,通过这些可以让标签的属性与具体的tag类的
属性关联起来。即
此自定义标签的class ,scope 和var属性与这里的clazz ,scope,var 对应。
它有一个静态的 不可被继承的Map scopes.然后通过一个static 块来初始化它如下:
static {
        scopes.put("page", new Integer(PageContext.PAGE_SCOPE));
        scopes.put("request", new Integer(PageContext.REQUEST_SCOPE ));
        scopes.put("session", new Integer(PageContext.SESSION_SCOPE));
        scopes.put("application", new Integer(PageContext.APPLICATION_SCOPE));
    }
因为使用的是jdk1.5所以这种写法没有错误 :)。
getScope(String scopeName) 这个方法的作用就是通过从上边定义的那个scopes里取与scopeName对应的
值。返回的数据
类型是int.
覆盖了TagSupport的 doStartTag方法,此方法的作用是把class 属性指定的类中的var指定的属性保存在
scope所指定的范围内。
如果class 为空class的默认值是Constants。scope如果为空默认值是PageContext.PAGE_SCOPE,var如果
为空则保存全部声明的属性。
然后返回 SKIP_BODY(因为这个标签没有真文,所以不需要和正文发生交互,也不会判断正文,所以返回
SKIP_BODY).
覆盖了父类的release方法,在此方法中释放占用资源。个人认为它的这个方法有误(appfuse 1.9 jsf
)应该是scope = null;而不是
scope = Constants.class.getName();
类ConstantsTei 继承了TagExtraInfo 覆盖了getVariableInfo方法。
  注: 通过扩展类javax.servlet.jsp.TagExtraInfo定义tag extra info类。一个TagExtraInfo必须实
现getVariableInfo方法以返回包含下列信息的VariableInfo对象数组:
  变量名 ,变量类 ,变量是否引用新对象 , 变量可用性 。
  Web容器向getVariableInfo方法传递包含每一个标签属性的属性-值元组的名为data的参数。这些属性
可以用于为VariableInfo对象提供脚本变量名和类。
所以这个类是与脚本变量有关的。这个方法的作用是把class指定的类的var属性,作为一个脚本变量返回
。如果var为空,就对全部变量进行处理。
返回的脚本变量的类型为String. 变量可以引用新对象,变量可用的范围是VariableInfo.AT_END(在结束
标签之后直到页面的结束 ).
 
org.appfuse.webapp.util
此包下是一些在web开发过程中用到的实用类。
FacesUtils 类,这个类的所有方法都是静态的。
getServletContext() 通过Facescontext对象获得servletContext对象。
getJsfEl(String value) 这个 方法的作用是 返回 "#{" + value + "}";
Application getApplication()
这个方法通过 FactoryFinder 获取application对象。
FactoryFinder提供了查找在jsf API中定义的所有factory对象的算法。如果在 system 中有一个属性与
想要查找的factory同名,它的值就做为你想要查找的factory返回.在system 中没有找到,如果在你的
web 应用的资源路径里找faces.properties文件。如果文件中包含与所要查找对象同名的属性,这个属性
就做为这个factory执行类的名字。最后如果存在META-INF/services/{factory-class-name}资源文件(比
如在lib包里的jar文件中)。读此文件的第一行做为factory实现类的名字。
factory Name有这么几种
APPLICATION_FACTORY------与ApplicationFactory对应的属性名.
FACES_CONTEXT_FACTORY----FacesContextFactory对应的属性名.
LIFECYCLE_FACTORY--------LifecycleFactory对应的属性名.
RENDER_KIT_FACTORY-------RenderKitFactory对应的属性名.
TREE_FACTORY-------------TreeFactory对应的属性名.
在这里通过FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY)获得ApplicationFactory
对象.然后再通过它获得Application对象.
getValueBinding(String el) 这个方法如下:
return getApplication().createValueBinding(el);
这个类是根据输入的对象名创建一个ValueBinding对象.比如在jsf的配置文件里定义了一个managed bean
"userBean",通过以下方式getValueBinding("#{userBean}")就创建了一个ValueBinding对象,通过此对象
可以获得userBean实例.
getELValue(String el)这个方法如下:
getValueBinding(el).getValue(FacesContext.getCurrentInstance());
此方法就是获得与el相对应的实例.
  
getManagedBean(String beanName) 此方法代码大体如下:
getValueBinding(getJsfEl(beanName)).getValue( FacesContext.getCurrentInstance());
:)就是通过前面定义的一系列方法获得与beanName对应的 Managed bean实例.
resetManagedBean 这个方法的作用是删除与beanName对应的Managed bean.具体如下:
getValueBinding(getJsfEl(beanName)).setValue(FacesContext.getCurrentInstance(),null); 
setManagedBeanInSession(String beanName,Object managedBean)这个方法的作用是
把managedBean 放到session范围内的属性beanName里。
getRequestParameter(String name)  从 request范围取得与parameter对应的参数。
addInfoMessage(String clientId,String msg)给一个特定的客户(由clientId指定)添加一条jsf消息,
消息的安全程度是FacesMessage.SEVERITY_INFO.
addInfoMessage(String msg) 这个方法就是添加一条jsf消息。如下:
addInfoMessage(null, msg);
addErrorMessage(String clientId, String msg)和 addErrorMessage(String msg)与前边类似分别是给
特定客户添加一条错误消息和添加一条错误消息。
evalInt(String el)
这个方法是判断参数el是不是某个对象的 jsf 表达,
如果是,通过el构建ValueBinding对象,进而获得此el对应的对象.如果是Integer 直接返回,否则把它转换
成Integer返回
否则 返回Integer(el);
还有 getRequest(),getResponse(),getSession()方法.
 RequestUtil 类的所有方法也是静态的。
setCookie(HttpServletResponse response, String name, String value, String path)
这个是用来发送cookie的。以参数name 和value构建一个cookie,它是非安全的,路径设为path.生命期为30天。然后添加到response里。
getCookie(HttpServletRequest request, String name) 根据name 从参数request里找到相应的cookie.
deleteCookie(HttpServletResponse response, Cookie cookie, String path)
通过把生命周期设为0的方式删除一个cookie.
getAppURL(HttpServletRequest request)
这个方法是通过request来获得应用的URL.

 

posted @ 2006-08-31 17:18 刘文涛| 编辑 收藏

     摘要: 1 Manager 2 LookupManager 3 RoleManager 4 UserManager 和包org.appfuse.dao下的1Da...  阅读全文
posted @ 2006-08-31 17:01 刘文涛| 编辑 收藏

     摘要: 刘文涛 原创  转载请注明作者 此主题 涉及到 加下有这么四个包 :一 :在org.appfuse下有这么一个类Constants.java,它是一个存储常量的类, 如果我们在程序中有一些常量需要改变名称,而这个常量分布在不同的类里,如果我们要修改它,就是一项很大的工程。而且重新编译也会遇到很多问题。我们通过建立一个常量类,如果我们需要修改,只需要在这里修改...  阅读全文
posted @ 2006-08-31 17:01 刘文涛| 编辑 收藏

每一个认证对象都有属于它自己的拦截器来负责处理它每一次请求。包括如下的一系列操作。



拦截器有如下几种。

1  : 针对类的方法的拦截器  :MethodSecurityInterceptor

在appfuse中用到了这种方式。如下:

<bean id="txProxyTemplate" abstract="true"
        
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    
<property name="transactionManager" ref="transactionManager"/>
    
<property name="transactionAttributes">
        
<props>
            
<prop key="save*">PROPAGATION_REQUIRED</prop>
            
<prop key="remove*">PROPAGATION_REQUIRED</prop>
            
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
        
</props>
    
</property>
</bean>

<!-- Generic manager that can be used to do basic CRUD operations on any objects -->
<bean id="manager" parent="txProxyTemplate">
    
<property name="target">
        
<bean class="org.appfuse.service.impl.BaseManager">
            
<property name="dao" ref="dao"/>
        
</bean>
    
</property>
</bean>

<!-- Transaction declarations for business services.  To apply a generic transaction proxy to
         all managers, you might look into using the BeanNameAutoProxyCreator 
-->
<bean id="userManager" parent="txProxyTemplate">
    
<property name="target">
        
<bean class="org.appfuse.service.impl.UserManagerImpl">
            
<property name="userDao" ref="userDao"/>
        
</bean>
    
</property>
    
<!-- Override default transaction attributes b/c of UserExistsException -->
    
<property name="transactionAttributes">
        
<props>
            
<prop key="save*">PROPAGATION_REQUIRED,-UserExistsException</prop>
            
<prop key="remove*">PROPAGATION_REQUIRED</prop>
            
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
        
</props>
    
</property>
    
<!-- This property is overriden in applicationContext-security.xml to add
             method
-level role security -->
    
<property name="preInterceptors">
        
<list>
            
<ref bean="userSecurityInterceptor"/>
        
</list>
    
</property>
</bean>

在 bean "userManager" 中的preInterceptors中有一个<ref bean="userSecurityInterceptor"/>。
bean "userSecurityInterceptor"的定义如下  :

    <bean id="userSecurityInterceptor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="userSecurityAdvice"/>
        <property name="patterns" value=".*saveUser"/>
    </bean>

可以看到,当我们调用userManager的时候,通过spring的aop机制在它执行的前边要先执行userSecurityInterceptor.

posted @ 2006-08-31 16:59 刘文涛| 编辑 收藏

一、Appfuse简介

Appfuse是Matt Raible 开发的一个指导性的入门级J2EE框架,它对如何集成流行的Spring、Hibernate、ibatis、struts、Xdcolet、junit等基础框架给出了示范,最新的1.9.4 Upgraded to Spring 2.0, Hibernate 3.2 and added Facelets + Ajax4JSF,在持久层,AppFuse采用了Hibernate O/R映射工具(http://www.hibernate.org);在容器方面,它采用了Spring Framework(http://www.springframework.org)。用户可以自由选择Struts、Spring/MVC,Webwork,Taperstry、JSF这几个web框架。采用TDD的开发方式,使用JUnit测试各层,甚至测试 jsp 输出的 w/o 错误。为了简化开发,预定义好了一套目录结构、基类、用来创建数据库、配置Tomcat、测试部署应用的 Ant 任务,帮助快速自动生成源程序和自动维护部分配置文件。




1.9.1 较 1.8.2 更新内容大体如下:
 
1 :AppGen 现在支持从数据库表生成xml和pojo.(以前只能通过pojo生成表)。
       现在我们可以选择是通过pojo生成表还有通过表生成pojo.

2 :AppGen 可以支持iBATIS.但是还有一些小问题。

3 :通过添加了一个XFire 组件可以使appfuse提供web services服务。
 
4 :在用户表中把主键由"username"改成了传统的“id”.

5 :鉴于spring 的推荐和API,把所有的*DAO类变成了*Dao.

6 :在ant 的new 任务里添加了一个提示。

7 :修改了一些中文字符的问题。
 
8 :在使用DisplayTag的时候提供了西班牙语和中文的支持。

9 :在list列表中把“Cancel”按钮变成了Done.

posted @ 2006-08-31 16:57 刘文涛| 编辑 收藏

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
五、以上规则对其它对象锁同样适用.
举例说明:
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

package ths;

public class Thread1 implements Runnable {
 public void run() {
  synchronized(this) {
   for (int i = 0; i < 5; i++) {
    System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
   }
  }
 }
 public static void main(String[] args) {
  Thread1 t1 = new Thread1();
  Thread ta = new Thread(t1, "A");
  Thread tb = new Thread(t1, "B");
  ta.start();
  tb.start();
 }
}

结果:

A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

package ths;

public class Thread2 {
 public void m4t1() {
 synchronized(this) {
 int i = 5;
 while( i-- > 0) {
 System.out.println(Thread.currentThread().getName() + " : " + i);
 try {
 Thread.sleep(500);
 } catch (InterruptedException ie) {
 }
 }
 }
 }
 public void m4t2() {
 int i = 5;
 while( i-- > 0) {
 System.out.println(Thread.currentThread().getName() + " : " + i);
 try {
 Thread.sleep(500);
 } catch (InterruptedException ie) {
 }
 }
 }
 public static void main(String[] args) {
  final Thread2 myt2 = new Thread2();
  Thread t1 = new Thread(
   new Runnable() {
   public void run() {
   myt2.m4t1();
   }
   }, "t1");

  Thread t2 = new Thread(
   new Runnable() {
   public void run() {
   myt2.m4t2();
   }
   }, "t2" );

  t1.start();
  t2.start();
 }
}

结果:

t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0


三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。


//修改Thread2.m4t2()方法:

public void m4t2() {
 synchronized(this) {
  int i = 5;
  while( i-- > 0) {
   System.out.println(Thread.currentThread().getName() + " : " + i);
   try {
    Thread.sleep(500);
   } catch (InterruptedException ie) {
   }
  }
 }
}

结果:

t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

//修改Thread2.m4t2()方法如下:

public synchronized void m4t2() {
 int i = 5;
 while( i-- > 0) {
  System.out.println(Thread.currentThread().getName() + " : " + i);
  try {
   Thread.sleep(500);
  } catch (InterruptedException ie) {
  }
 }
}


结果:

t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0

五、以上规则对其它对象锁同样适用:

package ths;

public class Thread3 {
 class Inner {
 private void m4t1() {
  int i = 5;
  while(i-- > 0) {
   System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
   try {
    Thread.sleep(500);
   } catch(InterruptedException ie) {
   }
  }
 }

 private void m4t2() {
  int i = 5;
  while(i-- > 0) {
   System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
   try {
    Thread.sleep(500);
   } catch(InterruptedException ie) {
    }
   }
  }
 }

 private void m4t1(Inner inner) {
  synchronized(inner) { //使用对象锁
   inner.m4t1();
  }
 }

 private void m4t2(Inner inner) {
  inner.m4t2();
 }

 public static void main(String[] args) {
  final Thread3 myt3 = new Thread3();
  final Inner inner = myt3.new Inner();
  Thread t1 = new Thread(
   new Runnable() {
    public void run() {
     myt3.m4t1(inner);
    }
   }, "t1");

  Thread t2 = new Thread(
   new Runnable() {
   public void run() {
   myt3.m4t2(inner);
   }
   }, "t2");

  t1.start();
  t2.start();
 }
}

结果:

尽管线程t1获得了对Inner的对象锁,但由于线程t2访问的是同一个Inner中的非同步部分。所以两个线程互不干扰。

t1 : Inner.m4t1()=4
t2 : Inner.m4t2()=4
t1 : Inner.m4t1()=3
t2 : Inner.m4t2()=3
t1 : Inner.m4t1()=2
t2 : Inner.m4t2()=2
t1 : Inner.m4t1()=1
t2 : Inner.m4t2()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=0


现在在Inner.m4t2()前面加上synchronized:


private synchronized void m4t2() {
 int i = 5;
 while(i-- > 0) {
  System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
  try {
   Thread.sleep(500);
  } catch(InterruptedException ie) {
  }
 }
}

结果:

尽管线程t1与t2访问了同一个Inner对象中两个毫不相关的部分,但因为t1先获得了对Inner的对象锁,所以t2对Inner.m4t2()的访问也被阻塞,因为m4t2()是Inner中的一个同步方法。

t1 : Inner.m4t1()=4
t1 : Inner.m4t1()=3
t1 : Inner.m4t1()=2
t1 : Inner.m4t1()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=4
t2 : Inner.m4t2()=3
t2 : Inner.m4t2()=2
t2 : Inner.m4t2()=1
t2 : Inner.m4t2()=0

posted @ 2006-08-31 15:54 刘文涛| 编辑 收藏

一 : transient

private transient ServletContext servletContext;

transient (临时) 只能用在类的成员变量上,不能用在方法里.
transient (临时) 变量不能是final和static的
transient (临时) 关键字控制序列化过程时,可能有一个特定的子对象不愿让Java的序列化机制自动保存与恢复。

一般地,若那个子对象包含了不想序列化的敏感信息(如密码),就会面临这种情况。即使那种信息在对象中具有“private”(私有)属性,但一旦经序列化处理,人们就可以通过读取一个文件,或者拦截网络传输得到它。为防止对象的敏感部分被序列化,一个办法是将自己的类实现为Externalizable,就象前面展示的那样。这样一来,没有任何东西可以自动序列化,只能在writeExternal()明确序列化那些需要的部分。然而,若操作的是一个Serializable对象,所有序列化操作都会自动进行。为解决这个问题,可以用transient(临时)逐个字段地关闭序列化,它的意思是“不要麻烦你(指自动机制)保存或恢复它了——我会自己处理的”。例如,假设一个Login对象包含了与一个特定的登录会话有关的信息。校验登录的合法性时,一般都想将数据保存下来,但不包括密码。

posted @ 2006-08-31 15:53 刘文涛| 编辑 收藏

在appfuse的web.xml声明文件中有下面的类似声明:

< filter >
        
< filter - name > rewriteFilter </ filter - name >
        
< filter - class > org.tuckey.web.filters.urlrewrite.UrlRewriteFilter </ filter - class >
        
< init - param >
            
< param - name > logLevel </ param - name >
            
< param - value > commons </ param - value >
        
</ init - param >
</ filter >

< filter - mapping >
        
< filter - name > rewriteFilter </ filter - name >
        
< url - pattern > /* </url-pattern>
</filter-mapping>


它所应用到的包存放在lib下的

urlrewrite - 3.0 - beta.jar

它和以上的两部分,以及

/ web - inf / urlrewrite.xml

构成了对url地址的美化

打开这个文件,我们可以看到以下的声明:

<? xml version = " 1.0 "  encoding = " utf-8 " ?>
<! DOCTYPE urlrewrite PUBLIC  " -//tuckey.org//DTD UrlRewrite 3.0//EN "
    
" http://tuckey.org/res/dtds/urlrewrite3.0.dtd " >

<!--  https: // urlrewrite.dev.java.net/manual/3.0 -->
< urlrewrite >
    
< rule >
        
< from >^/ user / (. * ).html$ </ from >
        
< to type = " forward " >/ editUser.html\ ? username = $ 1 </ to >
    
</ rule >
</ urlrewrite >


这样系统就完成了url地址的editUser.html?username=****的美化

如将
http://localhost/aaa/bbb.jsp?id=99&name=abc
表现为:
http://localhost/aaa/bbb/99_abc.html
配置如下:

< rule >  
    
< from >
        
/ aaa / bbb / ([ 0 - 9 ] + )_([a - z] + ).html
    
</ from >  

    
< to >
        
/ .. / aaa / bbb.jsp ? id = $ 1 & name = $ 2
    
</ to >  
</ rule >  

 


 

posted @ 2006-08-31 14:14 刘文涛| 编辑 收藏

一 : FAQ

Q:   能否脱离Spring框架来使用Acegi ?

A:  虽然Acegi 没有要求必须使用Spring Framework,但事实上Acegi很大程度上利用了Spring的IOC和AOP,很难脱离Spring的单独使用。

Q:  Acegi有对xfire的支持吗 ?

A: 有,详见http://jira.codehaus.org/browse/XFIRE-389

Q: 为何无论怎么设置都返回到登陆页面无法成功登陆 ?

A:  检查登陆页面或登陆失败页面是否只有ROLE_ANONYMOUS权限


二 : Acegi 补习班

要了解Acegi,首先要了解以下几个重要概念:

1 :Authentication  (认证)对象 :



      
Authentication对象包含了 :

1 principal
2 credentials
3 authorities(authorities要赋予给principal的),

同时也可以包含一些附加的认证请求信息,如

1 TCP / IP地址
2 Session id等。


2 :SecurityContextHolder
 
        SecurityContextHolder包含ThreadLocal私有属性用于存取SecurityContext, 
        SecurityContext包含Authentication私有属性, 看以下一段程序

   public   void  getSecurityContextInformations()   {  
       
//SecurityContextHolder包含ThreadLocal私有属性用于存取SecurityContext
    SecurityContext sc  =  SecurityContextHolder.getContext(); 
       
// SecurityContext包含Authentication私有属性
    Authentication auth  =  sc.getAuthentication();  
        
//Authentication对象包含了principal
    Object principal  =  auth.getPrincipal();  

     
if  (principal  instanceof  UserDetails)   {   
         
// 用户密码    
         String password  =  ((UserDetails) principal).getPassword();  
         
// 用户名称    
         String username  =  ((UserDetails) principal).getUsername();  
         
// 用户权限     authorities(authorities要赋予给principal的),
         GrantedAuthority[] authorities  =  ((UserDetails) principal).getAuthorities();   
         
for  ( int  i  =   0 ; i  <  authorities.length; i ++ )   {    
            String authority  
=  authorities[i].getAuthority();   
        }
   
    }
   

    Object details  
=  auth.getDetails();  

     
if  (details  instanceof  WebAuthenticationDetails)   {   
         
// 用户session id    
         String SessionId  =  ((WebAuthenticationDetails) details).getSessionId();  
    }
  
}
 

AuthenticationManager

通过Providers 验证 在当前 ContextHolder中的Authentication对象是否合法。
 
AccessDecissionManager

经过投票机制来审批是否批准操作 

RunAsManager

当执行某个操作时,RunAsManager可选择性地替换Authentication对象 

Interceptors

拦截器(如FilterSecurityInterceptor,JoinPoint,MethodSecurityInterceptor等)用于协调授权,认证等操作
posted @ 2006-08-31 12:00 刘文涛| 编辑 收藏