【JavaWeb-17】国际化、自定义拦截器、struts2文件上传与下载、OGNL、contextMap

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>

源代码:JavaEE 拦截器用户登录案例源代码

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;
    }
    ……
}

源代码:JavaEE struts2文件上传

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,就可以直接下载文件了,前提是我们把一个文件拷贝到服务器对应项目的目录中去。

源代码:JavaEE Struts2文件下载

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这部分我们主要的目的是了解数据是怎么存的和怎么取出来用的。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值