1、国际化。先写几个properties文件,格式是文件名(随便写)+语言名+国家名。
——我们创建3个配置文件,用的是同一个key(UserField),但是值不一样。其中有一个没有语言和国家名的文件是默认文件,如果其他找不到的话就会用这个默认文件。
——然后,我们用ResourceBundle获取文件资源,它有一个getBundle方法可以添加2个参数,第二个参数就是locale参数,用于指定国家的。
——需要注意的是,这里面获取文件资源时把文件当做类文件来用,所有路径是com.hello.test.hi
,不需要添加后缀properties。
2、我们结合jsp文件来使用的时候。key是有规范的,一般是jsp+文件名+控件名
。我们可以在hi_zh_CN.properties
文件中写如下代码,然后在其他文件中写另外一些配置。
jsp.index.title=首页
jsp.index.username=用户名
……
——在jsp文件中,使用时如下。
<%
Locale locale=request.getLocale();
ResourceBundle bundle=ResourceBundle.getBundle("com.hello.test.hi",locale);
%>
……
<title><%=bundle.getString("jsp.index.title")%></title>
// 逐个替换即可
——当然,我们可以用jstl来代替上面的代码,因为我们不应该在jsp文件中写太多的java代码。
3、利用Struts2配置国际化。我们在struts.xml文件中加入一个常量。
<constant name="struts.custom.i18n.resource" value="com.hello.test.hi">
——我们的配置文件有3个地方可以放。一是放在单独的包里面比如com.hello.resource
,二是放在com.hello包(package)里,还有一个是和action动作类同级里面。如果直接访问jsp文件,那么就直接全局的配置文件,如果经过了动作,那么就会优先去找动作类里面的。当找到资源包了但是没有对应的key那么就把key的值输出来,如果找不到资源包,它会按照搜索顺序向上找,具体详见struts的localization文档。
4、 自定义拦截器。
——先定义一个继承AbstractInterceptor的拦截器类,实现interceptor方法。
public class MyInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation arg0) throws Exception {
System.out.println("MyInterceptor-放行之前!");
arg0.invoke();
System.out.println("MyInterceptor-放行之后!");
return null;
}
}
——然后需要在struts中配置这个拦截器,然后在里面配置使用。
<struts>
<constant name="struts.devMode" value="true"></constant>
<package name="p_name_1" extends="struts-default">
<interceptors>
<interceptor name="MyInterceptor" class="com.hello.web.interceptor.MyInterceptor"></interceptor>
</interceptors>
<action name="action1" class="com.hello.web.action.MyAction1" method="go">
<interceptor-ref name="MyInterceptor"></interceptor-ref>
<result name="success">index.jsp</result>
</action>
</package>
</struts>
——我们可以观察执行顺序,是先被拦截器拦截了,但是执行的是放行前的代码,然后放行去action,action再去对应的jsp文件,最后再返回到拦截器放行后的代码。
MyInterceptor-放行之前!
MyAction1的go方法执行了!
index.jsp文件执行了!
MyInterceptor-放行之后!
——我们看到在拦截器的interceptor方法里arg0.invoke();
的值是success,所以如果我们不自己定义的话,一系列拦截器就会一路返回success,我们可以在这个方法里面自定义返回input等,这样在动作类的结果视图配置部分我们就可以增加一个<result name="input">index.jsp</result>
用于回显。
——当有多个拦截器的时候,执行顺序由引用拦截器的顺序决定的,而不是声明拦截器的顺序决定的。执行顺序就是:
MyInterceptor-1-放行之前!
MyInterceptor-2-放行之前!
MyAction1的go方法执行了!
index.jsp文件执行了!
MyInterceptor-2-放行之后!
MyInterceptor-1-放行之后!
简单拦截器案例源代码:JavaEE 拦截器简单示例源代码
5、做一个用户登录的例子,结合自定义拦截器。我们有3个页面,一个是登录页面,一个是主页,一个是 其他页面。在没有登录的情况下是无法访问主页和其他页面的,登录之后可以访问主页并在主页上链接到其他页面。就是要做一个自定义拦截器判断是否登录。
——我们主要看的是自定义拦截器的实际案例中的配置。这是我们的拦截器。注意看到的是我们的拦截器继承了MethodFilterInterceptor
,而不是AbstractInterceptor
,因为前者多了几个方法包括排除不需要拦截器方法的方法等。我们这里的login页面不需要拦截器拦截来判断,所以需要排除。
public class CheckLoginInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation arg0) throws Exception {
HttpSession session=ServletActionContext.getRequest().getSession();
Object obj=session.getAttribute("user");
if(obj==null){
return "input";
}
String rtValue=arg0.invoke();
return rtValue;
}
}
——其次是在struts.xml里面的配置。可以看到,我们先自定义了一个拦截器栈,然后把我们定义的拦截器栈设置为默认拦截器栈,这里有2个原因,一个是我们如果使用了自己定义的那1个拦截器,那么其他默认拦截器是不工作的,所以这个时候需要我们再把他们手动配置进来,然后加上我们自定义的,组成一个新的栈,用的标签是interceptor-stack
。第二个原因是如果我们每次都要在action里面写引用我们自定义的拦截器栈很麻烦,所以我们直接把它设置成默认的,这个默认的设置是在struts-default.xml文件中定义的,我们在项目的struts.xml文件中设置的话,会覆盖struts-default.xml里面的配置,所以这里我们就用标签default-interceptor-ref
做了一次覆盖的默认拦截器的设置。
——此外,如果我们在很多action里面都用到同一个结果视图,我们就可以把它设置成全局结果视图,这种用法尤其适合404等页面的设置。我们用的标签是global-results
。
<struts>
<constant name="devMode" value="true"></constant>
<package name="p_name_1" extends="struts-default ">
<interceptors>
<interceptor name="CheckLoginInterceptor" class="com.hello.web.interceptor.CheckLoginInterceptor"></interceptor>
<interceptor-stack name="MyDefaultInterceptor">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="CheckLoginInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="MyDefaultInterceptor"></default-interceptor-ref>
<global-results>
<result name="input">/login.jsp</result>
</global-results>
<action name="login" class="com.hello.web.action.MyAction" method="login">
<interceptor-ref name="MyDefaultInterceptor">
<param name="CheckLoginInterceptor.excludeMethods">login</param>
</interceptor-ref>
<result type="redirectAction">showMain</result>
</action>
<action name="showMain" class="com.hello.web.action.MyAction">
<result>/index.jsp</result>
</action>
<action name="showOther" class="com.hello.web.action.MyAction">
<result>/other.jsp</result>
</action>
</package>
</struts>
——最后一个知识点是,我们既然设置了默认拦截器,这就意味着每个action都会被拦截,但是我们如果不需要被拦截的话,就需要在单独的action里面设置被排除在外的方法,即不需要拦截的方法。下面的意思是,执行login.action的时候,要经过拦截器,但是经过拦截器的时候,遇到login方法的话,就不执行我们自定义的拦截器CheckLoginInterceptor
。
<action name="login" class="com.hello.web.action.MyAction" method="login">
<interceptor-ref name="MyDefaultInterceptor">
<param name="CheckLoginInterceptor.excludeMethods">login</param>
</interceptor-ref>
<result type="redirectAction">showMain</result>
</action>
6、文件上传入门案例。
public class MyAction extends ActionSupport {
private String username;
private File avatar;
//这个获取文件名的命名是有规范的,avatar是表单的name值+FileName即可
private String avatarFileName;
private String avatarContentType;
public String upload(){
ServletContext application=ServletActionContext.getServletContext();
String filePath=application.getRealPath("/WEB-INF/avatar");
File file=new File(filePath);
if(!file.exists()){
file.mkdirs();
}
// rename的话是把临时文件直接剪切到目标地址,FileUtils.copyFile是复制临时文件。
avatar.renameTo(new File(file,avatarFileName));
return null;
}
//省略几个setter和getter方法
}
——上传较大文件时,有错误HTTP Status 404 - No result defined for action com.hello.web.action.MyAction and result input
,需要更改默认文件大小,默认是2M。在struts.xml中修改:
<constant name="struts.multipart.maxSize" value="100000000"></constant>
——我们定义了可允许的上传后缀:
<action name="upload" class="com.hello.web.action.MyAction" method="upload">
<interceptor-ref name="defaultStack">
<param name="fileUpload.allowedExtensions">jpg,png,bmp</param>
</interceptor-ref>
<result name="input">/index.jsp</result>
</action>
上传其他后缀时有如下报错:
——还可以规定文件类型:
<param name="fileUpload.allowedTypes">image/jpg,image/png</param>
如果有错误,错误提示如下,有提示文字+{0}{1}{2}{3}部分组成:
——但是这个错误是可以修改的。在src下面新建一个properties文件,这个文件里面配置的就是错误信息,其中第三个就是文件类型错误时候的提示信息,我们把{0}"{1}""{2}"{3}
去掉,只保留提示文字。
——然后在struts.xml文件中配置:
<constant name="struts.custom.i18n.resources" value="com.hello.web.action.uploadMessage"></constant>
——最终我们可以看到这个错误提示变成了:
——接着说,上传多个文件,就是把之前那些文件变量和文件名变量变成数组,然后增加了一个for循环移动文件:
public class MyAction extends ActionSupport {
private String username;
private File[] avatar;
private String[] avatarFileName;
private String[] avatarContentType;
public String upload(){
ServletContext application=ServletActionContext.getServletContext();
String filePath=application.getRealPath("/WEB-INF/avatar");
File file=new File(filePath);
if(!file.exists()){
file.mkdirs();
}
for(int i=0;i<avatar.length;i++){
avatar[i].renameTo(new File(file,avatarFileName[i]));
}
return null;
}
……
}
7、struts2文件下载。主要两部分,一部分是在动作类中做一些操作,另一部分是在struts.xml中配置一些操作。
——在动作类中:
public class MyAction extends ActionSupport {
private InputStream inputStream;
public String download() throws FileNotFoundException{
//找到文件路径
String filePath=ServletActionContext.getServletContext().getRealPath("/WEB-INF/test.jpg");
//把文件读到输入流里面
inputStream=new FileInputStream(filePath);
//返回成功
return SUCCESS;
//剩下的由
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
——在struts.xml中:
<package name="p_name_1" extends="struts-default">
<action name="download" class="com.hello.web.action.MyAction" method="download">
<result name="success" type="stream">
<param name="contentType">application/octet-stream</param>
<param name="contentDisposition">attachment;filename=test.jpg</param>
<param name="inputname">inputStream</param>
</result>
</action>
</package>
在浏览器中输入http://localhost:8080/Day01_StrutsDownload/download.action
,就可以直接下载文件了,前提是我们把一个文件拷贝到服务器对应项目的目录中去。
8、OGNL(Object Graphic Navigation Language)对象图导航语言。Struts2将OGNL作为默认的表达式语言。
——OGNL的使用需要结合struts标签库。
<body>
<s:property value="'这是普通字符串,要加单引号!'" /><br>
OGNL访问普通方法,test字符串的长度:
<s:property value="'test'.length()" /><br>
OGNL访问静态属性,需要@全类名@静态属性名称:
<s:property value="@java.lang.Integer@MAX_VALUE" /><br>
OGNL默认是不能访问静态方法的,在default.properties中有一个配置,我们在struts.xml中覆盖设置一下即可,struts.ognl.allowStaticMethodAccess=true
<s:property value="@java.lang.Math@random()" /><br>
OGNL利用{}相当于创建list,可以用于radio表单,但是这里的value值就是内容值男和女,如果我们想设置value值,需要用map
<s:radio name="gender" list="{'男','女'}"></s:radio><br>
OGNL使用“# {}”创建Map,可以指定表单的value。
<s:radio name="gender" list="#{'0':'男','1':'女'}"></s:radio><br>
</body>
结果显示:
——利用OGNL修改文件下载的那个案例。那个案例中,我们在struts.xml指定了文件下载的名字。其实可以通过采用OGNL表达式进行修改。
<param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(filename,"UTF-8")}</param>
这里的filename就是我们在动作类里面自定义的,并且给这个filename赋值了。
9、contextMap。
——动作类每次请求是都会实例化,所以是多例的,线程安全的。
——每次动作执行前,核心控制器StrutsPrepareAndExecuteFilter都会创建ActionContext和ValueStack对象,每次都会创建。都放在ThreadLocal里面,是安全的。
——ContextMap是OGNL的上下文。它包含了ActionContext和ValueStack。ContextMap本身是一个Map。
——其中ActionContext里面有:application(是一个Map,封装了应用域的属性)、session(是一个Map,封装了会话域的属性)、request(是一个Map,封装了请求域的属性)、parameters(是一个Map,封装了参数)、attr(是一个Map,封装了4个域的全部属性)。里面的4个都是Map。
——其中ValueStack是一个List。
——contextMap这部分我们主要的目的是了解数据是怎么存的和怎么取出来用的。