Struts2是在WebWork2基础上发展而来的,和struts1同样,struts2也属于MVC框架。不过有一点你们须要注意的是:尽管Struts2和Struts1在名字上的差异不是很大,可是Struts2和Struts1在代码风格上几乎是不同的。那么既然有Struts1,为什么还要推出Sturts2?java
搭建Struts2开发环境
搭建Struts2环境时,咱们通常须要作一下几个步骤的工做web
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。session
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>app
为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按模块添加在多个配置文件中。jsp
如何接收请求参数
在学习中我发现一个知识要点,那就是在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{post
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方法,那么,这个时候就要注意了,多是在类型转换的时候出现了错误,这时候,要检查类型转换器。