您现在的位置:首页 >> 前端 >> 内容

Beetl学习总结(4)——Web集成

时间:2016/11/10 10:01:00 点击:

  核心提示:4.1. Web提供的全局变量Web集成模块向模板提供web标准的变量,做如下说明request 中的所有attribute.在模板中可以直接通过attribute name 来引用,如在contro...

4.1. Web提供的全局变量

Web集成模块向模板提供web标准的变量,做如下说明

request 中的所有attribute.在模板中可以直接通过attribute name 来引用,如在controller层 request.setAttribute("user",user),则在模板中可以直接用${user.name} .

session 提供了session会话,模板通过session["name"],或者session.name 引用session里的变量

request 标准的HTTPSerlvetRequest,可以在模板里引用request属性(getter),如${request.requestURL}。

parameter 用户读取用户提交的参数。如${parameter.userId} (仅仅2.2.7以上版本支持)

ctxPath Web应用ContextPath

servlet 是WebVariable的实例,包含了HTTPSession,HTTPSerlvetRequest,HTTPSerlvetResponse.三个属性,模板中可以通过request.response,session 来引用,如 ${serlvet.request.requestURL};

所有的GroupTemplate的共享变量

pageCtx是一个内置方法 ,仅仅在web开发中,用于设置一个变量,然后可以在页面渲染过程中,调用此api获取,如pageCtx("title","用户添加页面"),在其后任何地方,可以pageCtx("title") 获取该变量。(仅仅2.2.7以上版本支持)

你可以在模板任何地方访问这些变量

如果你需要扩展更多属性,你也可以配置beetl.properties配置文件的WEBAPP_EXT属性,实现WebRenderExt接口,在渲染模板之前增加自己的扩展,如:

RESOURCE.root=/WEB-INF/views

WEBAPP_EXT = com.park.oss.util.GlobalExt

public class GlobalExt implements WebRenderExt{

static long version = System.currentTimeMillis();

@Override

public void modify(Template template, GroupTemplate arg1, HttpServletRequest arg2, HttpServletResponse arg3) {

//js,css 的版本编号

template.binding("sysVersion",version);

}

}

这样,每次在模板里都可以访问变量sysVersion了,不需要再controller里设置,或者通过servlet filter来设置

4.2. 集成技术开发指南

Beetl默认提供了WebRender用于帮助web集成开发,所有内置的集成均基于此方法。如果你认为Beetl内置的各个web框架集成功能不够,你可以继承此类,或者参考此类源码重新写,其代码如下

package org.beetl.ext.web;

import java.io.IOException;

import java.io.OutputStream;

import java.io.Writer;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.beetl.core.GroupTemplate;

import org.beetl.core.Template;

import org.beetl.core.exception.BeetlException;

/**

* 通常web渲染的类,将request变量赋值给模板,同时赋值的还有session,request,ctxPath

* 其他框架可以继承此类做更多的定制

* @author joelli

*

*/

public class WebRender

{

GroupTemplate gt = null;

public WebRender(GroupTemplate gt)

{

this.gt = gt;

}

/**

* @param key 模板资源id

* @param request

* @param response

* @param args 其他参数,将会传给modifyTemplate方法

*/

public void render(String key, HttpServletRequest request, HttpServletResponse response, Object... args)

{

Writer writer = null;

OutputStream os = null;

try

{

// response.setContentType(contentType);

Template template = gt.getTemplate(key);

Enumeration attrs = request.getAttributeNames();

while (attrs.hasMoreElements())

{

String attrName = attrs.nextElement();

template.binding(attrName, request.getAttribute(attrName));

}

WebVariable webVariable = new WebVariable();

webVariable.setRequest(request);

webVariable.setResponse(response);

webVariable.setSession(request.getSession());

template.binding("session", new SessionWrapper(webVariable.getSession()));

template.binding("servlet", webVariable);

template.binding("request", request);

template.binding("ctxPath", request.getContextPath());

modifyTemplate(template, key, request, response, args);

String strWebAppExt = gt.getConf().getWebAppExt();

if(strWebAppExt!=null){

WebRenderExt renderExt = this.getWebRenderExt(strWebAppExt);

renderExt.modify(template, gt, request, response);

}

if (gt.getConf().isDirectByteOutput())

{

os = response.getOutputStream();

template.renderTo(os);

}

else

{

writer = response.getWriter();

template.renderTo(writer);

}

}

catch (IOException e)

{

handleClientError(e);

}

catch (BeetlException e)

{

handleBeetlException(e);

}

finally

{

try

{

if (writer != null)

writer.flush();

if (os != null)

{

os.flush();

}

}

catch (IOException e)

{

handleClientError(e);

}

}

}

/**

* 可以添加更多的绑定

* @param template 模板

* @param key 模板的资源id

* @param request

* @param response

* @param args 调用render的时候传的参数

*/

protected void modifyTemplate(Template template, String key, HttpServletRequest request,

HttpServletResponse response, Object... args)

{

}

/**处理客户端抛出的IO异常

* @param ex

*/

protected void handleClientError(IOException ex)

{

//do nothing

}

/**处理客户端抛出的IO异常

* @param ex

*/

protected void handleBeetlException(BeetlException ex)

{

throw ex;

}

}

4.3. Serlvet集成

只需要在Servlet代码里引用ServletGroupTemplate就能集成Beetl,他提供了一个render(String child, HttpServletRequest request, HttpServletResponse response)方法。例子如下:

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html;charset=UTF-8");

//模板直接访问users

request.setAttribute("users",service.getUsers());

ServletGroupTemplate.instance().render("/index.html", request, response);

}

ServletGroupTemplate同其他web集成一样,将读取配置文件来配置,如果需要通过代码配置,可以在Serlvet listener里 ServletGroupTemplate.instance().getGroupTemplate()方法获取GroupTemplate

4.4. SpringMVC集成

需要做如下配置即可

同其他集成方式一样,模板的配置将放在beetl.properties中。

如果想获取GroupTemplate,可以调用如下代码

BeetlGroupUtilConfiguration config = (BeetlGroupUtilConfiguration) this.getApplicationContext().getBean(

"beetlConfig");

GroupTemplate group = config.getGroupTemplate();

Controller代码如下:

@RequestMapping(value = "/", method = RequestMethod.GET)

public ModelAndView index(HttpServletRequest req) {

ModelAndView view = new ModelAndView("/index");

//total 是模板的全局变量,可以直接访问

view.addObject("total",service.getCount());

return view;

}

https://git.oschina.net/xiandafu/springbeetlsql有完整例子

4.5. SpringMVC集成高级

spring集成还允许注册被spring容器管理的Function,Tag等,也还允许配置多个视图解析器等功能

如上图所示,BeetlGroupUtilConfiguration有很多属性,列举如下

总共 ${list.~size}

hello,${user.nickname};

当前页${pager.pageNumber},总共${pager.pageCount}页

configFileResource 属性指定了配置文件所在路径,如果不指定,则默认在classpath下

functions 指定了被spring容器管理的function,key为注册的方法名,value-ref 指定的bean的名称

functionPackages,指定了被spring容器管理的functionPackage,key为注册的方法包名,value-ref 指定的bean的名称

tagFactorys ,注册tag类,key是tag类的名称,value-ref指向一个org.beetl.ext.spring.SpringBeanTagFactory实例,该子类是一个Spring管理的Bean。属性name对应的bean就是tag类。需要注意,由于Tag是有状态的,因此,必须申明Scope为 "prototype"。如代码:

@Service

@Scope("prototype")

public class TestTag extends Tag {

}

typeFormats: 同functions,参数是 Map, Format>,其中key为类型Class

formats:同functions,参数是 Map,其中key为格式化函数名

virtualClassAttributes 同functions,参数Map, VirtualClassAttribute>,其中key为类型Class

virtualAttributeEvals ,类型为List

resourceLoader,资源加载器 ,值是 实现ResourceLoader的一个Bean

errorHandler ,错误处理,值是实现ErrorHandler的一个Bean

sharedVars,同functions,类型是Map,可以在此设置共享变量

configProperties,类型是Properties,可以覆盖配置文件的某些属性

如下配置,指定了三个视图解析器,一个用于beetl页面渲染,一个用于cms,采用了beetl技术,另外一个一些遗留的页面采用jsp

/template/**

/cmstemplate/**

Beetl视图解析器属性同spring自带的视图解析器一样,支持contentType,order,prefix,suffix等属性。

注意视图解析器里属性viewNames,这个用于判断controller返回的path到底应该交给哪个视图解析器来做。

以/template开头的是beetlViewResolver来渲染。

以/cmstemplate是交给cmsBeetlViewResolver渲染。

如果都没有匹配上,则是jsp渲染

如果你想更改此规则,你只能增加canHandle方法指定你的逻辑了。详情参考org.springframework.web.servlet.view.UrlBasedViewResolver.canHandle

对于仅仅需要redirect和forward的那些请求,需要加上相应的前缀

以"redirect:"为前缀时:表示重定向,不产生BeetlView渲染模版,而直接通过Servlet的机制返回重定向响应.redirect:前缀后面的内容为重定向地址,可以采用相对地址(相对当前url),绝对地址(完整的url),如果采用/开头的地址,会自动的在前面接上当前Web应用的contextPath,即contextPath为test的Web应用中使用redirect:/admin/login.html 实际重定向地址为 /test/admin/login.html

以"forward:"为前缀时:表示转发,不产生BeetlView渲染模版。而是直接通过Servlet的机制转发请求(关于转发和重定向的区别,请自行查看Servlet API) forward:前缀后面的内容为转发地址,一般都是以/开头相对于当前Web应用的根目录

其他集成需要注意的事项:

spring集成,请不要使用spring的 前缀配置,改用beetl的RESOURCE.ROOT 配置,否则include,layout会找不到模板

4.6. Spring Boot集成

Spring Boot 通过java config来配置 beetl需要的BeetlGroupUtilConfiguration,和 BeetlSpringViewResolver,参考代码如下

@Configuration

public class BeetlConf {

@Value("${beetl.templatesPath}") String templatesPath;//模板跟目录

@Bean(initMethod = "init", name = "beetlConfig")

public BeetlGroupUtilConfiguration getBeetlGroupUtilConfiguration() {

BeetlGroupUtilConfiguration beetlGroupUtilConfiguration = new BeetlGroupUtilConfiguration();

try {

ClasspathResourceLoader cploder = new ClasspathResourceLoader(BeetlConf.class.getClassLoader(),templatesPath);

beetlGroupUtilConfiguration.setResourceLoader(cploder);

return beetlGroupUtilConfiguration;

} catch (Exception e) {

throw new RuntimeException(e);

}

}

@Bean(name = "beetlViewResolver")

public BeetlSpringViewResolver getBeetlSpringViewResolver(@Qualifier("beetlConfig") BeetlGroupUtilConfiguration beetlGroupUtilConfiguration) {

BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver();

beetlSpringViewResolver.setContentType("text/html;charset=UTF-8");

beetlSpringViewResolver.setOrder(0);

beetlSpringViewResolver.setConfig(beetlGroupUtilConfiguration);

return beetlSpringViewResolver;

}

}

spring boot集成需要注意的是要添加spring-devtools.properties文件,并配置如下选项

restart.include.beetl=/beetl-xxx.jar

restart.include.beetlsql=/beetlsql-xxx..jar

spring-devtools.properties 为spring boot的配置文件,位于META-INF目录下

4.7. Jodd集成

需要配置web.xml,将所有请求交给jodd处理,参考:https://jodd.org/doc/madvoc/setup.html

madvoc

jodd.madvoc.MadvocServletFilter

madvoc.webapp

test.MyWebApplication

madvoc.configurator

test.MyAutomagicMadvocConfigurator

madvoc

/*

MyWebApplication 和 MyAutomagicMadvocConfigurator 需要自己参照如下例子写一个,前者用来设置beetl作为视图渲染,后者配置Jodd不要扫描beetl struts集成里引用的struts类

public class MyAutomagicMadvocConfigurator extends AutomagicMadvocConfigurator {

public MyAutomagicMadvocConfigurator(){

super();

//不扫描beetl 里jar文件里的action和result,否则,会扫描StrutsResultSupport不相干的class

this.rulesJars.exclude("**/*beetl*.jar");

}

}

public class MyWebApplication extends WebApplication{

@Override

protected void init(MadvocConfig madvocConfig, ServletContext servletContext) {

//设置默认

madvocConfig.setDefaultActionResult(BeetlActionResult.class);

}

}

最后,可以写Action了,浏览器输入/index.html,jodd将执行world方法,并渲染ok.html模板。如果你想配置GroupTemplate,正如其他集成框架一样,只需要写一个beetl.properties 即可。

@MadvocAction

public class IndexAction {

@Out

String value;

@Action("/index.html")

public String world() {

value = "Hello World!";

return "/ok.html";

}

}

https://git.oschina.net/xiandafu/beetl-jodd-sample有完整例子

4.8. JFinal集成

Beetl提供 JFinal 集成,使用BeetlRenderFactory ,通过如下注册即可使用beetl模板引擎

import org.beetl.ext.jfinal.BeetlRenderFactory

public class DemoConfig extends JFinalConfig

{

public void configConstant(Constants me)

{

me.setMainRenderFactory(new BeetlRenderFactory());

// 获取GroupTemplate ,可以设置共享变量等操作

GroupTemplate groupTemplate = BeetlRenderFactory.groupTemplate ;

}

业务逻辑代码:

public void modify(){

int artId = getParaToInt(0, -1);

setAttr("title", "修改文章");

List cateLists = Cate.getAllCate();

//模板里访问cateLists,atr,

setAttr("cateLists", cateLists);

setAttr("art", Article.dao.findById(artId));

render("/modify.html");

}

BeetlRenderFactory 默认使用FileResourceLoader ,其根目录位于WebRoot目录下,如果你需要修改到别的目录,可以设置配置文件,如

1

RESOURCE.root= /WEB-INF/template/

https://git.oschina.net/xiandafu/beetl-jfinal-sample有完整例子,采用jfinal+beetl写的一个博客系统

4.9. Nutz集成

Nutz集成提供了 BeetlViewMaker ,实现了 ViewMaker方法,如下代码

@At("/ctx")

@Ok("beetl:ctx.btl")

public Context withContext() {

Context ctx = Lang.context();

Pager pager = dao.createPager(1, 20);

pager.setRecordCount(dao.count(UserProfile.class));

List list = dao.query(UserProfile.class, null, pager);

ctx.set("pager", pager);

ctx.set("list", list);

return ctx;

}

需要注意的是,如果使用了nutz的obj(https://www.nutzam.com/core/mvc/view.html),则需要在模板顶部申明obj是动态对象,如

${obj.user.title}

${obj.user.name}

4.10. Struts2集成

需要在struts2配置文件里添加result-types做如下配置

.......

text/html; charset=UTF-8

/hello.html

/table.html#table

........

该类会根据struts配置文件获取模板,如上例的hello.html,并将formbean的属性,以及request属性作为全局变量传递给模板

https://git.oschina.net/xiandafu/beetl-struts2-sample有完整例子

郑重申明

鉴于struts2有安全漏洞,而官方补丁打法很消极,所以请谨慎使用Struts2,Beetl的安全性已经通过在线体验和多个使用Beetl的网站得以体现 一旦你的struts2网站被攻破,请先确定是否是struts2 的问题

4.11. 直接Web中运行Beetl模板

对于web应用来说,必须通过controller才能渲染模板,beetl也可以写完模板后,在未完成controller情况下,直接渲染模板 此方法既可以作为通常的全栈式开发人员使用,也可以用于前端人员单独开发模板用。

步骤如下:

配置监听器,监听器指定对*.btl的请求进行监听(假定模板名字都是以btl.结尾)。

实现监听器,该监听器继承父类 org.beetl.ext.web.SimpleCrossFilter,实现protected abstract GroupTemplate getGroupTemplate()方法。依据不同的集成方式,比如你的环境是Servlet,则只需要调用ServletGroupTemplate.instance().getGroupTemplate(),如果是Jfinal,需要调用BeetlRenderFactory.groupTemplate等

SimpleCrossFilter 提供一些有用的方法,可以帮助你定制一些特性,可以参考源码了解

置完成后,对于要测试的模板,可以新建一个对应的伪模型文件,比如要测试模板WebRoot/user/userList.html,可以新建立WebRoot/values/user/userList.html.var 。 values是监听器默认的伪模型的根目录

编辑伪模型文件,对应于userList.html需要的全局变量,userList.html.var可以申明这些些变量

var proudct = {id:1,name:'测试产品',pic:'xxxx.jpg'};

var userList = [{id:2,name:'用户一'}];

var session= {admin:{id:1,name:'admin'}};

通过浏览器直接访问https://ip:port/user/userList.html,监听器会预先执行userList.html.var,并将返回值作为模板的全局变量,传给userList.html

可以将一些公共的变量放到WebRoot/values/common.var里(比如上面代码的session). 监听器会先执行common.var,然后再执行userList.html.var

直接访问模板前提是使用了伪模型,这与实际的项目采用的模型并不一致,因此当模板采用伪模型验证后,需要重启web应用,才能使用真正的模型去测试,否则,模板引擎会报错,这是因为beetl默认的FastRuntimeEngine会根据模型优化模板,对同一个模板不同的模型会报错,除非采用DefaultTemplateEngine 或者页面申明类型变量是动态的。

4.12. 整合ajax的局部渲染技术

越来越多web网站依赖于ajax,如table的翻页,流行方式是浏览器发出ajax请求,后台处理后返回一个json,浏览器端将json数据拆开,拼成一条一条的行数据,然后生成dom节点,追加到表格里。 作为另外一种可选技术,beetl支持局部渲染技术,允许后台处理返回的是一个完成的html片段,这样,前端浏览器可以直接将这个html片段追加到表格里。在我做的性能测试里,俩种方式性能差别不大(https://beetlajax.oschina.mopaas.com/)

比如模板index.html有很多动态内容,有动态生成的菜单,有右侧的top10,也有核心区域的表格,大概内容如下

<#menu/>

<#top10> ....

id姓名

${user.id}${user.name}

当前页面${page!1} next pre

#ajax 用于告诉告诉模板引擎,此处是个局部渲染标记,标记为"userTable",对于正常渲染视图"index.html"页面,#ajax标记没什么用处,table仍能得到正常渲染。如果渲染的视图是index.html#userTable,则模板只会渲染#ajax标记得模板片段,其他部分将忽略。关于完整例子,可以参考https://beetlajax.oschina.mopaas.com/

后台代码如下:

render("/index.html#userTable");

ajax 片段渲染也支持默认情况下不渲染,仅仅做为一个片段使用,如一个页面有许多后台交互操作,并返回相应的html片段,可以将这些html片段也放到同一个模板里,使用ajax norender,表示渲染整个模板的时候默认并不需要渲染此ajax片段

操作成功

#ajax norender failure: { %>

操作失败

这样,此页面默认情况下并没有输出success,和 failure片段

注意,Ajax片段本质上是从模版的ajax标记处开始渲染,因此,ajax需要的变量在模版里也必须是全局变量,如果你只是个局部变量,beetl会报出找不到变量,即使你binding了这个变量,beetl也认为这个是局部变量,如:

" >

变量tableData是从paras里获取的,是个临时变量,因此就算你在后台binding了一个tableData,beetl 也不能识别。在渲染ajax片段的时候会报变量tableData找不到。改正的办法只能是让tableData全局变量。

返回Json好还是返回html片段好?这个难以定论.

从后台性能看,将模型序列化成json性能会比渲染模板性能更好,但是,json还需要前端重新解析生成最终html dom节点,这可能会延迟最终数据的现实效果。而返回的html片段就是已经生成好的dom

从网络传入来看,json无疑更好的,html片段会有额外的html标记,css属性,以及有可能的js调用。传入流量有可能增加50%到100%。但是,对于web应用类,这些额外数据,并不算多。

从开发效率来讲,返回html片段的开发效率更高一些,因为渲染在后台操作,可以随心所欲的用模板语言来渲染,来取得后台数据,完成复杂渲染,而json就比较困难,可以说所有的json lib都没有完美的解决办法。

从用户体验上来讲,Beetl 采用ajax标记,混合了传统的模板渲染和ajax加载。用户进入页面即能看到数据,而经典的ajax json方式还需要异步加载,显示延迟。另外如果页面同时有多个ajax加载,则会对服务器造成很大的压力。

关心服务器cpu消耗? 模板方式消耗更多的cpu,json方式则少点。但是俩者差距并不大。而且更多的web网站面临的情况是有富余的服务器CPU能力

关心客户端CPU消耗? 过多的js无疑是客户端运行慢的主要原因。如果采用经典的json方式,返回的json数据必然还需要经过js的计算和渲染。会影响客户机器cpu。

4.13. 在页面输出错误提示信息

2.2.3版本以后,新增加org.beetl.ext.web.WebErrorHandler,可以在web开发的时候在页面输出提示信息,在产品模式下载后台输出提示信息(通过配置属性ESOURCE.autoCheck= true来认为是开发模式),仅仅需要配置如下:

ERROR_HANDLER = org.beetl.ext.web.WebErrorHandler

Tags:BE EE ET TL 
作者:网络 来源:一杯甜酒