appfuse源码分析三(web)
 

2008-12-23 作者:刘文涛 来源:blogjava.net

 

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.


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织