title: jsp改造之sitemesh注意事项 tags:
- sitemesh
- jsp
- html
- body categories: 工作日志 date: 2017-12-23 10:50:38
背景
- 现在各种现代化的浏览器确实惯坏了开发者 智能纠错 无论是忘记关闭标签甚至重复等等都有可能被chrome这些浏览器智能纠错===》chrome会合并多个body
- 使用jsp开发的小伙伴新开一个新的组件页面时可能从原先的页面拷贝而来 那么里面包含了如下一些
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="首页">
<jsp:include page="${path}/WEB-INF/page/common/reference.jsp"/>
<script type="text/javascript" src="${staticPath}/js/index/index.js"></script>
<script type="text/javascript" src="${staticPath}/js/index/scroll.js"></script>
<script type="text/javascript" src="<%=path%>/plugins/slider/slider.js"></script>
<script type="text/javascript">
var sessionIdOrg = "${sessionScope.organization.pkId}";
</script>
</head>
<body class="mainBody">
</body>
</html>
复制代码
复制代码
这样造成一个页面从jsp处理完毕后输出的响应流会包含多个
可以参考问题
多个body对于chrome来说会做自动忽略,那么对于我们使用sitemesh呢???
这个问题自然是存在的 否则也就不会有这篇博客了!
sitemesh的工作原理很单纯 使用filter对响应进行拦截,如果返回的是html那么判断是否需要进行装饰。如果需要找到合适的装饰模板进行装饰 并返回新的响应!
代码
对于sitemesh 默认来说会注入规则CoreHtmlTagRuleBundle
public void install(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
// Core rules for SiteMesh to be functional.
defaultState.addRule("head", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("head"), false));
defaultState.addRule("title", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("title"), false));
defaultState.addRule("body", new ExportTagToContentRule(siteMeshContext, contentProperty.getChild("body"), false));
defaultState.addRule("meta", new MetaTagRule(contentProperty.getChild("meta")));
// Ensure that while in <xml> tag, none of the other rules kick in.
// For example <xml><book><title>hello</title></book></xml> should not affect the title of the page.
defaultState.addRule("xml", new StateTransitionRule(new State()));
}
public void cleanUp(State defaultState, ContentProperty contentProperty, SiteMeshContext siteMeshContext) {
// In the event that no <body> tag was captured, use the default buffer contents instead
// (i.e. the whole document, except anything that was written to other buffers).
if (!contentProperty.getChild("body").hasValue()) {
contentProperty.getChild("body").setValue(contentProperty.getValue());
}
}
复制代码
从这边可以看出body使用的规则是ExportTagToContentRule
我们细看一下ExportTagToContentRulede的源码
/**
* Exports the contents of a match tag to property of the passed in {@link ContentProperty}.
*
* Additionally, if this tag has attributes, they will be written as child properties.
*
* <h3>Example</h3>
*
* <pre>
* // Java
* myState.addRule("foo", new ExportTagToContentRule(content, "bar");
*
* // Input
* <foo x=1 b=2>hello</foo>
*
* // Exported properties of Content
* bar=hello
* bar.x=1
* bar.b=2
* </pre>
*
* @author Joe Walnes
*/
public class ExportTagToContentRule extends BasicBlockRule {
private final ContentProperty targetProperty;
private final boolean includeInContent;
private final SiteMeshContext context;
;
/**
* @param targetProperty ContentProperty to export tag contents to.
* @param includeInContent Whether the tag should be included in the content (if false, it will be stripped
* from the current ContentProperty that is being written to.
* @see ExportTagToContentRule
*/
public ExportTagToContentRule(SiteMeshContext context, ContentProperty targetProperty, boolean includeInContent) {
this.targetProperty = targetProperty;
this.includeInContent = includeInContent;
this.context = context;
}
@Override
protected Object processStart(Tag tag) throws IOException {
// Some terminology:
// Given a tag: '<foo>hello</foo>'
// INNER contents refers to 'hello'
// OUTER contents refers to '<foo>hello</foo>'
Tag t = tag;
// Export all attributes of the opening tag as child nodes on the target ContentProperty.
for (int i = 0; i < t.getAttributeCount(); i++) {
// attributes of tags using this rule doesn't expand sitemesh:write
// https://github.com/sitemesh/sitemesh3/issues/23
String value = t.getAttributeValue(i);
// only if there might be another tag inside the attribute
if(value != null && (value.indexOf('<') < value.indexOf('>'))){
StringBuilder sb = new StringBuilder();
context.getContentProcessor().build(CharBuffer.wrap(value), context).getData().writeValueTo(sb);
value = sb.toString();
if(!(t instanceof CustomTag)){
t = new CustomTag(t);
}
CustomTag custom = (CustomTag) t;
custom.setAttributeValue(i, value);
}
targetProperty.getChild(t.getAttributeName(i)).setValue(value);
}
// Push a buffer for the OUTER contents.
if (!includeInContent) {
// If the tag should NOT be included in the contents, we use a data-only buffer,
// which means that although the contents won't be written
// back to the ContentProperty, they will be available in the main Content data.
// See Content.createDataOnlyBuffer()
tagProcessorContext.pushBuffer(targetProperty.getOwningContent().createDataOnlyBuffer());
} else {
tagProcessorContext.pushBuffer();
}
// Write opening tag to OUTER buffer.
t.writeTo(tagProcessorContext.currentBuffer());
// Push a new buffer for storing the INNER contents.
tagProcessorContext.pushBuffer();
return null;
}
@Override
protected void processEnd(Tag tag, Object data) throws IOException {
// Get INNER content, and pop the buffer for INNER contents.
CharSequence innerContent = tagProcessorContext.currentBufferContents();
tagProcessorContext.popBuffer();
// Write the INNER content and closing tag, to OUTER buffer and pop it.
tagProcessorContext.currentBuffer().append(innerContent);
if (tag.getType() != Tag.Type.EMPTY) { // if the tag is empty we have already written it in processStart().
tag.writeTo(tagProcessorContext.currentBuffer());
}
CharSequence outerContent = tagProcessorContext.currentBufferContents();
tagProcessorContext.popBuffer();
// Write the OUTER contents to the current buffer, which is now the buffer before the
// tag was processed. Note that if !includeInContent, this buffer will not be written
// to the ContentProperty (though it will be available in Content.getData()).
// See comment in processStart().
tagProcessorContext.currentBuffer().append(outerContent);
// Export the tag's inner contents to
if (!targetProperty.hasValue()) {
targetProperty.setValue(innerContent);
}
}
}
复制代码
可以看到export的最后做了判断为
if (!targetProperty.hasValue()) {
targetProperty.setValue(innerContent);
}
复制代码
可以看到当页面上存在多个body标签时sitemesh只会输出第一个body里面的内容!!!
措施
虽然chrome如此智能,虽然我们也不用考虑对其他老旧慢浏览器的支持。但是对于一个合格的后端开发来说
html的输出格式还是要注意的。因此考虑将多余的body删除【大量引入popup的jsp的都包含了一整套的html body】
那么需要清除一些jsp中的多余的body
建议
新建的jsp如果不是页面【是指将会被include其他的jsp中】那么而建议用_打头
比如一个关于左侧导航的可以考虑使用_left.jsp
以_打头的jsp中不允许出现html body 等标签
小问题
有小伙伴发现sitemesh的script提取 也有同样的问题么?^_^