如何将Struts约定与Tiles集成在一起,同时保持约定的好处?
问题是,约定将url链接到操作以自动生成结果,并很好地实现了jsp、velocity和freemarker结果。它不希望处理平铺结果。
使用磁贴时,我们通常希望所有 UI 操作(而不是 json/xml 服务操作)都使用磁贴,但这样做会丢失结果组件的约定,需要使用注释。注释允许我们偏离预期,但在大型应用程序中,当期望使用磁贴时,这是一个烦恼。进一步的约定允许我们仅通过指定视图来创建操作。我们也希望在使用瓷砖时保留这种好处。为了纠正这一点,我们需要建立一个将 TRANSFER 到 tile 结果的约定,这样我们就不需要使用注释将操作绑定到切片结果,并且我们可以继续创建没有操作类的 JSP,这将获得约定的好处(没有 xml)和瓷砖的好处(所有样板都分解到瓷砖中)。
如何实现这一点?
这是一个自我回答,以帮助其他希望解决这个问题的人
以下是所需的步骤:
上述步骤需要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);
}
}
TilesUn
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;
}
}
如何使用我们的“位置”字符串的一个例子,它的形式是:名称空间“#”action name。jsp”,注意这个定义< code >
<?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
就像您使用约定和平铺一样,如果您创建了一个名为< code > com . myapp . action . something . my action 的操作类,而没有任何结果类型,则此代码将执行,并且< code >/we b-INF/content/something/my-action . JSP 结果仍将呈现。
在那里,您可以使用没有更多注释的约定磁贴(对于正常情况来说很好)。
注意事项:
默认值
和 REGEXP:(.*)#(.*)
定义之间放置定义来覆盖正常情况 REGEXP:(.*)#(.*
)。例如,可以在这两个定义之间放置一个名为 authenticated\(.*)
的定义。毕竟,如果您不能做到这一点并且所有页面都必须以相同的方式平铺,我们真的不会使用磁贴!对于许多项目来说,不打破已有的约定,对于配置等有可预测的默认值是非常适合的。现在,Spring MVC对 约定优于配置 这个实践已经有了显式的支持。这意味着什么呢?意味着如果你为项目选择了一组命名上的约定/规范,你将能减少 大量 的配置项,比如一些必要的处理器映射、视图解析器、ModelAndView实例的配置等。这能帮你快速地建立起项目原型,此外在某种程度上(通常是好的方面)维护了整个代码库的一
我们如何集成Struts 2和Tiles 3?struts2-tiles-plugin目前(2.3.4.1)支持旧版本的tiles(版本2.0.6 ),这可能有点麻烦。 这是一个自我回答,帮助他人融入。
命名约定 在正式开始使用 LCUI 前,我们先了解一下 LCUI 的命名约定,这将有助于记忆和查找你需要的 API。 数据类型 大部分公共的数据类型都采用驼峰式命名法(Camel-Case),并带有 LCUI_ 前缀,像链表(LinkedList)、红黑树(RBTree) 和字典(Dict)这类基础数据类型,由于名称长度和可替代性,未加上 LCUI_ 前缀。 对于常以指针形态引用的数据类型,它的定
问题内容: 以下模型是一个事件的简单邀请,从一个用户发送到另一个用户。我想确保邀请在以下三列中是唯一的:to_user,from_user和event。 我尝试使用本专栏,但它不起作用。该约束应在SQLite以及生产中的MySQL上均有效。如何定义此唯一约束? 问题答案: 您需要将约束添加到表,而不是模型。为此,请使用声明式: 如果已经在数据库中创建了表,则需要删除该表并再次运行,或者使用Alem
在参照PHP的规范,编写一段代码实现或程序的时候,“必须”被 理解成一定要要按照“必须”的所描述的规范来编写代码。相对的,“绝不能”可 以被理解为禁止的行为。 可能会出现的一种情况是,规范中并不存在的约束规范被违反了,并没有 关于这种情况“必须”或者“绝不能”的规范要求。对于这种规范中并没有提及 的情况,可以理解为“规范未定义”,或者被遗漏、疏忽掉的规范。这些情况 其实都是“没有被定义的规范”。
我们坚信,约定优于配置(convention over configuration)。虽然学习 CakePHP 的约定需要一些时间,但从长远来看,您可以节省时间。通过遵循约定,您可以获得免费的功能,并将您从维护跟踪配置文件的噩梦中解放出来。约定也会带来非常统一的开发体验,可以允许其他开发者参与进来并提供帮助。 控制器约定 控制器的类名是复数,首字母大写, 并且以 Controller 结尾。Use