如何整合与瓷砖Struts的约定,同时保持规范的好处?
问题是,公约自动链接URL到行动到结果,并做到这一点很好地对JSP,Velocity和FreeMarker结果。 它不指望对付瓷砖的结果。
当使用瓷砖我们通常希望我们所有的UI操作(如反对JSON / XML服务行动)使用的瓷砖,但我们这样做失去了约定的结果成分,需要使用注解。 注释让我们从预期的偏离,但在大型应用程序期望使用的瓷砖,这是一个烦恼的时候。 进一步规范允许我们通过只指定一个视图中创建行动。 我们想用瓷砖时也保持这样的好处。 为了纠正这一点,我们需要建立承载虽然到瓷砖的惯例导致这样的,我们并不需要使用注释,以配合行动,以瓷砖结果,而我们可以继续不动作类将获得的好处创建的JSP公约(无XML)和瓷砖的好处(所有锅炉板被分解成砖)。
如何实现这一目标?
这是一个自答案去帮助别人谁愿意解决这个问题
下面是所需的步骤:
- 创建自定义的瓷砖导致其动态地构建一个“位置”的字符串(位置字符串是传递给瓷砖的价值),这考虑到了命名空间,actionName。
- 创建一个使用这个结果(命名为“瓦”)的软件包,并有约定使用,因为它的父包
- 实现并注册了“com.opensymphony.xwork2.UnknownHandler”,这一步是最关键的时候,结果不能得到解决这个处理程序被调用
- 瓦片定义(一个或多个),其利用“所在地”从第一步骤中通过
上述步骤在需要的struts.xml以下
<struts>
<constant name="struts.convention.default.parent.package" value="tiles-package"/>
<bean type="com.opensymphony.xwork2.UnknownHandler" name="tilesUnknownHandler" class="com.kenmcwilliams.tiles.result.TilesUnknownHandler"/>
<package name="tiles-package" extends="convention-default">
<result-types>
<result-type default="true" name="tiles" class="com.kenmcwilliams.tiles.result.TilesResult"/>
</result-types>
</package>
</struts>
自定义结果类型的实现:
package com.kenmcwilliams.tiles.result;
import com.opensymphony.xwork2.ActionInvocation;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.ServletDispatcherResult;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.servlet.ServletRequest;
import org.apache.tiles.request.servlet.ServletUtil;
public class TilesResult extends ServletDispatcherResult {
private static final Logger log = Logger.getLogger(TilesResult.class.getName());
public TilesResult() {
super();
}
public TilesResult(String location) {
super(location);
}
@Override
public void doExecute(String location, ActionInvocation invocation) throws Exception {
//location = "test.definition"; //for test
log.log(Level.INFO, "TilesResult doExecute() location: {0}", location);
//Start simple conventions
//
if (/** tiles && **/location == null) {
String namespace = invocation.getProxy().getNamespace();
String actionName = invocation.getProxy().getActionName();
location = namespace + "#" + actionName + ".jsp"; //Warning forcing extension
log.log(Level.INFO, "TilesResult namespace: {0}", namespace);
log.log(Level.INFO, "TilesResult actionName: {0}", actionName);
log.log(Level.INFO, "TilesResult location: {0}", location);
}
//End simple conventions
setLocation(location);
ServletContext context = ServletActionContext.getServletContext();
ApplicationContext applicationContext = ServletUtil.getApplicationContext(context);
TilesContainer container = TilesAccess.getContainer(applicationContext);
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ServletRequest servletRequest = new ServletRequest(applicationContext, request, response);
container.render(location, servletRequest);
}
}
TilesUnknownHandler实现:
package com.kenmcwilliams.tiles.result;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.config.entities.ResultConfig.Builder;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import flexjson.JSONSerializer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.convention.ConventionUnknownHandler;
public class TilesUnknownHandler extends ConventionUnknownHandler {
private static final Logger log = Logger.getLogger(TilesUnknownHandler.class.getName());
private static final String conventionBase = "/WEB-INF/content";
@Inject
public TilesUnknownHandler(Configuration configuration, ObjectFactory objectFactory,
ServletContext servletContext, Container container,
@Inject("struts.convention.default.parent.package") String defaultParentPackageName,
@Inject("struts.convention.redirect.to.slash") String redirectToSlash,
@Inject("struts.convention.action.name.separator") String nameSeparator) {
super(configuration, objectFactory, servletContext, container, defaultParentPackageName,
redirectToSlash, nameSeparator);
log.info("Constructed TilesUnknownHandler");
}
@Override
public ActionConfig handleUnknownAction(String namespace, String actionName)
throws XWorkException {
ActionConfig actionConfig;
log.info("TilesUnknownHandler: before handleUnknownAction");
ActionConfig handleUnknownAction = super.handleUnknownAction(namespace, actionName);
log.info("TilesUnknownHandler: after handleUnknownAction, returning with:");
log.log(Level.INFO, "...ActionConfig value: {0}", (new JSONSerializer().serialize(handleUnknownAction)));
log.log(Level.INFO, "Modifying handleUnknowAction result handler");
Map<String, ResultConfig> results = handleUnknownAction.getResults();
ResultConfig resultConfig = results.get("success");
Builder builder = new ResultConfig.Builder("com.opensymphony.xwork2.config.entities.ResultConfig", "com.kenmcwilliams.tiles.result.TilesResult");
Map<String, String> params = resultConfig.getParams();
String tilesResultString = null;
String location = params.get("location");
if (location != null && !location.isEmpty()) {
int length = conventionBase.length();
if(StringUtils.startsWith(location, conventionBase)){
String subString = location.substring(length); //chop off "/WEB-INF/content"
int count = StringUtils.countMatches(subString, "/");//TODO: maybe check for "//", although I don't know why it would be in the string
if (count == 1){//empty namespace
tilesResultString = subString.replaceFirst("/", "#"); //TODO: because I am doing a straight replacement of the last element the else can probably be removed
}else{ //replace the last slash between the namespace and the file with "#"
int lastIndex = subString.lastIndexOf("/");
//subString.substring(lastIndex, lastIndex);
String nameSpace = subString.substring(0, lastIndex);
String file = subString.substring(lastIndex + 1);
tilesResultString = nameSpace + "#" + file;
}
}
}
Map<String, String> myParams = new LinkedHashMap<String, String>();
myParams.put("location", tilesResultString);
builder.addParams(myParams);
ResultConfig build = builder.build();
Map<String, ResultConfig> myMap = new LinkedHashMap<String, ResultConfig>();
myMap.put("success", build);
log.log(Level.INFO, "\n\n...results: {0}\n\n", (new JSONSerializer().serialize(results)));
actionConfig = new ActionConfig.Builder(handleUnknownAction).addResultConfigs(myMap).build();
//className("com.kenmcwilliams.tiles.result.TilesResult")
return actionConfig;
}
@Override
public Result handleUnknownResult(ActionContext actionContext, String actionName,
ActionConfig actionConfig, String resultCode) throws XWorkException {
log.info("TilesUnknownHandler: before handleUnknownResult");
Result handleUnknownResult = super.handleUnknownResult(actionContext, actionName, actionConfig, resultCode);
log.info("TilesUnknownHandler: after handleUnknownResult, returning with:");
log.log(Level.INFO, "...Result value: {0}", (new JSONSerializer().serialize(handleUnknownResult)));
return handleUnknownResult;
}
}
命名空间+“#” + ActionName +“的.jsp”,请注意这样的定义:如何使用我们的“位置”的字符串,是在形式的例子<definition name="REGEXP:(.*)#(.*)" extends="default">
在以下内容:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="default" template="/WEB-INF/template/template.jsp">
<put-list-attribute name="cssList" cascade="true">
<add-attribute value="/style/cssreset-min.css" />
<add-attribute value="/style/cssfonts-min.css" />
<add-attribute value="/style/cssbase-min.css" />
<add-attribute value="/style/grids-min.css" />
<add-attribute value="/script/jquery-ui-1.8.24.custom/css/ui-lightness/jquery-ui-1.8.24.custom.css" />
<add-attribute value="/style/style.css" />
</put-list-attribute>
<put-list-attribute name="jsList" cascade="true">
<add-attribute value="/script/jquery/1.8.1/jquery.min.js" />
<add-attribute value="/script/jquery-ui-1.8.24.custom/js/jquery-ui-1.8.24.custom.min.js" />
<add-attribute value="/script/jquery.sort.js" />
<add-attribute value="/script/custom/jquery-serialize.js" />
</put-list-attribute>
<put-attribute name="title" value="defaults-name" cascade="true" type="string"/>
<put-attribute name="head" value="/WEB-INF/template/head.jsp"/>
<put-attribute name="header" value="/WEB-INF/template/header.jsp"/>
<put-attribute name="body" value="/WEB-INF/template/body.jsp"/>
<put-attribute name="footer" value="/WEB-INF/template/footer.jsp"/>
</definition>
<definition name="REGEXP:(.*)#(.*)" extends="default">
<put-attribute name="title" cascade="true" expression="OGNL:@com.opensymphony.xwork2.ActionContext@getContext().name"/>
<put-attribute name="body" value="/WEB-INF/content{1}/{2}"/>
</definition>
</tiles-definitions>
有了这个地方,你可以创建/WEB-INF/content/someplace/my-action.jsp下JSP的
就像你跟,如果你创建了一个名为action类公约和瓷砖来装饰它适当地以及com.myapp.action.someplace.MyAction
没有任何结果类型的代码将执行和/WEB-INF/content/someplace/my-action.jsp
结果仍然会呈现。
有你有它公约+有没有更多的注释(以及用于正常情况)瓷砖。
注意事项 :
- 这个答案当然是不完美的,但它确实提供了可应用于其他视图技术(SiteMesh的,其他)战略的一个工作示例。
- 目前,你可以看到“的.jsp”在瓷砖被附加在砖的定义,这是不灵活导致不。 具体的扩展应该砖内指定,即定义中的身体属性应该追加特定视图类型(.JSP,.fml,.vm),因为你应该在那个时候最了解。
- 需要注意的是定义在他们被给定的顺序尝试是很重要的,这样你就可以覆盖正常情况下
REGEXP:(.*)#(.*)
通过将之间定义default
和REGEXP:(.*)#(.*)
的定义。 例如,一个所谓的定义authenticated\(.*)
可以放置这两个定义之间。 毕竟,如果你不能做到这一点,所有的页面必须平铺我们真的不会使用瓷砖是一样的! - 只要你知道使用tiles3(tiles3插件Struts2的)时,可以使用所有三种类型的视图技术(JSP,FreeMarker的,速度)组成一个瓷砖。 有用。 你可能会一直使用一个视图技术,但它很高兴知道这是可能的。
文章来源: How to Integrate Struts Conventions with Tiles such that the benefit of conventions is maintained