winestory中struts2的使用

Jul 22, 2017 20:27 · 6804 words · 14 minute read

使用ssh框架做的一个小论坛是我的第一个项目,ssh框架在现在早已经过时,但是当初学习和解决问题的精神却是最重要的,因此我将相关的博文进行归档,记录并缅怀初学编程时的历程。这一篇是struts2框架相关内容。

winestory中struts2的使用 🔗

什么是Struts 🔗

Struts直译过来就是支柱,枝干的意思,它实现了基于javaweb应用的Model-View——Controller设计模式的应用框架

Struts的体系结构 🔗

一个请求在Struts2框架中的处理大概会经过以下结构步骤

  1. 客户端发出一个指向Servlet容器(例如Tomcat)的请求
  2. 这个请求会经过结构过滤器Filter(ActionContextCleanUP可选过滤器,其他web过滤器如SiteMesh等),最后到达FilterDispatcher过滤器
  3. 接着FilterDispatcher(过滤调度器)过滤器被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
  4. 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给Action对象的代理(ActionProxy)
  5. ActionProxy通过配置管理器(Configuration Manager)读取框架的相关的配置文件(Struts.xml以及它包含的*.xml配置文件)找到需要调用的Action类
  6. 找到需要调用的Action类之后,ActionProxy会创建一个ActionInvocation(动作调用)的实例
  7. ActionInvocation在调用Action的过程之前,会先依次调用相关配置拦截器(Intercepter)执行结果返回结果字符串
  8. ActionInvocation负责查找结果字符串对应的Result。然后再执行这个Result,再返回对应的结果视图(如JSP)来呈现界面
  9. 再次调用所用的配置拦截器(调用顺序与第7步相反),然后响应(HttpServletResponse)被返回给浏览器

Struts2的优点 🔗

  • Struts2是非侵入式设计,即不依赖与Servlet API和StrutsAPI
  • Strtus2提供了强大的拦截器,利用拦截器可进行AOP编程(面向切面的编程),实现权限的拦截等功能
  • Strtus2提供了类型转换器,可以很方便地进行类型转换,例如将特殊的的请求参数转换成需要的类型
  • Struts2支持多种表现层技术,如JSP、FreeMarker、Vectocity
  • Struts2的输入验证可以对指定的方法进行验证笔记来源: 实验楼

struts2核心配置文件struts.xml详解 🔗

struts.xml文件示例 🔗

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="false" />
    <constant name="struts.devMode" value="true" />
    <package name="default" namespace="/" extends="struts-default">
        <default-action-ref name="index" />
        <global-results>
            <result name="error">/WEB-INF/jsp/error.jsp</result>
        </global-results>
        <global-exception-mappings>
            <exception-mapping exception="java.lang.Exception" result="error"/>
        </global-exception-mappings>
        <action name="index">
            <result type="redirectAction">
                <param name="actionName">HelloWorld</param>
                <param name="namespace">/example</param>
            </result>
        </action>
    </package>
    <include file="example.xml"/>
</struts>

constant 🔗

包含一些属性设置,他可以改变struts框架的一些行为。例如示例中的 struts.enable.DynamicMethodInvocation 设为true,表示设置动态方法调用为真,而 struts.devMode 表示是否启用开发者模式。

package 🔗

在struts中,package用来管理action、result、interceptor、interceptor-stack等配置信息

  • name:必须唯一,这样其他package如果引用本package的话,才能找得到。
  • extends:当本package继承其他package的时候,会继承父package的所有配置属性(例如action、result等等);由于package的信息获取是按照struts.xml文件中的先后顺序进行的,因此父package必须在子package之前先定义。通常情况下,继承一个“struts-default.xml”的package,这是 Struts2 默认的package。namespace:namespace的配置会改变项目的url访问地址,主要是针对比较大型的项目以方便管理action,因为不同namespace中的action可以同名,从而解决action重名的问题。如果没有指定namespace,则默认为“”。

action 🔗

  • name:action的名称
  • class:action对应的java类
  • method:在该class中对应执行Action的函数方法,默认是execute()。
  • converter:类型转换器

result 🔗

  • name:具体来说,就是根据某个返回结果,指定响应逻辑,默认是success。
  • type:返回结果的类型,默认为dispatcher。

default-action-ref 🔗

如果找不到项目请求的action,就会报出404错误,而且这种错误不可避免,所以我们可以使用 default-action-ref 来指定一个默认的action,如果系统出现找不到action的情况,就会来调用这个默认的action。

global-results 🔗

设置package范围内的全局响应结果。在多个action都返回同一个逻辑视图(通常为某个jsp页面)的情况下,可以通过该标签来统一配置。

global-exception-mapping 🔗

配置发生异常时的视图信息。exception-mapping是控制action范围内的,而global-exception-mapp是控制package范围内的。两个都配置时,exception-mapping的优先级更高。

include 🔗

使用include引入外部配置文件,直接给出url即可

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

使用 include 的好处在于,例如当我们开发一个比较大型的项目的时候,配置文件肯定会写一大堆。如果写在一个配置文件里就不好查看和修改,不便于维护;所以使用 include 后可以根据模块、也可以根据功能来划分,这样就比较清晰,方便管理和维护。其他较为常用的还有 拦截器等等。来源:实验楼  https://www.shiyanlou.com/courses/32/labs/920/document

struts2中action接收参数的三种方法 🔗

struts2中action接收参数的方法主要有一下三种

1.使用action 中的属性接收参数 🔗

  • 定义:在action类中定义属性,创建get和set方法
  • 接收:通过属性接收参数,如userId;
  • 发送:通过属性名传递参数,如:user_usershow?userId=1;

2.使用DomainModel接收参数: 🔗

  • 定义:定义Model类,在action中定义Model类的对象(不需要new),创建该对象的get和set方法
  • 接收:通过对象的属性接收参数,如user.getUserName();
  • 发送:使用对象的属相传递参数,如user_usershow?user.user.userName=xxx;

3使用ModelDriven接收参数 🔗

  • 定义:action实现ModelDriven泛型接口,定义Model类的对象(必须new),通过getModel方法返回该对象;
  • 接收:通过对象的属性接收参数,如:user.getUserName();
  • 发送:直接使用属性名传递参数,如:user_usershow?userName=xxx

Action获取表单提交数据 🔗

获取表单提交数据的三种主要方式 🔗

1.使用ActionContext 🔗

Map<String,Object>getParameters() 返回一个包含所有HttpServletRequest参数信息

  1. 因为方法不是静态的方法,需要创建ActionContext类的对象
  2. ActionContext对象不是new出来的 static ActionContext getContext() 获取当前线程的ActionContext对象
  3. 实例
public String execute() throws Exception {
        //第一种方式:使用ActionContext类获取
        //获取ActionContext对象
        ActionContext context=ActionContext.getContext();
        //调用方法得到表单数据
        Map<String, Object> map = context.getParameters();
        Set<String> keys=map.keySet();
        //根据key得到vaule,使用数组形式是因为输入项可能有复选框
        for(String key:keys){
            Object[] obj=(Object[]) map.get(key);
            System.out.println(Arrays.toString(obj));
        }
        return NONE;
    }

2.使用ServletActionContext类 🔗

public String execute() throws Exception {
        //使用ServletActionContext获取request对象
        HttpServletRequest request=ServletActionContext.getRequest();
        //调用request里面的方法得到结果
        String username=request.getParameter("username");
        String password=request.getParameter("password");
        String address=request.getParameter("address");
        System.out.println(username+" "+password+" "+address);
        return NONE;
    }

3.使用接口注入方式 🔗

在action操作域对象 🔗

//操作三个域对象
        //1.request域
        HttpServletRequest request=ServletActionContext.getRequest();
        request.setAttribute("req","reqvalue" );
        //2.session域
        HttpSession session=request.getSession();
        session.setAttribute("sess", "sessValue");
        //3ServletContext域
        ServletContext context=ServletActionContext.getServletContext();
        context.setAttribute("contextname", "contextValue");

struts2提供获取表单数据方式 🔗

1.原始方式获取表单封装到实体类对象 🔗

public String execute() throws Exception {
        HttpServletRequest request=ServletActionContext.getRequest();
        String username=request.getParameter("username");
        String password=request.getParameter("password");
        String address=request.getParameter("address");
        //封装到实体类
        User user=new User();
        user.setUsername(username);
        user.setPassword(password);
        user.setAddress(address);
        System.out.println(user);
        return NONE;
    }

2.属性封装 🔗

  1. 直接把表单提交属性封装到action属性中
  2. 实现步骤
  3. 在action成员变量位置定义变量,变量名称和表单输入项的name属性值一样
  4. 生成变量的get和set方法
  5. 使用属性封装获取表单数据到属性里面,不能直接把数据封装到实体类对象里面
  6. 实例
public class sysAction extends ActionSupport{    
    private String username;    
   
    public String login() throws Exception {    
        System.out.println(username);    
        return SUCCESS;    
    }    
   
    public String getUsername() {    
        return username;    
    }    
    public void setUsername(String username) {    
        this.username= username;    
    }    
}

3.模型驱动封装,对于要传入多个model,第二种方式不方便 🔗

  1. 可以直接把表单数据封装到实体类对象
  2. 实现步骤
  3. action实现接口ModelDriven
  4. 实现接口里面的方法getModel方法
  5. 在action里面创建实体类对象
public class DataDemo2Action extends ActionSupport implements ModelDriven<User>{
    //创建对象
    private User user=new User();
    public User getModel() {
        // 返回创建的对象
        return user;
    }
    public String execute() throws Exception {
        
        return NONE;
    }
}

4.可以使用多个module实体对象的封装 🔗

此种封装方法,变量名称和表单输入项的name属性值不一样,变单输入项样式为

<form action="sys/login.action" method="post">    
    <input type="text" name="user.username">    
    <input type="text" name="teacher.level">    
    <input type="submit" value="submit">    
</form>
public class sysAction extends ActionSupport{    
    private User user;    
    private Teacher teacher;    
   
    public String login() throws Exception {    
        System.out.println(user.getUsername());    
        System.out.println(teacher.getLevel());    
        return SUCCESS;    
    }    
   
    public void setUser(User user) {    
        this.user = user;    
    }    
    public void setTeacher(Teacher teacher) {    
        this.teacher = teacher;    
    }    
}

封装数据到集合 🔗

封装数据到list集合 🔗

  1. 在action声明List
  2. 生成list变量的get和set方法
  3. 在表单输入项写表达式

封装数据到map集合 🔗

  1. 在action声明map集合
  2. 生成list变量的get和set方法
  3. 在表单输入项写表达式

struts2结果页面配置 🔗

action的访问 🔗

  • 使用method方法
  • 使用通配符实现 在action标签里面name属性,name属性值写符号 * ,表示匹配任何内容
  • 动态访问

结果页面配置 🔗

全局结果页面 🔗

  1. 如果多个action,方法里面返回相同值的时候,可以使用全局结果界面配置

局部结果页面配置 🔗

  1. 当存在全局结果页面配置时,依旧以局部结果页面配置为准

result便签的type属性 🔗

  1. type属性:如何到路径里
  2. type属性值:
  • 默认值:转发操作,值是dispatcher
  • 重定向:redirect
  • chain:转发到action
  • redirectAction:重定向到action

实例 🔗

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <package name="demo1" extends="struts-default" namespace="/">
    <action name="user_*" class="userAction" method="{1}">
        <!-- <result name="loginsuccess">/index.jsp</result> -->
        <result name="loginsuccess" type="redirectAction">story_indexlist</result>
        <result name="login">/login.jsp</result>
         <result name="logout" type="redirectAction">story_indexlist</result>
        <result name="registersuccess">/login.jsp</result>
        <result name="usershow">/myinfo.jsp</result>
        <result name="showUserinfo">/myinfoedit.jsp</result>
        <result name="update" type="redirectAction">story_indexlist</result>
    </action>
    <action name="story_*" class="storyAction" method="{1}">
        <result name="toAddPage">/newstory.jsp</result>
        <result name="add" type="redirectAction">story_indexlist</result>
        <result name="indexlist">/index.jsp</result>
        <result name="storyshow">/storyshow.jsp</result>
        <result name="getAllStory">/showbypage.jsp</result>
        <result name="addComment">/showbypage.jsp</result>
    </action>
        <action name="comment_*" class="commentAction" method="{1}">
            <result name="add" type="redirectAction">story_show?story_id=${comment.story.story_id}</result>
        </action>
    </package>
</struts>

struts2中的重定向问题 🔗

在处理ssh框架的小论坛项目的时候,遇到一个问题,在一个帖子下需要进行评论,但是在点击评论按钮的时候,后台做出这几个动作:

  1. 存储评论信息到数据库,也就是comment实体;
  2. 重新加载帖子详情和评论详情 要实现以上需求,可以通过struts的重定向功能来实现

struts重定向的几种方式 🔗

  • dispatcher —— 请求转发到一个页面 (默认),不可以用这种方式转发到一个action
  • chain —— 一个action请求转发至另一个 action
  • redirect —— 响应重定向到一个页面,也可以实现响应重定向到action
  • redirectAction —— 一个action响应重定向至另一个 action

struts重定向的实例 🔗

  • redirectAction —— 一个action响应重定向至另一个 action,这种是不带参数的
<result name="loginsuccess" type="redirectAction">story_indexlist</result>
  • 带参数的redirectAction
<result type="redirect">successAction?name=${name}</result>

我的问题解决 🔗

我的项目的重定向就需要参数,在重现加载帖子和评论的时候需要知道帖子的id,具体的解决方案如下

<result name="add" type="redirectAction">story_show?story_id=${comment.story.story_id}</result>

在comment的action中,重定向,并获取参数,这里可能有问题的就是参数的获取,这个参数的获取和action方法中的属性获取有关系,可能有以下的集中情况

  • 根据你的action获取表单数据方式的不同,${}里面的参数书写方式也不同,如果是属性,并有getter和setter方法,那么可以直接写属性值,如果是其他的方式获取,则要根据你的情况来设置
  • 如果这个参数是实体内的实体对象,比如我需要的storyId,则是comment实体内的一个story对象,在这个story对象里具有storyId这个值,那么就需要使用上面的方式${comment.story.story_id}如果使用${comment.story_id}是会报错 我的comment Action如下
package cn.haigeek.action;
import cn.haigeek.entity.Comment;
import cn.haigeek.service.CommentService;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Date;
/**
 * Created by haigeek on 2017/7/22.
 */
public class CommentAction extends ActionSupport  {
    Date date = new Date();
    private Comment comment;
    private CommentService commentService;
    //private Integer storyId;
    public void setCommentService(CommentService commentService) {
        this.commentService = commentService;
    }
    public Comment getComment() {
        return comment;
    }
    public void setComment(Comment comment) {
        this.comment = comment;
    }
    public String add(){
        comment.setCommentDate(date);
        commentService.add(comment);
        return "add";
    }
}

struts2文件上传(以上传头像为例) 🔗

在java的ssh项目中,需要为注册的用户设置一个头像,具体解决方案如下

jsp视图层 🔗

<form action="${pageContext.request.contextPath }/user_updateavatar.action?user.usid=${user.uid}"
        method=post enctype="multipart/form-data">
    <input type="hidden" name="user.uid" value="${user.uid }"/>
    <div class="panel panel-default">
        <div class="panel-body">
            <br>
            <div class="mdui-card-header-title">头像上传</div>
            <div class="mdui-container">
                <input type="file" name="avatar">
                <button class="mdui-btn mdui-btn-dense mdui-color-blue mdui-float-right mdui-m-a-2">
                    保存
                </button>
            </div>
        </div>
    </div>
</form>

input类型要设置为type为File类型,name需要对应action中的命名

action层 🔗

private File avatar;//上传文件域
private String avatatFileType;//上传文件类型
private String avatarFileName;//上传文件名字
private String savePathAvatar;//文件存储位置,在struts2中配置
//对应的get和set方法
public String updateavatar(){
  //文件在本地的具体存储位置
        String path=ServletActionContext.getServletContext().getRealPath("/")+getSavePathAvatar()+"\\"+getAvatarFileName();
  //文件存储到数据库的路径(根据实际情况来设置)
        String path2=getSavePathAvatar()+"/"+getAvatarFileName();
        try{
            FileOutputStream fos=new FileOutputStream(path);
            FileInputStream fis=new FileInputStream(getAvatar());
            byte[]buffer=new byte[1024];
            int len=0;
            try {
                while ((len=fis.read(buffer))>0){
                    fos.write(buffer,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
  //接受页面传过来的用户id
        int uid=user.getUid();
        User user4=userService.findOne(uid);
        user4.setAvatar(path2);
  //调用service进行路径的存储
        userService.update(user4);
        return "update";
    }

Struts.xml的配置 🔗

<action name="user_*" class="userAction" method="{1}">
  <!-- 动态设置存储位置的值 -->  
<param name="savePathAvatar">/upload/avatar</param>
</action>

显示 🔗

<img src="${story.user.avatar}"/></div>

我的项目是显示这个story对应的用户的头像

ssh框架实现分页显示 🔗

目的 🔗

最近在写的一个java小论坛的首页想要实现首页帖子列表的分页显示

实现 🔗

分页类 🔗

package cn.haigeek.entity;

/**
 * Created by haigeek on 2017/7/16.
 */
public class pageShow {
    //此类用于分页
    private int pageNow;//当前页
    private int totalSize;//总条数
    private int totalPage;//总页数
    private int pageSize = 10;//每页显示条数
    private boolean hasPre;//是否有上一页
    private boolean hasNext;//是否有下一页
    private boolean hasFirst;//是否有首页
    private boolean hasLast;//是否有尾页

    public pageShow(int pageNow, int totalSize) {
        //构造方法
        this.setPageNow(pageNow);
        this.setTotalSize(totalSize);
    }

    public pageShow(int pageNow, int totalSize, int pageSize) {//可动态改变每页条数
        //构造方法
        this.setPageNow(pageNow);
        this.setTotalSize(totalSize);
        this.pageSize = pageSize;
    }

    public void setPageNow(int pageNow) {
        this.pageNow = pageNow;
    }

    public int getPageNow() {
        return pageNow;
    }

    public void setTotalSize(int totalSize) {
        this.totalSize = totalSize;
    }

    public int getTotalSize() {
        return totalSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getPageSize() {
        return pageSize;
    }

    public int getTotalPage() {//总页数 = 总条数/每页显示条数
        totalPage = this.getTotalSize() / this.getPageSize();
        if (this.getTotalSize() % this.getPageSize() != 0) {
            totalPage++; //若余数为不0 则要多加一页
        }
        return totalPage;
    }

    public void setTotalPage(int totalPage) {
        this.totalPage = totalPage;
    }

    public boolean isHasPre() {//是否有上一页   除第一页以外都有上一页  说明有首页的就有上一页
        if (this.isHasFirst()) {
            return true;
        } else return false;
    }

    public void setHasPre(boolean hasPre) {
        this.hasPre = hasPre;
    }

    public boolean isHasNext() {//是否有下一页   除最后一页以外都有下一页  说明有尾页的就有下一页
        if (this.isHasLast()) {
            return true;
        } else return false;
    }

    public void setHasNext(boolean hasNext) {
        this.hasNext = hasNext;
    }

    public boolean isHasFirst() {//是否有首页 除第一页以外都有首页
        if (this.pageNow == 1)//是第一页就没有首页
            return false;
        else
            return true;
    }

    public void setHasFirst(boolean hasFirst) {
        this.hasFirst = hasFirst;
    }

    public boolean isHasLast() {//是否有尾页  除最后一页以外都有尾页
        if (pageNow == this.getTotalPage()) {//最后一页
            return false;
        } else return true;
    }

    public void setHasLast(boolean hasLast) {
        this.hasLast = hasLast;
    }
}

编写action 🔗

public String getAllStory(){
		List <Story>StoryList=storyService.getAllStory(pageNow,pageSize);
  			//放进域对象
			ServletActionContext.getRequest().setAttribute("StoryList", StoryList);
  			//放入request对象
			Map request= (Map) ActionContext.getContext().get("request");
			pageShow page=new pageShow(pageNow,storyService.findStorySize(),pageSize);
			request.put("page",page);
			return "getAllStory";
		}

编写service层 🔗

	//service层
public List <Story>getAllStory(int page,int pageSize){
        return storyDao.getAllStory(page,pageSize);
    }
    //获取帖子数目
 public int findStorySize(){
        return storyDao.findStorySize();
  	}

Dao接口层 🔗

    int findStorySize();

    List <Story>getAllStory(int page, int pageSize);

Dao层的实现层 🔗

public class StoryDaoImpl extends HibernateDaoSupport implements StoryDao {
public int findStorySize() {
        Session session=getSessionFactory().openSession();
        String hql = "from Story ";
        int size = session.createQuery(hql).list().size();
        session.close();
        return size;
    }
    public List<Story> getAllStory(int pageNow, int pageSize) {
    //获取session的方式很重要,这里的dao层是继承了HibernateDaoSupport来实现dao接口
        Session session=this.getHibernateTemplate().getSessionFactory().getCurrentSession();
        String hql="from Story order by story_id desc ";
        Query query = session.createQuery(hql);//执行查询操作
        query.setFirstResult((pageNow - 1) * pageSize);
        query.setMaxResults(pageSize);
        List <Story> StoryList = query.list();
        return StoryList;

    }
 }

视图层 🔗

<div class="panel-body">
    <c:forEach items="${StoryList }" var="story">
        <ul class="mdui-list">
            <a href="${pageContext.request.contextPath }/story_show.action?story_id=${story.story_id}">
                <li class="mdui-list-item mdui-ripple">
                    <div class="mdui-list-item-avatar"><img src="avatar1.jpg"/></div>
                    <div class="mdui-list-item-content">
                        <div class="mdui-list-item-title mdui-list-item-two-line">${story.story_title}</div>
                        <div class="mdui-list-item-text mdui-list-item-one-line"> ${story.user.username}发表在说天谈地
                            最后回复:haigeek 2017.06.07
                        </div>
                    </div>
                </li>
            </a>
        </ul>
    </c:forEach>
    <s:set name="page" value="#request.page"></s:set>
    <nav aria-label="Page navigation">
        <ul class="pagination center-block">
            <s:if test="#page.hasPre">
            <li>
                <a href=story_getAllStory?pageNow=<s:property value="#page.pageNow-1"/>
                aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            </s:if>
                <s:if test="#page.hasFirst">
                <li>
                    <a href="story_getAllStory?pageNow=1"
                        aria-label="Previous">
                        <span aria-hidden="true">首页</span>
                    </a>
                </li>
                </s:if>

            <li><a href="story_getAllStory?pageNow=1">1</a></li>
            <li><a href="story_getAllStory?pageNow=2">2</a></li>
            <li><a href="story_getAllStory?pageNow=3">3</a></li>
            <li><a href="story_getAllStory?pageNow=4">4</a></li>
            <li><a href="story_getAllStory?pageNow=5">5</a></li>
            <s:if test="#page.hasLast">
            <li>
                <a href=story_getAllStory?pageNow=<s:property value="#page.totalPage"/>
                    aria-label="Previous">
                    <span aria-hidden="true">尾页</span>
                </a>
            </li>
            </s:if>
            <s:if test="#page.hasNext">
            <li>
                <a href="story_getAllStory?pageNow=<s:property value="#page.pageNow+1" /> "
                    aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
            </s:if>
        </ul>

    </nav>
</div>

视图使用了一些样式(bootstrap等),主要是标签的使用,需要加入

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="/struts-tags" prefix="s" %>

配置文件 🔗

配置struts.xml和spring配置文件即可,我的项目主要是添加功能,配置文件已经配置好,只贴了核心的代码,最后在struts.xml添加一项配置即可

comments powered by Disqus