WebX基础知识

什么是Webx

Webx是基于Java Servlet API的通用web框架,它建立在SprintExt框架基础之上,由SprintExt进行模块组装,提供扩展机制,所以Webx继承了Spring的所有功能,但是比Spring具有更强大的扩展能力。


为什么要用Webx

Webx建立在SprintExt框架的基础之上,所以不仅具有Spring的所有特性,而且有比Spring更强大的扩展性;Webx鼓励层次化的设计,SpringExt提供了创建和配置层次化组件的机制,使系统中的每一个部分都可以被替换或增强,而Webx本身也采用了层次化的设计,你可以使用Webx框架的一部分,也可能使用全部Webx框架。


Webx的层次结构

Webx框架可以划分为三个层次:

1、SpringExt

     基于Spring,并提供了扩展组件的能力,是Webx框架的基础。

2、Webx Framework

     基于Servlet API,提供基础服务,例如:初始化spring、被始化日志、错误处理、接收请求、开发模式等,它只与servlet和spring有关,并不关心如模板渲染、action处理、表单处理等。

3、Webx Turbine

     基于Webx Framework,实现具体的网页功能,如模板渲染、action处理、表单处理等。


环境准备

1、eclipse采用Juno64位版本

2、maven采用2.2.1版本

3、为eclipse安装m2eclipse插件

创建WebX项目

1、新建Maven Project

打开eclipse后,进入菜单File->New->Other,在Maven项下选择Maven Project,点击下一步


2、选择project name和workspace弹窗使用默认设置,点下一步


3、选择Archetype弹窗,可以用filter输入框筛选一下,选择archetype-webx-quickstart,其它设置保持默认,点击下一步


4、archetype parameters弹窗中输入Group Id和Artifact Id,点击finished


5、到此一个简单的webx项目已经生成了,来看一下目录结构


2、配置项目

1、webx项目各个目录的功用后面再说,先让这个项目运行起来。在项目名称上右键,选择Run As->Run Configurations,弹出一个弹窗如下


如果Maven Build项下没有子项,则双击之,会出现配置窗口


在这个配置窗口中有三项要填,name输入框为此配置设置一个名字、Base directory选择file system里此项目的路径、Goals输入框填tomcat:run,以上的配置是让此项目运行在tomcat下,如果要项目运行在jetty下的话可以在Goals输入框写入jetty:run,配置输入完成,点击Run

2、所有配置已经完成,可以运行一下试试了,在浏览器里打开http://localhost:8080/firstdemo/



~~~OK,这个简单的webx项目已经运行起来了


webx的页面结构

webx页面由三部分组成:

  1. layout 对应着templetas文件夹里的layout文件夹,负责页面的布局。
  2. screen 对应着templates文件夹里的screen文件夹,是页面的主体内容。
  3. control 对应着templates文件夹里的control文件夹,是页面的通用部分,可以被多个screen或layout引用,例如页面导航、页脚等
每个页面有一个layout,每个layout只能有一个screen,每个layout或screen可以有多个control。


webx执行流程

webx的执行流程是由pipeline控制的,所以流程的定义就在pipeline配置中,下面是推荐的pipeline配置

<services:pipeline xmlns="http://www.alibaba.com/schema/services/pipeline/valves">

    <!-- 初始化turbine rundata,并在pipelineContext中设置可能会用到的对象(如rundata、utils),以便valve取得。 -->
    <prepareForTurbine />

    <!-- 设置日志系统的上下文,支持把当前请求的详情打印在日志中。 -->
    <setLoggingContext />

    <!-- 分析URL,取得target。 -->
    <analyzeURL homepage="homepage" />

    <!-- 检查csrf token,防止csrf攻击和重复提交。假如request和session中的token不匹配,则出错,或显示expired页面。 -->
    <checkCsrfToken />

    <loop>
        <choose>
            <when>
                <!-- 执行带模板的screen,默认有layout。 -->
                <pl-conditions:target-extension-condition extension="null, vm, jsp" />
                <performAction />
                <performTemplateScreen />
                <renderTemplate />
            </when>
            <when>
                <!-- 执行不带模板的screen,默认无layout。 -->
                <pl-conditions:target-extension-condition extension="do" />
                <performAction />
                <performScreen />
            </when>
            <otherwise>
                <!-- 将控制交还给servlet engine。 -->
                <exit />
            </otherwise>
        </choose>

        <!-- 假如rundata.setRedirectTarget()被设置,则循环,否则退出循环。 -->
        <breakUnlessTargetRedirected />
    </loop>

</services:pipeline>

可以看出webx根据target来判断执行哪一个流程。如果后缀为空或为vm、jsp时,则执行第一个流程,如果后缀是do则执行第二个流程。


如果用户输入一个地址http://localhost:8080的话,webx的执行流程如下:

1、<anaylzeURL>分析url,取得target

由于http://localhost:8080并不包含context path,所以分析得到的target为null,在这种情况下target会取默认值homepage,target并不是模板也不是类,只是一个抽象概念,在后面的步骤中会根据target映射到模板和module类。

2、<choose>分支

根据取得的target可以确定,符合条件extension="null,vm,asp",webs会执行这一流程

3、<performAction>执行action

webx里的action专门用来处理用户提交的表单,由于本次请求没有action参数,所以忽略了这一步骤

4、<performTemplateScreen>查找并执行screen

在这一步,会把target映射成screen的module类,映射的规则如下:如果target是xxx/yyy/zzz,那么webs会按照以下的顺序来screen模块:

screen.xxx.yyy.zzz

screen.xxx.yyy.Default

screen.xxx.Default

screen.Default

本次请求的target是homepage,所以会查找screen.homepage和screen.default两个模块类,如果找到就执行,如果找不到也没关系。

5、<renderTemplates>渲染模板

这一步又分两部分:将target映射成screen模板、将target映射成layout模板

假设target是xxx/yyy/zzz,则webs会查找screen模板/templetes/screen/xxx/yyy/zzz,如果没找到就会报404错误。找到screen模板后,webs会按以下顺序查找layout模板:

/templates/layout/xxx/yyy/zzz

/templates/layout/xxx/yyy/default

/templates/layout/xxx/default

/templates/layout/default

如果没找到layout模板,则直接渲染screen模板,如果找到了layout模板,则将渲染的screen模板嵌入到layout模板中。

6、<breakunlessTargetRedirect>内部重定向

如果设置了内部重定向,则模板渲染完成后会循环loop标签,如果没有定义重定向,则执行完成,退出循环。

至此,一个webx请求的执行流程完成了~


说起LogConfiguratorListener来,还得从配置文件web.xml开始说起,在web.xml配置中,有一项很重要的配置项,那就是日志系统初始化:

[html]  view plain copy
  1. <!-- 初始化日志系统 -->  
  2. <listener>  
  3.     <listener-class>com.alibaba.citrus.logconfig.LogConfiguratorListener</listener-class>  
  4. </listener>  

在WEB应用被起动的时候,这个listener就会被激活,进行日志系统的初始化工作。


LogConfiguratorListener的作用

正如前面所说的,LogConfiguratorListener就是负责在WEB应用起动的时候初始化日志系统,在做这些工作的时候,它会读取init参数,这些参数也同样配置在web.xml文件中,如下:

[html]  view plain copy
  1. <context-param>  
  2.     <param-name>loggingRoot</param-name>   
  3.     <param-value>/tmp/logs</param-value>  
  4. </context-param>  
  5. <context-param>  
  6.     <param-name>loggingLevel</param-name>   
  7.     <param-value>INFO</param-value>  
  8. </context-param>  
  9. <context-param>  
  10.     <param-name>loggingCharset</param-name>   
  11.     <param-value>UTF-8</param-value>  
  12. </context-param>  
  13. <context-param>  
  14.     <param-name>log...</param-name>   
  15.     <param-value>...</param-value>  
  16. </context-param>  


LogConfiguratorListener实现

LogConfiguratorListener实现了ServletContextListener接口,ServletContextListener是ServletContext的监听接口,当ServletContext被创建时,ServletContextListener的contextInitialized方法会被调用,日志系统的初始化就在这个方法里实现。当应用被关闭时,ServletContextListener的contextDestroyed方法会被调用,在这个方法里会把日志系统关闭。下面是方法的源码:

[java]  view plain copy
  1. public void contextInitialized(ServletContextEvent event) {  
  2.     ServletContext servletContext = event.getServletContext();  
  3.   
  4.     // 取得所有以log开头的init params。  
  5.     Map<String, String> params = getLogInitParams(servletContext);  
  6.   
  7.     // 从context init-param中取得logSystem的值,可能为null。  
  8.     String[] logSystems = getLogSystems(params);  
  9.   
  10.     // 取得指定名称的logConfigurator,如未取得,则抛出异常,listener失败。  
  11.     logConfigurators = LogConfigurator.getConfigurators(logSystems);  
  12.   
  13.     for (LogConfigurator logConfigurator : logConfigurators) {  
  14.         String logSystem = logConfigurator.getLogSystem();  
  15.   
  16.         // 取得指定logConfigurator的配置文件。  
  17.         String logConfiguration = getLogConfiguration(params, logSystem);  
  18.   
  19.         servletContext.log(String.format("Initializing %s system", logSystem));  
  20.   
  21.         // 取得log配置文件。  
  22.         URL logConfigurationResource;  
  23.   
  24.         try {  
  25.             logConfigurationResource = servletContext.getResource(logConfiguration);  
  26.         } catch (MalformedURLException e) {  
  27.             logConfigurationResource = null;  
  28.         }  
  29.   
  30.         // 如未找到配置文件,则用默认的值来配置,否则配置之。  
  31.         if (logConfigurationResource == null) {  
  32.             servletContext  
  33.                     .log(String  
  34.                                  .format("Could not find %s configuration file \"%s\" in webapp context.  Using default configurations.",  
  35.                                          logSystem, logConfiguration));  
  36.   
  37.             logConfigurator.configureDefault();  
  38.         } else {  
  39.             Map<String, String> props = logConfigurator.getDefaultProperties();  
  40.             initProperties(props);  
  41.             props.putAll(params);  
  42.   
  43.             logConfigurator.configure(logConfigurationResource, props);  
  44.         }  
  45.     }  
  46. }  

分析一下上面的源码,看看方法contextInitialized都干了哪些事儿呢?

1、取init参数

在web.xml文件里配置的context-param参数中,查找以log开头的参数。

2、根据init参数,生成日志配置器

在步骤1中取到的以log开头的参数中,查找logSystem参数,里面设置了要采用的日志系统名称,如果有多个的话会以逗号或空格分隔。如果web.xml中没有设置logSystem参数,则默认采用slf4j中的日志系统。拿到logSystem名称后,根据名称构造出LogConfigurator。

3、读取每个日志配置器的配置文件,如果配置文件不存在就用默认值来配置

在web.xml文件中,logConfiguration参数配置了日志系统配置文件的路径,如果web.xml文件中没有这个文件,那么采用默认路径"/WEB-INF/日志系统名称.xml“,如果配置文件找到了,用它来配置步骤2中生成的LogConfigurator;如果没有找到配置文件,则还有一个默认的配置文件,名称是”日志系统名称-default.xml"。



WebX是基于Java Servlet API的通用web框架。WebX致力于提供极具扩展性的机制,以满足web应用不断变化的需求,而springext正是webx扩展机制的基石。springext是对spring的扩展,在spring的基础之上提供了扩展能力。


springext能作什么呢?

以ResourceLoadingService为例,它是webs的非常有用的一个service,可以在各种输入源中加载资源文件。


以bean方式装配ResourceLoadingService

如果以Spring Bean方式配置ResourceLoadingService,会是如下这样子:

[html]  view plain copy
  1. <bean id="resourceLoadingService" class="com.alibaba…ResourceLoadingService">  
  2.      <property name="mappings">  
  3.           <map>  
  4.                <entry key="/file" value-ref="fileLoader" />  
  5.                <entry key="/webroot" value-ref="webappLoader" />  
  6.           </map>  
  7.      </property>  
  8. </bean>  
  9. <bean id="fileLoader" class="com.alibaba…FileResourceLoader" >  
  10.      <property name="basedir" value="${user.home}" />  
  11. </bean>  
  12. <bean id="webappLoader" class="com.alibaba…WebappResourceLoader" />  

这样的配置简单易行,体现了spring的IoC原则,但是它具有以下缺点:

1、没有检验机制,即使是spring配置写错了,也要等到运行时才会发现。

2、无法了解更多约束,即使是查看了api文档,也未必能知道哪些参数是必填的,哪些参数是互斥的。

3、由于spring配置依赖于服务的实现,所以当服务的实现改变时,spring配置会失败。

以spring scheme 方式装配ResourceLoadingService

Spring 2.0以后,开始支持了xml schema来定义配置,用Spring Schema来配置ResourceLoadingService会变成下面的样子:

[html]  view plain copy
  1. <resource-loading id="resourceLoadingService" xmlns="http://www.alibaba.com/schema/services/resource-loading">  
  2.      <resource pattern="/file">  
  3.           <file-loader basedir="${user.home}" />  
  4.      </resource>  
  5.      <resource pattern="/webroot">  
  6.           <webapp-loader />  
  7.      </resource>  
  8. </resource-loading>  

相对于spring bean定义方式,spring schema方式有以下优点:

1、更易读,配置描述的是服务提供的内容,没有了像bean、property之类的编程术语。

2、具有了检验机制,任何支持xml schema的xml编辑器都可以告诉你配置是否有错误。

3、包含了更多约束,xml schema可以告诉你哪些参数是可选的,哪些参数是必填的。

4、最重要的一点,服务的实现对装配者来说是透明的,只要xml schema不变,即使服务实现发生了变化,也不用修改xml配置。

最后一点实现是通过schema解释器来实现的,服务提供都会负责提供这个解释器,它的作用就是把xml schema配置翻译成spring bean配置。

将具体的实现交给服务提供者,使服务使用者可以用它所能理解的语言来装配服务,这就是spring schema的核心价值。

spring schema有一个很大的缺点:无法扩展。假设你想在上面的例子中添加一个新的资源加载器database-loader,由于在设计schema时没有考虑这种情况,加上去之后会报错,除非你修改schema,而这显然违反了OCP原则。

以springext schema方式装配ResourceLoadingService

SpringExt扩展了Spring,使Spring Schema具有了扩展能力,如果用SpringExt来定义相同的配置会是下面的样子:

[html]  view plain copy
  1. <resource-loading id="resourceLoadingService"  
  2.      xmlns="http://www.alibaba.com/schema/services"  
  3.      xmlns:loaders="http:www.alibaba.com/schema/services/resource-loading/loaders">  
  4.      <resource pattern="/file">  
  5.           <loaders:file-loader basedir="${user.home}" />  
  6.      </resource>  
  7.      <resource>  
  8.           <loaders:webapp-loader />  
  9.      </resource>  
  10. </resource-loading>  

如果要添加一种新的ResourceLoader--DatabaseResourceLoader,只需要两步:

1、配置maven依赖

2、在配置文件中添加配置项如下:

<resource>

     <loaders:database-loader connection="jdbc:mysql:mydb" />

</resource>


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值