`
Kenny.Lee
  • 浏览: 515680 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

自定义struts2的url标签的生成

 
阅读更多

本文是针对struts2的struts-tags中的s:url标签的使用进行扩展。

 

在J2EE开发中,使用struts2的时候我们很多时候会使用"/"来做URL地址定义,即使用项目的绝对路径。因为如果使用相对路径的话会十分麻烦,谁叫struts2中的"相对",指的并不是存放的目录结构,相对的是目标是指action的命名空间。

 

而由于实际服务器环境中的一些原因,可能会造成s:url生成后的地址的访问资源并不存在!

 

举个例子,当你部署的应用的服务器,在a机器的8080端口中。你可以在内网中使用http://ip:8080/a/ 访问,但外网访问时,却被映射到 http://www.foo.com/abc/a/ 中。

 

这样的情况下,通过s:url生成的url会变成是  /a 开头,而实际上是/abc/a/ 才能访问到你的应用。

 

陷入这个困境2天了,找不到好的解决方案,唯一的方案就是把应用映射到root,然后把应用名改成abc,前面用apache做proxy,这样的方案使用AJP的话会失败,但直接做跳转就成功,不过就丢失了request的IP。

 

所以最后只好把原因是使用s:url这个标签的生成不够自由的关系(没办法,我真的努力了,学艺不精啊)

 

基于把责任都推在s:url上的这个前提上就好办了,我决定重构一下他的标签,变得更加适合我用。

 

查了下源码,最后把目标定于org.apache.struts2.components.ComponentUrlProvider.java(其实方案比较多的,个人喜欢吧)

 

直接上源码

 

 

package org.apache.struts2.components;

import java.util.Map;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;

import com.foo.utils.file.PropertiesHelper;

import com.opensymphony.xwork2.util.ValueStack;

/**
 * Override org.apache.struts2.components.ComponentUrlProvider(struts2.core.jar)
 *
 * @author KennyLee <br />
 * @version 1.0.0<br/>
 */
public class ComponentUrlProvider implements UrlProvider {

	private static final String URL_SEPARATOR = "/";
	private static final String PROPERTY_FILE_NAME = "config.properties";
	private static final String URL_CONTEXT_PATH_KEY = "url.context.path";
	private static final String URL_SERVER_NAME_KEY = "url.server.name";
	private static boolean isInitPro = false;
	private static String forceContextPath = "";
	private static String forceServerName = "";
	protected HttpServletRequest httpServletRequest;
	protected HttpServletResponse httpServletResponse;

	protected String includeParams;
	protected String scheme;
	protected String value;
	protected String action;
	protected String namespace;
	protected String method;
	protected boolean encode = true;
	protected boolean includeContext = true;
	protected boolean escapeAmp = true;
	protected String portletMode;
	protected String windowState;
	protected String portletUrlType;
	protected String anchor;
	protected boolean forceAddSchemeHostAndPort;
	protected String urlIncludeParams;
	protected ExtraParameterProvider extraParameterProvider;
	protected UrlRenderer urlRenderer;

	protected Component component;
	@SuppressWarnings("rawtypes")
	private Map parameters;

	/**
	 *
	 * @param component
	 *            The component used to delagete some calls to
	 * @param parameters
	 *            parameters passed from <param...>
	 */
	public ComponentUrlProvider(Component component,
			@SuppressWarnings("rawtypes") Map parameters) {
		this.component = component;
		this.parameters = parameters;
	}

	@Override
	public String determineActionURL(String action, String namespace,
			String method, HttpServletRequest req, HttpServletResponse res,
			@SuppressWarnings("rawtypes") Map parameters, String scheme,
			boolean includeContext, boolean encodeResult,
			boolean forceAddSchemeHostAndPort, boolean escapeAmp) {
		// XXX add by KennyLee 2012-05-08 01:21:37, fix URL by Action.
		String url = null;
		String ori_path = component.determineActionURL(action, namespace,
				method, req, res, parameters, scheme, includeContext,
				encodeResult, forceAddSchemeHostAndPort, escapeAmp);
		url = ori_path;
		String forceContextPath = getForceContextPath();
		if (StringUtils.isNotBlank(ori_path)
				&& StringUtils.isNotBlank(forceContextPath)) {
			if (ori_path.startsWith(URL_SEPARATOR)) {
				url = new StringBuilder().append(URL_SEPARATOR)
						.append(forceContextPath).append(ori_path).toString();
			} else if (ori_path.startsWith("http")) {
				String[] splits = StringUtils.split(ori_path, URL_SEPARATOR);
				int count = 0;
				for (String string : splits) {
					count++;
					url += string;
					if (count == 3) {// At after add serverName
						url += URL_SEPARATOR;
						url += forceContextPath;
					}
					if (count != splits.length) {
						url += URL_SEPARATOR;
					}
				}
			}
		}
		return url;
	}

	public String determineNamespace(String namespace, ValueStack stack,
			HttpServletRequest req) {
		return component.determineNamespace(namespace, stack, req);
	}

	public String findString(String expr) {
		return component.findString(expr);
	}

	@SuppressWarnings("rawtypes")
	public Map getParameters() {
		return parameters;
	}

	public HttpServletRequest getHttpServletRequest() {
		return httpServletRequest;
	}

	public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
		this.httpServletRequest = httpServletRequest;
	}

	public HttpServletResponse getHttpServletResponse() {
		return httpServletResponse;
	}

	public void setHttpServletResponse(HttpServletResponse httpServletResponse) {
		this.httpServletResponse = httpServletResponse;
	}

	public String getIncludeParams() {
		return includeParams;
	}

	public void setIncludeParams(String includeParams) {
		this.includeParams = includeParams;
	}

	public String getScheme() {
		return scheme;
	}

	public void setScheme(String scheme) {
		this.scheme = scheme;
	}

	public boolean isPutInContext() {
		return component instanceof ContextBean;
	}

	public String getVar() {
		return isPutInContext() ? ((ContextBean) component).getVar() : null;
	}

	public String getValue() {
		// XXX add by KennyLee 2012-05-08 00:43:48, fix URL by value.
		if (StringUtils.isNotBlank(value)) {
			StringBuilder link = new StringBuilder();
			String forceContextPath = getForceContextPath();
			String forceServerName = getForceServerName();
			if (StringUtils.startsWith(value, URL_SEPARATOR)
					&& StringUtils.isNotBlank(forceContextPath)) {
				HttpServletRequest request = httpServletRequest;
				String path = request.getContextPath();
				String serverName = request.getServerName();
				if (StringUtils.isNotBlank(forceServerName)) {
					serverName = forceServerName;
				}
				String basePath = request.getScheme() + "://" + serverName
						+ ":" + request.getServerPort();
				link.append(basePath).append(URL_SEPARATOR)
						.append(forceContextPath).append(path).append(value);
			} else {
				link.append(value);
			}
			return link.toString();
		}
		return value;
	}

	/**
	 * <p>
	 * get forceServerName
	 * </p>
	 *
	 * @return
	 */
	private String getForceServerName() {
		if (StringUtils.isBlank(forceServerName)) {
			initProValues();
		}
		return forceServerName;
	}

	/**
	 * <p>
	 * get forceContextPath
	 * </p>
	 *
	 * @return
	 */
	private String getForceContextPath() {
		if (StringUtils.isBlank(forceContextPath)) {
			initProValues();
		}
		return forceContextPath;
	}

	/**
	 * <p>
	 * init property values
	 * </p>
	 */
	private synchronized void initProValues() {
		if (!isInitPro) {
			Properties pro = PropertiesHelper.getInstance()
					.getPropertiesInstance(PROPERTY_FILE_NAME, false);
			if (pro != null) {
				String contextPath = pro.getProperty(URL_CONTEXT_PATH_KEY, "");
				String serverName = pro.getProperty(URL_SERVER_NAME_KEY, "");
				if (StringUtils.isNotBlank(contextPath)) {
					forceContextPath = contextPath;
				}
				if (StringUtils.isNotBlank(serverName)) {
					forceServerName = serverName;
				}
			}
			isInitPro = true;
		}
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getAction() {
		return action;
	}

	public void setAction(String action) {
		this.action = action;
	}

	public String getNamespace() {
		return namespace;
	}

	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public boolean isEncode() {
		return encode;
	}

	public void setEncode(boolean encode) {
		this.encode = encode;
	}

	public boolean isIncludeContext() {
		return includeContext;
	}

	public void setIncludeContext(boolean includeContext) {
		this.includeContext = includeContext;
	}

	public boolean isEscapeAmp() {
		return escapeAmp;
	}

	public void setEscapeAmp(boolean escapeAmp) {
		this.escapeAmp = escapeAmp;
	}

	public String getPortletMode() {
		return portletMode;
	}

	public void setPortletMode(String portletMode) {
		this.portletMode = portletMode;
	}

	public String getWindowState() {
		return windowState;
	}

	public void setWindowState(String windowState) {
		this.windowState = windowState;
	}

	public String getPortletUrlType() {
		return portletUrlType;
	}

	public ValueStack getStack() {
		return component.getStack();
	}

	public void setPortletUrlType(String portletUrlType) {
		this.portletUrlType = portletUrlType;
	}

	public String getAnchor() {
		return anchor;
	}

	public void setAnchor(String anchor) {
		this.anchor = anchor;
	}

	public boolean isForceAddSchemeHostAndPort() {
		return forceAddSchemeHostAndPort;
	}

	public void setForceAddSchemeHostAndPort(boolean forceAddSchemeHostAndPort) {
		this.forceAddSchemeHostAndPort = forceAddSchemeHostAndPort;
	}

	public void putInContext(String result) {
		if (isPutInContext()) {
			((ContextBean) component).putInContext(result);
		}
	}

	public String getUrlIncludeParams() {
		return urlIncludeParams;
	}

	public void setUrlIncludeParams(String urlIncludeParams) {
		this.urlIncludeParams = urlIncludeParams;
	}

	public ExtraParameterProvider getExtraParameterProvider() {
		return extraParameterProvider;
	}

	public void setExtraParameterProvider(
			ExtraParameterProvider extraParameterProvider) {
		this.extraParameterProvider = extraParameterProvider;
	}

	public UrlRenderer getUrlRenderer() {
		return urlRenderer;
	}

	public void setUrlRenderer(UrlRenderer urlRenderer) {
		this.urlRenderer = urlRenderer;
	}
}

 

说明:

 

回头想想我刚刚举的例子,其中最主要是那个我们预想之外的abc子域名,如果我们能把它都添加在应用名之前就完毕了,而且以后就算前面加多少个子域名目录,也依然可以正常访问。

 

所以,这里我用一个forceContextPath来存放这个值。

 

而另外一个关键的地方是 serverName,即我们访问时用的IP或者域名。用原生的requet方法getServerName的话,有些时候会造成跟访问域名不一致的情况。例如你用 www.foo.com 访问的,但request.getServerName得出的结果是 192.168.1.1 。这种情况下,有时候可能影响变大,但如果域名做了多映射的条件下,也会造成访问障碍,为了安全起见,也提供强制定义的方式来定义serverName。这也是为什么我不使用原生的forceAddSchemeHostAndPort参数来获取完整路径的原因。

 

即另外一个重要的参数值 forceServerName。

 

这个两个值我是放在classes根目录下的config.properties目录下,相信如果专注于J2EE开发的人对这类型文件不会陌生吧。forceContextPath在config.properties中的key为url.context.path,而forceServerName的key为url.server.name。

 

Q:为什么使用value构造URL时,我把http和域名等信息都加上了?

 

A:对于这点,其实我也挺无奈的。因为如果不这样做,我遇到会产生forceContextPath被重叠了两次的情况。即变成了 /abc/abc/ 但我实际需要的只是 /abc/ (注:我的应用映射到root中,而应用名跟子级域名一致,即应用名也叫abc)。也想过用javascript来解决这个问题,不过最后还是懒得再继续探究下去了。因为其实上线环境中,绑定域名其实没多大问题的。

 

附上上面代码中使用到的一个工具类PropertiesHelper的的代码

 

 

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

/**
 * <b>类名称:</b>PropertiesHelper<br/>
 * <b>类描述:</b>java.util.Properties的工具类<br/>
 * <b>创建时间:</b>2009-10-12 下午2:48:05<br/>
 * <b>备注:</b><br/>
 *
 * @author KennyLee <br />
 * @version 1.0.0<br/>
 */
public class PropertiesHelper {

	private volatile static PropertiesHelper uniqueInstance;
	private static final Map<String, Properties> propertiesMap = new HashMap<String, Properties>();

	private PropertiesHelper() {
	};

	public static PropertiesHelper getInstance() {
		if (uniqueInstance == null) {
			synchronized (PropertiesHelper.class) {
				if (uniqueInstance == null) {
					uniqueInstance = new PropertiesHelper();
				}
			}
		}
		return uniqueInstance;
	}

	/**
	 * <p>
	 * Get properties instance by fileName.
	 * </p>
	 *
	 * @param fileName
	 * @param isKeepProperties
	 *            是否保存Properties对象到全局。
	 * @return
	 */
	public Properties getPropertiesInstance(String fileName,
			boolean isKeepProperties) {
		Properties resultInstence = null;
		if (StringUtils.isBlank(fileName))
			return null;
		resultInstence = propertiesMap.get(fileName);
		if (resultInstence == null) {
			resultInstence = new Properties();
			InputStream inputStream = null;
			try {
				inputStream = this.getClass().getClassLoader()
						.getResourceAsStream(fileName);
				resultInstence.load(inputStream);
			} catch (IOException e) {
				resultInstence = null;
				e.printStackTrace();
			} finally {
				IOUtils.closeQuietly(inputStream);
			}
			if (resultInstence != null && isKeepProperties)
				propertiesMap.put(fileName, resultInstence);
		}
		return resultInstence;
	}

	/**
	 * <p>
	 * Get properties instance by fileName.
	 * </p>
	 *
	 * @param fileName
	 * @return
	 */
	public Properties getPropertiesInstance(String fileName) {
		return getPropertiesInstance(fileName, true);
	}
}

 

 

希望对大家有用,或者给你带来一定的启发!

 

EOF

1
2
分享到:
评论

相关推荐

    Struts2分页-自定义标签-类似百度分页

    在Struts2中,自定义标签可以用来扩展JSP页面的功能,使其能够更好地与后台业务逻辑交互。 自定义标签的实现通常分为以下几个步骤: 1. **创建标签库**:定义一个XML文件(如struts-tags.xml),用于声明自定义...

    Struts2 标签使用简介

    一、Struts2标签库概述 Struts2的标签库是由一系列预定义的JSP标签组成的,这些标签用于处理表单、显示数据、控制流程等任务,减少了开发者编写大量Java脚本和HTML代码的工作量。它们遵循JavaServer Pages (JSP) 2.0...

    Struts标签库 Struts 标签库

    Struts2标签库是Java开发Web应用程序时常用的一个强大工具,尤其在构建MVC(Model-View-Controller)架构的应用程序中发挥着重要作用。Struts2框架提供了丰富的自定义标签库,极大地简化了视图层的开发,使得开发者...

    struts2 标签库 帮助文档

    Struts 2 标签库(文档手册) Tags-API-CLSW-JSP &lt;%@ taglib prefix="s" uri="/struts-tags" %&gt; 就能使用struts2.0的标签库 下面就介绍每个标签的具体应用实例说明:按字母排列 A: 1. 2. &lt;s:a href=""&gt;&lt;/s:a&gt;-...

    ssh2中的urlrewrite和Struts2的生成静态页面

    ### SSH2中的URLRewrite与Struts2生成静态页面 #### URLRewrite的理解与应用 在SSH2(Spring + Struts2 + Hibernate)框架中,URLRewrite是一种强大的工具,可以用于实现URL重写功能,使得网站的URL更加友好、易于...

    struts2相关标签描述

    在实际项目中,通过Struts2标签,我们可以轻松实现用户界面的动态渲染,比如根据后端数据动态生成表格、列表等。同时,利用其强大的逻辑控制标签,可以简化前端的逻辑处理,提高开发效率。 7. **优化与扩展**: ...

    Struts2 常用标签

    #### 二、Struts2标签分类 根据功能的不同,Struts2中的标签可以大致分为以下几类: 1. **基本HTML标签**:如`&lt;s:textfield&gt;`、`&lt;s:textarea&gt;`等,主要用于表单元素的生成。 2. **表单标签**:如`&lt;s:form&gt;`,用于...

    在Struts2中使用JasperReports生成报表

    5. **Struts2配置**:在struts.xml文件中,配置一个Action映射,让Struts2知道何时调用报表生成的方法。例如: ```xml &lt;param name="contentType"&gt;application/pdf &lt;param name="inputName"&gt;reportStream ...

    Struts_2_标签库(文档手册)_Tags-API-CLSW-JSP

    Struts 2标签库包含了大量预定义的标签,这些标签封装了常见的Web页面元素和交互逻辑,如表单控件、数据绑定、条件判断等,可以显著提高开发效率和代码质量。以下是对部分标签的详细介绍: #### `&lt;s:a&gt;` 标签 该...

    struts2+freemarker 生成静态页面

    4. **实现过程**:在Struts2中,可以通过自定义Result类型来实现静态化。这个Result类型会在Action处理完请求后,将生成的FreeMarker模板内容写入到一个静态HTML文件中。同时,需要设置适当的缓存策略,例如根据内容...

    Struts2 标签二

    一、Struts2标签库概述 Struts2的标签库主要由两个部分组成:核心标签库和自由标签库。核心标签库提供了一些基础的控制流和数据处理功能,而自由标签库则包含了更复杂的UI组件,如表单元素、数据展示等。 1. 核心...

    struts2+MySQL+jfreechart生成带热点

    在"struts2+MySQL+jfreechart生成带热点"的场景中,我们可以构建一个Web应用,该应用能够从MySQL数据库中检索数据,然后使用JFreeChart生成包含热点的图表。热点是指图表上的区域,当用户点击这些区域时,可以触发...

    struts2-s标签

    ### Struts2 S 标签库详解 #### 引言 Struts2 是一款流行的 Java Web 开发框架,它提供了一系列强大的功能来帮助开发者构建健壮的 Web 应用程序。其中,Struts2 的标签库是其核心特性之一,为开发者提供了丰富的 ...

    struts2标签详解

    Struts2标签详解 在Java Web开发中,Struts2框架因其强大的控制层能力而备受开发者喜爱。它提供了一套丰富的标签库,使得视图层的构建更为便捷,减轻了开发者的工作负担。本文将深入探讨Struts2标签库,帮助你更好...

    JSP Struts之HTML标签库详解

    `id`定义了自定义标签的脚本变量名,`name`和`scope`用于定位和访问JavaBeans,`property`用于指定bean中的特定属性。此外,Struts标签支持嵌套引用,使得处理复杂的对象结构变得简单。 **使用注意事项** 使用...

    struts2 1 8生成返回json 修正

    Struts2提供了一个内置的JSON插件,这个插件使得在Action中生成JSON输出变得简单。要使用JSON插件,首先需要在`struts.xml`配置文件中添加相关的配置,启用JSON结果类型。例如: ```xml ...

    struts2标签库学习

    在本教程中,我们将深入理解Struts2标签库的各个方面。 首先,Struts2标签库由多种类型的标签组成,包括用户界面(UI)标签、表单标签、非表单标签以及非用户界面(非UI)标签。UI标签主要用于生成HTML元素,表单标签...

    struts2教程+struts标签中文API

    2. **struts2-tags-API**:这可能是Struts2标签库的中文API文档,提供了所有可用标签的详细说明,对开发者进行视图层开发非常有帮助。 通过阅读这两个资源,开发者可以全面了解Struts2框架,掌握其核心概念和使用...

    struts2 API帮助文档

    - Struts2的拦截器是其一大特色,它们允许在Action执行前后插入自定义逻辑,如日志记录、权限验证、数据校验等。用户可以自由组合拦截器栈,以实现不同的业务需求。 4. **结果类型(Result Types)** - 结果类型...

    struts2所需jar包 lib.zip

    2. **struts2-convention-plugin**: 这个插件实现了约定优于配置的原则,使得开发者可以不写XML配置文件,而通过类名和方法名自动映射URL。 3. **struts2-dojo-plugin**: 提供了与Dojo JavaScript库的集成,便于...

Global site tag (gtag.js) - Google Analytics