Struts2

struts2-第一天

框架搭建

    1.jar包的准备

    2.配置web.xml,配置struts2核心控制器,接收所有的请求(可在dispatcher.ng.filter包下拷贝,把.class去掉)。

    3.拷贝struts.xml和log4j到src目录下。

        3.1写个package包,给个名字:user,包就相当于一个模块,再添加extends="struts-default"

        3.2里面写action标签,写name和实体类

        3.3里面写result标签,写name和页面

执行的过程:请求url满足了/*,就交给了核心控制器,它启动会把struts.xml文件的配置全部读到内存中,用一个对象来保存,会在这<action>里面找有没有login的配置,接着会找到对应的类,当action配置里没有配method属性,它就执行业务逻辑控制器类里的execute(),方法完成后会返回字符串,这个字符串会交给核心控制器,再找<result>里对应的字符串,匹配就会用这个页面生成响应到客户端。

struts2-core-2.3.15.1.jar里org/apaqi/struts有配置文件default.properties,

    1.更改请求后缀<constant name="struts.action.extension" value="do">

    2.开启开发模式<constant name="struts.devMode" value="true">

ActionSupport类

    struts.xml里,<package>配置extends="struts-default"的好处是,如果action里没有配置class属性,将使用struts-default.xml(在核心jar包里)中配置的默认类com.opensymphony.xwork2.ActionSupport(最后面),如果没有配置method,就使用execute(),如果result里没有配置name,默认是success

请求参数接收

    一、标量值方式接收参数

        struts对于提交到action的参数,会当作这个action有这个属性,就会调用这个参数set方法来接收(保存)提交的参数。

    二、复合类型方式接收参数(常用)

    三、模型驱动方式获取参数(少用)

        1.业务逻辑控制器类要实现ModelDriven<?>接口

        2.预先创建user,并提供方法返回user

        3.代码不需要写getset方法

    四、servlet API方式获取参数(需掌握)

        1.业务逻辑控制器类要继承ActionSupport

        2.通过ServletActionContext.getRequest()得到HttpServletRequest

        3.就可以用以前getParameter()

Servlet APi 获取

    一、ServletActionContext类(重点)

        ServletActionContext.getRequest().getSession()

    二、Struts2Aware拦截器IOC模式(重点)

        1.实现ServletRequestAware接口,重写setServletRequest(HttpServletRequest request),把参数赋值给成员变量HttpServletRequest request,后面就可以使用request。

    三、四、ActionContext类的get方法

Action动态方法

    需要开启动态方法调用支持,<constant name="struts.enable.DynamicMethodInvocation" value="true"/>

    一、login!login.do叹号方式.注意:用感叹号动态调用时,配置文件的action是不写!的,在提交地址那里写,如不写!指定调用的方法,默认调用execute方法。

    二、login_*统配

命名空间

    1.命名空间前面加/

        规则是:1.1如果命名空间存在,进入命名空间搜索action,action存在就执行,不存在就报异常

    1.2如果命名空间不存在,自动删除最后的命名空间,再进行搜索

    2.加命名空间后,提交地址要加前面那段(不能/开头),设置了后缀加后缀,但配置那里的action的name属性不加后缀(这里必须注意)。

    3.当配置文件struts.xml的<package/>用了命名空间时,响应页面一般要加/。而提交地址要加命名空间.

六、类型转换

    2013-1-15默认会转换?月份有0吗?

    自定义类型转换器的实现

        方式1.继承DefaultTypeConverter类

        1.1如上,UserAction类要继承ActionSupport类(如不继承,无法显示异常等)

        1.2重写convertValue方法

        1.3在action包下新建一个同名的UserAction-conversion.properties,文件内容:user.loginTime属性 = org.fkjava.converter.MyConverter转换器

            全局的跟struts.xml配置文件放在一起

    方式2.继承StrutsTypeConverter类(抽象类)(推荐使用)

        --有两个要实现的方法,主要实现前台到后台convertFromString(Map context,String[] values,Class toType)方法,第一个参数是上下文,第二个是提交的实际数据,第三个是提交数据的类型

            方法实现逻辑:判断values不为空后,如果写的是转换时间的转换器,判断toType==Date.class,一旦涉及到时间对象,simpleDateFormat,从字符串转换为Date类型用sdf.parse(values[0])

    全局:在src下xwork-conversion.properties,java类型=自定义转换器

    转换错误显示:

        1.默认:

        1.1<%@taglib prefix="s" uri="/struts-tags"%>,<s:fielderror>

    2.局部类型转换失败自定义信息

        2.1在action包下新建一个UserAction.properties文件,内容是invalid.fieldvalue.属性名(表单提交的name属性user.loginTime)=提示语句

        2.2throw new TypeConstraintException("日期类型转换失败!");

    3.全局类型转换失败自定义信息

        3.1 src根目录下,fkjava.properties国际化资源文件:xwork.default.invalid.fieldvalue={0},提示信息

        3.2 struts.xml下配置常量name="struts.custom.i18n.resources" value="fkjava"/>

七、后台验证

      如果希望使用struts2提供的验证框架,必须extends ActionSupport

      调用步骤:

      1.先类型转换

      2.验证

      3.调用目标action方法

      方式一:代码方式实现输入验证

          a.对action所有方法进行验证

          方式1:重写ActionSupport中的validate方法

          //针对本action的所有action方法进行验证

    public void validate(){

        System.out.println("-------validate()--------");

        if(user == null || "".equals(user.getUserName())){

            /**一旦addFieldError()加了东西,就会返回INPUT*/

            this.addFieldError("user.userName","请输入用户名");

        }

    }

    --输出错误信息:<s:fielderror/>

      b.对单个方法进行验证,只需要在这个方法的后面加你要验证的方法,如login方法要验证,validateLogin()

      方式二:配置xml方式实现输入验证(比较常用)

          xml文件名写法:NoticeAction-addNotice-validation.xml针对一个action请求做后台验证(中间是<action name=""/>里name的值)

                     NoticeAction-validation.xml针对整个NoticeAction处理类都做后台验证

      例子:

      <!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0//EN" 

    "http://struts.apache.org/dtds/xwork-validator-1.0.dtd">

    <validators>

        <field name="user.userName">

            <field-validator type="requiredstring">

                <param name="trim">true</param>

                <message>请输入用户名3!</message>

            </field-validator>

            <field-validator type="stringlength">

                <param name="minLength">4</param>

                <param name="maxLength">6</param>

                <message>请输入${minLength}~${maxLength}范围的用户名3</message>

            </field-validator>

        </field>

    </validators>

国际化

    表单:

    1.写fkjava.properties国际化文件,依次再写其他语言的properties。说明:里面写的就是需要替换的字眼

    2.用struts2标签库写表单(先引入),表单里的每一项多一项key的属性,写的就是properties里面的key

    3.如果想动态改变语言环境,加一个带request_locale参数的表单,如直接提交到jsp不行,就配置一个最简单的action把表单提交到action,再<result>到jsp

    页面:

    1.使用i18n标签,name=资源文件前缀,<s:text name=资源文件中的key>,再里层标签<s:param>${username}</s:param>

    <s:i18n name="fkjava">

        <s:text name="welcome">

            <s:param>${user.userName}</s:param>

        </s:text>

    </s:i18n>

    代码中:

    1.put之后,利用ActionContext.getContext.put("msgs",this.getText("国际化资源里的key")),页面上取出:${msgs}

result的type属性

    服务器重定向

        1.dispatcher(默认),转到资源(jsp html 其他视图技术)

    2.chain,转到一个action,参数有actionName,nameSpace,method等。

    客户端重定向(参数不会自动传递,如需传递,在actionName拼接,或加<param name="user.userPass">123456</param>)

    动态跳转:定义动态结果页面(如果登录名等于tom,跳到tom.html)定义成员变量typePath,并提供getset方法

    全局result

        1.包里配置<global-results>。当局部没有找到返回的字符串时,会去找全局,如果全局也找不到就生成默认错误响应。

    2.如果局部全局都有,局部起作用。

异常处理

    1.局部<exception-mapping result="result字符串" exception="java异常"/>

    2.全局<global-exception-mappings>

        <exception-mapping result="" exception=""/>

    注意:当局部异常处理找不到才会找全局

    3.异常信息显示

        方式一:引用struts标签库,<s:property value="exception.message">和<s:property value="exceptionStack">

    方式二:直接EL表达式:${exception.message}和${exceptionStack}

拦截器

   自定义拦截器

    1.写一个类继承AbstractInterceptor,重写intercept方法。

    2.invocation.invoke()把控制权限交给下一个拦截器,没有的话交给action,返回值String就是提交给action执行的方法执行完以后返回的字符串

    3.配置拦截器在package里,引用(使用)是在action里。

    4.一旦自定了拦截器,默认的就不起作用。

    5.拦截器的定义顺序决定了调用的顺序,最好先配置好默认的拦截器

    6.如果有一堆的拦截器要用,可以配置一个拦截器栈

    7.如果在一个package中不想每一个action都写引用拦截器,可以在package里定义默认拦截器<default-interceptor-ref name=""/>(本包有效)

   方法拦截器

        1.写一个类继承MethodFilterIntercepter,重写doIntercept方法。

    2.引用时多一项参数<param name="includeMethods">login,logout</param>,指定哪些会调用拦截器(白名单)

    3.如果白名单和黑名单冲突,白取胜(邪不压正)。

   内置拦截器

       timer:统计action方法执行耗时的拦截器

       token:防止表单重复提交

           1.在表单提交的action上调用默认拦截器<interceptor-ref name="token"/>

       2.配置当出现重复提交时到什么页面<result name="invalid.token">error.html</result>

       3.页面上用struts标签库,在form里加上<s:token/>,其他不变

   登录检查

       1.自定义一个拦截器

       String result = "login";

    if(flag){

        HttpSession session = ServletActionContext.getRequest().getSession();

        User u = (User)session.getAttribute("loginUser");

        if(u != null){

            result = invocation.invoke();

        }

    }else{

        result = invocation.invoke();

    }

    return result;

       2.配置(声明)拦截器

       3.调用拦截器(先调用系统默认),里面还可以给拦截器参数(拦截器的成员变量要有getset方法)

freemarker

    使用要引入一个jar包和要在web.xml文件中做一个配置。

文件上传下载(注意这里:配置跟default.properties有关!)开始是在struts2-core-2.3.15.1.jar这个jar文件里的

    一、单个文件上传

        上传的form表单的method属性必须是post,enctype必须是multipart/form-data

        1.页面的表单<form action="upload.do" method="post" enctype="multipart/form-data">

            <input type="file" name="headImg">

            <input type="submit" value="上传"/>

            </form>

    这时要配置action:

         <package name="user" extends="struts-default" >

              <action name="upload" class="org.fkit.action.UserAction" method="upload">

            <result name="upload">success.jsp</result>

            <result name="input">error.jsp</result>

          </action>

         </package>

    注意这里action配置没有命名空间,如果有,表单提交action要加命名空间。??

    2.UserAction里

         2.1增加属性

            private File headImg;//接收文件用的属性(可以跟表单的不一致,因调的是set方法)

            private String headImgFileName;

            private String headImgContentType;

            //三个属性要增加getset方法

            说明:这样写才能获取到文件名和文件类型

         2.2.upload方法里

            //获取服务器的资源(存储)路径

        String path = ServletActionContext.getServletContext().getRealPath("images");

        File file = new File(path,headImgFileName);

        if(!file.getParentFile().exists()){

             file.getParentFile().mkdirs();

        }

        FileUtils.copyFile(headImg, file);

         2.3如想方便查看上传的文件,可设置上传路径:双击tomcat,在ServerLocations那里,

               选中间那一栏(使用tomcat安装路径)。

    二、多个文件上传

    属性改为数组,循环new File,其他基本不用变。

    上传约束配置

        1.全局大小限制:struts.xml里,配置整个工程:<constant name="struts.multipart.maxSize" value="数值">

    2.局部大小限制:局部的大小限制必须小于全局大小限制!在对应的action下配置<interceptor-ref name="fileUpload"/>拦截器,拦截器里可以设置参数:

         <param name="maximumSize">数值</param>

    注意点:建议把默认的拦截器放在上传约束拦截器下面。

    3.类型约束:<param name="allowedTypes">image/pjpeg,image/x-png</param>

    4.后缀约束:<param name="allowedExtensions">.jpg,.png,.bmp</param>

    三、下载

        1.struts-user.xml文件配置下载的action:

        <package name="user" namespace="/" extends="struts-default">

            <action name="download" class="org.fkjava.action.UserAction">//没配method默认执行execute方法

            <result name="success" type="stream">

                <param name="contentType">application/octet-stream</param>

                <param name="inputName">fileStream(要与Action类getset方法对应)</param>

                <param name="contentDisposition">attachment;filename="${fileName}"</param>//对应action类fileName属性get方法

            </result>

        </action>

        </package>

    2.在UserAction类增加getFileStream方法

    public InputStream getFileStream(){

          return ServletActionContext.getServletContext().getResourceAsStream("/images/"+fileName);

    }

    还要增加fileName属性,提供getset方法,在页面上增加这个参数。

    3.中文乱码:在取流的方法里,return之前加上这句(getResource里的变量毫无疑问改成s):

    String s = new String(fileName.getBytes("ISO-8859-1"),"UTF-8");

    4.下载页面:

    <a href="download.do?fileName=mm.jpg">下载1</a>

OGNL表达式

     <s:debug/>标签:用来调试程序

     值栈(valueStack):取值栈里的值:${user.userName}或者<s:property value="user.userName"/>

     栈(stack)中获取数据要加#号:<s:property value="#request.r"/>或者${requestScope.r}或者${r}(先找小范围,再找大范围,如果小范围找到,不会再找大范围)

     访问对象属性中还是对象:<s:property value="user.course.name"/>即:对象.对象.属性

     访问方法:调用值栈中对象的普通方法:<s:property value="user.sayHello()"/>

     开启允许静态方法调用:

         <constant name="struts.ognl.allowStaticMethodAccess" value="true"/>

取值栈的一个例子:

<td><s:radio theme="simple" list="#{'1':'男','2':'女'}" id="sex" name="vo.sex" value="%{#request.vo.sex}"></s:radio></td>

Struts2标签

     1.控制标签

          1.1-iterator标签操作Map:

      方式一:操作简单的Map:

      value是json数据,var是临时变量,显示用<s:property value="#var里的变量.key">,<s:property value="#var里的变量.value">

      方式二:后台准备数据,

      Map<String, Object> request = ac.getContextMap();

      Map<String, String> maps = new HashMap<>();

      maps.put("A","中国");

      maps.put("B","美国");

      maps.put("C","英国");

      request.put("maps",maps);

      页面:value="#request.maps",其他不变

      1.2—iterator标签操作List:

           方式一:这时value不用#号{'a','b','c'},显示就去掉.key和.value,其他一样。

           方式二:List<User> userList = new ArrayList<>();

           userList.add(new User("admin",12,new Date()));

           userList.add(new User("admin2",13,new Date()));

           request.put("userList",userList);

           页面:value="#request.userList" var="l",显示<s:property value="#l.userName"/>

      iterator标签进行数据过滤操作

           ?获取满足条件的所有数据

           value="#request.userList.{?#this.userPass %2 != 0}"//意思是判断当前迭代对象的userPass与2取余不为0就输出

           ^获取满足条件的第一条数据

           value="#request.userList.{^#this.userPass %2 != 0}"

           $获取满足条件的最后一条数据

           value="#request.userList.{$#this.userPass %2 != 0}"

     if...else if...else

          <s:if test="attr.number % 7 == 0">

               输出语句

          </s:if>

          <s:elseif test="attr.number % 5 == 0">

               输出语句

          </s:elseif>

          <s:else>

               输出语句

          </s:else>

     append集合合并标签:把两个list拼接在一起放在newList里

          <s:append var="newList">

               <s:param value="{1,2,3}"/>

           <s:param value="#userList"/>

          </s:append>

          <s:iterator value="#newList" var="nl">

               <s:property value="#nl"/><br>

          </s:iterator>

     generator字符串分割标签:分割完是一个数组g

          <s:generator separator="," val="'a,b,c'" var="g">

          <s:iterator value="#g" var="nl">

               <s:property value="#nl"/><br>

          </s:iterator>

     subset获取子集合

        方式一:在里面迭代

          <s:subset source="{1,2,3,4,5}" start="2" count="2">

               <s:iterator var="s">

                    <s:property value="#s"/><br>

           </s:iterator>

          </s:subset>

        方式二:在其他地方迭代

          <s:subset source="{1,2,3,4,5}" start="2" count="2" var="subs"/>

          <s:iterator value="#attr.subs" var="s">

           <s:property value="#s"/><br>

          </s:iterator>

    2.数据标签

         set标签:指定把某值用var的变量名字设置到某个范围

          <s:set value="要存的值" var="myVar" scope="session"/>

          <s:property value="#session.myVar"/>

     push标签:将某个值置于值栈的顶部,标签结束后,将从值栈中移除。

          <s:push value="'push中的数据'">

               <s:property/>

          </s:push>

     bean标签:可以在页面创建一个对象

          <s:bean name="org.fkit.bean.User" var="u">

               <s:param name="userName">tom</s:param>

               <s:param name="userPass">123456</s:param>

              </s:bean>

          <s:property value="#u.userName"/>--<s:property value="#u.userPass"/>

     date标签:

          <%

               request.setAttribute("now",new java.util.Date());

          %>

          <s:date name="#request.now" format="yyyy年MM月dd日HH时mm分ss秒"/>

     include标签

================================

4、struts2的处理流程

a、browser-->struts2控制器-->查找action(在配置文件找)-->(回到)struts2控制器-->拦截器1,2,3...-->动作Action-->(request)struts2控制器-->查找视图(配置文件找)-->struts2控制器-->视图-->browser

一个请求在Struts2框架中的处理大概分为以下几个步骤 

1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求 

2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)

3 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action 

4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy 

5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类 

6 ActionProxy创建一个ActionInvocation的实例。 

7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。 

8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper

在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。

5、Struts2配置文件

a、可以给每个模块定义一个包(Package元素的namesapce属性)

b、继承某个package(Package元素的extends属性)

c、为了让大型应用的struts.xml更好管理,可以把它分成几个较小的文件ps:

<include file="user.xml"></include>

6、拦截器是什么?struts-default.xml

是将控制器不做的事情封装在另外一个地方

a标签路径最好是通过绝对路径来:request.getContextPath()

接收参数三种方式:

1.Action属性来接收

2.用VO来封装表单属性

3.利用ModelDriven模型驱动来接收

要实现 implements ModelDriven<T>接口,Action里面重写getModel()方法。

==========填充下拉列表==========

第一种:

<s:select list="#{1:'男',2:'女'}" name="employee.sex" headerKey="0" headerValue="--请选择性别--"></s:select>

第二种:

先定义private List<Map<String,Object>> jobs;

<s:select list="jobs" name="employee.job.id" headerKey="0" headerValue="--请选择职位--"  listKey="id" listValue="name"></s:select>

第三种:

$.ajax({

    url : "${ctx}/dept/loadDeptAjax.action",

    type:"post",

    dataType:"json",

    async : true,

    success:function(data){

        //获取选中的

        var deptId = "${employee.dept.id}";

        alert();

        $.each(data,function(){

            $("<option/>").val(this.id).text(this.name).attr("selected",this.id == deptId).appendTo("#deptSelect");

        });

    }

});

List<Map<String,Object>>dept = deptMapper.find(null);

========================Struts2========================

namespace

1、命名空间一般是以“/”开头

2.当namespace=""时,它会匹配所有的。

request.getScheme()+request.getServletName()+request.getServletPort()+path;

动态方法调用(DMI):

意思就是不配置method属性,在请求的时候这样:user!add

<action name="student*" class="xx" method="{1}">

    <result >/student{1}_success.jsp</result>

</action>

<action name="*_*" class="{1}Action" method="{2}">

    <result >/{1}_{2}_success.jsp</result>

</action>

参数接收:

1.普通类型

2.用DTO来接收

public class UserDTO{

    private String name;

    private String pwd;

    private String cpwd;

}

3.模型驱动:

public class UserAction implements ModelDriven<User>{

    private User user = new User();

    public String add(){

        return SUCCESS;

    }

    public User getModel(){

        return user;

    }

}

参数校验:

addFieldError("name","name is error!");

页面:

<s:fielderror fieldName="name" theme="simple"/>

(Map)ActionContext.getContext().get("request");

session = ActionContext.getContext().getSession();

在页面上拿,要用#key这种方式。

Result的配置

<result type="chain">

forward到另外一个action

全局result

<global-results>

    <result name ="mainpage">/main.jsp</result>

</global-results>

动态结果集

<result>${r}</result>

遍历操作:

<s:iterator value="{1,2,3}">

</s:iterator>

<s:iterator value="{'aaa','bbb','ccc'}" var="x">

  <s:property value="#x.toUpperCase()"> |

</s:iterator>

<s:iterator value="{'aaa','bbb','ccc'}" status="status">

  <s:property /> |

  遍历过的总数:<s:property value="#status.count" />

  遍历的元素索引<s:property value="#status.index" />

  当前是偶数:<s:property value="#status.even" />

  当前是奇数:<s:property value="#status.odd" />

  是第一个元素吗:<s:property value="#status.first" />

  是最后一个元素吗:<s:property value="#status.last" />

</s:iterator>

遍历map

<s:iterator value="#{1:'aaa',2:'bbb',3:'ccc'}" >

  <s:property value="key"> | <s:property value="value">

</s:iterator>

<s:iterator value="#{1:'aaa',2:'bbb',3:'ccc'}" var="x">

  <s:property value="#x.key"> | <s:property value="#x.value"/>

</s:iterator>

struts2错误标签自动有样式:<s:fielderror /> errorMessage

可以.fielderror ul li{ list-style-type:none;  }解决

struts2Filter -> 拦截器 -> xxAction

                                             ↓

struts2Filter <- 拦截器 <- xxAction

默认拦截器都配置在struts-default.xml

拦截器也是配置在struts.xml文件里,

<package name="webpub" extends="struts-default" namespace="/">

    <interceptors>

        <!-- 配置拦截器与拦截器栈 -->

        <interceptor name="" class=""></interceptor>

        <interceptor-stack name="customStack">

            <interceptor-ref name="exceptionFilter"></interceptor-ref>

            <interceptor-ref name="defaultStack"></interceptor-ref>

            <interceptor-ref name="authority"></interceptor-ref>

        </interceptor-stack>

    </interceptors>

    <!-- 配置默认的拦截器(拦截器栈) -->

    <default-interceptor-ref name="customStack"></default-interceptor-ref>

    <!-- 当我们在配置Action的时候,如果没有为某个Action指定具体的class值时,系统将自动引用<default-class-ref>标签中所指定的类。在Struts2框架中,系统默认的class为ActionSupport,该配置我们可以在xwork的核心包下的xwork-default.xml文件中找到。有特殊需要时,可以手动指定默认的class -->

    <default-class-ref class=""></default-class-ref>

    <!-- 当配置里的action都对应不上的时候,最后就会执行这个 -->

    <default-action-ref name=""></default-action-ref>

</package>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章