Struts2是在WebWork2基础上发展而来的,和struts1一样,struts2也属于MVC框架。不过有一点大家需要注意的是:尽管Struts2和Struts1在名字上的差别不是很大,但是Struts2和Struts1在代码风格上几乎是不一样的。那么既然有Struts1,为何还要推出Sturts2?

  • 1>在软件设计上Struts2没有像Struts1那样个ServletApi跟SturtsApi有着紧密的耦合,Struts2的应用可以不依赖于ServletApi和StrutsApi,Struts2的这种设计属于无侵入式设计,而Struts1却属于侵入式设计。
  • public class OrderListAction extends Action
  • {
  • public ActionForward execute(ActionMapping mapping ,ActionForm form,HttpServletRequest request,HttpServletResponse response)throws Exception{
  • }
  • }
  • 2>Struts2提供了拦截器,利用拦截器可以进行AOP编程,实现如权限拦截功能
  • 3>Struts2提供了类型转换器,我们可以把特殊的请求参数转换为需要的类型,在Struts1中,如果我们要实现同样的功能,就必须向Struts1的底层实现BeanUtil注册类型转换器才行。
  • 4>Struts2提供支持多种表现层技术,如:JSP,freeMaiker,Velocity等等。
  • 5>Struts2的输入校验可以对指定的方法进行校验,解决了Struts1长久之痛。
  • 6>提供了全局范围,包范围和Action范围的国际化资源文件管理实现。

搭建Struts2开发环境
搭建Struts2环境时,我们一般需要做一下几个步骤的工作

  • 1》找到开发Struts2应用需要的jar文件
  • 2》编写Struts2的配置文件
  • 3》在web.xml中加入Sturts2 MVC 框架启动配置
  • 搭建Struts2开发环境--开发Struts2应用依赖的jar文件
  • struts1-core-2.x.x.jar:Struts2框架的核心类库
  • xwork-2.x.x.jar:xwork类库,Struts2在其构建
  • ognl-2.6.x.jar:对象图导航语言,struts2框架通过其读写对象的属性
  • freemarker-2.3.x.jar:struts2的UI标签的模板使用FreeMarker编写
  • commons-logging-1.1.x.jar:ASF出品的日志包,Struts2框架使用这个日志包支持Log4J和JDK1.4的日志记录
  • commons-fileupload-1.2.1.jar:文件上传组件,2.1.6版本后必须加入此文件

Struts2应用的配置文件
Struts2默认的配置文件为struts.xml,该文件需要存放在web-inf/classes下,该文件的配置模板如下:
Struts2在web中的启动配置
在struts1.x中,struts框架是通过Servlet启动的,在struts2中,struts框架是通过Filter启动的,他在web.xml中的配置如下
在strutsPrepareAndExecuteFilter的init()方法中将会读取类路径下默认的配置文件struts.xml,完成初始化操作。
注意,struts2读取到struts.xml的内容后,以javabean形式存放在内存中,以后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件
Struts.xml配置中的包的介绍
在struts2框架中使用包来管理Action,包的作用和java中的类包是非常相似的,它主要用于管理一组业务功能先关的action,在实际应用中,我们应该把一组业务功能相关的action,在实际应用中,我们应该把一组业务功能相关的Action放在同一个包下。
配置包时必须指定name属性,该name属性值可以任意取名,但必须唯一,他不对应java的类包,如果其他包要继承该包,必须通过该属性进行引用,包的namespace属性用于定义该包的命名空间,命名空间作为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径为:/test/helloworld.action。namespace属性可以不配置,对本例而言,如果不指定该属性,默认的命名空间为""。(空字符串)
通常每个包都应该继承struts-default包,因为struts2很多核心的功能都是拦截器来实现的,如:从请求中把请求参数封装到action,文件上传和数据验证等等都是通过拦截器来实现的,可以这么说,当包继承了struts-default才能使用struts2提供的核心功能,struts-default包是在strust2-core-2.x.x.jar文件中的struts-default.xml中定义。struts-default.xml也是struts2默认配置文件。Struts2每次都会自动加载struts-default.xml文件。
包还可以通过abstract="true"定义为抽象包,抽象包中不能包含action。

Action名称的搜索顺序
1,获得请求路径的URI,例如url是:http://server/struts2/path1/path2/path3/test.action
2,首先寻找namespace为/path1/path2/path3的package,如果不存在这个package则执行步骤3:如果存在这个package,则在这个package中寻找名字为test的action,当该package下寻找不到action时就会直接跑到默认的namespace的package里面寻找action(默认的命名空间为空字符串),如果在默认namespace的package里面还寻找不到该action,页面提示找不到action。

 

<package name="itcast" namespace="/test" extends="struts-default">
<action name="helloworld" class="cn.itcast.action.HelloWorldAction" method="execute">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>
1》如果没有为action指定class,默认是ActionSupport。
2》如果没有为action指定method,默认执行action中的execute()方法。
3》如果没有指定result的name属性,默认值success。

Action中result的各种转发类型
<action name="helleworld" class="cn.itcast.action.HelloWorldAction">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
result配置类似于struts1中的forward,但是struts2中提供了多种结果类型,常用的类型有,dispatcher(默认值),redirect,plainText,redirectAction。
在result中还可以使用${属性名}表达式访问action中的属性,表达式里的属性名对应action中的属性,如下
<result type="redirect">helloworld</result>
下面是redirectAction结果类型的例子,如果重定向的action中同一包下:
<result type="redirectAction">helloworld</result>
如果重定向的action在别的命名空间下:
<result type="redirectAction">
<param name="actionName">helloworld</param>
<param name="namespace">/test<param>
</result>
plaintext显示原始文本内容,例如,当我们需要原样显示jsp文件源代码的时候,我们可以使用此类型,
<result name="source" type="plainText">
<param name="location">/xx.jsp</param>
<param name="charSet">UTF-8</param><!-- 指定读取文件的编码 -->
</result>
一个项目需求
使用重定向的技术,地址栏上面显示了用户名,通过编码的方法来显示
public String execute() throws Exception{
this.username = URLEncoder.encode("传智播客","UTF-8");
return "success";
}
in jsp write
<% URLDecoder.decode(new String(request.getParameter("username").getBytes(ISO8859-1),"UTF-8"),"UTF-8")%>
配置文件
<result name="success" type="reidirect">/employeeAdd.jsp?username=${username}</result>

 

为Action属性注入值
为Action的属性注入值,如果要实现依赖注入,要实现setter and getter method
<action name="itcast" class="cn.itcast.action.HelloWorld">
<param name="savepath">/images</param>
</action>
通过这些信息的配置,可以实现给cn.itcast.action.HelloWorld这个类的savepath属性依赖注入值。
指定Struts2处理的请求后缀
前面我们都是默认使用.action后缀名访问action。其实默认后缀名是可以通过常量"struts.action.extension"进行修改的,例如,我们可以
配置Struts2只处理以.do为后缀的请求路径。
<struts>
<constant name="struts.action.extension" value="do,action,jsp"/>
</struts>
如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号隔开,
细说常量定义
常量可以在struts.xml或struts.properties中配置,建议在struts.xml中配置,两种配置方式如下,在struts.xml文件中配置常量
<struts>
<constant name="struts.action.extension" value="do"/>
</struts>
在struts.properties中配置常量
struts.action.extension=do

因为常量可以在下面多个配置文件进行定义,所以我们需要了解struts2加载常量的搜索顺序,
struts-default.xml
struts-plugin.xml
strus.xml
strus.properties
web.xml
如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值。
常量可以在struts.xml
Struts2的处理流程与Action的管理方式
StrutsPrepareAndExecuteFilter是Struts2框架的核心控制器,他负责拦截由<url-pattern></url-pattern>指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求,默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入Struts2框架处理,否则Struts框架将略过该请求的处理,当请求转入Struts2框架处理时会先经过一系列的拦截器,然后再到Action,与Struts1不同,Struts2对用户的每一次请求都会创建一个Action,所以Struts2中的Action是线程安全的。
为应用指定多个配置文件
在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿,为了避免struts.xml文件过于庞大,臃肿,提高struts.xml文件的可读性,我们可以将一个struts.xml配置文件分解成多个配置文件,然后再struts.xml文件中包含其他配置文件。
<struts>
<include file="struts-user.xml"/>
<include file="struts-order.xml"/>
</struts>
通过这种方式,我们就可以将struts2的Action按模块添加在多个配置文件中。

如何接收请求参数
在学习中我发现一个知识要点,那就是在Action中定义一个方法,
public String test()
{
return "success";
}
在返回success的时候,也就是返回一个success页面的时候,会把这个Action的参数也给带过去。所以,假如说,这个"success"对应的是一个页面,那么这个页面可以使用到该Action里面的属性。
接收请求参数
采用基本类型接收请求参数(get/post)
requestPath:http://localhost:8080/test/view.action?id=78
public class Production{

private Integer id;
public void setId(Integer id){this.id = id;}
public Integer getId(){return this.id;}

}
采用复合类型接收请求参数
requestPath:http://localhost:8080/test/view.action?product.id=78
public class ProductAction{

private Product product ;
public void setProduct(Product product){this.product = product;}
public Product getProduct(){return product;}
}
struts2首先通过反射技术调用Product的默认构造器创建Product对象,然后再通过反射技术调用product中与请求参数同名的属性的setter方法来获取请求参数值。
如果使用符合类型了接收参数的话,注意一定要给一个默认的构造器,such as
public class Person{
public Person(){}
}
自定义类型转换器

 

动态方法调用
如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法,如下:
public class HelloWorld{

private String message;
...
public String execute() throws Exception{
this.message="我的第一个struts2应用";
return "success";
}
public String other()throws Exception{
this.message="第二个方法";
return "success";
}
}
假设访问上面action的URL路径为:/struts/test/helloworld.action
要访问action的other()方法,我们可以这样调用:
/struts/test/hellowrold.action
如果我们不想使用动态方法调用,我们可以通过常量struts.enable.DynamicMethodinvocation关闭动态方法调用。
<constant name="struts.enable.DynamicMethodinvocation" value="false">

使用通配符定义action
<packge name="itcast" namespace="/test" extends="struts-default">
<action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">
<result name="success">/WEB-INF/page/hello.jsp</result>
</action>
</package>

public class HelloworldAction extends ActionSupport{

private String message ;
public String execute() throws Exception{
this.message="我的第一个struts2应用";
return "success";
}
public String other()throws Exception{
this.message="第二个方法";
return "success";
}
}

要访问other方法,可以通过这样的url访问:/test/helloworld_other.actoin

Struts也可以实现AOP的编程
这个功能的实现是通过拦截器来实现的,下面是一个案例
如果用户登录后可以访问action的所有方法,如果,用户没有登录不允许访问action的所有方法,并显示用户没有访问权限。
1,在jsp页面中通过session来设置用户的登录状态。
2,拦截器跟Filter很相像,自定义的拦截器要实现Interceptor接口。该接口一共有三个方法
public void init()
public void destroy()
public String intercept(ActionInvocation invocation)throws Exception()
3,注册拦截器,在struts.xml中定义一个拦截器
<interceptors>
<interceptor name="permiession"class="cn.itcast.interceptor.PermissInterceptor"/>
</inpterceptors>

<action name="list_*" class="cn.itcast.action.HelloworldAction" method="{1}">
<interceptor-ref name="permission"/>
</action>
//采用上述的方法可以获得一个拦截器的使用permiession,但是却失去了struts定义好的所有默认的拦截器。所以采用下面的方法
<interceptors><!-- 注意这个struts提供给我们的defaultStack栈是要放在前面的 -->
<interceptor name="permiession"class="cn.itcast.interceptor.PermissInterceptor"/>
<interceptor-stack name="permiessionStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="permiession"/>
</interceptor-stack>

</inpterceptors>

<action name="list_*" class="cn.itcast.action.HelloworldAction" method="{1}">
<interceptor-ref name="permiessionStack"/>
</action>


//struts-default.xml定义了一个拦截器栈<interceptor name="defaultStack">,这个栈里面放置一些常用的拦截器,
上面我配置的拦截器只能作用于一个action,如果想作用于整个包底下的action,我可以这样定义

<interceptors>
<interceptor name="permiession"class="cn.itcast.interceptor.PermissInterceptor"/>
<interceptor-stack name="permiessionStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="permiession"/>
</interceptor-stack>
</inpterceptors>
<default-interceptor-ref name="permiessionStack"/>
<action name="list_*" class="cn.itcast.action.HelloworldAction" method="{1}">
</action>
//通过<default-inpterceptor-ref>来声明一个全局的拦截器作用于整个包下
//注意如果这个时候在action中再定义一个拦截器的话,那么<default-interceptor-ref>定义的拦截器将不会作用在该action中。

 

因为struts2中如文件上传,数据验证,封装验证,封装请求参数到action等功能都是系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。
如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name="permiessionStack"/>把拦截器定义为默认拦截器,注意,每个包只能指定一个默认拦截器,另外,一旦我们为该包中的某个action显示指定了某个拦截器,则默认拦截器不会起什么作用。

对Action中所有的方法进行输入校验
在struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验。
对于输入校验struts2提供了两种实现方法:
1,采用手工编写代码实现
2,基于XML配置方式实现
手工编写代码实现对action中所有的方法输入校验
通过重写validate()方法实现,validate()中所有与execute方法签名相同的方法,当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldError添加失败信息,(为了使用addFieldError方法,action可以继承ActionSupport),如果系统的fieldError包含失败信息,struts2会将请求转发到名为input的result视图,在input视图中可以通过<s:fielderror/>显示失败信息。
validate()使用例子
public void validate(){  if(this.mobile==null||"".equals(this.mobile.trim()){this.addFieldError("username","手机号不能为空!");}

  else if(Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim().matches()){
  this.addFieldError("mobile","手机号格式不正确!");
}
}
验证失败后,请求转发到input视图
  <result name="input">/WEB-INF/page/addUser.jsp</result>
在addUser.jsp页面中使用<s:fielderror/>显示失败信息。


项目需求,一个是用户名不能为空,另一个是手机号的长度为符合手机号的格式,1,开始,3,5,8为第二个,后面一共有九个数字。
1,定义一个jsp页面
  用户名:<input type="text" name="username"/>
  手机号:<input type="text" name="tel"/>
  <input type="submit" value="提交"/>
2,定义一个action,
public PersonAction extends ActionSupport
{
  private String username ;
  private String mobile ;
  ...setter and getter method
  public String save()throws Exception(){return "success";}
  public Stirng add() throws Exception(){return "fail";}

}
//重写validate方法
public void validate()
{
  if(this.username==null&&"".equals(this.username.trim())){
}

}

手工编写代码实现actioin指定方法输入校验
通过重写validateXxxx()方法实现,validateXxxx()只会校验action中方法名为Xxx的方法,其中Xxx的第一个字母要大写,当某个数据校验失败时,我们应该调用addFieldError()方法往系统中fieldError添加校验失败信息(为了使用addFieldError方法,action可以继承ActionSupport,如果系统的fieldError包含失败信息,struts2会将请求转发到名为input的result,在input视图中可以通过<s:fielderror/>显示失败信息
validateXxx()方法使用例子
public String add()throws Exception {return "success";}
public void validateAdd(){if..............this.addFieldError(''''}

 

输入校验的流程
  1,类型转换器对请求参数执行类型转换,并把转换后的值赋予action中的属性。
  2,如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息封装到fieldError里,不管类型转换是否出现异常,都会进入第三步。
  3,系统通过反射技术先调用acion中的validateXxx方法,Xxx为方法名。
  4,在调用action中的validate方法。
  5,经过上面四步,如果系统中的fieldError存在错误信息,(即存在错误信息的集合的size大于0),系统自动将请求转发至名称input的视图,如果系统中的fieldError没有任何错误信息,系统将执行action的处理方法。

注://这里有一个需要说明的是当我把validate或者validateXxx方法里面的代码全部清空,结果,发现系统还是执行input方法,那么,这个时候就要注意了,可能是在类型转换的时候出现了错误,这时候,要检查类型转换器。

 

posted on 2014-11-03 21:44  六水先生  阅读(232)  评论(0编辑  收藏  举报