首页 > 代码库 > Struts2零散笔记

Struts2零散笔记

Struts2-Action:
搭建MyEclipse框架,在Window/Prefrence中设置Server/Tomcat的目录,然后设置java选项卡中JDK的路径。

Struts的运行机制:
URL请求通过HTTP协议发送给TOMCAT(假设我们的url为http://localhost:8080/$webapplication的名字$/hello),
根据URL中请求的webapplication再交给对应的webapplication处理,然后参考其web.xml的配置,其中有:
...
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
...
被org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter接受,它又会参考struts.xml的namespace,再根据其下对应的action和result,
将请求转到对应的JSP文件。

struts的作用就是将请求和视图分开。

struts.xml解释:
<struts>

<constant name="struts.devMode" value="http://www.mamicode.com/true" />
<package name="front" extends="struts-default" namespace="/front"> //package区分重名的情况;namespace不为空时必须用‘/‘开头,当想访问这个namespace中的action就需要在前面带上这个路径
<action name="index">***
<result>/Namespace.jsp</result>
</action>
</package>

<package name="main" extends="struts-default" namespace="">
<action name="index">
<result>/Namespace.jsp</result>
</action>
</package>

</struts>

例如要访问带***号的name="index"的action,则url为http://localhost:8080/$webapplication的名字$/front/index
namespace决定了action的访问路径,不写时默认为"",可以接收所有路径的action
namespace可以写为/,或者/xxx,或者/xxx/yyy,对应的action访问路径为/index.action,/xxx/index.action,或者/xxx/yyy/index.action.
namespace最好也用模块来进行命名。

<constant name="struts.devMode" value="http://www.mamicode.com/true" />
<package name="front" extends="struts-default" namespace="/">
<action name="index" class="com.bjsxt.struts2.front.action.IndexAction1"> //当请求这个action的时候,找到对应的class,执行它里面的execute()方法,返回值时String,就把他当成一个Action。
<result name="success">/ActionIntroduction.jsp</result>
</action>
</package>

具体视图的返回可以由用户自己定义的Action来决定。
具体的手段是根据返回的字符串找到对应的配置项,来决定视图的内容。
具体Action的实现可以是一个普通的java类,里面有public String execute方法即可。
或者实现Action接口。
不过最常用的是从ActionSupport继承,好处在于可以直接使用Struts2封装好的方法。

Struts1和Struts2的一个区别:Struts2的每一个Action都是新NEW的,而Struts1不是,故2解决了线程同步问题。

struts2中的路径问题是根据action的路径而不是jsp路径来确定,所以尽量不要使用相对路径。
虽然可以用redirect方式解决,但redirect方式并非必要。
解决办法非常简单,统一使用绝对路径。(在jsp中用request.getContextRoot方式来拿到webapp的路径)
或者使用myeclipse经常用的,指定basePath:
<% //获取根目录路径
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<base href="http://www.mamicode.com/" /> //设置当前页面下的链接的根路径

Action执行的时候并不一定要执行execute方法。
可以在配置文件中配置Action的时候用method=来指定执行哪个方法
也可以在url地址中动态指定(动态方法调用DMI)(推荐):!+方法名
前者会产生太多的action,所以不推荐使用。

使用通配符可以将配置降到最低,不过一定要遵守“约定优于配置”。视频14。

在action中传参数有三种方法。

值堆栈中每一个元素都有一个名字和一个值。

//用javascript指定这个form提交的action。
<input type="button" value="http://www.mamicode.com/submit1" onclick="javascript:document.f.action=‘login/login1‘;document.f.submit();" />

----LoginAction1.java----
public class LoginAction1 extends ActionSupport {

private Map request;
private Map session;
private Map application;

public LoginAction1() {
//利用这种方法发到request(类型为Map),session,application
request = (Map)ActionContext.getContext().get("request");
session = ActionContext.getContext().getSession();
application = ActionContext.getContext().getApplication();
}

public String execute() {
//在处理请求的时候往其中放值,在前台可以访问
request.put("r1", "r1");
session.put("s1", "s1");
application.put("a1", "a1");
return SUCCESS;
}
前台页面:
...
//利用<s:property value="http://www.mamicode.com/#request.r1"/>访问,也可以用传统的方式<%=request.getAttribute("r1") %>来取值
<s:property value="http://www.mamicode.com/#request.r1"/> | <%=request.getAttribute("r1") %> <br />
<s:property value="http://www.mamicode.com/#session.s1"/> | <%=session.getAttribute("s1") %> <br />
<s:property value="http://www.mamicode.com/#application.a1"/> | <%=application.getAttribute("a1") %> <br />
...
----LoginAction2.java----
****最常用的方法****

package com.bjsxt.struts2.user.action;

import java.util.Map;

import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class LoginAction2 extends ActionSupport implements RequestAware,SessionAware, ApplicationAware {

private Map<String, Object> request;
private Map<String, Object> session;
private Map<String, Object> application;

public String execute() {
//直接可以用(设计思想IoC或者DI),需要实现RequestAware,SessionAware, ApplicationAware借口
request.put("r1", "r1");
session.put("s1", "s1");
application.put("a1", "a1");
return SUCCESS;
}

//利用setRequest(Map<String, Object> request)设置request
public void setRequest(Map<String, Object> request) {
this.request = request;
}

public void setSession(Map<String, Object> session) {
this.session = session;
}

public void setApplication(Map<String, Object> application) {
this.application = application;
}


}


在struts.xml中增加以下语句可以解决中文编码问题:
<constant name="struts.i18n.encoding" value="http://www.mamicode.com/GBK" /> <!-- internationalization -->

<include file="login.xml" /> //用于包含另外一个.xml文件到struts.xml文件中

<default-action-ref name="index"><*default-action-ref>定义默认Action,当找不到指定Action(或者不指定Action)时,转到那个package下的Action

Action总结:
1.实现一个Action的最重用的方式:从ActionSupport继承
2.DMI动态方法调用+!
3.通配符:*asd*--{1}{2}
*_*--{1}{2}
4.接受参数的方法(一般用属性或者DomainModel来接受)
5.简单参数验证addFieldError
一般不适用Struts2的UI标签
6.访问Web元素
Map类型
loc(最常用)
依赖Struts2
原始类型
loc
依赖Struts2
7.包含文件配置
8.默认Action处理

result可以指定类型,默认为dispatcher(用服务器跳转到结果页面,不可以是Action),还有:
redirect(不可以跳转到Action),chain(跳转到一个Action),redirectAction(跳转到另外一个Action)。
以上四种最常用,前两中最最常用。还有freemarker,httpheader,stream,velocity,xsit,plaintext,tiles等类型。
客户端跳转和服务器端跳转--视频27
dispatcher和chain为服务器端跳转,而redirect和redirectAction为客户端跳转。

chain类型跳转到另一个Action要带参数:
<result type="chain">
<param name="actionName">aasdafa</param> //指定Action的名字
<param name="namespace">/asdasd</param> //指定Action的NameSpace
</result>

global-results用于处理包里的共有结果集,extend用于继承其他package中的global-results。

动态指定result:在Action实现类中可以指定字符串参数来指向JSP页面地址,如:
...
private String r;

public String execute() throws Exception {
if(type == 1) r="/user_success.jsp";
else if (type == 2) r="/user_error.jsp";
return "success";
}
...
在struts.xml中,
...
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>${r}</result>
</action>
...
来确定result的展示页面,因为Action中的参数都会存放在值堆栈中。
在index.jsp中直接用:
<li><a href="http://www.mamicode.com/user/user?type=1">返回success</a></li>
<li><a href="http://www.mamicode.com/user/user?type=2">返回error</a></li>
来给action实现类中的参数传递参数。


一次request中若有服务端跳转,则跳转前后的所有Action公用值堆栈,故各个Action之间不需要传递参数。
但若跳转到jsp页面所传递的参数不存放在值堆栈中,只能利用actioncantext中取值。
...
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result type="redirect">/user_success.jsp?t=${type}</result>
</action>
...

...
from valuestack: <s:property value="http://www.mamicode.com/t"/><br/> //从值堆栈中取值
from actioncontext: <s:property value="http://www.mamicode.com/#parameters.t"/> //从actioncantext中取值
...

OGNL(object grapg navigation language)表达式:
必须为Action类传递参数,它才会构造(new)这个对象,此时必须有空的构造方法。
也可以不传递参数而自己直接在类中构造。

访问静态方法需要在struts.xml中设置如下语句:
<constant name="struts.ognl.allowStaticMethodAccess" value="http://www.mamicode.com/true"></constant>
访问静态属性和静态方法:
<s:property value="http://www.mamicode.com/@com.bjsxt.struts2.ognl.S@s()"/>
<s:property value="http://www.mamicode.com/@com.bjsxt.struts2.ognl.S@STR"/>
"@类名@属性(方法)"
"@@方法(属性)"--只能访问Math的方法和属性

----ognl.jsp----

<?xml version="1.0" encoding="GB18030" ?>
<%@ page language="java" contentType="text/html; charset=GB18030"
pageEncoding="GB18030"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030" />
<title>OGNL表达式语言学习</title>
</head>
<body>
<ol>
<li>访问值栈中的action的普通属性: username = <s:property value="http://www.mamicode.com/username"/> </li>
<li>访问值栈中对象的普通属性(get set方法):<s:property value="http://www.mamicode.com/user.age"/> | <s:property value="http://www.mamicode.com/user[‘age‘]"/> | <s:property value="http://www.mamicode.com/user[/"age\"]"/> | wrong: <%--<s:property value="http://www.mamicode.com/user[age]"/>--%></li>
<li>访问值栈中对象的普通属性(get set方法): <s:property value="http://www.mamicode.com/cat.friend.name"/></li>
<li>访问值栈中对象的普通方法:<s:property value="http://www.mamicode.com/password.length()"/></li>
<li>访问值栈中对象的普通方法:<s:property value="http://www.mamicode.com/cat.miaomiao()" /></li>
<li>访问值栈中action的普通方法:<s:property value="http://www.mamicode.com/m()" /></li>
<hr />
<li>访问静态方法:<s:property value="http://www.mamicode.com/@com.bjsxt.struts2.ognl.S@s()"/></li>
<li>访问静态属性:<s:property value="http://www.mamicode.com/@com.bjsxt.struts2.ognl.S@STR"/></li>
<li>访问Math类的静态方法:<s:property value="http://www.mamicode.com/@@max(2,3)" /></li>
<hr />
<li>访问普通类的构造方法:<s:property value="http://www.mamicode.com/new com.bjsxt.struts2.ognl.User(8)"/></li>
<hr />
<li>访问List:<s:property value="http://www.mamicode.com/users"/></li>
<li>访问List中某个元素:<s:property value="http://www.mamicode.com/users[1]"/></li>
<li>访问List中元素某个属性的集合:<s:property value="http://www.mamicode.com/users.{age}"/></li>
<li>访问List中元素某个属性的集合中的特定值:<s:property value="http://www.mamicode.com/users.{age}[0]"/> | <s:property value="http://www.mamicode.com/users[0].age"/></li>
<li>访问Set:<s:property value="http://www.mamicode.com/dogs"/></li>
<li>访问Set中某个元素:<s:property value="http://www.mamicode.com/dogs[1]"/></li>
<li>访问Map:<s:property value="http://www.mamicode.com/dogMap"/></li>
<li>访问Map中某个元素:<s:property value="http://www.mamicode.com/dogMap.dog101"/> | <s:property value="http://www.mamicode.com/dogMap[‘dog101‘]"/> | <s:property value="http://www.mamicode.com/dogMap[/"dog101\"]"/></li>
<li>访问Map中所有的key:<s:property value="http://www.mamicode.com/dogMap.keys"/></li>
<li>访问Map中所有的value:<s:property value="http://www.mamicode.com/dogMap.values"/></li>
<li>访问容器的大小:<s:property value="http://www.mamicode.com/dogMap.size()"/> | <s:property value="http://www.mamicode.com/users.size"/> </li>
<hr />
<li>投影(过滤):<s:property value="http://www.mamicode.com/users.{?#this.age==1}[0]"/></li>
<li>投影:<s:property value="http://www.mamicode.com/users.{^#this.age>1}.{age}"/></li>
<li>投影:<s:property value="http://www.mamicode.com/users.{$#this.age>1}.{age}"/></li>
<li>投影:<s:property value="http://www.mamicode.com/users.{$#this.age>1}.{age} == null"/></li>
<hr />
<li>[]:<s:property value="http://www.mamicode.com/[0].username"/></li>

</ol>

<s:debug></s:debug>
</body>
</html>

投影(过滤):{?#this.age==1}、{^#this.age>1}、{$#this.age>1}为过滤条件。
^代表第一个,$代表末尾那个。
<s:property value="http://www.mamicode.com/[0].username"/>代表从第0个一直到末尾的集合。在服务器端跳转时就会有多个Action。


Struts2的标签可以分为通用标签,控制标签,UI标签,AJAX标签,$#%的区别。

1.通用标签:
a)property
b)set
i.默认为action scope,会将值放入request和ActionContext中
ii.page、request、session、application
c)bean
d)include(对中文文件支持有问题,不建议使用,如需包含,改用jsp包含)
e)param
f)debug

2.控制标签
a)if elseif else
b)iterator
i.collections map enumeration iterator array
c)subset

3.UI标签
a)theme
i.simple xhtml(默认) css_xhtml ajax

4.AJAX标签
a)补充

5.$ # %的区别
a)$用于i18n和struts配置文件
b)#取得ActionContext的值
c)%将原本的文本属性解析为ognl,对于本来就是ognl的属性不起作用
i. 参考<s:property 和 <s:include

 

 


命名规范:

1.数据库命名:
表命名(简单就是美):
_表名,如:
_Topic (使用驼峰标示,尽管数据库不区分大小写)

字段命名:
保持与属性名一致(尽量不要和数据库的命名冲突)
如:ID属性 id
外键 topicId

库名:
使用项目的名称


2.类和文件的命名:
有两种方式:1.先分模块再分层次2.先分层次再分模块
采用层次划分(包的命名都用小写):
com.bjsxt.bbs.action.model.service

Action类采用Action结尾,如:xxxAction.java

JSP命名:*_*

namespace:前台直接使用"/"
后台使用"/admin"

package:
前台:aciton
后台:adminAction


项目开发顺序:
1.建立界面原型
2.建立Struts.xml
a.确定namespace
b.确定package
c.确定Action的名称,空的方法
d.确定Rssult
e.将界面原形页面进行修改,匹配现有设置
f.测试
g.做好规划
3.建立数据库(或者实体类)
4.建立Model层
5.建立Service层(hibernate)
使用Junit进行单元测试
6.着手开发


请求到来后被Filter过滤,调用特定的Action的特定方法,拦截器就是在调用的时候加东西。
(其实先调用拦截器,拦截器再调用Action,再返回给拦截器,又返回给Filter)

I18N
在普通Editer直接写中文会乱码。
国际化文件可以用propertiesEditer插件,在这里面写的中文会转为UTF-8保存。

Struts2的国际化有三种方式:
Action-Package-App级
主要用App,对整个项目有效。


struts2的实际执行过程:
1.Struts2Flter接受页面请求
2.调用Dispatcher的serviceAction()方法
3.Dispatcher创建一个ActionProxy对象,并调用它的execute()方法
4.execute()方法又会调用ActionInvocation的invoke()方法
5.invoke()方法又会调用第一个inerceptor对象的interceptor()方法
6.interceptor()方法又会调用ActionInvocation对象的invoke()方法
7.nvoke()方法又会调用下一个inerceptor对象的interceptor()方法
8.反复直到所有inerceptor调用完毕,ActionInvocation对象调用最中的Action类的execute()方法

 

传递多个相同参数的时候,可以使用集合容器,但是要使用泛型。
比如在action类中有属性:
......
List<interst>; //不用new
......

使用token拦截器控制重复提交(很少用)

写自己的转换器:
public class MyPointConverter extends DefaultTypeConverter{

@Override
public Object convertValue(Object value, Class toType) {
if(toType == Point.class) {
Point p = new Point();
String[] strs = (String[])value;
String[] xy = strs[0].split(",");
p.x = Integer.parseInt(xy[0]);
p.y = Integer.parseInt(xy[1]);
return p;
}
if(toType == String.class) {
return value.toString();
}
return super.convertValue(value, toType);
}

}
public class MyPointConverter extends StrutsTypeConverter{



@Override
public Object convertFromString(Map context, String[] values, Class toClass) {

Point p = new Point();
String[] strs = (String[])values;
String[] xy = strs[0].split(",");
p.x = Integer.parseInt(xy[0]);
p.y = Integer.parseInt(xy[1]);
return p;


}

@Override
public String convertToString(Map context, Object o) {
// TODO Auto-generated method stub
return o.toString();
}

}

三种注册方式:
i. 局部:XXXAction-conversion.properties
1. p(属性名称) = converter
ii. 全局:xwork-conversion.properties
1. com.xxx.XXX(类名)= converter
iii. Annotation
d) 如果遇到非常麻烦的映射转换
i. request.setAttribute();
ii. session