一直想在Mac下面找一个免费的好用的图片圈点,截图,截屏软件,增加点注释啊,画个箭头啊什么简单的功能,只要好用即可,找了半天,总算找到一个好用的软件Skitch。 在Mac商店中搜索Skitch ,然后下载即可。现在是免费的了,被Evernote收购之后就免费了,蛮好用的,强烈推荐。
作者: 默默
How To Get The ServletContext In Struts 2
早期版本中可以如下操作
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import javax.servlet.ServletContext; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class CustomerAction extends ActionSupport{ public String execute() throws Exception { ServletContext context = ServletActionContext.getServletContext(); return SUCCESS; } } |
如果报告
|
1 2 |
Caused by: java.lang.NullPointerException at org.apache.struts2.ServletActionContext.getServletContext(ServletActionContext.java:139) |
则代表这个版本的stucts2 中不支持这种写法,貌似2.3.16 版本的就不行,则可以通过实现ServletContextAware 接口来让spring拦截器来完成注入即可。
|
1 2 3 4 5 6 7 8 9 10 |
import javax.servlet.ServletContext; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class CustomerAction extends ActionSupport implements ServletContextAware{ private ServletContext context; public void setServletContext(ServletContext context) { this.context = context; } } |
Ubuntu 13.10 卸载 Apache2 重新安装找不到apache2.conf
Ubuntu 13.10 卸载 Apache2 重新安装后在 /etc/apache2 目录下apache2.conf不存在 或者里面内容为空,解决方法如下
|
1 2 3 4 5 6 7 |
$ sudo apt-get --purge remove apache2 $ sudo apt-get --purge remove apache2.2-common $ sudo apt-get autoremove #找到没有删除掉的配置文件,一并删除 $ sudo find /etc -name "*apache*" -exec rm -rf {} \; $ sudo rm -rf /var/www |
Ubuntu 13.10 Apache 2.2 通过 AJP 整合 Tomcat 7
前置条件为Apache 2.2,Tomcat 7都是通过apt-get install来安装的版本。
|
1 2 3 |
$ sudo apt-get install apache2 $ sudo apt-get install tomcat7 |
1.开启Apache的AJP协议支持,Apache 2.2已经默认在/var/lib/apache2/module目录下面包含了AJP协议模块,只要打开支持就可以了。
|
1 2 3 |
$ sudo a2enmod proxy_ajp $ sudo a2enmod proxy |
2.编辑VirtualHost,在Ubuntu下面这个目录在/etc/apache2/sites-available下面
可以看到default.conf跟default-ssl.conf两个类似的文件,我这边的default.conf莫名的被重命名成了000-default.conf,倒也无妨。
|
1 |
$ sudo vim /etc/apache2/sites-available/000-default.conf |
在
|
1 |
<VirtualHost *:80></VirtualHost> |
之间增加
|
1 2 3 |
ProxyPass / ajp://127.0.0.1:8009/ ProxyPassReverse / ajp://127.0.0.1:8009/ ServerName localhost |
注意,此处的8009端口,要对应下面的Tomcat 7中配置的AJP的端口。
3.修改Tomcat 7的配置文件
|
1 |
$ sudo vim /var/lib/tomcat7/conf/server.xml |
去掉被注释掉的,如果没有则增加
|
1 2 3 4 |
<!-- Define an AJP 1.3 Connector on port 8009 --> <!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> --> |
注意是增加或去掉原来的配置信息,不是修改默认的8080端口上的“HTTP 1.1”部分
另外注意,如果直接去掉注释,那么根据The AJP Connector中的介绍说明(注意address部分),如果没有指定IP地址的话,默认是绑定任意地址,这样就导致外网也可以访问这个端口。因此出于安全考虑,我们需要增加这个address的设置,并且绑定到127.0.0.1。最终结果如下:
|
1 2 |
<!-- Define an AJP 1.3 Connector on port 8009 --> <Connector port="8009" protocol="AJP/1.3" address="127.0.0.1" redirectPort="8443" /> |
4.重启Tomcat 7跟Apache 2
|
1 2 3 |
$ sudo service apache2 restart $ sudo service tomcat7 restart |
5.验证是否成功
在Tomcat的ROOT目录(/var/lib/tomcat7/webapps)下面创建一个index.jsp里面随便写点东西即可,不需要是JSP,比如可以写 "Hello JSP"
然后访问http://127.0.0.1/index.jsp如果能正常显示出来,则代表配置成功。
6.如果重启Apache2出现:
|
1 |
Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName. |
在/etc/apache2/apache2.conf文件最后加上:
|
1 2 |
#ServerName XXX (XXX 为domain name) ServerName localhost |
7.出于安全原因,禁止外网访问Tomcat的8080端口
只允许Tomcat在本地的8080端口监听即可,修改
|
1 |
$ sudo vim /var/lib/tomcat7/conf/server.xml |
添加 address="127.0.0.1"
|
1 2 3 |
<Connector port="8080" address="127.0.0.1" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> |
重启Tomcat7
|
1 |
$ sudo service tomcat7 restart |
8.Apache2对Tomcat进行反向代理的时候,会发生重定向问题。
配置参考
在Ubuntu 12.04 LTS上安装OpenGrok浏览Android源码
具体的Apache2的配置文件如下:"/etc/apache2/sites-available/default"
|
1 2 3 4 |
#for Tomcat 7 openGrok ProxyPass /AndroidXRef/ ajp://127.0.0.1:8009/AndroidXRef/ ProxyPassReverse /AndroidXRef/ ajp://127.0.0.1:8009/AndroidXRef/ ServerName localhost |
则在正常访问http://www.mobibrw.com/AndroidXRef的时候是正常的,但是在点击里面按钮的时候会报告404跳转错误。ProxyPass后面必须携带"/",否则就会出现404问题。
产生这个错误的原因是:Apache2进行代理的时候更改了URL路径,而Tomcat7并不知道URL发生变化了,因此没有在跳转时候携带Apache2增加的跳转地址。
解决方法是在Tomcat7的配置文件/var/lib/tomcat7/conf/server.xml中的Host字段中增加
|
1 |
<Context path="/AndroidXRef" docBase="source/"/> |
即可。
如下所示:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="/AndroidXRef" docBase="source/"/> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <!-- Access log processes all example. Documentation at: /docs/config/valve.html Note: The pattern used is equivalent to using pattern="common" --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> |
为maven设置镜像解决国内下载依赖时超时的问题
国内访问repo1.maven.org访问不了,导致maven不能下载依赖,解决方法是自己设置maven的mirrors,就是设置镜像:
在~/.m2/目录下建立一个settings.xml文件,内容如下
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | --> <mirror> <id>id</id> <mirrorOf>central</mirrorOf> <name>name</name> <url>http://XXX</url> </mirror> </mirrors> </settings> |
最好是公司或者自己设置的镜像地址,公网地址基本上不怎么靠谱,不是修改了包的内容打广告就是稳定性很差。
注意,如果由于更换代理导致问题,可以尝试删除~/.m2/repository 目录。
Tomcat 7.0 启用gzip压缩配置
打开 conf/server.xml 文件可以看到:
|
1 |
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> |
要使用 gzip 压缩功能,可以在 Connector 实例中加上如下属性:
- compression="on" 打开压缩功能
- compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认为2KB
- noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩
- compressableMimeType="text/html,text/xml" 压缩类型
修改完成后
|
1 |
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" compression="on" compressionMinSize="256" noCompressionUserAgents="gozilla,traviata" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" /> |
验证是否成功, 用 wireshark 抓包,可以看到如图所示:
Ubuntu 12.04 “sudo apt-get update” 报告 “Reading package lists… Error!”
服务器上的Ubuntu 12.04 LTS,执行Update
|
1 |
sudo apt-get update |
结果出错,最后报告
|
1 |
Reading package lists… Error! |
解决方法:
|
1 2 |
sudo rm -rf /var/lib/apt/lists/* sudo apt-get update |
VelocityTracker简单用法
VelocityTracker顾名思义即速度跟踪,在android中主要应用于touch event, VelocityTracker通过跟踪一连串事件实时计算出
当前的速度,这样的用法在android系统空间中随处可见,比如Gestures中的Fling, Scrolling等,下面简单介绍一下用法。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//获取一个VelocityTracker对象, 用完后记得回收 //回收后代表你不需要使用了,系统将此对象在此分配到其他请求者 static public VelocityTracker obtain(); public void recycle(); //计算当前速度, 其中units是单位表示, 1代表px/毫秒, 1000代表px/秒, .. //maxVelocity此次计算速度你想要的最大值 public void computeCurrentVelocity(int units, float maxVelocity); //经过一次computeCurrentVelocity后你就可以用一下几个方法获取此次计算的值 //id是touch event触摸点的ID, 来为多点触控标识,有这个标识在计算时可以忽略 //其他触点干扰,当然干扰肯定是有的 public float getXVelocity(); public float getYVelocity(); public float getXVelocity(int id); public float getYVelocity(int id); |
下面是我写的一个简单Demo:
|
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 |
package com.bxwu.demo.component.activity; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.ViewGroup.LayoutParams; import android.widget.TextView; public class VelocityTrackerTest extends Activity { private TextView mInfo; private VelocityTracker mVelocityTracker; private int mMaxVelocity; private int mPointerId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInfo = new TextView(this); mInfo.setLines(4); mInfo.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mInfo.setTextColor(Color.WHITE); setContentView(mInfo); mMaxVelocity = ViewConfiguration.get(this).getMaximumFlingVelocity(); } @Override public boolean onTouchEvent(MotionEvent event) { final int action = event.getAction(); acquireVelocityTracker(event); final VelocityTracker verTracker = mVelocityTracker; switch (action) { case MotionEvent.ACTION_DOWN: //求第一个触点的id, 此时可能有多个触点,但至少一个 mPointerId = event.getPointerId(0); break; case MotionEvent.ACTION_MOVE: //求伪瞬时速度 verTracker.computeCurrentVelocity(1000, mMaxVelocity); final float velocityX = verTracker.getXVelocity(mPointerId); final float velocityY = verTracker.getYVelocity(mPointerId); recodeInfo(velocityX, velocityY); break; case MotionEvent.ACTION_UP: releaseVelocityTracker(); break; case MotionEvent.ACTION_CANCEL: releaseVelocityTracker(); break; default: break; } return super.onTouchEvent(event); } /** * * @param event 向VelocityTracker添加MotionEvent * * @see android.view.VelocityTracker#obtain() * @see android.view.VelocityTracker#addMovement(MotionEvent) */ private void acquireVelocityTracker(final MotionEvent event) { if(null == mVelocityTracker) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 释放VelocityTracker * * @see android.view.VelocityTracker#clear() * @see android.view.VelocityTracker#recycle() */ private void releaseVelocityTracker() { if(null != mVelocityTracker) { mVelocityTracker.clear(); mVelocityTracker.recycle(); mVelocityTracker = null; } } private static final String sFormatStr = "velocityX=%f\nvelocityY=%f"; /** * 记录当前速度 * * @param velocityX x轴速度 * @param velocityY y轴速度 */ private void recodeInfo(final float velocityX, final float velocityY) { final String info = String.format(sFormatStr, velocityX, velocityY); mInfo.setText(info); } } |
代码很简单,我们可以求出move过程中的伪瞬时速度, 这样在做很多控件的时候都是可以用到的,比如系统Launcher的分页,
ScrollView滑动等, 可根据此时的速度来计算ACTION_UP后的减速运动等。实现一些非常棒的效果。
原始链接 http://blog.csdn.net/bingxianwu/article/details/7446799
判断一个点是否在指定区域内
在图像处理时,我们会经常需要判断一个点是否位于多边形区域内。比较好用的是射线法。
射线法
算法思想非常巧妙:从待判断的点向某一个方向引射线,计算和多边形交点的个数,如果个数是偶数或者0则点在多边形外,如果是奇数,则在多边形内,如下图:
这里有二种特殊情况:
|
1 2 3 4 |
1. 射线经过顶点:当射线经过顶点时,判断就会出现异常情况。 2. 点在边上:这种情况也不能用交点个数的奇偶性来判断了,要快速地判断这个点是否在边上。 |
C的实现如下:
|
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 |
#define MIN(x,y) (x < y ? x : y) #define MAX(x,y) (x > y ? x : y) typedef struct { double x,y; } Point; int InsidePolygon(Point *polygon,int N,Point p) { int counter = 0; int i; double xinters; Point p1,p2; p1 = polygon[0]; for (i=1;i<=N;i++) { p2 = polygon[i % N]; if (p.y > MIN(p1.y,p2.y)) { //低 if (p.y <= MAX(p1.y,p2.y)) { //高 if (p.x <= MAX(p1.x,p2.x)) { //右 if (p1.y != p2.y) { //简单忽略平行X轴这种情况 xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; //交叉点坐标 参考./media/point-and-polygon/xinters.jpg if (p1.x == p2.x || p.x <= xinters) counter++; } } } } p1 = p2; } if (counter % 2 == 0) return 0; else return 1; } |
再来个C#版的
|
1 2 3 4 5 6 7 8 9 10 11 12 |
public static bool Contains( Point[] points, Point p ) { bool result = false; for( int i = 0; i < points.Length - 1; i++ ) { if( ( ( ( points[ i + 1 ].Y <= p.Y ) && ( p.Y < points[ i ].Y ) ) || ( ( points[ i ].Y <= p.Y ) && ( p.Y < points[ i + 1 ].Y ) ) ) && ( p.X < ( points[ i ].X - points[ i + 1 ].X ) * ( p.Y - points[ i + 1 ].Y ) / ( points[ i ].Y - points[ i + 1 ].Y ) + points[ i + 1 ].X ) ) { result = !result; } } return result; } |
值得一提的是射线法对于带岛的多边形依然有效:
改进:传统的射线法一开始就直接计算点和多边形的交点个数,这样的话,会花费大量的时间来作拓扑关系的判断,我们可以首先计算出最小外包矩形,迅速排除不在矩形内部的点,然后再做上面的判断。
在Struts 2.0中国际化(i18n)
国际化Hello World
下面让我们看一个例子——HelloWorld。这个例子演示如何根据用户浏览器的设置输出相应的HelloWorld。
- 在src文件夹中加入struts.properties文件,内容如下:
1struts.custom.i18n.resources=globalMessages
- 在src文件夹中加入globalMessages_en_US.properties文件,内容如下:
1HelloWorld=Hello World!
- 在src文件夹中加入globalMessages_zh_CN.properties文件,内容如下:
1HelloWorld=你好,世界!
- 在WebContent文件夹下加入HelloWorl.jsp文件,内容如下:
1234567891011<%@ page contentType="text/html; charset=UTF-8"%><%@taglib prefix="lang" uri="/struts-tags"%><html><head><title>Hello World</title></head><body><h2><lang:text name="HelloWorld"/></h2><h2><lang:property value="%{getText('HelloWorld')}"/></h2></body></html>
具体的存放路径如图所示
发布运行应用程序,在浏览器地址栏中输入http://localhost:8080/Struts2_i18n/HelloWorld.jsp ,出现图1所示页面。

图1 中文输出- 将浏览器的默认语言改为“英语(美国)”,刷新页面,出现图2所示页面。

图2 英文输出
上面的例子的做法,与Struts 1.x的做法相似,似乎并不能体现Struts 2.0的优势。不过,我在上面的例子用了两种方法来显示国际化字符串,其输出是相同的。其实,这就是Struts 2.0的一个优势,因为它默认支持EL,所示我们可以用getText方法来简洁地取得国际化字符串。另外更普遍的情况——在使用UI表单标志时,getText可以用来设置label属性,例如:
|
1 |
<lang:textfield name="name" label="%{getText('UserName')}"/> |
资源文件查找顺序
之所以说Struts 2.0的国际化更灵活是因为它可以能根据不同需要配置和获取资源(properties)文件。在Struts 2.0中有下面几种方法:
- 使用全局的资源文件,方法如上例所示。这适用于遍布于整个应用程序的国际化字符串,它们在不同的包(package)中被引用,如一些比较共用的出错提示;
- 使用包范围内的资源文件。做法是在包的根目录下新建名的package.properties和package_xx_XX.properties文件。这就适用于在包中不同类访问的资源;
- 使用Action范围的资源文件。做法为Action的包下新建文件名(除文件扩展名外)与Action类名同样的资源文件。它只能在该Action中访问。如此一来,我们就可以在不同的Action里使用相同的properties名表示不同的值。例如,在ActonOne中title为“动作一”,而同样用title在ActionTwo表示“动作二”,节省一些命名工夫;
- 使用<s:i18n>标志访问特定路径的properties文件。在使用这一方法时,请注意<s:i18n>标志的范围。在<s:i18n name="xxxxx">到</s:i18n>之间,所有的国际化字符串都会在名为xxxxx资源文件查找,如果找不到,Struts 2.0就会输出默认值(国际化字符串的名字)。
上面我列举了四种配置和访问资源的方法,它们的范围分别是从大到小,而Struts 2.0在查找国际化字符串所遵循的是特定的顺序,如图3所示:
假设我们在某个ChildAction中调用了getText("user.title"),Struts 2.0的将会执行以下的操作:
- 查找ChildAction_xx_XX.properties文件或ChildAction.properties;
- 查找ChildAction实现的接口,查找与接口同名的资源文件MyInterface.properties;
- 查找ChildAction的父类ParentAction的properties文件,文件名为ParentAction.properties;
- 判断当前ChildAction是否实现接口ModelDriven。如果是,调用getModel()获得对象,查找与其同名的资源文件;
- 查找当前包下的package.properties文件;
- 查找当前包的父包,直到最顶层包;
- 在值栈(Value Stack)中,查找名为user的属性,转到user类型同名的资源文件,查找键为title的资源;
- 查找在struts.properties配置的默认的资源文件,参考例1;
- 输出user.title。
参数化国际化字符串
许多情况下,我们都需要在动行时(runtime)为国际化字符插入一些参数,例如在输入验证提示信息的时候。在Struts 2.0中,我们通过以下两种方法做到这点:
- 在资源文件的国际化字符串中使用OGNL,格式为${表达式},例如:
1validation.require=${getText(fileName)} is required
使用java.text.MessageFormat中的字符串格式,格式为{ 参数序号(从0开始), 格式类形(number | date | time | choice), 格式样式},例如: -
1validation.between=Date must between {0, date, short} and {1, date, short}
在显示这些国际化字符时,同样有两种方法设置参数的值:
- 使用标志的value0、value1...valueN的属性,如:
1<lang:text name="validation.required" value0="User Name"/>
- 使用param子元素,这些param将按先后顺序,代入到国际化字符串的参数中,例如:
12<lang:text name="validation.required"><lang:param value="User Name"/>
让用户方便地选择语言
开发国际化的应用程序时,有一个功能是必不可少的——让用户快捷地选择或切换语言。在Struts 2.0中,通过ActionContext.getContext().setLocale(Locale arg)可以设置用户的默认语言。不过,由于这是一个比较普遍的应用场景(Scenario),所以Struts 2.0为您提供了一个名i18n的拦截器(Interceptor),并在默认情况下将其注册到拦截器链(Interceptor chain)中。它的原理为在执行Action方法前,i18n拦截器查找请求中的一个名为"request_locale"的参数。如果其存在,拦截器就将其作为参数实例化Locale对象,并将其设为用户默认的区域(Locale),最后,将此Locale对象保存在session的名为“WW_TRANS_I18N_LOCALE”的属性中。
下面,我将提供一完整示例演示它的使用方法。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package tutorial; import java.util.Hashtable; import java.util.Locale; import java.util.Map; publicclass Locales { public Map<String, Locale> getLocales() { Map<String, Locale> locales =new Hashtable<String, Locale>(2); locales.put("American English", Locale.US); locales.put("Simplified Chinese", Locale.CHINA); return locales; } } |
tutorial/Locales.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<%@taglib prefix="lang" uri="/struts-tags"%> <script type="text/javascript"> <!-- function langSelecter_onChanged() { document.langForm.submit(); } //--> </script> <lang:set name="SESSION_LOCALE" value="#session['WW_TRANS_I18N_LOCALE']"/> <lang:bean id="locales" name="tutorial.Locales"/> <form action="<lang:url includeParams="get" encode="true"/>" name="langForm" style="background-color: powderblue; padding-top: 4px; padding-bottom: 4px;"> Language: <lang:select label="Language" list="#locales.locales" listKey="value" listValue="key" value="#SESSION_LOCALE == null ? locale : #SESSION_LOCALE" name="request_locale" id="langSelecter" onchange="langSelecter_onChanged()" theme="simple"/> </form> |
LangSelector.jsp
上述代码的原理为,LangSelector.jsp先实例化一个Locales对象,并把对象的Map类型的属性locales赋予下拉列表(select) 。如此一来,下拉列表就获得可用语言的列表。大家看到LangSelector有<s:form>标志和一段Javascript脚本,它们的作用就是在用户在下拉列表中选择了后,提交包含“reqeust_locale”变量的表单到Action。在打开页面时,为了下拉列表的选中的当前区域,我们需要到session取得当前区域(键为“WW_TRANS_I18N_LOCALE”的属性),而该属性在没有设置语言前是为空的,所以通过值栈中locale属性来取得当前区域(用户浏览器所设置的语言)。
你可以把LangSelector.jsp作为一个控件使用,方法是在JSP页面中把它包含进来,代码如下所示:
|
1 |
<lang:include value="/LangSelector.jsp"/> |
在例1中的HellloWorld.jsp中<body>后加入上述代码,并在struts.xml中新建Action,代码如下:
|
1 2 3 |
<action name="HelloWorld"> <result>/HelloWorld.jsp</result> </action> |
或者,如果你多个JSP需要实现上述功能,你可以使用下面的通用配置,而不是为每一个JSP页面都新建一个Action。
|
1 2 3 |
<action name="*"> <result>/{1}.jsp</result> </action> |
分布运行程序,在浏览器的地址栏中输入http://localhost:8080/Struts2_i18n/HelloWorld.action,出现图4所示页面:
在下拉列表中,选择“American English”,出现图5所示页面:

图4 HelloWorld.action
可能大家会问为什么一定要通过Action来访问页面呢?
你可以试一下不用Action而直接用JSP的地址来访问页面,结果会是无论你在下拉列表中选择什么,语言都不会改变。这表示不能正常运行的。其原因为如果直接使用JSP访问页面,Struts 2.0在web.xml的配置的过滤器(Filter)就不会工作,所以拦截器链也不会工作。
参考 http://www.blogjava.net/max/archive/2006/11/01/78536.html



