winestory中struts2的使用
使用ssh框架做的一个小论坛是我的第一个项目,ssh框架在现在早已经过时,但是当初学习和解决问题的精神却是最重要的,因此我将相关的博文进行归档,记录并缅怀初学编程时的历程。这一篇是struts2框架相关内容。
winestory中struts2的使用
什么是Struts
Struts直译过来就是支柱,枝干的意思,它实现了基于javaweb应用的Model-View——Controller设计模式的应用框架
Struts的体系结构
一个请求在Struts2框架中的处理大概会经过以下结构步骤
- 客户端发出一个指向Servlet容器(例如Tomcat)的请求
- 这个请求会经过结构过滤器Filter(ActionContextCleanUP可选过滤器,其他web过滤器如SiteMesh等),最后到达FilterDispatcher过滤器
- 接着FilterDispatcher(过滤调度器)过滤器被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
- 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给Action对象的代理(ActionProxy)
- ActionProxy通过配置管理器(Configuration Manager)读取框架的相关的配置文件(Struts.xml以及它包含的*.xml配置文件)找到需要调用的Action类
- 找到需要调用的Action类之后,ActionProxy会创建一个ActionInvocation(动作调用)的实例
- ActionInvocation在调用Action的过程之前,会先依次调用相关配置拦截器(Intercepter)执行结果返回结果字符串
- ActionInvocation负责查找结果字符串对应的Result。然后再执行这个Result,再返回对应的结果视图(如JSP)来呈现界面
- 再次调用所用的配置拦截器(调用顺序与第7步相反),然后响应(HttpServletResponse)被返回给浏览器
Struts2的优点
- Struts2是非侵入式设计,即不依赖与Servlet API和StrutsAPI
- Strtus2提供了强大的拦截器,利用拦截器可进行AOP编程(面向切面的编程),实现权限的拦截等功能
- Strtus2提供了类型转换器,可以很方便地进行类型转换,例如将特殊的的请求参数转换成需要的类型
- Struts2支持多种表现层技术,如JSP、FreeMarker、Vectocity
- Struts2的输入验证可以对指定的方法进行验证笔记来源:实验楼
struts2核心配置文件struts.xml详解
struts.xml文件示例
1<?xml version="1.0" encoding="UTF-8" ?>
2<!DOCTYPE struts PUBLIC
3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
4 "http://struts.apache.org/dtds/struts-2.3.dtd">
5<struts>
6 <constant name="struts.enable.DynamicMethodInvocation" value="false" />
7 <constant name="struts.devMode" value="true" />
8 <package name="default" namespace="/" extends="struts-default">
9 <default-action-ref name="index" />
10 <global-results>
11 <result name="error">/WEB-INF/jsp/error.jsp</result>
12 </global-results>
13 <global-exception-mappings>
14 <exception-mapping exception="java.lang.Exception" result="error"/>
15 </global-exception-mappings>
16 <action name="index">
17 <result type="redirectAction">
18 <param name="actionName">HelloWorld</param>
19 <param name="namespace">/example</param>
20 </result>
21 </action>
22 </package>
23 <include file="example.xml"/>
24</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参数信息
- 因为方法不是静态的方法,需要创建ActionContext类的对象
- ActionContext对象不是new出来的 static ActionContext getContext() 获取当前线程的ActionContext对象
- 实例
1public String execute() throws Exception {
2 //第一种方式:使用ActionContext类获取
3 //获取ActionContext对象
4 ActionContext context=ActionContext.getContext();
5 //调用方法得到表单数据
6 Map<String, Object> map = context.getParameters();
7 Set<String> keys=map.keySet();
8 //根据key得到vaule,使用数组形式是因为输入项可能有复选框
9 for(String key:keys){
10 Object[] obj=(Object[]) map.get(key);
11 System.out.println(Arrays.toString(obj));
12 }
13 return NONE;
14 }
2.使用ServletActionContext类
1public String execute() throws Exception {
2 //使用ServletActionContext获取request对象
3 HttpServletRequest request=ServletActionContext.getRequest();
4 //调用request里面的方法得到结果
5 String username=request.getParameter("username");
6 String password=request.getParameter("password");
7 String address=request.getParameter("address");
8 System.out.println(username+" "+password+" "+address);
9 return NONE;
10 }
3.使用接口注入方式
在action操作域对象
1//操作三个域对象
2 //1.request域
3 HttpServletRequest request=ServletActionContext.getRequest();
4 request.setAttribute("req","reqvalue" );
5 //2.session域
6 HttpSession session=request.getSession();
7 session.setAttribute("sess", "sessValue");
8 //3ServletContext域
9 ServletContext context=ServletActionContext.getServletContext();
10 context.setAttribute("contextname", "contextValue");
struts2提供获取表单数据方式
1.原始方式获取表单封装到实体类对象
1public String execute() throws Exception {
2 HttpServletRequest request=ServletActionContext.getRequest();
3 String username=request.getParameter("username");
4 String password=request.getParameter("password");
5 String address=request.getParameter("address");
6 //封装到实体类
7 User user=new User();
8 user.setUsername(username);
9 user.setPassword(password);
10 user.setAddress(address);
11 System.out.println(user);
12 return NONE;
13 }
2.属性封装
- 直接把表单提交属性封装到action属性中
- 实现步骤
- 在action成员变量位置定义变量,变量名称和表单输入项的name属性值一样
- 生成变量的get和set方法
- 使用属性封装获取表单数据到属性里面,不能直接把数据封装到实体类对象里面
- 实例
1public class sysAction extends ActionSupport{
2 private String username;
3
4 public String login() throws Exception {
5 System.out.println(username);
6 return SUCCESS;
7 }
8
9 public String getUsername() {
10 return username;
11 }
12 public void setUsername(String username) {
13 this.username= username;
14 }
15}
3.模型驱动封装,对于要传入多个model,第二种方式不方便
- 可以直接把表单数据封装到实体类对象
- 实现步骤
- action实现接口ModelDriven
- 实现接口里面的方法getModel方法
- 在action里面创建实体类对象
1public class DataDemo2Action extends ActionSupport implements ModelDriven<User>{
2 //创建对象
3 private User user=new User();
4 public User getModel() {
5 // 返回创建的对象
6 return user;
7 }
8 public String execute() throws Exception {
9
10 return NONE;
11 }
12}
4.可以使用多个module实体对象的封装
此种封装方法,变量名称和表单输入项的name属性值不一样,变单输入项样式为
1<form action="sys/login.action" method="post">
2 <input type="text" name="user.username">
3 <input type="text" name="teacher.level">
4 <input type="submit" value="submit">
5</form>
1public class sysAction extends ActionSupport{
2 private User user;
3 private Teacher teacher;
4
5 public String login() throws Exception {
6 System.out.println(user.getUsername());
7 System.out.println(teacher.getLevel());
8 return SUCCESS;
9 }
10
11 public void setUser(User user) {
12 this.user = user;
13 }
14 public void setTeacher(Teacher teacher) {
15 this.teacher = teacher;
16 }
17}
封装数据到集合
封装数据到list集合
- 在action声明List
- 生成list变量的get和set方法
- 在表单输入项写表达式
封装数据到map集合
- 在action声明map集合
- 生成list变量的get和set方法
- 在表单输入项写表达式
struts2结果页面配置
action的访问
- 使用method方法
- 使用通配符实现 在action标签里面name属性,name属性值写符号 * ,表示匹配任何内容
- 动态访问
结果页面配置
全局结果页面
- 如果多个action,方法里面返回相同值的时候,可以使用全局结果界面配置
局部结果页面配置
- 当存在全局结果页面配置时,依旧以局部结果页面配置为准
result便签的type属性
- type属性:如何到路径里
- type属性值:
- 默认值:转发操作,值是dispatcher
- 重定向:redirect
- chain:转发到action
- redirectAction:重定向到action
实例
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE struts PUBLIC
3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
4 "http://struts.apache.org/dtds/struts-2.3.dtd">
5<struts>
6 <package name="demo1" extends="struts-default" namespace="/">
7 <action name="user_*" class="userAction" method="{1}">
8 <!-- <result name="loginsuccess">/index.jsp</result> -->
9 <result name="loginsuccess" type="redirectAction">story_indexlist</result>
10 <result name="login">/login.jsp</result>
11 <result name="logout" type="redirectAction">story_indexlist</result>
12 <result name="registersuccess">/login.jsp</result>
13 <result name="usershow">/myinfo.jsp</result>
14 <result name="showUserinfo">/myinfoedit.jsp</result>
15 <result name="update" type="redirectAction">story_indexlist</result>
16 </action>
17 <action name="story_*" class="storyAction" method="{1}">
18 <result name="toAddPage">/newstory.jsp</result>
19 <result name="add" type="redirectAction">story_indexlist</result>
20 <result name="indexlist">/index.jsp</result>
21 <result name="storyshow">/storyshow.jsp</result>
22 <result name="getAllStory">/showbypage.jsp</result>
23 <result name="addComment">/showbypage.jsp</result>
24 </action>
25 <action name="comment_*" class="commentAction" method="{1}">
26 <result name="add" type="redirectAction">story_show?story_id=${comment.story.story_id}</result>
27 </action>
28 </package>
29</struts>
struts2中的重定向问题
在处理ssh框架的小论坛项目的时候,遇到一个问题,在一个帖子下需要进行评论,但是在点击评论按钮的时候,后台做出这几个动作:
- 存储评论信息到数据库,也就是comment实体;
- 重新加载帖子详情和评论详情 要实现以上需求,可以通过struts的重定向功能来实现
struts重定向的几种方式
- dispatcher —— 请求转发到一个页面 (默认),不可以用这种方式转发到一个action
- chain —— 一个action请求转发至另一个 action
- redirect —— 响应重定向到一个页面,也可以实现响应重定向到action
- redirectAction —— 一个action响应重定向至另一个 action
struts重定向的实例
- redirectAction —— 一个action响应重定向至另一个 action,这种是不带参数的
1<result name="loginsuccess" type="redirectAction">story_indexlist</result>
- 带参数的redirectAction
1<result type="redirect">successAction?name=${name}</result>
我的问题解决
我的项目的重定向就需要参数,在重现加载帖子和评论的时候需要知道帖子的id,具体的解决方案如下
1<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如下
1package cn.haigeek.action;
2import cn.haigeek.entity.Comment;
3import cn.haigeek.service.CommentService;
4import com.opensymphony.xwork2.ActionSupport;
5import java.util.Date;
6/**
7 * Created by haigeek on 2017/7/22.
8 */
9public class CommentAction extends ActionSupport {
10 Date date = new Date();
11 private Comment comment;
12 private CommentService commentService;
13 //private Integer storyId;
14 public void setCommentService(CommentService commentService) {
15 this.commentService = commentService;
16 }
17 public Comment getComment() {
18 return comment;
19 }
20 public void setComment(Comment comment) {
21 this.comment = comment;
22 }
23 public String add(){
24 comment.setCommentDate(date);
25 commentService.add(comment);
26 return "add";
27 }
28}
struts2文件上传(以上传头像为例)
在java的ssh项目中,需要为注册的用户设置一个头像,具体解决方案如下
jsp视图层
1<form action="${pageContext.request.contextPath }/user_updateavatar.action?user.usid=${user.uid}"
2 method=post enctype="multipart/form-data">
3 <input type="hidden" name="user.uid" value="${user.uid }"/>
4 <div class="panel panel-default">
5 <div class="panel-body">
6 <br>
7 <div class="mdui-card-header-title">头像上传</div>
8 <div class="mdui-container">
9 <input type="file" name="avatar">
10 <button class="mdui-btn mdui-btn-dense mdui-color-blue mdui-float-right mdui-m-a-2">
11 保存
12 </button>
13 </div>
14 </div>
15 </div>
16</form>
input类型要设置为type为File类型,name需要对应action中的命名
action层
1private File avatar;//上传文件域
2private String avatatFileType;//上传文件类型
3private String avatarFileName;//上传文件名字
4private String savePathAvatar;//文件存储位置,在struts2中配置
5//对应的get和set方法
6public String updateavatar(){
7 //文件在本地的具体存储位置
8 String path=ServletActionContext.getServletContext().getRealPath("/")+getSavePathAvatar()+"\\"+getAvatarFileName();
9 //文件存储到数据库的路径(根据实际情况来设置)
10 String path2=getSavePathAvatar()+"/"+getAvatarFileName();
11 try{
12 FileOutputStream fos=new FileOutputStream(path);
13 FileInputStream fis=new FileInputStream(getAvatar());
14 byte[]buffer=new byte[1024];
15 int len=0;
16 try {
17 while ((len=fis.read(buffer))>0){
18 fos.write(buffer,0,len);
19 }
20 } catch (IOException e) {
21 e.printStackTrace();
22 }
23 } catch (FileNotFoundException e) {
24 e.printStackTrace();
25 }
26 //接受页面传过来的用户id
27 int uid=user.getUid();
28 User user4=userService.findOne(uid);
29 user4.setAvatar(path2);
30 //调用service进行路径的存储
31 userService.update(user4);
32 return "update";
33 }
Struts.xml的配置
1<action name="user_*" class="userAction" method="{1}">
2 <!-- 动态设置存储位置的值 -->
3<param name="savePathAvatar">/upload/avatar</param>
4</action>
显示
1<img src="${story.user.avatar}"/></div>
我的项目是显示这个story对应的用户的头像
ssh框架实现分页显示
目的
最近在写的一个java小论坛的首页想要实现首页帖子列表的分页显示
实现
分页类
1package cn.haigeek.entity;
2
3/**
4 * Created by haigeek on 2017/7/16.
5 */
6public class pageShow {
7 //此类用于分页
8 private int pageNow;//当前页
9 private int totalSize;//总条数
10 private int totalPage;//总页数
11 private int pageSize = 10;//每页显示条数
12 private boolean hasPre;//是否有上一页
13 private boolean hasNext;//是否有下一页
14 private boolean hasFirst;//是否有首页
15 private boolean hasLast;//是否有尾页
16
17 public pageShow(int pageNow, int totalSize) {
18 //构造方法
19 this.setPageNow(pageNow);
20 this.setTotalSize(totalSize);
21 }
22
23 public pageShow(int pageNow, int totalSize, int pageSize) {//可动态改变每页条数
24 //构造方法
25 this.setPageNow(pageNow);
26 this.setTotalSize(totalSize);
27 this.pageSize = pageSize;
28 }
29
30 public void setPageNow(int pageNow) {
31 this.pageNow = pageNow;
32 }
33
34 public int getPageNow() {
35 return pageNow;
36 }
37
38 public void setTotalSize(int totalSize) {
39 this.totalSize = totalSize;
40 }
41
42 public int getTotalSize() {
43 return totalSize;
44 }
45
46 public void setPageSize(int pageSize) {
47 this.pageSize = pageSize;
48 }
49
50 public int getPageSize() {
51 return pageSize;
52 }
53
54 public int getTotalPage() {//总页数 = 总条数/每页显示条数
55 totalPage = this.getTotalSize() / this.getPageSize();
56 if (this.getTotalSize() % this.getPageSize() != 0) {
57 totalPage++; //若余数为不0 则要多加一页
58 }
59 return totalPage;
60 }
61
62 public void setTotalPage(int totalPage) {
63 this.totalPage = totalPage;
64 }
65
66 public boolean isHasPre() {//是否有上一页 除第一页以外都有上一页 说明有首页的就有上一页
67 if (this.isHasFirst()) {
68 return true;
69 } else return false;
70 }
71
72 public void setHasPre(boolean hasPre) {
73 this.hasPre = hasPre;
74 }
75
76 public boolean isHasNext() {//是否有下一页 除最后一页以外都有下一页 说明有尾页的就有下一页
77 if (this.isHasLast()) {
78 return true;
79 } else return false;
80 }
81
82 public void setHasNext(boolean hasNext) {
83 this.hasNext = hasNext;
84 }
85
86 public boolean isHasFirst() {//是否有首页 除第一页以外都有首页
87 if (this.pageNow == 1)//是第一页就没有首页
88 return false;
89 else
90 return true;
91 }
92
93 public void setHasFirst(boolean hasFirst) {
94 this.hasFirst = hasFirst;
95 }
96
97 public boolean isHasLast() {//是否有尾页 除最后一页以外都有尾页
98 if (pageNow == this.getTotalPage()) {//最后一页
99 return false;
100 } else return true;
101 }
102
103 public void setHasLast(boolean hasLast) {
104 this.hasLast = hasLast;
105 }
106}
编写action
1public String getAllStory(){
2 List <Story>StoryList=storyService.getAllStory(pageNow,pageSize);
3 //放进域对象
4 ServletActionContext.getRequest().setAttribute("StoryList", StoryList);
5 //放入request对象
6 Map request= (Map) ActionContext.getContext().get("request");
7 pageShow page=new pageShow(pageNow,storyService.findStorySize(),pageSize);
8 request.put("page",page);
9 return "getAllStory";
10 }
编写service层
1 //service层
2public List <Story>getAllStory(int page,int pageSize){
3 return storyDao.getAllStory(page,pageSize);
4 }
5 //获取帖子数目
6 public int findStorySize(){
7 return storyDao.findStorySize();
8 }
Dao接口层
1 int findStorySize();
2
3 List <Story>getAllStory(int page, int pageSize);
Dao层的实现层
1public class StoryDaoImpl extends HibernateDaoSupport implements StoryDao {
2public int findStorySize() {
3 Session session=getSessionFactory().openSession();
4 String hql = "from Story ";
5 int size = session.createQuery(hql).list().size();
6 session.close();
7 return size;
8 }
9 public List<Story> getAllStory(int pageNow, int pageSize) {
10 //获取session的方式很重要,这里的dao层是继承了HibernateDaoSupport来实现dao接口
11 Session session=this.getHibernateTemplate().getSessionFactory().getCurrentSession();
12 String hql="from Story order by story_id desc ";
13 Query query = session.createQuery(hql);//执行查询操作
14 query.setFirstResult((pageNow - 1) * pageSize);
15 query.setMaxResults(pageSize);
16 List <Story> StoryList = query.list();
17 return StoryList;
18
19 }
20 }
视图层
<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">«</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">»</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添加一项配置即可