Struts2基本知识整理

     struts2实际上是struts和webwork结合的产物,主要应用的webwork的技术,但名字沿用了struts的名字,所以struts2和struts还是有区别的,具体的区别请参考“struts与struts2的区别”。作为一个优秀的MVC框架,struts2最核心的贡献就是将C和V通过配置文件的方式进行了解耦,对于某个请求如果想要更换不同视图,只需集中修改配置文件即可,非常的灵活。
1、namespace:
     struts2中namespace是<package>的一个属性,指定访问action的命名空间,所谓的<package>跟java中的package意义是一样的,就是为了解决重名问题,如果项目存在两个同名的action,那么必须放在不同的<package>中,比如项目的前台有个index的action后台也有个index的action,就可以配一个<package>,它的name为front,namespace="/front",在配一个<package name="behind" namespace="/behind">将两个index分别放在这两个package下。那么访问的时候就可以通过/front/index、/behind/index来访问了,说到这里namespace也就差不多清楚了,其实就是url中在访问action时的一个路径。
     另外namespace属性可以不写,那么默认为“”,如果不写,那么这个<package>就可以匹配所有的action,当然struts2首先还是会根据路径解析出严格的namespace,并从对应的package中查找aciton,在找不到的情况下,struts2会继续到namespace=""的package中查找,如果还找不到,那么就会报错了。

2、自定义action:
     struts2中用户自定义action有三种形式1)、普通java类提供一个String execute()方法;2)、实现Action接口;3)继承ActionSupport类;在实际的企业开发中用的就是第三种,因为ActionSupport中已经有一系列的可以直接使用的方法,更加的方便。在struts.xml指定自定义action的方法就是在action标签中添加class属性<action class="自定义action的全限定类名">,那么struts就会根据类名找到对应的action类并调用它的execute()方法,并得到它的String类型的返回值,根据这个返回值与<result>标签中的name属性对比,得到需要返回的逻辑视图。然后将请求转发给对应的视图。如果没有指定class属性,那么struts2默认执行的action类就是ActionSupport这个基类,它默认返回success。另外提一点,如果<result>的name属性没有指定,那么默认name="success"。

3、路径问题:
     在jsp中,“/”代表整个站点的跟路径(服务器的根路径),而不是当前应用的根路径,例如我在某个jsp中加一个<a hrdf="/index.jsp">首页</a>,那么点一下这个连接会链到tomcat的首页,而不是我们当前应用的首页。而在struts2中的路径问题是根据aciton的路径而不是jsp路径来确定,所以尽量不要用相对路径而要使用绝对路径,或者是想myeclipse一样使用basePath(如果我们使用相对路径,那么myeclipse会在这个路径前自动添加上basePath)。如果非要使用相对路径可以这么做,当前页面上方的地址栏会有一个地址,也就是说不管当前这个jsp位于项目中的什么位置,它当前的位置就是地址栏中显示的这个,根据这个路径与其他的jsp在项目中的位置的关系去配置超链接的路径就可以了。

4、动态方法调用(DMI:Dynamic method invoke)
     首先说明一下,自定义action中,可以没有execute(),前提是必须指定action要执行的方法,怎么指定呢?通过<action name="" class="" method="add">,那么当访问到这个action的时候struts2会调用add方法,当然了这个方法的返回值类型也必须是String类型的(为了跟<result>的name属性做匹配)。这么做就会出现一个问题,如果我这个自定义的action中定义了N个方法,那么这个action在struts.xml中是不是要配置N遍呢?为了解决这个问题我们可以采用DMI。当我们访问某个action时在aciton名字的后面加“!methodName”就可以动态的访问到action中methodName这个方法了,而且struts.xml中可以不用指定method属性。比如Http://localhost:8080/struts2_dmi/userAction!add,那么struts2就会调用UserAction这个类中的add方法,DMI可以大大的简化配置。

5、struts2的通配符(更大程度的简化配置)
     使用DMI可以从很大程度上简化struts.xml的配置,但是不同方法的返回值还要要配置不同的result,这个是没有办法简化的,这种情况下就可以使用struts2的通配符*来进行配置。比如有下面这么一段配置:
<action name="student*" class="xx.xx.StudentAction" method="{1}">
     <result>/student{1}_success.jsp</result>
     <result name="fail">/student{1}_fail.jsp</result>
</action>
我通过Http://localhost:8080/struts2_dmi/studentadd就可以访问StudentAction的add方法,如果返回值是success那么我就可以通过result的配置最终将请求转发给studentadd_success.jap,当返回值为fail时可以匹配到studentadd_fail.jsp。这样即解决了方法调用又解决了result的匹配问题。这里说明一下:通配符*可以有多个,按照出现顺序进行自然数编号,而{1}就代表第一个*,以此类推。
     struts.xml某个属性中是可以出现多个通配符的,那么对上面的配置进行改进,代码如下:
<action name="*_*" class="xx.xx.{1}Action" method="{2}">
     <result>/{1}_{2}_success.jsp</result>
     <result name="fail">/{1}{2}_fail.jsp</result>
</action>
根据上面对于通配符的说明是不是既可以匹配学生的各种方法,又可以匹配老师、课程....的各种方法了,这样就从更大程度是对配置文件进行了简化。如果使用通配符会出现一种情况,假如我既有通配符的这种模糊匹配又有上面介绍的精确配置,那么strut2会按照精确匹配优先的原则进行方法的调用。对于同等级的配置,比如这里介绍的两种都包含通配符,那么struts2会按照配置先后进行调用,也就是调用排在前面的配置。

6、通过action接受前台的参数:
action接收前台传递的参数有三种方式:1)、通过action的普通成员变量来接受参数;这种情况下,页面上的form表单项的name属性必须与action的成员变量的名字必须完全一致,struts2就会根据表单像的name属性向struts2的action设值,所以要求成员至少具备对应的set方法。2)、通过领域模型(DomainModel简单理解为实体类)获取前台参数;这种情况下,from表单项的name属性必须指定是哪个领域模型的哪个属性,例如:<input name="user.username"/>那么struts2就会把这个输入框的值设为user这个对象的username成员的值,同样的必须要有领域模型的set和get方法(必须要有get,因为struts要调用action的getUser().setUsername
()方法) 并且这个领域模型的属性也要有对应的set方法。3)、通过ModelDriven接口来接收前台的参数;这种情况下,action要实现ModelDriven<T>这个接口,并且重写getModel()方法,而且action中的领域模型要自己new(),也就是说struts2发现要调用的aciton实现了ModelDriven接口那么就会调用action的getModel()方法来获取接受参数的领域模型,从而调用它的set方法进行设值。具体代码如下:
public class UserAction extends ActionSupport immeplement ModelDriven<User>{
     private User user = new User();
     //user的get、set可以省略,因为struts2通过getModel来获取user对象。
     public String xxx(){
          return "xxxx";
     }
     public User getModel(){
         return user; 
     }
}
通常用的是第二种方式,第一种也经常用,第三种很少用,理解这个过程就好。

7、action访问web元素:
     当用户登录的时候我们通常会把用户相关的信息存放到session中,以便于用户向服务器发送多次请求时,不会丢失掉用户的信息,这就需要在action中访问到session、同样的还有request和application(分别代表请求和整个应用的信息环境)。为了用户自定义的action与servlet的API解耦,struts2提供了Map类型的request、session和application。其作用与真正的request、session、application是一样的。
1)、通过ActionContext工具类来获取(自动获取):
ActionContext实际封装这要调用的这个action的运行环境相关的一些信息,获取的方法如下:
request = (Map)ActionContext. getContext().get("requset" );
session = ( Map)ActionContext. getContext().getSession();
application = ( Map)ActionContext. getContext().getApplication();
当我们在action对这几个map类型的对象时,可以通过目标jsp页面中的request、session、application这个内置对象取出对应的值了。
2)、通过实现RequestAware、SessionAware、ApplicationAware接口来获取(被动注入--最常用):
     private Map<String, Object> request;
     private Map<String, Object> session ;
     private Map<String, Object> applicaton ;
     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> applicaton) {//接口方法实现
          this . applicaton = applicaton;
         
    }
3)、访问真正的request、session、application(ServletActionContext工具类),获取方法如下:
request = ServletActionContext. getRequest ();
session = request .getSession();
application = session .getServletContext();
或者可以通过实现ServletRequestAware接口来获取HttpServletRequest对象继而通过它来获取HttpSession,和ServletContext对象。
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest request) {//实现的方法
          this . request = request;
}

8、默认action配置:
     当访问某个<package>下的action时,如果我没有写action的名字,那么struts2肯定会报错,因为找不到要调用的action,为了处理这种问题,可以加一个默认的action用来处理这种请求,配置的语法如下:
<action-default-ref name="default"></action-default-ref>,如果action名字为空时,那么struts2默认调用name="default"的action来处理本次请求。(不太常用)

9、result的配置:>
     result的四种类型:dispatcher、redirect、chain、redirectAction分别代表转发到页面、重定向到页面、转发的其他的action、重定向到其他的action,这个要通过<result>的type属性来制定。
     全局结果集的配置:当一个包下所有的action都有一个共同的处理比如说当返回index时,跳转到首页,那么这时候就不需要为每个action都配置这个result了, 只需要将result作为一个全局结果配置到<package>标签下即可。语法:<global-results><result name="index">/index.jsp</result></global-results>。如果是其他包下面的action也想用着结果集,那么只需要<package>的extends属性指定为这个包的name属性即可。
     动态结果集:在action中添加一个成员private String result; 这个result是根据业务动态指定的。然后在struts.xml中就可以获取result的值,然后根据这个值跳转到不同的页面。那么在struts.xml怎么获取这个值呢?我们知道action运行时相关的一些值都会放到这个action的valuestack中,在struts.xml中可以通过${result}来获取值栈中的属性,就可以动态指定result的配置了。<result>${result}</result>(不常用,了解即可)。

10、ognl与tags
     struts2通过tags与ognl可以方便的取出值栈(Value Stack)中的一些属性,值栈中包含着本次请求中Action相关的一些请求、响应的信息。值栈其实是请求转发到视图时放到request中的,相当于request.setPara
meter("struts.valuestack", stack);每次请求对应都对应着一个值栈。可以在页面上通过<s:debug</s:debug>标签查看值栈中的信息,并在页面配合tags和ognl进行展现。

11、声明式异常处理及其原理:
     struts2的异常处理通常是配置在struts.xml中的,配置一个全局的异常处理标签来处理各种异常(即可具体配置处理特种异常,也可笼统的处理各种异常),代码中异常逐层上抛即可,struts2对捕捉这些异常并进行并根据异常处理的配置进行相应的跳转。这种机制实际是通过struts2的拦截器实现的,当struts2处理请求的时候,再调用业务action之前会调用一些列的拦截器,这些拦截器配置在struts-default.property文件中。负责处理异常的拦截器叫exception拦截器,我们整个的业务action的调用其实是在这个拦截器的intercept()方法中完成的,一旦捕获到程序抛出的异常就会与我们配置的异常进行对比,如果抛出的是我们指明了要处理的异常,那么struts2会将action返回的字符串进行修改,修改成我们配置的<exception>的result的值,进而跳转到我们指定的错误页面。

12、拦截器与自定义拦截器:

13、struts2运行原理:客户端浏览器发送请求,通过Http协议将请求发送到对应的主机端口,对应的服务器接到请求后会根据appName将请求发送对应的web应用,服务器读取应用的web.xml将请求交给struts2的filter处理(dofilter()方法),filter根据配置文件调用对应的action,并取得action的返回值,根据result的配置将请求转发到对应的视图,视图对客户端浏览器做出响应。实际上:在filter的filter方法中会调用Dispatcher的service Action()方法创建ActionProxy对象,并调用它的execute()方法。在这个方法中会调用ActionInvocation的invoke()方法,这个ActionInvocation中封装着我们的action和struts2的一些拦截器,invocation会调用拦截器的intecept()方法,intercept()会回调invocation的invoke()方法,当拦截器逐个执行完之后,invocation会通过反射机制获取需要调用的action中的方法,并进行调用。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值