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接口,在渲染模板之前增加自己的扩展,如:
1
2
|
RESOURCE.root=/WEB-INF/views
WEBAPP_EXT = com.park.oss.util.GlobalExt
|
1
2
3
4
5
6
7
8
9
10
|
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框架集成功能不够,你可以继承此类,或者参考此类源码重新写,其代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
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<String> 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)方法。例子如下:
1
2
3
4
5
6
7
8
9
10
|
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集成
需要做如下配置即可
1
2
3
4
5
6
|
|
同其他集成方式一样,模板的配置将放在beetl.properties中。
如果想获取GroupTemplate,可以调用如下代码
1
2
3
|
BeetlGroupUtilConfiguration config = (BeetlGroupUtilConfiguration) this.getApplicationContext().getBean(
"beetlConfig");
GroupTemplate group = config.getGroupTemplate();
|
Controller代码如下:
1
2
3
4
5
6
7
|
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(HttpServletRequest req) {
ModelAndView view = new ModelAndView("/index");
//total 是模板的全局变量,可以直接访问
view.addObject("total",service.getCount());
return view;
}
|
http://git.oschina.net/xiandafu/springbeetlsql 有完整例子
4.5. SpringMVC集成高级
spring集成还允许注册被spring容器管理的Function,Tag等,也还允许配置多个视图解析器等功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
|
如上图所示,BeetlGroupUtilConfiguration有很多属性,列举如下
-
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"。如代码:
1
2
3
4
|
@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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
|
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,参考代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
@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文件,并配置如下选项
1
2
|
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处理,参考:http://jodd.org/doc/madvoc/setup.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
|
MyWebApplication 和 MyAutomagicMadvocConfigurator 需要自己参照如下例子写一个,前者用来设置beetl作为视图渲染,后者配置Jodd不要扫描beetl struts集成里引用的struts类
1
2
3
4
5
6
7
8
9
|
public class MyAutomagicMadvocConfigurator extends AutomagicMadvocConfigurator {
public MyAutomagicMadvocConfigurator(){
super();
//不扫描beetl 里jar文件里的action和result,否则,会扫描StrutsResultSupport不相干的class
this.rulesJars.exclude("**/*beetl*.jar");
}
}
|
1
2
3
4
5
6
7
8
|
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 即可。
1
2
3
4
5
6
7
8
9
10
11
12
|
@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模板引擎
1
2
3
4
5
6
7
8
9
10
11
|
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 ;
}
|
业务逻辑代码:
1
2
3
4
5
6
7
8
9
|
public void modify(){
int artId = getParaToInt(0, -1);
setAttr("title", "修改文章");
List<Cate> 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方法,如下代码
1
2
3
4
5
6
7
8
9
10
11
|
@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<UserProfile> list = dao.query(UserProfile.class, null, pager);
ctx.set("pager", pager);
ctx.set("list", list);
return ctx;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
|
需要注意的是,如果使用了nutz的obj(http://www.nutzam.com/core/mvc/view.html),则需要在模板顶部申明obj是动态对象,如
1
2
3
4
5
6
|
<%
directive dynamic obj
%>
${obj.user.title}
${obj.user.name}
|
4.10. Struts2集成
需要在struts2配置文件里添加result-types做如下配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
|
该类会根据struts配置文件获取模板,如上例的hello.html,并将formbean的属性,以及request属性作为全局变量传递给模板
https://git.oschina.net/xiandafu/beetl-struts2-sample 有完整例子
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可以申明这些些变量
1
2
3
|
var proudct = {id:1,name:'测试产品',pic:'xxxx.jpg'};
var userList = [{id:2,name:'用户一'}];
var session= {admin:{id:1,name:'admin'}};
|
-
通过浏览器直接访问http://ip:port/user/userList.html,监听器会预先执行userList.html.var,并将返回值作为模板的全局变量,传给userList.html
-
可以将一些公共的变量放到WebRoot/values/common.var里(比如上面代码的session). 监听器会先执行common.var,然后再执行userList.html.var
4.12. 整合ajax的局部渲染技术
越来越多web网站依赖于ajax,如table的翻页,流行方式是浏览器发出ajax请求,后台处理后返回一个json,浏览器端将json数据拆开,拼成一条一条的行数据,然后生成dom节点,追加到表格里。 作为另外一种可选技术,beetl支持局部渲染技术,允许后台处理返回的是一个完成的html片段,这样,前端浏览器可以直接将这个html片段追加到表格里。在我做的性能测试里,俩种方式性能差别不大(http://beetlajax.oschina.mopaas.com/)
比如模板index.html有很多动态内容,有动态生成的菜单,有右侧的top10,也有核心区域的表格,大概内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<#menu/>
<#top10> ....</#top10>
|
#ajax 用于告诉告诉模板引擎,此处是个局部渲染标记,标记为"userTable",对于正常渲染视图"index.html"页面,#ajax标记没什么用处,table仍能得到正常渲染。如果渲染的视图是index.html#userTable,则模板只会渲染#ajax标记得模板片段,其他部分将忽略。关于完整例子,可以参考http://beetlajax.oschina.mopaas.com/
后台代码如下:
1
|
render("/index.html#userTable");
|
ajax 片段渲染也支持默认情况下不渲染,仅仅做为一个片段使用,如一个页面有许多后台交互操作,并返回相应的html片段,可以将这些html片段也放到同一个模板里,使用ajax norender,表示渲染整个模板的时候默认并不需要渲染此ajax片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<%
#ajax norender success: {
%>
|
这样,此页面默认情况下并没有输出success,和 failure片段
4.13. 在页面输出错误提示信息
2.2.3版本以后,新增加org.beetl.ext.web.WebErrorHandler,可以在web开发的时候在页面输出提示信息,在产品模式下载后台输出提示信息(通过配置属性ESOURCE.autoCheck= true来认为是开发模式),仅仅需要配置如下:
1
|
ERROR_HANDLER = org.beetl.ext.web.WebErrorHandler
|