本文共 17746 字,大约阅读时间需要 59 分钟。
1.国际化
1、 国际化原理 ? 什么是国际化 ? 同一款软件 可以为不同用户,提供不同语言界面 ---- 国际化软件 需要一个语言资源包(很多properties文件,每个properties文件 针对一个国家或者语言 , 通过java程序根据来访者国家语言,自动读取不同properties文件 ) 2、 资源包编写 properties文件命名 : 基本名称_语言(小写)_国家(大写).properties 例如 : messages_zh_CN.properties 中国中文 messages_en_US.properties 美国英文 3、 ResourceBundle 根据不同Locale(地域信息),读取不同国家 properties文件 ResourceBundle bundle = ResourceBundle.getBundle("messages", Locale.US);----------------------------
struts2中国际化: struts2中对国际化进行了封装,我们只需要根据其提供的API进行访问就可以。 问题1:在struts2中国际化时properties文件怎样定义?(怎样定义properties) 1.全局 需要通过一个常量来声明. struts.custom.i18n.resources=testmessages,testmessages2 对于properties配置文件可以放置在任意位置 <constant name="struts.custom.i18n.resources" value="message"> 代表message.properties在src下 <constant name="struts.custom.i18n.resources" value="cn.itcast.i18n.resource.message"> 代表message.properties在cn.itcast.i18n.resource包下. 2.局部 1.针对于action类 位置:与action类在同一个包下. 名称:ActionClassName.properties. 这个配置文件只对当前action有效。 2.针对于package下所有action 位置:在指定的包下 名称:package.properties 3.jsp页面临时使用某一个properties文件. <s:i18n name="cn.itcast.action.package"></s:i18n> 问题2:在struts2中国际化操作可以在哪些位置使用?(在哪此位置上使用) 1.action类中使用 2.配置文件中使用<validation.xml> 3.在jsp页面上使用问题3:怎样在struts2中操作国际化?(怎样使用) 1.在action类中使用 前提:action类要继承ActionSupport类。 getText(String name)就可以获取配置文件中对应名称的值。 2.在validation.xml文件中使用 <message key="名称"/> 3.在jsp页面上使用 <s:text name="名称"> 如果没有使用<s:i18n name="">来指定,会从全局配置文件中获取。 如果要从某一个配置文件中获取,通过name属性来指定, 包名.配置文件名称 . -------------------------------------------------------- 在struts2中国际化配置文件中使用动态文本 1.action中怎样使用 msg=hello world {0} this.getText("msg",new String[]{"tom"}) 结果就是 hello world tom 2.jsp页面上怎样使用 msg=hello world {0} <s:i18n name="cn.itcast.action.I18nDemo1Action"> <s:text name="msg"> <s:param>张三</s:param> </s:text> </s:i18n> 结果就是 hello world 张三. =================================================================================================== 2.拦截器(interceptor) 介绍拦截器: struts2拦截器使用的是AOP思想。 AOP的底层实现就是动态代理。 拦截器 采用 责任链 模式 * 在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。 * 责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行 struts2中在struts-default.xml文件中声明了所有的拦截器。 而struts2框架默认使用的是defaultStack这个拦截器栈。 在这个拦截器栈中使用了18个拦截器。简单说,struts2框架 在默认情况下,加载了18个拦截器。 ----------------------------------------------------- 1.struts2中怎样使用拦截器 问题:使用拦截器可以做什么? 可以通过使用拦截器进行控制action的访问。例如,权限操作。 怎样使用拦截器? 1.创建一个Interceptor 可以自定义一个类实现com.opensymphony.xwork2.interceptor.Interceptor 在这个接口中有三个方法 init destory intercept, intercept方法是真正拦截的方法。 在intercept方法中如果要向下继续执行,通过其参数ActionInvocation调用它的invoke()方法就可以。 2.声明一个Interceptor 在struts-default.xml文件中 <interceptors> <interceptor name="" class=""/> </interceptors> 注意:我们要自己声明一个interceptor可以在struts.xml文件中声明。 3.在action中指定使用哪些拦截器. <interceptor-ref name="my"/> 注意:只要显示声明使用了一个拦截器。那么默认的拦截器就不在加载。 ----------------------------------------------------------------------- 2.分析拦截器原理 源代码执行流程: 1.在StrutsPrepareAndExecuteFilter中查找 在doFilter方法内有一句话 execute.executeAction (request, response, mapping) 执行Action操作. 2.在executeAction执行过程中会访问Dispatcher类中的serviceAction,在这个方法中会创建一个 ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false); 这就是我们的Action的代理对象 3.查看ActionInvocation,查看其实现类 DefaultActionInvocation. 在其invoke方法中 if (interceptors.hasNext()) {//判断是否有下一个拦截器. final InterceptorMapping interceptor = interceptors.next(); //得到一个拦截器 String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); //调用得到的拦截器的拦截方法.将本类对象传递到了拦截器中。 } finally { UtilTimerStack.pop(interceptorMsg); } } 通过源代码分析,发现在DefaultActionInvocation中就是通过递归完成所有的拦截调用操作. 关于interceptor与Filter区别: 1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。 2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。 3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。 4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。 5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。 ----------------------------------------------------------------------- 3.案例 权限控制: 1.login.jsp------>LoginAction------------->book.jsp 登录成功,将用户存储到session。 2.在book.jsp中提供crud链接。 每一个连接访问一个BookAction中一个方法。 要求:对于BookAction中的add,update,delete方法要求用户必须登录后才可以访问。search无要求。 怎样解决只控制action中某些方法的拦截? 1.创建类不在实现Interceptor接口,而是继承其下的一个子类.MethodFilterInterceptor 不用在重写intercept方法,而是重写 doIntercept方法。 2.在struts.xml文件中声明 <interceptors> <intercept name="" class=""> <param name="includeMethods">add,update,delete</param> <param name="excludeMethods">search</param> </intercept> </interceptors> =================================================================================================== 3.struts2中文件上传与下载
1.上传
浏览器端: 1.method=post 2.<input type="file" name="xx"> 3.encType="multipart/form-data"; 服务器端: commons-fileupload组件 1.DiskFileItemFactory 2.ServletFileUpload 3.FileItem struts2中文件上传: 默认情况下struts2框架使用的就是commons-fileupload组件. struts2它使用了一个interceptor帮助我们完成文件上传操作。 <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> 在action中怎样处理文件上传? 页面上组件:<input type="file" name="upload"> 在action中要有三个属性: private File upload; private String uploadContentType; private String uploadFileName; 在execute方法中使用commons-io包下的FileUtils完成文件复制. FileUtils.copyFile(upload, new File("d:/upload",uploadFileName)); ------------------------------------------------------------------------ 关于struts2中文件上传细节: 1.关于控制文件上传大小 在default.properties文件中定义了文件上传大小 struts.multipart.maxSize=2097152 上传文件默认的总大小 2m 2.在struts2中默认使用的是commons-fileupload进行文件上传。 # struts.multipart.parser=cos # struts.multipart.parser=pell struts.multipart.parser=jakarta 如果使用pell,cos进行文件上传,必须导入其jar包. 3.如果出现问题,需要配置input视图,在页面上可以通过<s:actionerror>展示错误信息. 问题:在页面上展示的信息,全是英文,要想展示中文,国际化 struts-messages.properties 文件里预定义 上传错误信息,通过覆盖对应key 显示中文信息 struts.messages.error.uploading=Error uploading: {0} struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3} struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3} struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}修改为
struts.messages.error.uploading=上传错误: {0} struts.messages.error.file.too.large=上传文件太大: {0} "{1}" "{2}" {3} struts.messages.error.content.type.not.allowed=上传文件的类型不允许: {0} "{1}" "{2}" {3} struts.messages.error.file.extension.not.allowed=上传文件的后缀名不允许: {0} "{1}" "{2}" {3} {0}:<input type=“file” name=“uploadImage”>中name属性的值 {1}:上传文件的真实名称 {2}:上传文件保存到临时目录的名称 {3}:上传文件的类型(对struts.messages.error.file.too.large是上传文件的大小)4.关于多文件上传时的每个上传文件大小控制以及上传文件类型控制.
1.多文件上传 服务器端: 只需要将action属性声明成List集合或数组就可以。 private List<File> upload; private List<String> uploadContentType; private List<String> uploadFileName; 2.怎样控制每一个上传文件的大小以及上传文件的类型? 在fileupload拦截器中,通过其属性进行控制. maximumSize---每一个上传文件大小 allowedTypes--允许上传文件的mimeType类型. allowedExtensions--允许上传文件的后缀名. <interceptor-ref name="defaultStack"> <param name="fileUpload.allowedExtensions">txt,mp3,doc</param> </interceptor-ref> ---------------------------------------------------------------------------------------------- 2.下载 文件下载方式: 1.超连接 2.服务器编码,通过流向客户端写回。 1.通过response设置 response.setContentType(String mimetype); 2.通过response设置 response.setHeader("Content-disposition;filename=xxx"); 3.通过response获取流,将要下载的信息写出。 struts2中文件下载: 通过<result type="stream">完成。 <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> 在StreamResult类中有三个属性: protected String contentType = "text/plain"; //用于设置下载文件的mimeType类型 protected String contentDisposition = "inline";//用于设置进行下载操作以及下载文件的名称 protected InputStream inputStream; //用于读取要下载的文件。 在action类中定义一个方法 public InputStream getInputStream() throws FileNotFoundException { FileInputStream fis = new FileInputStream("d:/upload/" + filename); return fis; } <result type="stream"> <param name="contentType">text/plain</param> <param name="contentDisposition">attachment;filename=a.txt</param> <param name="inputStream">${inputStream}</param> 会调用当前action中的getInputStream方法。 </result> 问题1:<a href="${pageContext.request.contextPath}/download?filename=捕获.png">捕获.png</a>下载报错 原因:超连接是get请求,并且下载的文件是中文名称,乱码。 问题2:下载捕获文件时,文件名称就是a.txt ,下载文件后缀名是png,而我们在配置文件中规定就是txt? <result type="stream"> <param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 --> <param name="contentDisposition">attachment;filename=${downloadFileName}</param> <param name="inputStream">${inputStream}</param><!-- 调用当前action中的getInputStream()方法 --> </result> 在struts2中进行下载时,如果使用<result type="stream">它有缺陷,例如:下载点击后,取消下载,服务器端会产生异常。 在开发中,解决方案:可以下载一个struts2下载操作的插件,它解决了stream问题。 ======================================================================================================== 4.ognl与valueStack。 问题:ognl是什么,它有什么用? OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言. 比el表达式功能强大。 struts2将ognl表达式语言,集成当sturts2框架中,做为它的默认表达式语言。 OGNL 提供五大类功能 1、支持对象方法调用,如xxx.doSomeSpecial(); 2、支持类静态的方法调用和值访问 3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 ) 4、支持赋值操作和表达式串联 5、操作集合对象。 问题:valueStack是什么,它有什么用?
1.ognl与valueStack
ognl中有一个OgnlContext,它可以设置root与非root .root中数据获取时,不需要加#,而非root中数据在获取时,需要加上#. 重点:学习struts2中使用ognl时,最后要知道 谁是OgnlContext,谁是root,谁是非root. ------------------------------------------------------------------------------------- 1.ognl介绍 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。 * xwork 提供 OGNL表达式 * ognl-3.0.5.jar OGNL 是一种比EL 强大很多倍的语言OGNL 提供五大类功能
1、支持对象方法调用,如xxx.doSomeSpecial(); 2、支持类静态的方法调用和值访问 3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 ) 4、支持赋值操作和表达式串联 5、操作集合对象。 演示:在struts2中使用ognl表达式 需要结合struts2的标签使用<s:property value="ognl表达式"> <s:property value="'abc'.length()"/> 演示对象调用方法 <s:property value="@java.lang.Math@max(10,20)"/> 演示静态成员访问. 注意:在struts2中使用静态成员访问,必须设置一个常量: struts.ognl.allowStaticMethodAccess=false -------------------------- 2.ValueStack 它是一个接口com.opensymphony.xwork2.util.ValueStack。 我们使用它是将其做为一个容器,用于携带action数据到页面. 在在页面上通过ognl表达式获取数据。 ===================================================================== 问题1:什么是valueStack? valueStack主要是将action数据携带到页面上,通过ognl获取数据 1.ValueStack有一个实现类叫OgnlValueStack. 2.每一个action都有一个ValueStack.(一个请求,一个request,一个action,一个valueStack) valueStack生命周期就是request生命周期。 3.valueStack中存储了当前action对象以及其它常用web对象(request,session,application.parameters) 4.struts2框架将valueStack以“struts.valueStack”为名存储到request域中。 --------------------------------------------- 问题2:valueStack结构? ValueStack中 存在root属性 (CompoundRoot) 、 context 属性 (OgnlContext ) * CompoundRoot 就是ArrayList * OgnlContext 就是 Map list集合中存储的是action相关信息 map集合中存储的是相关映射信息,包含 paramters,request,session,application attr等。 我们想要从list中获取数据,可以不使用#号.(它就是ognl的root) 如果从map中获取数据,需要使用#. (其实在struts2中的map--context其实就是ognlContext) 结论: ValueStack它有两部分 List Map 在struts2中List就是root Map就是ognlContext. 默认情况下,在struts2中从valueStack获取数据从root中获取。----------------------------------------------
问题3: 值栈对象的创建 ,ValueStack 和 ActionContext 是什么关系 ? ActionContext ctx = ActionContext.getContext(); if (ctx != null) { stack = ctx.getValueStack(); } valueStack是每一次请求时,都会创建. 在ActionContext中持有了valueStack的引用。 ------------------------------------------------------- 问题4:如何获得值栈对象? 对于valueStack获取有两种方式: 1.通过 request获取 ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); 2.通过ActionContext获取. ValueStack vs=ActionContext.getContext().getValueStack(); --------------------------------------------------------- 问题5:向值栈保存数据 (主要针对 root) 主要有两个方法 push(Object obj)------->底层就是 root.add(0,obj) 将数据存储到栈顶。 set(String name,Object obj);----->底层是将数据封装到HashMap中,在将这个HashMap通过push存储。 在jsp中 通过 <s:debug /> 查看值栈的内容 ------------------------------------------------------------ 问题6: 在JSP中获取值栈的数据 root中数据不需要#,而context中数据需要# 1.如果栈顶是一个Map集合,获取时,可以直接通过Map集合的key来获取value. <s:property value="username"/> 2.如果栈顶数据不是一个Map,没有key值,可以使用序号来获取。 <s:property value="[0]"> 从0的位置向下查找所有。 <s:property value="[0].top"> 只查找0位置上数据。 如果获取OgnlContext中数据: 1.request数据 request.setAttribute() 2.session数据 session.setAttribute() 3.application数据 application.setAttribute() 4.attr 依次从request,session.application中查找 5.parameters 获取请求参数 -------------------------------------------------------------- ValueStack主流应用:就是解决将action数据携带到jsp页面。问题:action向jsp携带数据,都是什么样的数据?
1.文本(字符串) 1.fieldError 校验数据错误信息提示 2.actionError 关于逻辑操作时错误信息(例如登录失败) 3.message 就是一个信息. this.addFieldError("msg", "字段错误信息"); this.addActionError("Action全局错误信息"); this.addActionMessage("Action的消息信息");* fieldError 针对某一个字段错误信息 (常用于表单校验)、actionError (普通错误信息,不针对某一个字段 登陆失败)、 actionMessage 通用消息
在jsp中使用 struts2提供标签 显示消息信息
<s:fielderror fieldName="msg"/> <s:actionerror/> <s:actionmessage/> 2.复杂数据 可以使用valueStack存储. 在action中存储数据: List<User> users = new ArrayList<User>(); users.add(new User("tom", "123", 20, "男")); users.add(new User("james", "456", 21, "男")); users.add(new User("fox", "789", 26, "男"));vs.push(users);
在页面上获取数据:
使用了<s:iterator>标签来迭代集合。 <s:iterator value="[0].top" var="user"> 这是将集合中迭代出来每一个元素起个名称叫user,而user是存储在context中,不在root中.l <s:iterator value="[0].top" var="user"> username:<s:property value="#user.username"/><br> password:<s:property value="#user.password"/> <hr> </s:iterator> 注意:如果我们在使用<s:iterator>进行迭代时,没有给迭代出的元素起名. <s:iterator value="[0].top"> username:<s:property value="username"/><br> password:<s:property value="password"/> <hr> </s:iterator> --------------------------------------------------- 关于默认压入到valueStack中的数据. 1.访问的action对象会被压入到valueStack中. DefaultActionInvocation 的 init方法 stack.push(action); * Action如果想传递数据给 JSP,只有将数据保存到成员变量,并且提供get方法就可以了 2.ModelDriveInterceptor会执行下面操作 ModelDriven modelDriven = (ModelDriven) action; ValueStack stack = invocation.getStack(); Object model = modelDriven.getModel(); if (model != null) { stack.push(model); } 将实现了ModelDrive接口的action中getModel方法的返回值,也就是我们所说的model对象压入到了 valueStack. -------------------------------------------------------------------------- 问题7:为什么el表达式可以访问valueStack中数据? struts2框架中所使用的request对象,是增强后的request对象。 ${username}---->request.getAttribute("username"); 增强后的request,会首先在request域范围查找,如果查找不到,会在valueStack中查找。 StrutsPreparedAndExecuteFilter的doFilter代码中 request = prepare.wrapRequest(request); * 对Request对象进行了包装 ,StrutsRequestWrapper * 重写request的 getAttribute Object attribute = super.getAttribute(s); if (attribute == null) { attribute = stack.findValue(s); } 访问request范围的数据时,如果数据找不到,去值栈中找 ? request对象 具备访问值栈数据的能力 (查找root的数据) -------------------------------------------------------------------------------- OGNL表达式常见使用($ % #)1.#号
用法一 # 代表 ActionContext.getContext() 上下文 <s:property value="#request.name" /> ------------> ActionContext().getContext().getRequest().get("name"); #request #session #application #attr #parameters 用法二 : 不写# 默认在 值栈中root中进行查找 <s:property value="name" /> 在root中查找name属性 * 查询元素时,从root的栈顶元素 开始查找, 如果访问指定栈中元素 <s:property value="[1].name" /> 访问栈中第二个元素name属性 * 访问第二个元素对象 <s:property value="[1].top" /> 用法三 :进行投影映射 (结合复杂对象遍历 ) 1)集合的投影(只输出部分属性 <h1>遍历集合只要name属性</h1> <s:iterator value="products.{name}" var="pname"> <s:property value="#pname"/> </s:iterator> 2)遍历时,对数据设置条件 <h1>遍历集合只要price大于1500商品</h1> <s:iterator value="products.{?#this.price>1500}" var="product"> <s:property value="#product.name"/> --- <s:property value="#product.price"/> </s:iterator> 3)综合 <h1>只显示价格大于1500 商品名称</h1> <s:iterator value="products.{?#this.price>1500}.{name}" var="pname"> <s:property value="#pname"/> </s:iterator> 用法四: 使用#构造map集合 经常结合 struts2 标签用来生成 select、checkbox、radio <h1>使用#构造map集合 遍历</h1> <s:iterator value="#{'name':'aaa','age':'20', 'hobby':'sport' }" var="entry"> key : <s:property value="#entry.key"/> , value: <s:property value="#entry.value"/> <br/> </s:iterator>--------------------------------------------------------
2.%号 %作用:就是用于设定当前是否要解析其为 ognl表达式. %{表达式} 当前表达式会被做为ognl解析. %{'表达式'} 当前表达式不会被做为ognl解析。 <s:property value="表达式"> 对于s:property标签,它的value属性会被默认做为ognl. 以后,所有表达式如果想要让其是ognl %{表达式} ---------------------------------------------------------- 3.$号 $作用:就是在配置文件中使用ognl表达式来获取valueStack中数据. 1.struts.xml <result type="stream"> <param name="contentType">${contentType}</param> </result> 2.在校验文件中使用 ${min} ${max} ${minLength} ${maxLength} 3.在国际化文件中使用 在properties文件中 username=${#request.username} 在jsp页面 <s:text name="username"> ----------------------------------------------------------------- 总结: #就是用于获取数据 %就是用于设置是否是ognl表达式 $就是在配置文件中使用ognl. ============================================================================================================== 2.防止表单重复提交 问题:什么是表单重复提交? regist.jsp----->RegistServlet 表单重复提交 危害: 刷票、 重复注册、带来服务器访问压力(拒绝服务) 解决方案: 在页面上生成一个令牌(就是一个随机字符串),将其存储到session中,并在表单中携带. 在服务器端,获取数据时,也将令牌获取,将它与session中存储的token对比,没问题, 将session中令牌删除。 struts2中怎样解决表单重复提交: 在struts2中解决表单重复提交,可以使用它定义的一个interceptor。 <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/> 步骤: 1.在页面上需要使用一个token tag 在表单中添加一个标签 <s:token/> 就会在页面上生成一个令牌,并存在于表单中。 2.需要在action中引入token拦截器 <interceptor-ref name="token"/> 3.需要配置视图 <result name="invalid.token">/token.jsp</result> 通过 <s:actionError/> 显示错误信息 覆盖重复提交信息 struts.messages.invalid.token=您已经重复提交表单,请刷新后重试 =============================================================================================== 3。struts2中json插件使用 1.struts2中怎样处理异步提交(ajax) 原始: HttpServletResponse response = ServletActionContext.getResponse(); response.getWriter().write("hello " + msg); response.getWriter().close(); 还可以使用struts2中提供的json插件: 1.导入json插件包 在struts2的lib包下 struts2-json-plugin-2.3.15.1.jar。 2.在struts.xml文件中配置 1.<package extends="json-default"> 2.设置视图<result type="json"> 这样设置后,会将valueStack栈顶数据变成json。 对于我们的程序,也就是会将action对象转换成json。 <param name="root">p</param> 如果没有设置,可以理解成将整个action都转换成json的数据。也就是 在action中提供的getXxx方法,就是json中的一个属性。 如果设置了root,那么,只将指定数据转换成json. -------------------------------------------------------- 怎样设置转换成json的对象中不包含特定的属性? 1. @JSON(serialize=false) 在getXxx方法上设置 2. 还可以通过json插件的interceptor完成. <param name="includeProperties">ps\[\d+\]\.name,ps\[\d+\]\.price,ps\[\d+\]\.count</param>转载地址:http://gmmgi.baihongyu.com/