使用Struts开发 租房网站

MVC 设计模式
Model 1 模式开发web 应用时,分为两种情况:
  1:纯JSP 技术方式开发
  2:JSP +JavaBean 方式开发
Model 1 模式 的不足:
  --JSP 页面中嵌入大量Java代码,可读性差
  --大量代码在JSP中难以复用
  --后期维护及扩展的难度大
Model 2 模式的引入
由于 Model 1 模式存在的不足,当程序流程非常复杂的时候,要修改一个程序带来的工作量将非常大。为了克服Mode 1 模式的缺陷,人们引入了 Model 2 模式。在Model 1 模式中,JSP页面嵌入了流程控制代码和业务逻辑代码,将这部分代码提取出来,放入单独的类
(Servlet和JavaBean)中,也就是使用JSP(视图) +Servlet(控制器) +JavaBean(模型) 共同开发应用程序,这种方式就是Model 2 模式
    Model 2 模式体现了基于MVC(模型--视图--控制器)的设计模式,简单的说,Model 2 
模式就是将数据显示,流程控制和业务逻辑处理分离,使之相互独立,降低了耦合性。
   当一个程序按照 模型--视图--控制器3部分进行实现时,就可以称之为基于MVC 模式开发的应用程序
  MVC设计模式由3部分组成,各部分的作用如下:
  Model:模型:主要用于数据和业务的处理。
  View:视图:用于数据的显示
  Controller:控制器:用于进行流程控制


 MVC设计模式的特点:
   1:一个模型可以对应多个视图
   2:显示与逻辑控制分离。
   3:分层控制,降低了代码间的耦合性


优点

耦合性
视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
模型是自包含的,并且与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。如果把数据库从MySQL移植到 Oracle ,或者改变基于RDBMS数据源到 LDAP ,只需改变模型即可。一旦正确的实现了模型,不管数据来自数据库或是LDAP服务器,视图将会正确的显示它们。由于运用MVC的应用程序的三个部件是相互独立,改变其中一个不会影响其它两个,所以依据这种设计思想能构造良好的 松耦合 的构件。 [11]  
重用性高
随着技术的不断进步,需要用越来越多的方式来访问应用程序。 MVC模式 允许使用各种不同样式的视图来访问同一个服务器端的代码,因为多个视图能共享一个模型,它包括任何WEB(HTTP)浏览器或者无线浏览器(wap),比如,用户可以通过电脑也可通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面使用。例如,很多数据可能用HTML来表示,但是也有可能用WAP来表示,而这些表示所需要的命令是改变视图层的实现方式,而控制层和模型层无需做任何改变。由于已经将数据和业务规则从表示层分开,所以可以最大化的重用代码了。模型也有状态管理和数据持久性处理的功能,例如,基于会话的购物车和电子商务过程也能被Flash网站或者无线联网的应用程序所重用。 [11]  
生命周期 成本低
MVC使开发和维护用户 接口 的技术含量降低。
部署快
使用MVC模式使开发时间得到相当大的缩减,它使程序员(Java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。
可维护性高
分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。
有利软件工程化管理
由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化管理程序代码。控制器也提供了一个好处,就是可以使用控制器来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。给定一些可重用的模型和视图,控制器可以根据用户的需求选择模型进行处理,然后选择视图将处理结果显示给用户。 [12-13]  


缺点

没有明确的定义
完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。同时由于模型和视图要严格的分离,这样也给调试应用程序带来了一定的困难。每个构件在使用之前都需要经过彻底的测试。
不适合小型,中等规模的应用程序
花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。
增加系统结构和实现的复杂性
对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。
视图与控制器间的过于紧密的连接
视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。
视图对模型数据的低效率访问
依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
一般高级的界面工具或构造器不支持模式
改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,会造成MVC使用的困难

DTD验证XML文档
DTD的概念和作用:
  DTD是Document Type Definition 的缩写,即文档类型定义,  DTD用来描述XML文档的结构
  一个 DTD文档可能包含如下内容:
   元素的定义规则、元素之间的关系规则、属性的定义规则
 DTD的作用:
    DTD使每个XML文件可以携带一个自身格式的描述
    DTD使不同的组织的人可以使用一个通用的 DTD来交换数据
    DTD使应用程序可以使用一个标准 DTD校验从外部接收XML数据是否有效

CDATA 的写法<![CDATA[ ]]>

插曲:

所有 XML 文档中的文本均会被解析器解析。

只有 CDATA 区段(CDATA section)中的文本会被解析器忽略。

PCDATA

PCDATA 指的是被解析的字符数据(Parsed Character Data)。

XML 解析器通常会解析 XML 文档中所有的文本。

当某个 XML 元素被解析时,其标签之间的文本也会被解析:

<message>此文本也会被解析</message>

解析器之所以这么做是因为 XML 元素可包含其他元素,就像这个例子中,其中的 <name> 元素包含着另外的两个元素(first 和 last):

<name><first>Bill</first><last>Gates</last></name>

而解析器会把它分解为像这样的子元素:

<name>
   <first>Bill</first>
   <last>Gates</last>
</name>

转义字符

非法的 XML 字符必须被替换为实体引用(entity reference)。

假如您在 XML 文档中放置了一个类似 "<" 字符,那么这个文档会产生一个错误,这是因为解析器会把它解释为新元素的开始。因此你不能这样写:

<message>if salary < 1000 then</message>

为了避免此类错误,需要把字符 "<" 替换为实体引用,就像这样:

<message>if salary &lt; 1000 then</message>

在 XML 中有 5 个预定义的实体引用:

&lt; < 小于
&gt; > 大于
&amp; & 和号
&apos; ' 省略号
&quot; " 引号

注释:严格地讲,在 XML 中仅有字符 "<"和"&" 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。

CDATA

术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data)。

在 XML 元素中,"<" 和 "&" 是非法的。

"<" 会产生错误,因为解析器会把该字符解释为新元素的开始。

"&" 也会产生错误,因为解析器会把该字符解释为字符实体的开始。

某些文本,比如 JavaScript 代码,包含大量 "<" 或 "&" 字符。为了避免错误,可以将脚本代码定义为 CDATA。

CDATA 部分中的所有内容都会被解析器忽略。

CDATA 部分由 "<![CDATA[" 开始,由 "]]>" 结束:

<script>
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then
  {
  return 1;
  }
else
  {
  return 0;
  }
}
]]>
</script>

在上面的例子中,解析器会忽略 CDATA 部分中的所有内容。

关于 CDATA 部分的注释:

CDATA 部分不能包含字符串 "]]>"。也不允许嵌套的 CDATA 部分。

标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。


文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。

DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

内部的 DOCTYPE 声明

假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:

<!DOCTYPE 根元素 [元素声明]>

带有 DTD 的 XML 文档实例(请在 IE5 以及更高的版本打开,并选择查看源代码):

<?xml version="1.0"?>

<!DOCTYPE note [

  <!ELEMENT note (to,from,heading,body)>

  <!ELEMENT to      (#PCDATA)>

  <!ELEMENT from    (#PCDATA)>

  <!ELEMENT heading (#PCDATA)>

  <!ELEMENT body    (#PCDATA)>

]>

<note>

  <to>George</to>

  <from>John</from>

  <heading>Reminder</heading>

  <body>Don't forget the meeting!</body>

</note>

在您的浏览器中打开此 XML 文件,并选择“查看源代码”命令

以上 DTD 解释如下:

!DOCTYPE note (第二行)定义此文档是 note 类型的文档。

!ELEMENT note (第三行)定义 note 元素有四个元素:"to、from、heading,、body"

!ELEMENT to (第四行)定义 to 元素为 "#PCDATA" 类型

!ELEMENT from (第五行)定义 frome 元素为 "#PCDATA" 类型

!ELEMENT heading (第六行)定义 heading 元素为 "#PCDATA" 类型

!ELEMENT body (第七行)定义 body 元素为 "#PCDATA" 类型

外部文档声明

假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:

<!DOCTYPE 根元素 SYSTEM "文件名">

这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD: (在 IE5 中打开,并选择“查看源代码”命令。)

<?xml version="1.0"?>

<!DOCTYPE note SYSTEM "note.dtd">

<note>

<to>George</to>

<from>John</from>

<heading>Reminder</heading>

<body>Don't forget the meeting!</body>

</note> 

这是包含 DTD 的 "note.dtd" 文件:.dtd为文件后缀

<!ELEMENT note (to,from,heading,body)>

<!ELEMENT to (#PCDATA)>

<!ELEMENT from (#PCDATA)>

<!ELEMENT heading (#PCDATA)>

<!ELEMENT body (#PCDATA)>

为什么使用 DTD?

通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。

通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。

而您的应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据。

您还可以使用 DTD 来验证您自身的数据。


说明:在Struts 2 框架中都是引入外部DTD实现XML文件的验证


反射机制:
Java 反射概述:
Java 的反射是Java的特性之一,是构建框架技术所在。
Java反射机制是指在运行时,动态获取类或者对象的信息以及动态调用对象方法的功能。
用比较专业的说法,反射是指应用能够自描述和自控制的机制和能力。
Java反射具有3个动态性质:
  • 运行时生成对象实例
  • 运行时调用对象方法
  • 运行时更改对象属性
通过Java反射,可以实现以下功能:
  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断一个类所具有的方法和属性。
  4. 在运行时调用任意一个对象的方法
Java反射的原理:
反射是光学中的一个概念,在计算机领域是指程序可以访问、检查、修改它自身状态和行为的能力。
就如同人们照镜子,借助于光的反射,可以看到自身的每一部分一样,整理一下发型和衣领之类的。
反射还可以做更多,例如:在运行时加载编译期间不知道名字的类(也就是未来的类,比如有新功能的添加)
Java反射常用的API
Class类:public final class Class<T>
反射类的核心类,反射的所有操作都是围绕该类来生成的。通过Class类可以获取类的属性,方法等内容信息
Field类:类的属性
Method类:类的方法
Constructor类:类的构造方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestDemo {
   public static void main(String[] argsthrows ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        /*
         * 使用反射就是使用未知的类和未知的方法,所谓未知就是"com.reflectionDemo.Car"
         * 和"run" 这两个字符串可以在配置文件中读取
         */
       //这一行代表的是拿到对应的类名,而不是实例
       Class<?> cls= Class.forName("com.reflectionDemo.Car"); //类的完整名包括包名,否则抛异常
       //创建一个实例
       Object object=cls.newInstance();
       //获取实例的方法,注意这里并没有调用
       Method methodcls.getMethod("run");
       //调用这个方法,参数就是实例本身
       method.invoke(object);
       System.out.println("----------------");
       Class<?> circleClass=Class.forName("com.reflectionDemo.Circle");
       Object object3circleClass.newInstance();
          //                                     方法名      方法形参          当前对象   实际参数
                                                                 (多个用逗号分隔)
       circleClass.getMethod("setRadius",double.class).invoke(object3, 10);
       Object object2=  circleClass.getMethod("calcArea").invoke(object3);
       System.out.println(object2);
   }
}
使用反射获取类中的私有字段和方法
package com.reflectionDemo;
public class Car implements Vehicle {
    public Car() {
    }
    private double price = 123455.00;
    public double getPrice() {
        return price;
    }
    public void run() {
        System.out.println("汽车在行驶!");
    }
    private void say() {
        System.out.println("haha!");
    }
}
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestDemo {
   public static void main(String[] argsthrows Exception {
       Class<?> cls= Class.forName("com.reflectionDemo.Car");
       Object object=cls.newInstance();
          //测试获取私有方法
       Method methodcls.getDeclaredMethod("say");
       method.setAccessible(true); //true值表明反射的对象应当压制java语言访问检查时
       method.invoke(object);
        //测试获取私有字段
       Field fieldcls.getDeclaredField("price");
       field.setAccessible(true);//true值表明反射的对象应当压制java语言访问检查时
       double price=field.getDouble(object);   //调用这个方法,参数就是实例本身
       System.out.println("价格是:"+price);
   }
}

 Struts1(也是基于mvc模式)的不足:
  • 表现层单一:只支持jsp而不支持FreeMark、Velocity的视图技术
  • 对servletAPI的依赖性很强
  • 不利于代码的重用,耦合性很强
 Struts2以WebWork的设计思想为核心,吸收了Struts1的部分优点,建立了一个兼容WebWork和Struts1的MVC框架。WebWork更加强调系统之间的松耦合,使用拦截器来实现

必须引入的包

struts2-core.jar——Struts2的核心包

xwork-core.jar——Command模式框架,WebWork和Struts2都基于xwork

commons-logging.jar——Java日志工具类包

freemarker.jar——模板引擎,一个基于模板生成文本输出的通用工具

ognl.jar——Object-Graph Navigation Language,表达式语言,用来获取和设置Java对象属性

commons-fileupload.jar——文件上传

Javassist-xxx.GA.jar 对字节码进行处理

Commons-lang-xxx.jar:包含一些数据类型 的工具类

可选包

antlr.jar——语法分析器

aopalliance.jar——AOP联盟标准接口

classworlds.jar——class对象管理

commons-beanutils.jar——Bean处理工具类包

commons-chain.jar——流程链处理模型,Struts2处理HTTP请求处理的基础机制

commons-collections.jar——Commons项目组中的一个各种集合类和集合工具类的封装

commons-digester.jar——解析xml转换成Java对象


commons-io.jar——IO工具类封装

commons-lang.jar——Commons项目中用来处理Java基本对象方法的工具类包

commons-logging-api.jar——Java常用日志工具调用,如通过API调用Log4J

commons-validator.jar——Java对象验证框架

json-lib.jar——用来映射JSON(JavaScript Object Notiation是一种文本格式)到Java Bean的Java类库

oro.jar——文本处理工具

sslext.jar——Struts框架的一个扩展可以让开发者配置Web应用程序在http与https协议之间进行自动转换(需配置)

插件包

struts2-embeddedjsp-plugin.jar——嵌入式JSP插件,可以让你从类路径或者Jar包里面调用JSP页面(官网翻译大概是这个意思)

struts2-convention-plugin.jar——用来实现Struts2零配置的插件包,可以结合这东西实现项目REST风格应用。

struts2-json-plugin.jar——Apache提供的JSON插件包

<%@ taglib uri="/struts-tags"  prefix="s" %>  
      <div>
         <%--显示Struts Action中message属性内容,这就是ognl(对象导航图语言)表达式,等价于${message}--%>
         <s:property value="message"/>
      </div>  
package stu.action;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import stu.biz.ClassesBiz;
import com.opensymphony.xwork2.ActionSupport;
@Component("classesAction")
public class ClassesAction extends ActionSupport {
    @Autowired 
    private  ClassesBiz classesBiz;
    private Student student;
    public Student getStudent() {//通过getter来推送数据
        return student;
    }
    public void setStudent(Student student) {//通过setter来为属性赋值
        this.student = student;
    }  
    @Override
    public String execute() throws Exception {
    return SUCCESS;
    }
}
Struts2 访问ServletAPI对象
解耦方式访问,便于Action类测试
使用ActionContext获取到的HttpServletRequest、HttpSession、ServletContext都是其对应的Map对象
ActionContext是Action执行的上下文
第一种
       ActionContext ac=ActionContext. getContext ();
    Map<String, Object> application=ac.getApplication();
    Map<String, Object> session=ac.getSession();
    Map request=(Map)ac.get("request");  //没有getRequest()方法

第二种:
       分别实现
          org.apache.struts2.interceptor.RequestAware,该接口只有 setRequest这个方法要实现
      org.apache.struts2.interceptor.SessionAware,该接口只有 set Session 这个方法要实现
          org.apache.struts2.interceptor.ApplicationAware,该接口只有 set Application t这个方法要实现
    @Override
    public void setRequest(Map<String, Object> arg0) {  }   
耦合方式访问
第三种:
要直接获取Servlet API对象可以使用 org.apache.struts2.ServletActionContext类,该类是ActionContext的子类,提供获取Servlet API的方法都是静态方法
      ServletActionContext. getRequest ();
    ServletActionContext.getRequest().getSession();
    ServletActionContext.getResponse();
    ServletActionContext.getContext().getApplication();  

也可以实现org.apache.struts2.interceptor.ServletRequestAware;接口
也可以实现 org.apache.struts2.interceptor.ServletResponseAware; 接口
也可以实现 org.apache.struts2.util.ServletContextAware;    接口
Struts2 数据校验
ActionSupport类是一个默认的Action实现类,它的完全限定名是 com.opensymphony.xwork2.ActionSupport
这个类提供了很多默认的方法,包括获取国际化信息的方法、数据校验的方法以及默认处理用户请求的方法
如果在struts.xml 中 action元素的class属性不写,则表示访问ActionSupport类,而且执行的是execute()方法,直接return SUCCESS,同时ActionSupport类还增加了对验证,本地化等的支持

Struts2的标签
Struts2提供了强大的标签库,而且远远超出了传统标签库基本功能。完全可以自定义主题、模板更好地实现代码复用
Struts2 标签库分为两大类UI标签和通用标签(Generic Teg)
如果要使用首先得先导入标签库 <%@ taglib uri="/struts-tags"  prefix="s" %>   
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags"  prefix="s" %>
<!DOCTYPE HTML>
<html>
  <head>
   <title></title>
  </head>
  <body>
    <h1>用户登录</h1>
       <s:fielderror/>
    <form action="/Stu/userLogin" method="post">
      <label for="userName">用户名:</label><input type="text" id="userName"  name="userName"/><br/>
      <label for="password">密码:</label><input type=" password " id = "password"  name = "password"/><br/>
      <input type="submit" value="登录"/>
    </form>
  </body>
</html>
package stu.action;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.ActionSupport;
@Component("loginAction")
@Scope("prototype")
public class LoginAction extends ActionSupport {
    private static final long serialVersionUID = 1L;
          //模拟用户登录
    private String userName;
    public void setUserName(String userName) {
        this.userName = userName;
    }
    private String password;
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public String execute() throws Exception {
        return SUCCESS;
    }
    /*
     * 在LoginAction 中添加了validate()方法之后,一旦验证过程中添加了校验信息,那么
     * Struts2框架会根据Action的配置跳转到input的视图界面
     * */
    @Override
    public void validate() {
       if(this.userName==null||this.userName.trim().length()==0){
       this.addFieldError("userName""用户名不能为空");
       }
       if(this.userName==null||this.password.trim().length()<6){
       this.addFieldError("password""密码不能小于6位");
       }
    }
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
    <package name="default" namespace="/" extends="struts-default">
        <action name="index" class="studentAction" >
            <result name="success">/index.jsp</result>
        </action>
        <action name="userLogin" class="loginAction">
            <result name="success" type="redirect">index</result>
            <result name="input">/login.jsp</result>
        </action>
    </package>
</struts>    
除了使用validate()方法之外,也可以使用validateXxx()方法,其中Xxx是方法名,首字母大写
package stu.action;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.ActionSupport;
@Component("loginAction")
@Scope("prototype")
public class LoginAction extends ActionSupport {
     //省略属性
    public void  validateLogin () {//跳到input视图
       if(this.userName==null||this.userName.trim().length()==0){
              this .addFieldError( "userName" "用户名不能为空" );
       }
       if(this.userName==null||this.password.trim().length()<6){
             this .addFieldError( "password" "密码不能小于6位" );
       }
    }
    public String  login () throws Exception {
        return SUCCESS;
    }
}
防止表单重复提交:地址栏的后退、多次点击提交按钮、重复下订单
  jsp
<form action="/Stu/userLogin" method="post">
      <label for="userName">用户名:</label><input type="text" id="userName"  name="userName"/><br/>
      <label for="password">密码:</label><input type="text" id="password"  name="password"/><br/>
      <input type="submit" value="登录"/>
      <s:token></s:token><!--一定要有这个标签-->  
    </form>  
strutsl.xml
<action name="userLogin" class="loginAction">
            <result name="success" type="redirect">index</result>
            <result name="input">/login.jsp</result>
            <result name="invalid.token">TokenFailed.jsp</result>  
            <!-- 若重复提交,则会跳转到这个页面,注意这里result的名字,一定要是invalid.token -->  
            <interceptor-ref name="token"></interceptor-ref>  
            <interceptor-ref name="defaultStack"></interceptor-ref>  
            <!-- 这里一定要有这两个拦截器 -->  
        </action>  


防止表单重复提交,这是个很重要的知识点,而且很有用。当用户提交了一个表单,此时,地址栏显示的是处理这个表单的Action的地址,若此时刷新,则会重新发送一次表单数据,即又进行了一次提交,若这个Action是用来处理用户注册的,那么重复提交会再一次向数据库中插入之前已经插入的数据,这显然不是我们想要的。有两种方法,可以防止表单重复提交,一种是用Action的重定向,一种是用Session Token(Session令牌)。

第一种方法,Action处理完用户提交的数据后,重定向到另一个Action或是一个页面,使用户提交后,所停留的位置,不是当前处理数据的Action,这样用户再刷新时,就不会再次执行这个Action了,就会避免表单重复提交的问题了。

第二种方法,是一种很经典的处理这个问题的机制。这种方法是在用户要提交的表单中,加入一个<s:token>标签,这样,当浏览器第一次访问这个带有<s:token>标签的页面时,在服务器中,解析<s:token>标签的类(TokenTag.class),会生成一个随机的字符串(这个字符串,查看网页的源代码可以看到)

类似这样:

<input type="hidden" name="struts.token" value="6HGCH1POXPCZZZYZUKYPWPG2Z353ASPI" />

并且发送给客户端的浏览器,同时,在服务器中,会把这个随机字符串保存到用户的session对象中。当第一次提交表单时,在服务器中,会比较客户端和服务器中分别保存的这个随机字符串,因为是第一次提交,所以这两个字符串相等,然后进行正常的业务处理。第一次提交后,在服务器中的session中保存的这个随机字符串,会改变为其他的随机值,注意,这是很重要的一步!此时,地址栏停留在处理用户提交数据的Action中,客户端中保存的随机字符串没有改变,若是刷新页面,即重复提交,服务器再进行两个字符串的比较,会不相等,就会跳转到name为invalid.token的结果页面中,这样就会防止表单重复提交了。


很重要的三个文件struts-default.xml、struts-plugin.xml、struts.xml
struts.xml 中 constant(常量)元素用于配置常量
struts.xml 中package的默认的namespace 为 " "空串
用于处理中文乱码
<constant name="struts.i18n.encoding" value="utf-8"></constant>  
struts-plugin.xml文件是struts 2插件使用的配置文件
action中动态方法的调用(Dynamic Method Invocation):actionName!methodName.action
因为使用动态方法会有安全隐患(通过URI 可以执行Action中任意方法),基于这个原因struts 2 提供了一个属性的配置,用于禁止调用动态方法
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>  
使用method 属性 来替代动态调用,但是还是不能减少action 的配置,使用通配符和占位符来解决
配置默认的action 使用 
<default-action-ref name=""></default-action-ref>
配置默认的interceptor使用 
      <default-interceptor-ref name=""></default-interceptor-ref>  
动态匹配结果,使用${},访问的是getter属性

Struts 2 的拦截器
  • 在任何优秀的MVC框架都会提供一些通用的操作,如请求数据的封装,类型转换、数据校验、解析上传文件,防止表单多次提交等。早期的MVC框架将这些操作都写死在核心控制器中,而这些同用的操作并不是所有的请求都需要实现,这会导致框架的灵活性不足,可扩展性降低。
  • Struts 2 将它的核心功能放到拦截器中实现而不是集中在核心控制器中实现,八大部分控制器需要完成的工作按功能分开定义,每一个拦截器完成一个功能,而完成这些功能的拦截器可以自由选择、灵活组合,需要哪些拦截器,只需要在struts.xml中指定,从而增强了框架的灵活性。
  • 拦截器的方法在Action执行之前之后自动执行,从而将通用的操作动态的插入到Action执行的前后,这样有利于系统的解耦
  • 如果有一批拦截器经常固定在一起使用,可以使用拦截器栈,拦截器栈里都是拦截器的引用,从而构成一个更大粒度的拦截器,供其他拦截器使用
<interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                  <param name="excludeParams">dojo\..*,^struts\..*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
 </interceptor-stack>  
拦截器的工作原理:
拦截器围绕着Action和Result的执行而执行,Struts 2的拦截器实现原理和Servlet Filter的实现原理差不多,以链式执行,对真正要执行的方法进行拦截。首先执行Action配置的拦截器,在 Action和Result执行的之后,拦截器再一次执行(与先前调用相反的顺序),在此链式的执行过程中,任何一个拦截器都可以直接返回,从而终止其余的拦截器、Action、Result的执行
intercept()方法是拦截器的入口方法,它接收ActionInvocation 实例,先进行预处理工作,然后调用invocation.invoke()方法,其中 invocation.invoke()方法也会有对 intercept()方法的调用,这用就形成了递归链,紧接着进行后续处理,最终return  invoke()方法的返回值

com.opensymphony.xwork2.interceptor.Interceptor接口的说明可以了解到:

1、Interceptor是一个无状态的类,它采用了拦截器模式。采用了javax.servlet.Filter,以及AOP的思想。(这是Interceptor的原理)

2、Interceptor是一个动态的拦截Action调用的对象使用Interceptor可以在Action调用之前或者之后添加功能它也可以阻止Action的执行。使用拦截器可以定义一些通用的功能然后用于多个Action。(这是Interceptor的功能)

//这是一个打印日志的拦截器
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LoggingInterceptor extends AbstractInterceptor {
    private static final Logger LOG = LogManager.getLogger(LoggingInterceptor.class);
    private static final String FINISH_MESSAGE = "Finishing execution stack for action ";
    private static final String START_MESSAGE = "Starting execution stack for action ";
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        logMessage(invocation, START_MESSAGE);
        String result = invocation.invoke();
        logMessage(invocation, FINISH_MESSAGE);
        return result;
    }
    private void logMessage(ActionInvocation invocation, String baseMessage) {
        if (LOG.isInfoEnabled()) {
            StringBuilder message = new StringBuilder(baseMessage);
            String namespace = invocation.getProxy().getNamespace();
            if ((namespace != null) && (namespace.trim().length() > 0)) {
                message.append(namespace).append("/");
            }
            message.append(invocation.getProxy().getActionName());
            LOG.info(message.toString());
        }
    }
}
public String invoke() throws Exception {
        String profileKey = "invoke: ";
        try {
            UtilTimerStack.push(profileKey);
            if (executed) {
                throw new IllegalStateException("Action has already executed");
            }
             if (interceptors.hasNext()) {//通过内部状态判断是否有下一个拦截器
                final InterceptorMapping interceptorMapping = interceptors.next();
                String interceptorMsg = "interceptorMapping: " + interceptorMapping.getName();
                UtilTimerStack.push(interceptorMsg);
                try {
                    Interceptor interceptor = interceptorMapping.getInterceptor();
                    if (interceptor instanceof WithLazyParams) {
                        interceptor = lazyParamInjector.injectParams(interceptor, interceptorMapping.getParams(), invocationContext);
                    }
                               //invoke()对interceptor的intercept()方法的调用
                     resultCode = interceptor.intercept(DefaultActionInvocation.this);
                } finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
                resultCode = invokeActionOnly();
            }
            // this is needed because the result will be executed, then control will return to the Interceptor, which will
            // return above and flow through again
            if (!executed) {
                if (preResultListeners != null) {
                    LOG.trace("Executing PreResultListeners for result [{}]"result);
                    for (Object preResultListener : preResultListeners) {
                        PreResultListener listener = (PreResultListener) preResultListener;
                        String _profileKey = "preResultListener: ";
                        try {
                            UtilTimerStack.push(_profileKey);
                            listener.beforeResult(thisresultCode);
                        }
                        finally {
                            UtilTimerStack.pop(_profileKey);
                        }
                    }
                }
                // now execute the result, if we're supposed to
                if (proxy.getExecuteResult()) {
                    executeResult();
                }
                executed = true;
            }
            return resultCode;
        }
        finally {
            UtilTimerStack.pop(profileKey);
        }
    }  


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
    <package name="default" namespace="/" extends="struts-default">
        <interceptors>
          <interceptor name="" class=""></interceptor>
          <!--拦截器栈,用于组合多个拦截器-->
          <interceptor-stack name="">
              <interceptor-ref name=""></interceptor-ref>
          </interceptor-stack>
        </interceptors>
      
        <action name="userLogin" class="loginAction">
            <result name="success" type="redirect">index</result>
            <result name="input">/login.jsp</result>
            <result name="invalid.token">TokenFailed.jsp</result>
            <!-- 若重复提交,则会跳转到这个页面,注意这里result的名字,一定要是invalid.token -->
            <interceptor-ref name="token"></interceptor-ref>
            <interceptor-ref name="defaultStack"></interceptor-ref>
            <!-- 这里一定要有这两个拦截器 因为默认拦截器不包含token拦截器-->
        </action>
    </package>
</ struts >     
Struts2的内置拦截器
                    <interceptor-stack name = "defaultStack">
                        <!--exception拦截器位于所有拦截器的第一个-->
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
       <!--
         servletConfig拦截器提供了一种将源于Servlet API的各种对象注入Action 当中的简洁方法
       Action 必须实现相对应的接口,servletConfig 拦截器才能将对应的Servlet 对象注入Action中
ServletContextAware、ServletRequestAware、ServletResponseAware、
ParamterAware、RequestAware、SessionAware、ParamterAware、ApplicationAware、
     -->
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
              <!--将配置文件中的action元素的子元素param所设置的参数设置到对应的Action属性中-->
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                         <!--将请求中的数据设置到Action的属性中-->
                <interceptor-ref name="params">
                  <param name="excludeParams">dojo\..*,^struts\..*</param>
                </interceptor-ref>

                <interceptor-ref name="conversionError"/>

                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                           <!--当数据校验错误时终止执行流程的功能-->
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>

            </interceptor-stack>
自定义拦截器:要么实现要么继承
实现com.opensymphony.xwork2.interceptor.
Interceptor接口
继承AbstractInterceptor 类
public abstract class AbstractInterceptor implements Interceptor {
    /**
     * Does nothing 空实现
     */
    public void init() {
    }
    
    /**
     * Does nothing  空实现
     */
    public void destroy() {
    }
    /**
     * Override to handle interception
     */
    public abstract String intercept(ActionInvocation invocation) throws Exception;
}  

问:为什么在浏览器中直接请求页面,拦截器没有发挥作用?
因为在Struts 2中,拦截器只针对Action的请求才会发生作用,如果直接请求页面,拦截器是不进行响应的
Struts 2实现对文件的上传
第一步:添加commons-fileupload-xxx.jar和commons-io-xxx.jar包
第二步:设置 form表单的
enctype="multipart/form-data"  method="post"
  <form action="" enctype="multipart/form-data"  method="post">
        <s:textfield name="title" label="标题" value=""></s:textfield><br/>
        <s:file name="upload" label="选择文件" /><br/>
        <s:submit value="上传文件"/> 
    </form>  
第三步:在Action中 写三个私有属性
一个属性的类型是File 名称与jsp页面的
 <s:file name="upload" label="选择文件" />name属性相同
另外两个是String类型的 xxxFileName 和
String类型的xxxContentType,其中xxx表示文件控件的name属性



Struts2常用标签总结  
一 介绍  
1.Struts2的作用      
Struts2标签库提供了主题、模板支持,极大地简化了视图页面的编写,而且,struts2的主题、模板都提供了很好的扩展性。实现了更好的代码复用。Struts2允许在页面中使用自定义组件,这完全能满足项目中页面显示复杂,多变的需求。  
    Struts2的标签库有一个巨大的改进之处,struts2标签库的标签不依赖于任何表现层技术,也就是说strtus2提供了大部分标签,可以在各种表现技术中使用。包括最常用的jsp页面,也可以说Velocity和FreeMarker等模板技术中的使用  
2.Struts2分类  
(1) UI标签:(User  Interface, 用户界面)标签,主要用于生成HTML元素标签 UI标签又可分为表单标签非、表单标签和Ajax标签   
(2) 非UI标签,主要用于数据访问,逻辑控制等的标签 。非UI标签可分为流程控制标签(包括用于实现分支、循环等流程控制的标签)和数据访问标签(主要包括用户输出ValueStack中的值,完成国际化等功能的)  
(3) ajax标签  
3.Struts2标签使用前的准备:  
(1)在要使用标签的jsp页面引入标签库:     
<%@ taglib uri="/struts-tags" prefix="s"%>  
(2)在web.xml中声明要使用的标签    这样是struts2 2.3.1.2版本的引入方式  
<filter>  
   <filter-name>struts2</filter-name>  
    < filter- class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class>   
 </filter>  
4.标签的使用  
(1) property标签  
     用于输出ValueStack中对象属性的值,如果没有value 属性则默认输出栈顶的对象,类型为Object,default属性表示属性值为null时的
默认值类型为String
    <s:property value="%{@cn.csdn.hr.domain.User@Name}"  default="不能真确显示" />< br />   
        <s:property value="@cn.csdn.hr.domain.User@Name"/><Br/><!-- 以上两种方法都可以 -->  
        <s:property value="%{@cn.csdn.hr.domain.User@study()}"/>  
    以上可以访问某一个包的类的属性的集中方式,study()是访问方法的方法,并输出。  
  
  
    以下用java代码代替的,访问某一个范围内的属性  
    <%  
    //采用pageContext对象往page范围内存入值来 验证#attr搜索顺序是从page开始的 ,搜索的顺序为:page,reques,session,application。  
set存值的时候存到的是request中,在jsp页面中访问的时候不用加任何的标识符,即可直接访问,如果不同的作用域不一样了,  
pageContext.setAttribute("name""laoowang", PageContext.PAGE_SCOPE);  
%>  
<s:property value="#attr.name" />   
  
  
假设在action中设置了不同作用域的类  
不同的作用域的标签的访问:  
        
  <h3>获取的是requet中的对象值</h3>  
  第一种方式:<s:property value="#request.user1.realName"/>  
  <br/>  
  第二种方式:<s:property value="#request.user1['realName']"/>  
   <br/>  
  第三种方式:<s:property value="#user1.realName"/>  
   <br/>  
    第四种方式:<s:property value="#user1['realName']"/>  
      <br/>  
       第五种方式:${requestScope.user1.realName }  || ${requestScope.user1['realName'] }  
         
   第六种:<s:property value="#attr.user1.realName"/>  
  attr对象按page==>  request sessionapplictio找的  
    
    
  <h3>获取session中的值</h3>  
    第一种方式:<s:property value="#session.user1.realName"/>  
  <br/>  
  第二种方式:<s:property value="#session.user1['realName']"/>  
    
    
    第五种方式:${sessionScope.user1.realName }  || ${sessionScope.user1['realName'] }  
    
  <h3>获取application中的对象的值</h3>  
    第一种方式:<s:property value="#application.user1.realName"/>  
  <br/>  
  第二种方式:<s:property value="#application.user1['realName']"/>  
    
    
    第五种方式:${applicationScope.user1.realName }  || ${applicationScope.user1['realName'] }  
      
(2)iterator标签的使用  
    第一种:list集合  
        <!-- 设置set集合  value-->  
        <!-- status 可选属性,该属性指定迭代时的IteratorStatus实例 -->  
        <!-- value="#attr.list"   list存放到了request中  可以value="#request.list"  
                    statu.odd返回当前被迭代元素的索引是否是奇数  
                -->  
    <s:set name="list" value="{'a','b','c','d'}"></s:set>  
        <s:iterator var="ent" value="#request.list" status="statu">  
            <s:if test="%{#statu.odd}">  
                <font color="red"><s:property value="#ent" />  
                </font>  
            </s:if>  
            <s:else>  
        <s:property value="#ent" />  
        </s:else>  
</s:iterator>  

      
第二种:map集合中的使用  
    <h3>Map集合</h3>  
                <!-- map集合的特点:   
                  语法格式:# {key:value,key1:value1,key2:value2,.....}     
 以上的语法中就直接生成了一个Map类型的集合,该Map对象中的每个key-value对象之间用英文的冒号隔开     
   ,多个元素之间用逗号分隔。     
                -->  
            </div>  
  
            <s:set var="map" value="#{'1':'laowang','2':'老王','3':'猩猩'}"></s:set>  
            遍历Map:  
            <br />  
            <s:iterator value="#map">  
                <s:property value="key" />:::<s:property value="value" />  
                <Br />  
            </s:iterator>\  
  
第三种:集合的变量  
<h3>遍历集合:::</h3>  
            <div>  
                <!-- 遍历出价格大于3000的 -->  
                <s:iterator var="user" value="#session['users']">  
                    <s:if test="%{#user['price']>3000}">  
                        <s:property value="#user['price']"/>  
                    </s:if>  
                </s:iterator>  
                  
                  
                <hr color="blue"/><!-- $是取出价格 大于3000的最后一个值 -->  
                <s:iterator var="u" value="#session.users.{$(#this['price']>3000)}">  
                    <s:property value="price"/>  
                </s:iterator>  
            </div>  
注:users是User的对象,price是User中的一个属性  
  
简述一下iterator的介绍:  
iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组。  
<s:set name="list" value="{'zhangming','xiaoi','liming'}" />  
<s:iterator value="#list" status="st">  
    <font color=<s:if test="#st.odd">red</s:if><s:else>blue</s:else>>  
    <s:property /></font><br>  
</s:iterator>   
value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合。  
id:可选属性,指定集合里元素的id。  
status:可选属性,该属性指定迭代时的IteratorStatus实例。该实例包含如下几个方法:  
    int getCount(),返回当前迭代了几个元素。  
    int getIndex(),返回当前迭代元素的索引。  
    boolean isEven(),返回当前被迭代元素的索引是否是偶数  
    boolean isOdd(),返回当前被迭代元素的索引是否是奇数  
    boolean isFirst(),返回当前被迭代元素是否是第一个元素。  
    boolean isLast(),返回当前被迭代元素是否是最后一个元素。  
  
  
(3)if else语句的使用  
    <s:set name="age" value="21" />  
<s:if test="#age==23">  
    23  
</s:if>  
<s:elseif test="#age==21">  
    21  
</s:elseif>  
<s:else>  
    都不等  
</s:else>   
  
(4)URL标签  
    <!-- 声明一个URL地址 -->  
        <s:url action="test" namespace="/tag" var="add">  
            <s:param name="username">laowangang</s:param>  
            <s:param name="id">12</s:param>  
        </s:url>  
        <s:a href="%{add}">测试URL</s:a>  
        <s:a action="test" namespace="/tag"></s:a>  
    以上的两个<s:a>签的作用是一样的。  
(5)data标签  
<%  
            pageContext.setAttribute("birth",new Date(200,03,10),PageContext.REQUEST_SCOPE);  
         %>  
        <s:date name="#request.birth" format="yyyy年MM月dd日"/>  
        <s:date name="#request.birth" nice="true"/>  
    这个标签是按照format的格式去输出的。  
  
(6)表单  
  
              
        <h1>from表单</h1>  
        <s:form action="test" namespace="/tag">  
            <s:textfield label="用户名" name="uname" tooltip="你的名字" javascriptTooltip="false"></s:textfield>  
            <s:textarea  name="rmake" cols="40" rows="20" tooltipDelay="300" tooltip="hi" label="备注" javascriptTooltip="true"></s:textarea>  
            <s:password label="密码" name="upass"></s:password>  
            <s:file name="file" label="上传文件"></s:file>  
            <s:hidden name="id" value="1"></s:hidden>  
              
            <!--   
            <select name="edu">  
                <option value="listKey">listValue</option>  
             -->  
            <s:select list="#{'1':'博士','2':'硕士'}" name="edu" label="学历" listKey="key" listValue="value"></s:select>  
              
            <s:select list="{'java','.net'}" value="java"></s:select><!-- value是选中的 -->  
              
            <!-- 必须有name -->  
            <s:checkbox label="爱好 " fieldValue="true" name="checkboxFiled1"></s:checkbox>  
              
            <!-- 多个checkbox -->  
            <s:checkboxlist list="{'java','css','html','struts2'}" label="喜欢的编程语言" name="box" value="{'css','struts2'}"></s:checkboxlist>  
          
          
            <!-- map集合前要加# -->  
            <s:checkboxlist list="#{1:'java',2:'css',3:'html',4:'struts2',5:'spring'}" label="喜欢的编程语言" name="boxs" value="{1,2}"></s:checkboxlist>  
              
              
            <!-- listKey   
                listValue  
                  
                <input type="text" name="boxs" value="listKey">显示值listValue  
             -->  
                      
            <!-- radio -->          
            <%  
                //从服务器传过来值   
                pageContext.setAttribute("sex","男",PageContext.REQUEST_SCOPE);  
                pageContext.setAttribute("sex1","男",PageContext.REQUEST_SCOPE);  
             %>  
            <s:radio list="{'男','女'}" name="sex" value="#request.sex"></s:radio>      
              
              
            <s:radio list="#{1:'男',2:'女'}" name="sex1" listKey="key" listValue="value" value="#request.sex1"></s:radio>           
          
            <!-- 防止表单提交的方式 -->  
            <s:token></s:token>  
                              
            <s:submit value="提交"></s:submit>  
        </s:form>   
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值