JAVAEE复习笔记

MySQL数据库

*数据库就是一个文件系统,访问数据需要通过标准的SQL语言访问。

*MySQL语句中只有两种占位符:%代表1个或多个字符,_代表1个字符。

*MySQL之间的关系:一个  数据库服务器  中可以存在多个  数据库,一个数据库可以存在多个   表,每个表中有多个字段,字段和java中  类的属性   是对应的,每一条记录对应的是一个java实例对象。

*SQL语句:

    #Structed Query Lanague,结构化查询语句,非过程性语言,每写一句,就可以执行一句。

    #sql语言的分类:DDL,数据定义语言,创建数据库、创建表

                              DML,数据操纵语言,插入数据、修改数据、删除数据(*****)

                               DCL,数据控制语言,if else

                            DQL,数据查询语言,select。。(*****)

*数据库的增删改查CRUD:#增:create database 数据库名称;create database 数据库名称 character set 'utf8' collate                                             'utf8_bin';

                                        #查:show databases ;  show create database 数据库名称。

                                        #删:drop database 数据库名称。

                                        #改:alter database 数据库名称 character set 编码 collate 校验规则。

                                        #其他操作:切换数据库 (*****)use 数据库名称。

*表的增删改查CRUD

    #增:create table 表名(

                                    字段1 类型(长度) 约束

                                    字段2  类型(长度)约束

                                    );

                                

    数据类型:字符:VARCHAR(长度可变),CHAR(长度不可变);数值:TINYINT 、SMALLINT、INT、    BIGINT、        FLOAT、DOUBLE;日期型:DATE(日)、TIME(时分秒)、DATETIME(日期和时分秒,需要手动录入)、TIMESTAMP(日期和时分秒,系统时间)

create table employee(
id int,
name varchar(20),
gender varchar(10),
birthday date,
entry_date date,
job varchar(100),
salary double,
resume text

);

约束(单表):(*****)主键约束:primary key(默认唯一,非空),auto_increment(数据库维护主键,自动增长)。

                                     唯一约束:unique.   非空约束:not null。

create table employee2(
id int primary key auto_increment,
name varchar(20) unique not null,
gender varchar(10) not null,
birthday date not null,
entry_date date not null,
job varchar(100) not null,
salary double not null,
resume text not null
); 

#查:desc 表名:查看表的信息;show tables:查看当前数据库中所有的表;show create table 表名:查看建表语句和字符集。

# 删:drop table 表名。

# 改:                       alter table 表名 add 字段 类型(长度) 约束; -- 添加字段
alter table 表名 drop 字段; -- 删除字段
alter table 表名 modify 字段 类型(长度) 约束; -- 修改类型或者约束

alter table 表名 change 旧字段 新字段 类型(长度) 约束 -- 修改字段的名称

                                rename table 表名 to 新表名; -- 修改表名

alter table 表名 character set utf8; -- 修改字符集

*数据的增删改查

        #增数据:insert into 表名 (字段1,字段2,字段3..) values(值1,值2,值3...); 有几列就插入多少的值。

* insert into 表名 values(值1,值2,值3...); 插入所有的列

        #改数据:update 表名 set 字段=值,字段=值... [where ]

        #删数据:delete from  表名 [where ]; 删除数据

                        truncate from  表名;删除所有数据 

                    * truncate 和 delete的区别:

* truncate删除数据,先删除整个表。再创建一个新的空的表。(效率)

* delete删除数据,一条一条删除的。(*****)

        #查:select * from 

                select 语句总结:S-F-W-G-H-O 组合 select ... from ... where ... group by... having... order by ... ; 

顺序不能改变

多表操作:

    外键约束:

        添加外键:alter table emp add foreign key 当前表名(当前表的字段) references 关联的表(关联表的字段);

                        alter table emp add foreign key emp(dno) references dept(did);

 *多表查询

           # 笛卡尔积:表A 表B

                        aid aname bid bname
                        a1 aa1 b1 bb1
                        a2 aa2 b2 bb2

                        b3 bb3

                    * 查询的语法

            select * from 表A,表B; 返回的结果就是笛卡尔积。

                    结果:

                    a1 aa1 b1 bb1
    a1 aa1 b2 bb2
    a1 aa1 b3 bb3
    a2 aa2 b1 bb1
    a2 aa2 b2 bb2

    a2 aa2 b3 bb3

                #内连接(用的比较多)

                    * 普通内连接
* 前提条件:需要有外键的。
* 提交关键字 inner join ... on

select * from dept inner join emp on dept.did = emp.dno;

                    *隐式内连接:

                                * 可以不使用inner join ... on关键字

select * from dept,emp where dept.did = emp.dno;

                  #外连接(用的比较多)

                        * 左外链接(看左表,把左表所有的数据全部查询出来)
* 前提条件:需要有外键的。
* 语法: 使用关键字 left [outer] join ... on

select * from dept left outer join emp on dept.did = emp.dno;

                        * 右外链接(看右表,把右表所有的数据全部查询出来)
* 前提条件:需要有外键的。
* 语法: 使用关键字 right [outer] join ... on

select * from dept right join emp on dept.did = emp.dno;

Struts2

struts2快速入门:
index.jsp------>HelloServlet-------->hello.jsp  web开发流程.

index.jsp------>HelloAction--------->hello.jsp  struts2流程

框架的配置:

                1.web.xml文件中配置前端控制器(核心控制器)-----就是一个Filter
目的:是为了让struts2框架可以运行。
<filter>
<filter-name>struts2</filter-name>
<filter- class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>

</filter-mapping>

            2.创建一个struts.xml配置文件 ,这个是struts2框架配置文件。
目的:是为了struts2框架流程可以执行。
名称:struts.xml

位置:src下(classes下)

            3.创建一个HelloAction类
要求,在HelloAction类中创建一个返回值是String类型的方法,注意,无参数。
public String say(){
return "good";

            4.在struts.xml文件中配置HelloAction

<package name="default" namespace="/" extends="struts-default">
<action name="hello" class="cn.itcast.action.HelloAction"
method="say">
<result name="good">/hello.jsp</result>
</action>

</package>

            5.在index.jsp中添加连接,测试
<a href="${pageContext.request.contextPath}/hello">第一次使用struts2</a>
在地址栏中输入:http://localhost/struts2_day01/index.jsp  访问连接,就可以看到
HelloAction类中的say方法执行了,也跳转到了hello.jsp.

struts2配置(重点)
1.struts2配置文件加载顺序
struts2框架要能执行,必须先加载StrutsPrepareAndExecuteFilter.

  2.关于Action的配置

1.<package>  作用:是用于声明一个包。用于管理action。
1.name     它用于声明一个包名,包名不能重复,也就是它是唯一的。 
2.namespace  它与action标签的name属性合并确定了一个唯一访问action的路径。
3.extends  它代表继承的包名。
4.abstrace 它可以取值为true/false,如果为true,代表这个包是用于被继承的。
2<action>  用于声明 一个action
1.name  就是action的一个名称,它是唯一的(在同包内) 它与package中的namespace确定了访问action的路径。
2.class Action类的全名
3.method 要访问的Action类中的方法的名称,方法无参数 ,返回值为String.
3.<result> 用于确定返回结果类型

1.name  它与action中的方法返回值做对比,确定跳转路径。 

关于action配置其它细节:
1.关于默认值问题
<package namespace="默认值"> namespace的默认值是""
<action class="默认值"  method="默认值">
class的默认值是  com.opensymphony.xwork2.ActionSupport


method的默认值是  execute
<result\d X name="默认值"> name的默认值是 "success" 


2.关于访问action的路径问题
现在的action的配置是:
<package name="default" namespace="/" extends="struts-default">
<action name="hello" class="cn.itcast.action.DefaultAction">
<result>/hello.jsp</result>
</action>
</package>

当我们输入:
http://localhost/struts2_day01_2/a/b/c/hello
也访问到了action。

原因:struts2中的action被访问时,它会首先查找
1.namespace="/a/b/c"  action的name=hello  没有.
2.namespace="/a/b     action的name=hello  没有
3.namespace="/a"      action的name=hello  没有
4.namespace="/"        action的name=hello  查找到了.
 
如果最后也查找不到,会报404错误.

3.默认的action。
作用:处理其它action处理不了的路径。

<default-action-ref name="action的名称" />
配置了这个,当访问的路径,其它的action处理不了时,就会执行name指定的名称的action。

4.action的默认处理类
在action配置时,如果class不写。默认情况下是 com.opensymphony.xwork2.ActionSupport。

<default-class-ref class="cn.itcast.action.DefaultAction"/>

如果设置了,那么在当前包下,默认处理action请的的处理类就为class指定的类。

Action

1.关于Action类的创建方式介绍:
有三种方式
1.创建一个POJO类.
简单的Java对象(Plain Old Java Objects)
指的是没有实现任何接口,没有继承任何父类(除了Object)

优点:无耦合。
缺点:所以工作都要自己实现。

在struts2框架底层是通过反射来操作:
* struts2框架 读取struts.xml 获得 完整Action类名 
* obj = Class.forName("完整类名").newInstance();
* Method m = Class.forName("完整类名").getMethod("execute");  m.invoke(obj); 通过反射 执行 execute方法


2.创建一个类,实现Action接口.  com.opensymphony.xwork2.Action

优点:耦合低。提供了五种结果视图,定义了一个行为方法。
缺点:所以工作都要自己实现。

public static final String SUCCESS = "success";  // 数据处理成功 (成功页面)
public static final String NONE = "none";  // 页面不跳转  return null; 效果一样
public static final String ERROR = "error";  // 数据处理发送错误 (错误页面)
public static final String INPUT = "input"; // 用户输入数据有误,通常用于表单数据校验 (输入页面)
public static final String LOGIN = "login"; // 主要权限认证 (登陆页面)


3.创建一个类,继承自ActionSupport类.  com.opensymphony.xwork2.ActionSupport
ActionSupport类实现了Action接口。

优点:表单校验、错误信息设置、读取国际化信息 三个功能都支持.
缺点:耦合度高。

在开发中,第三种会使用的比较多.
--------------------------------------------------------------------------
关于action的访问:

1.通过设置method的值,来确定访问action类中的哪一个方法.
<action name="book_add" class="cn.itcast.action.BookAction" method="add"></action>
当访问的是book_add,这时就会调用BookAction类中的add方法。
<action name="book_update" class="cn.itcast.action.BookAction" method="update"></action>
当访问的是book_update,这时就会调用BookAction类中的update方法。

2.使用通配符来简化配置
1.在struts.xml文件中
<action name="*_*" class="cn.itcast.action.{1}Action" method="{2}"></action>
2.在jsp页面上
book.jsp
<a href="${pageContext.request.contextPath}/Book_add">book add</a><br>
<a href="${pageContext.request.contextPath}/Book_update">book update</a><br>
<a href="${pageContext.request.contextPath}/Book_delete">book delete</a><br>
<a href="${pageContext.request.contextPath}/Book_search">book search</a><br>
product.jsp
<a href="${pageContext.request.contextPath}/Product_add">product add</a><br>
<a href="${pageContext.request.contextPath}/Product_update">product update</a><br>
<a href="${pageContext.request.contextPath}/Product_delete">product delete</a><br>
<a href="${pageContext.request.contextPath}/Product_search">product search</a><br>

当访问book add时,这时的路径是  Book_add,那么对于struts.xml文件中.
第一个星就是   Book
第二个星就是   add
对于{1}Action---->BookAction
对于method={2}--->method=add

使用通配符来配置注意事项:
1.必须定义一个统一的命名规范。
2.不建议使用过多的通配符,阅读不方便。
----------------------------------------------
3.动态方法调用 (了解)
在struts.xml文件中
<action name="book" class="cn.itcast.action.BookAction"></action>
访问时路径: http://localhost/struts2_day01_2/book!add
就访问到了BookAction类中的add方法。

对于book!add 这就是动态方法调用。

注意:struts2框架支持动态方法调用,是因为在default.properties配置文件中设置了
                 动态方法调用为true.


struts.enable.DynamicMethodInvocation = true
====================================================================================================
在struts2框架中获取servlet api

对于struts2框架,不建议直接使用servlet api;

在struts2中获取servlet api有三种方式:
1.通过ActionContext来获取
1.获取一个ActionContext对象。
ActionContext context=ActionContext.getContext();
2.获取servlet api
注意:通过ActionContext获取的不是真正的Servlet api,而是一个Map集合。

1.context.getApplication()
2.context.getSession()
3.context.getParameter();---得到的就相当于request.getParameterMap()
4.context.put(String,Object) 相当于request.setAttribute(String,String);


2.注入方式获取(这种方式是真正的获取到了servlet api)

1.要求action类必须实现提定接口。
ServletContextAware : 注入ServletContext对象
ServletRequestAware :注入 request对象
ServletResponseAware : 注入response对象


2.重定接口中的方法。
private HttpServletRequest request;
3.声明一个web对象,使用接口中的方法的参数对声明的web对象赋值.
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}

扩展:分析其实现:
是使用struts2中的一个interceptor完成的.
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>

if (action instanceof ServletRequestAware) { //判断action是否实现了ServletRequestAware接口
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST); //得到request对象.
((ServletRequestAware) action).setServletRequest(request);//将request对象通过action中重写的方法注入。
}

3.通过ServletActionContext获取.
在ServletActionContext中方法都是static。
getRequest();
getResposne();
getPageContext();
-------------------------------------------------------------------------------
Result结果类型

<result>标签
1.name  与action中的method的返回值匹配,进行跳转.

2.type  作用:是用于定义跳转方式

对于type属性它的值有以下几种:
在struts-default.xml文件中定义了type可以取的值

<result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
<result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
<result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
<result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
<result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
<result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
<result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
<result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
<result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />

必会: chain  dispatcher  redirect redirectAction  stream

dispatcher:它代表的是请求转发,也是默认值。它一般用于从action跳转到页面。
chain:它也相当于请求转发。它一般情况下用于从一个action跳转到另一个action。

redirect:它代表的是重定向  它一般用于从action跳转到页面
redirectAction: 它代表的是重定向  它一般用于从action跳转另一个action。

stream:代表的是服务器端返回的是一个流,一般用于下载。

了解: freemarker  velocity

----------------------------------------------------
局部结果页面与全局结果页面
局部结果页面 和 全局结果页面 
<action name="result" class="cn.itcast.struts2.demo6.ResultAction">
<!-- 局部结果  当前Action使用 -->
<result name="success">/demo6/result.jsp</result> 
</action>
 
<global-results>
<!-- 全局结果 当前包中 所有Action都可以用-->
<result name="success">/demo6/result.jsp</result>

</global-results>

1.struts2中获取请求参数


在struts2中action是什么?(struts2是一个mvc框架)
V:jsp
M:action
C:action  StrutsPrepareAndExecuteFilter

在struts2中获取请求参数:
1.属性驱动
1.直接将action做一个model,就可以得到请求参数.
问题1:action封装请求参数,会不会存在线程安全问题?
不会:因为每一次请求,都是一个新的action。
缺点:需要单独定义javaBean,将action中属性copy到javaBean中。
优点:简单。
这种方式 ,底层是通过反射来实现的。

2.在action中声明一个model。
private User user;提供get/set方法

在页面上使用ognl来描述
<input type="text" name="user.username">

优点:简单,解决了第一种封装的问题
缺点:在页面上使用了ognl表达式,页面不通用了。

问题:这种方式,数据是怎样封装的?
是通过struts2中的interceptor进行了数据封装.
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>



2.模型驱动(在开发中应用比较多)
步骤:
1.让action类实现ModelDriven
2.重写getModel方法
3.在action中实现化一个model对象,让getModel方法返回这个对象。

public class Login3Action extends ActionSupport implements ModelDriven<User> {


private User user = new User();


public User getModel() {
return user;
}

优点:解决了属性驱动存在的问题
缺点:一次只能封装一个model对象.

struts2 有很多围绕模型驱动的特性 
* <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> 为模型驱动提供了更多特性


--------------------------------------------------------------------------
扩展:
1.将数据封装到List集合
页面:
username1:<input type="text" name="users[0].username"><br>
password1:<input type="password" name="users[0].password"><br>

username2:<input type="text" name="users[1].username"><br>
password2:<input type="password" name="users[1].password"><br>

action类:
private List<User> users;
get/set方法

2.将数据封装到Map集合
页面:
username1:<input type="text" name="map['aaa'].username"><br>
password1:<input type="password" name="map['aaa'].password"><br>

username2:<input type="text" name="map['bbb'].username"><br>
password2:<input type="password" name="map['bbb'].password"><br>

action类:
private Map<String, User> map;
提供get/set

===================================================================================================
struts2中提供的类型转换

在web中我们使用beanutils直接将表单数据封装到javaBean中。---类型转换

struts2中action得到请求参数,也可以直接封装到javaBean.

struts2 内部提供大量类型转换器,用来完成数据类型转换问题 
boolean 和 Boolean
char和 Character
int 和 Integer
long 和 Long
float 和 Float
double 和 Double
Date 可以接收 yyyy-MM-dd格式字符串
数组  可以将多个同名参数,转换到数组中
集合  支持将数据保存到 List 或者 Map 集合

例如:日期类型,我们传递  yyyy-MM-dd  yyyy年MM月dd日格式都可以,但是如果是yyyy/MM/dd
就会出现问题.

关于struts2中的类型转换器:
struts2中的类型转换器根接口是:com.opensymphony.xwork2.conversion.TypeConverter。

-------------------------------------------------------
自定义类型转换器:

步骤:
1.创建一个类实现TypeConverter接口.
2.重写接口中方法,实现类型转换操作.
3.注册类型转换器.

详解说明:
1.创建一个自定义类型转换器
1.实现TypeConverter需要重写
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
如果实现接口,这个方法参数太多(6个)

2.不推荐实现接口,可以继承 DefaultTypeConverter类
优点:重写的方法参数没有那么多
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
return convertValue(value, toType);
}

3.个人推荐使用 继承DefaultTypeConverter类的一个子类StrutsTypeConverter.
原因:在这个类中将从页面传递的数据怎样封装,以及action中的数据怎样在页面上显示做了分离.

  public abstract Object convertFromString(Map context, String[] values, Class toClass);
  public abstract String convertToString(Map context, Object o);





2.怎样注册一个自定义类型转换器.

1.局部--针对于action
配置文件所在位置以及名称:  在Action类所在包 创建 Action类名-conversion.properties , 
配置文件书写:    格式 : 属性名称=类型转换器的全类名 
2.局部--针对于model
配置文件所在位置以及名称:  在model类所在包 创建 model类名-conversion.properties , 
配置文件书写:    格式 : 属性名称=类型转换器的全类名 
3.全局
配置文件所在位置以及名称:在src下创建一个xwork-conversion.properties
配置文件书写:  格式:  要转换的类型全名=类型转换器的全类名 

-----------------------------------------------------------------------------
注意:
对于struts2中类型转换器,如果表单数据提交时,将数据向model封装,出现了问题,会报错:
No result defined for action cn.itcast.action.RegistAction and result input

上面的意思是说,在RegistAction的配置中没有配置input结果视图.
<action name="regist" class="cn.itcast.action.RegistAction">
<result name="input">/success.jsp</result>
</action>
如果配置了,出现类型转换问题,就会跳转到input指定的视图。

问题:为什么会向input视图跳转?
是因为struts2中的拦截器(interceptor).

在struts2中的
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
用于记录类型转换问题
 
在struts2中
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
用于得到问题,向input视图跳转。
 
关于错误信息展示:

通过分析拦截器作用,得知当类型转换出错时,自动跳转input视图 ,在input视图页面中 <s:fieldError/> 显示错误信息
* 在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息



如果是自定义类型转换器,出现类型转换问题,要跳转到input视图,在类型转换器中,必须抛出异常才可以。
========================================================================================================================
关于struts2提供的数据校验

在开发中,请求参数是需要校验的。
客户端校验---->js
服务器校验---->java代码。

struts2中提供的校验-----服务器端校验。

分成两种:
1.手动校验(编码校验)
2.配置校验(annotation,xml) 我们讲的是xml。

1.手动校验:(了解)
要求:action类必须继承自ActionSupport。需要重写一个方法 validate

通过测试发现在action中重写的validate方法执行了。并且是在请求处理方法(execute)之前执行的。


对于struts2提供的校验,它也是通过拦截器实现的。

问题:在validate方法中怎样存储校验错误信息?

在validate方法中   this.addFieldError(Sting name,String value);

问题:在页面上怎样获取错误信息?(在input视图上)
<s:fielderror> 展示所有错误信息

<s:fielderror fieldName="">展示特定名称的错误信息.

------------------
问题:在同一个Action中有多个请求处理方法(login,regist)那么有些方法是需要校验的,有些是不需要的,怎样处理?
解决方案:创建一个名称叫   validate+请求处理方法名   例如:请求处理方法叫  regist()  校验 的方法名 validateRegist().

-------------------------------------------------------------------------------------------------------------
2.配置校验(xml)
struts2的校验框架。
已经完成了校验操作(做了很多校验方法)。
而我们在使用时,只需要将它们调用就可以(通过配置文件)

要求:action类必须继承自ActionSupport。

问题:配置文件怎样配置?

位置:xml文件要与action类在同一个包下
名称:action类名-validation.xml
约束: xwork-core-2.3.7.jar 中 xwork-validator-1.0.3.dtd 下
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
书写:
1.根元素
<validators>
2.子元素
<field name="属性名称"></field>

3.<field>子元素
<field-validator type="校验器"> 这个是指定校验器
问题:校验器有哪些?
xwork-core-2.3.7.jar 中 /com/opensymphony/xwork2/validator/validators/default.xml下

<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
<validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
<validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
<validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
<validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
<validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
<validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
<validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
<validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
<validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
<validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
<validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
<validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
<validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
<validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>

4.<field-validator>子元素  
<message>错误信息</message>

5.<field-validator>子元素
<param name="">值</param>

用于指定校验器中的参数.

2.拦截器(interceptor)
介绍拦截器:
struts2拦截器使用的是AOP思想。
AOP的底层实现就是动态代理。
拦截器 采用 责任链 模式 
*  在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。
    *  责任链每一个节点,都可以继续调用下一个节点,也可以阻止流程继续执行

struts2中在struts-default.xml文件中声明了所有的拦截器。
而struts2框架默认使用的是defaultStack这个拦截器栈。
在这个拦截器栈中使用了18个拦截器。简单说,struts2框架
在默认情况下,加载了18个拦截器。
-----------------------------------------------------
1.struts2中怎样使用拦截器

问题:使用拦截器可以做什么?
可以通过使用拦截器进行控制action的访问。例如,权限操作。

怎样使用拦截器?
1.创建一个Interceptor  可以自定义一个类实现com.opensymphony.xwork2.interceptor.Interceptor
在这个接口中有三个方法  init  destory intercept, intercept方法是真正拦截的方法。

在intercept方法中如果要向下继续执行,通过其参数ActionInvocation调用它的invoke()方法就可以。

2.声明一个Interceptor  
在struts-default.xml文件中
<interceptors>
<interceptor name="" class=""/>
</interceptors>
注意:我们要自己声明一个interceptor可以在struts.xml文件中声明。

3.在action中指定使用哪些拦截器.
<interceptor-ref name="my"/>

注意:只要显示声明使用了一个拦截器。那么默认的拦截器就不在加载。
-----------------------------------------------------------------------
2.分析拦截器原理

源代码执行流程:
1.在StrutsPrepareAndExecuteFilter中查找
在doFilter方法内有一句话 execute.executeAction (request, response, mapping) 执行Action操作.

2.在executeAction执行过程中会访问Dispatcher类中的serviceAction,在这个方法中会创建一个
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
这就是我们的Action的代理对象

3.查看ActionInvocation,查看其实现类 DefaultActionInvocation.

在其invoke方法中
if (interceptors.hasNext()) {//判断是否有下一个拦截器.
final InterceptorMapping interceptor = interceptors.next(); //得到一个拦截器
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 
//调用得到的拦截器的拦截方法.将本类对象传递到了拦截器中。
}
finally {
UtilTimerStack.pop(interceptorMsg);
}


通过源代码分析,发现在DefaultActionInvocation中就是通过递归完成所有的拦截调用操作.


关于interceptor与Filter区别:
1、拦截器是基于java反射机制的,而过滤器是基于函数回调的。
2、过滤器依赖于servlet容器,而拦截器不依赖于servlet容器。
3、拦截器只能对Action请求起作用,而过滤器则可以对几乎所有请求起作用。
4、拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。

5、在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时被调用一次。

3.案例

权限控制:
1.login.jsp------>LoginAction------------->book.jsp
登录成功,将用户存储到session。

2.在book.jsp中提供crud链接。
每一个连接访问一个BookAction中一个方法。

要求:对于BookAction中的add,update,delete方法要求用户必须登录后才可以访问。search无要求。

怎样解决只控制action中某些方法的拦截?
1.创建类不在实现Interceptor接口,而是继承其下的一个子类.MethodFilterInterceptor
不用在重写intercept方法,而是重写 doIntercept方法。

2.在struts.xml文件中声明
<interceptors>
<intercept name="" class="">
<param name="includeMethods">add,update,delete</param>
<param name="excludeMethods">search</param>
</intercept>
</interceptors>

===================================================================================================
3.struts2中文件上传与下载


1.上传
浏览器端:
1.method=post
2.<input type="file" name="xx">
3.encType="multipart/form-data";

服务器端:
commons-fileupload组件
1.DiskFileItemFactory
2.ServletFileUpload
3.FileItem

struts2中文件上传:
默认情况下struts2框架使用的就是commons-fileupload组件.
struts2它使用了一个interceptor帮助我们完成文件上传操作。
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
 
在action中怎样处理文件上传?
页面上组件:<input type="file" name="upload">

在action中要有三个属性:
private File upload;
private String uploadContentType;
private String uploadFileName;

在execute方法中使用commons-io包下的FileUtils完成文件复制.
FileUtils.copyFile(upload, new File("d:/upload",uploadFileName));

------------------------------------------------------------------------
关于struts2中文件上传细节:
1.关于控制文件上传大小
在default.properties文件中定义了文件上传大小
struts.multipart.maxSize=2097152 上传文件默认的总大小 2m

2.在struts2中默认使用的是commons-fileupload进行文件上传。
# struts.multipart.parser=cos
# struts.multipart.parser=pell
struts.multipart.parser=jakarta

如果使用pell,cos进行文件上传,必须导入其jar包.

3.如果出现问题,需要配置input视图,在页面上可以通过<s:actionerror>展示错误信息.
问题:在页面上展示的信息,全是英文,要想展示中文,国际化
 
struts-messages.properties 文件里预定义 上传错误信息,通过覆盖对应key 显示中文信息
struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}


修改为
struts.messages.error.uploading=上传错误: {0}
struts.messages.error.file.too.large=上传文件太大: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=上传文件的类型不允许: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=上传文件的后缀名不允许: {0} "{1}" "{2}" {3}

{0}:<input type=“file” name=“uploadImage”>中name属性的值
{1}:上传文件的真实名称
{2}:上传文件保存到临时目录的名称
{3}:上传文件的类型(对struts.messages.error.file.too.large是上传文件的大小)



4.关于多文件上传时的每个上传文件大小控制以及上传文件类型控制.

1.多文件上传
服务器端:
只需要将action属性声明成List集合或数组就可以。

private List<File> upload;
private List<String> uploadContentType;
private List<String> uploadFileName;

2.怎样控制每一个上传文件的大小以及上传文件的类型?
在fileupload拦截器中,通过其属性进行控制.

maximumSize---每一个上传文件大小
allowedTypes--允许上传文件的mimeType类型.
allowedExtensions--允许上传文件的后缀名.

<interceptor-ref name="defaultStack">
<param name="fileUpload.allowedExtensions">txt,mp3,doc</param>
</interceptor-ref>
----------------------------------------------------------------------------------------------
2.下载
文件下载方式:
1.超连接
2.服务器编码,通过流向客户端写回。

1.通过response设置  response.setContentType(String mimetype);
2.通过response设置  response.setHeader("Content-disposition;filename=xxx");
3.通过response获取流,将要下载的信息写出。



struts2中文件下载:
通过<result type="stream">完成。

<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
在StreamResult类中有三个属性:
  protected String contentType = "text/plain"; //用于设置下载文件的mimeType类型
  protected String contentDisposition = "inline";//用于设置进行下载操作以及下载文件的名称
  protected InputStream inputStream; //用于读取要下载的文件。

在action类中定义一个方法
public InputStream getInputStream() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("d:/upload/" + filename);
return fis;
}

<result type="stream">
<param name="contentType">text/plain</param>
<param name="contentDisposition">attachment;filename=a.txt</param>
<param name="inputStream">${inputStream}</param> 会调用当前action中的getInputStream方法。
</result>


问题1:<a href="${pageContext.request.contextPath}/download?filename=捕获.png">捕获.png</a>下载报错
原因:超连接是get请求,并且下载的文件是中文名称,乱码。


问题2:下载捕获文件时,文件名称就是a.txt ,下载文件后缀名是png,而我们在配置文件中规定就是txt?
<result type="stream">
<param name="contentType">${contentType}</param> <!-- 调用当前action中的getContentType()方法 -->
<param name="contentDisposition">attachment;filename=${downloadFileName}</param>
<param name="inputStream">${inputStream}</param><!-- 调用当前action中的getInputStream()方法 -->
</result>

在struts2中进行下载时,如果使用<result type="stream">它有缺陷,例如:下载点击后,取消下载,服务器端会产生异常。
在开发中,解决方案:可以下载一个struts2下载操作的插件,它解决了stream问题。

========================================================================================================
4.ognl与valueStack。

问题:ognl是什么,它有什么用?
OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言.
比el表达式功能强大。
struts2将ognl表达式语言,集成当sturts2框架中,做为它的默认表达式语言。

OGNL 提供五大类功能 
   1、支持对象方法调用,如xxx.doSomeSpecial(); 
   2、支持类静态的方法调用和值访问
   3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 )
   4、支持赋值操作和表达式串联

   5、操作集合对象。

1.ognlvalueStack

ognl中有一个OgnlContext,它可以设置root与非root .root中数据获取时,不需要加#,而非root中数据在获取时,需要加上#.

重点:学习struts2中使用ognl时,最后要知道 谁是OgnlContext,谁是root,谁是非root.

-------------------------------------------------------------------------------------

1.ognl介绍

OGNLObject Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。

* xwork 提供 OGNL表达式

* ognl-3.0.5.jar

OGNL 是一种比EL 强大很多倍的语言

OGNL 提供五大类功能

   1、支持对象方法调用,如xxx.doSomeSpecial()

   2、支持类静态的方法调用和值访问

   3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 )

   4、支持赋值操作和表达式串联

   5、操作集合对象。

演示:struts2中使用ognl表达式

需要结合struts2的标签使用<s:property value="ognl表达式">

<s:property value="'abc'.length()"/>  演示对象调用方法

<s:property value="@java.lang.Math@max(10,20)"/> 演示静态成员访问.

注意:struts2中使用静态成员访问,必须设置一个常量:

struts.ognl.allowStaticMethodAccess=false

--------------------------

2.ValueStack

它是一个接口com.opensymphony.xwork2.util.ValueStack

我们使用它是将其做为一个容器,用于携带action数据到页面.

在在页面上通过ognl表达式获取数据。

=====================================================================

问题1:什么是valueStack?

valueStack主要是将action数据携带到页面上,通过ognl获取数据

1.ValueStack有一个实现类叫OgnlValueStack.

2.每一个action都有一个ValueStack.(一个请求,一个request,一个action,一个valueStackvalueStack生命周期就是request生命周期。

3.valueStack中存储了当前action对象以及其它常用web对象(request,session,application.parameters)

4.struts2框架将valueStack以“struts.valueStack”为名存储到request域中。

---------------------------------------------

问题2:valueStack结构?

ValueStack中 存在root属性 (CompoundRoot) context 属性 (OgnlContext

* CompoundRoot 就是ArrayList

* OgnlContext 就是 Map

list集合中存储的是action相关信息

map集合中存储的是相关映射信息,包含  paramters,request,session,application attr等。

我们想要从list中获取数据,可以不使用#.(它就是ognlroot)

如果从map中获取数据,需要使用#. (其实在struts2中的map--context其实就是ognlContext)

结论:

ValueStack它有两部分 List  Map

struts2List就是root   Map就是ognlContext.

默认情况下,在struts2中从valueStack获取数据从root中获取。

----------------------------------------------

问题3: 值栈对象的创建 ValueStack ActionContext 是什么关系 ?

 ActionContext ctx = ActionContext.getContext();

            if (ctx != null) {

                stack = ctx.getValueStack();

            }

valueStack是每一次请求时,都会创建.

ActionContext中持有了valueStack的引用。

-------------------------------------------------------

问题4:如何获得值栈对象?

对于valueStack获取有两种方式:

1.通过 request获取

ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

2.通过ActionContext获取.

ValueStack vs=ActionContext.getContext().getValueStack();

---------------------------------------------------------

问题5:向值栈保存数据 (主要针对 root

主要有两个方法

push(Object obj)------->底层就是 root.add(0,obj) 将数据存储到栈顶。

set(String name,Object obj);----->底层是将数据封装到HashMap中,在将这个HashMap通过push存储。

jsp中 通过 <s:debug /> 查看值栈的内容

------------------------------------------------------------

问题6: JSP中获取值栈的数据

root中数据不需要#,而context中数据需要#

1.如果栈顶是一个Map集合,获取时,可以直接通过Map集合的key来获取value.

<s:property  value="username"/>

2.如果栈顶数据不是一个Map,没有key值,可以使用序号来获取。

<s:property value="[0]">  0的位置向下查找所有。

<s:property value="[0].top"> 只查找0位置上数据。

如果获取OgnlContext中数据:

1.request数据    request.setAttribute()

2.session数据    session.setAttribute()

3.application数据  application.setAttribute()

4.attr   依次从request,session.application中查找

5.parameters 获取请求参数

--------------------------------------------------------------

ValueStack主流应用:就是解决将action数据携带到jsp页面。

问题:actionjsp携带数据,都是什么样的数据?

1.文本(字符串)

1.fieldError   校验数据错误信息提示

2.actionError 关于逻辑操作时错误信息(例如登录失败)

3.message 就是一个信息.

this.addFieldError("msg", "字段错误信息");

this.addActionError("Action全局错误信息");

this.addActionMessage("Action的消息信息");

* fieldError 针对某一个字段错误信息 (常用于表单校验)、actionError (普通错误信息,不针对某一个字段 登陆失败)actionMessage 通用消息

jsp中使用 struts2提供标签 显示消息信息

<s:fielderror fieldName="msg"/>

<s:actionerror/>

<s:actionmessage/>

2.复杂数据

可以使用valueStack存储.

action中存储数据:

List<User> users = new ArrayList<User>();

users.add(new User("tom", "123", 20, ""));

users.add(new User("james", "456", 21, ""));

users.add(new User("fox", "789", 26, ""));

vs.push(users);

在页面上获取数据:

使用了<s:iterator>标签来迭代集合。

<s:iterator value="[0].top" var="user"> 这是将集合中迭代出来每一个元素起个名称叫user,user是存储在context中,不在root.l

<s:iterator value="[0].top" var="user">

username:<s:property value="#user.username"/><br>

password:<s:property value="#user.password"/>

<hr>

</s:iterator>

注意:如果我们在使用<s:iterator>进行迭代时,没有给迭代出的元素起名.

<s:iterator value="[0].top">

username:<s:property value="username"/><br>

password:<s:property value="password"/>

<hr>

</s:iterator>

---------------------------------------------------

关于默认压入到valueStack中的数据.

1.访问的action对象会被压入到valueStack.

DefaultActionInvocation init方法 stack.push(action);

* Action如果想传递数据给 JSP,只要将数据保存到成员变量,并且提供get方法就可以了

2.ModelDriveInterceptor会执行下面操作

ModelDriven modelDriven = (ModelDriven) action;

ValueStack stack = invocation.getStack();

Object model = modelDriven.getModel();

if (model !=  null) {

stack.push(model);

}

将实现了ModelDrive接口的actiongetModel方法的返回值,也就是我们所说的model对象压入到了

valueStack.

--------------------------------------------------------------------------

问题7:为什么el表达式可以访问valueStack中数据?

struts2框架中所使用的request对象,是增强后的request对象。

${username}---->request.getAttribute("username");

增强后的request,会首先在request域范围查找,如果查找不到,会在valueStack中查找。

StrutsPreparedAndExecuteFilterdoFilter代码中 request = prepare.wrapRequest(request);

* Request对象进行了包装 ,StrutsRequestWrapper

* 重写requestgetAttribute

Object attribute = super.getAttribute(s);

if (attribute == null) {

   attribute = stack.findValue(s);

}

  访问request范围的数据时,如果数据找不到,去值栈中找

? request对象 具备访问值栈数据的能力 (查找root的数据)

--------------------------------------------------------------------------------

OGNL表达式常见使用($ % #)

1.#

用法一  # 代表 ActionContext.getContext() 上下文

  <s:property value="#request.name" />  ------------>  ActionContext().getContext().getRequest().get("name");

  #request

  #session

  #application

  #attr

  #parameters

用法二 不写# 默认在 值栈中root中进行查找

   <s:property value="name" /> root中查找name属性

* 查询元素时,从root的栈顶元素 开始查找, 如果访问指定栈中元素   

<s:property value="[1].name" />  访问栈中第二个元素name属性

* 访问第二个元素对象 <s:property value="[1].top" />

用法三 :进行投影映射 (结合复杂对象遍历

   1)集合的投影(只输出部分属性

<h1>遍历集合只要name属性</h1>

<s:iterator value="products.{name}" var="pname">

<s:property value="#pname"/>

</s:iterator>

   2)遍历时,对数据设置条件

<h1>遍历集合只要price大于1500商品</h1>

<s:iterator value="products.{?#this.price>1500}" var="product">

<s:property value="#product.name"/> --- <s:property value="#product.price"/>

</s:iterator>

   3)综合

   <h1>只显示价格大于1500 商品名称</h1>

<s:iterator value="products.{?#this.price>1500}.{name}" var="pname">

<s:property value="#pname"/>

</s:iterator>   

用法四: 使用#构造map集合

经常结合 struts2 标签用来生成 selectcheckboxradio

<h1>使用#构造map集合 遍历</h1>

<s:iterator value="#{'name':'aaa','age':'20', 'hobby':'sport' }" var="entry">

key : <s:property value="#entry.key"/> , value:  <s:property value="#entry.value"/> <br/>

</s:iterator>

--------------------------------------------------------

2.%

%作用:就是用于设定当前是否要解析其为 ognl表达式.

%{表达式}  当前表达式会被做为ognl解析.

%{'表达式'} 当前表达式不会被做为ognl解析。

<s:property value="表达式"> 对于s:property标签,它的value属性会被默认做为ognl.

以后,所有表达式如果想要让其是ognl  %{表达式}

----------------------------------------------------------

3.$

$作用:就是在配置文件中使用ognl表达式来获取valueStack中数据.

1.struts.xml

<result type="stream">

<param name="contentType">${contentType}</param>

</result>

2.在校验文件中使用

${min}  ${max}

${minLength} ${maxLength}

3.在国际化文件中使用

properties文件中

username=${#request.username}

jsp页面

<s:text name="username">

-----------------------------------------------------------------

总结: #就是用于获取数据  %就是用于设置是否是ognl表达式  $就是在配置文件中使用ognl.

==============================================================================================================

2.防止表单重复提交

问题:什么是表单重复提交?

regist.jsp----->RegistServlet

表单重复提交 危害: 刷票、 重复注册、带来服务器访问压力(拒绝服务)

解决方案:

在页面上生成一个令牌(就是一个随机字符串),将其存储到session中,并在表单中携带.

在服务器端,获取数据时,也将令牌获取,将它与session中存储的token对比,没问题,

session中令牌删除。

struts2中怎样解决表单重复提交:

struts2中解决表单重复提交,可以使用它定义的一个interceptor

 <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>

 

步骤:

1.在页面上需要使用一个token tag

在表单中添加一个标签 <s:token/>

就会在页面上生成一个令牌,并存在于表单中。

2.需要在action中引入token拦截器

<interceptor-ref name="token"/>

3.需要配置视图

<result name="invalid.token">/token.jsp</result>

 通过 <s:actionError/> 显示错误信息

 覆盖重复提交信息  struts.messages.invalid.token=您已经重复提交表单,请刷新后重试

 

===============================================================================================

3struts2json插件使用

1.struts2中怎样处理异步提交(ajax)

原始:

HttpServletResponse response = ServletActionContext.getResponse();

response.getWriter().write("hello " + msg);

response.getWriter().close();

还可以使用struts2中提供的json插件:

1.导入json插件包

struts2lib包下  struts2-json-plugin-2.3.15.1.jar

2.struts.xml文件中配置

1.<package extends="json-default">

2.设置视图<result type="json">

这样设置后,会将valueStack栈顶数据变成json

对于我们的程序,也就是会将action对象转换成json

<param name="root">p</param> 如果没有设置,可以理解成将整个action都转换成json的数据。也就是

 action中提供的getXxx方法,就是json中的一个属性。

 

如果设置了root,那么,只将指定数据转换成json.

--------------------------------------------------------

怎样设置转换成json的对象中不包含特定的属性?

1. @JSON(serialize=false) getXxx方法上设置

2. 还可以通过json插件的interceptor完成.

<param name="includeProperties">ps\[\d+\]\.name,ps\[\d+\]\.price,ps\[\d+\]\.count</param>

 

位置:任意

需要在struts.xml文件中声明常量。

2.局部

1.针对于action

位置:action类在同一个包下

名称:actionClassNam.properties

2.针对于包

位置:在指定的包下.

名称:package.properties

3.jsp页面临时使用

位置:任意

jsp页面上使用<s:i18n name="">

2.struts2中什么位置使用国际化

1.action类中

2.validation配置文件

3.jsp页面

3.struts2中怎样使用国际化

getText();

<message key="">

<s:text>

2.拦截器

struts2中的Interceptor是使用AOP思想  AOP思想底层实现就是动态代理。

Interceptor它使用了责任链模式。

1.struts2中怎样使用拦截器

1.创建一个类实现Interceptor接口(继承AbstractInterceptor,继承MethodFilterInterceptor)

实现接口:intercept

重写的方法:doIntercept

让拦截器继续执行:通过方法的参数 ActionInvocation.invoke();

2.struts.xml文件中声明

<interceptors>

<intercept name="" class="">

也可以声明拦截器栈

</interceptors>

3.action中引用

<interceptor-ref name="">

注意:只要显示声明拦截器,默认的defaultStack失效。

2.源代码分析:

Dispatcher中通过获取Action的代理对象  ActionProxy,在其执行操作时,会调用

它的invoke方法,在invoke方法中将所有的拦截器调用 。

3.文件上传下载

上传:

下载:

-----------------------------------------------------------------------------------------------

1.ognlvalueStack

ognl中有一个OgnlContext,它可以设置root与非root .root中数据获取时,不需要加#,而非root中数据在获取时,需要加上#.

重点:学习struts2中使用ognl时,最后要知道 谁是OgnlContext,谁是root,谁是非root.

-------------------------------------------------------------------------------------

1.ognl介绍

OGNLObject Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。

* xwork 提供 OGNL表达式

* ognl-3.0.5.jar

OGNL 是一种比EL 强大很多倍的语言

OGNL 提供五大类功能

   1、支持对象方法调用,如xxx.doSomeSpecial()

   2、支持类静态的方法调用和值访问

   3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 )

   4、支持赋值操作和表达式串联

   5、操作集合对象。

演示:struts2中使用ognl表达式

需要结合struts2的标签使用<s:property value="ognl表达式">

<s:property value="'abc'.length()"/>  演示对象调用方法

<s:property value="@java.lang.Math@max(10,20)"/> 演示静态成员访问.

注意:struts2中使用静态成员访问,必须设置一个常量:

struts.ognl.allowStaticMethodAccess=false

--------------------------

2.ValueStack

它是一个接口com.opensymphony.xwork2.util.ValueStack

我们使用它是将其做为一个容器,用于携带action数据到页面.

在在页面上通过ognl表达式获取数据。

=====================================================================

问题1:什么是valueStack?

valueStack的作用:valueStack主要是将action数据携带到页面上,通过ognl获取数据

1.ValueStack有一个实现类叫OgnlValueStack.

2.每一个action都有一个ValueStack.(一个请求,一个request,一个action,一个valueStackvalueStack生命周期就是request生命周期。

3.valueStack中存储了当前action对象以及其它常用web对象(request,session,application.parameters)

4.struts2框架将valueStack以“struts.valueStack”为名存储到request域中。

---------------------------------------------

问题2:valueStack结构?

ValueStack中 存在root属性 (CompoundRoot) context 属性 (OgnlContext

* CompoundRoot 就是ArrayList

* OgnlContext 就是 Map

list集合中存储的是action相关信息

map集合中存储的是相关映射信息,包含  paramters,request,session,application attr等。

我们想要从list中获取数据,可以不使用#.(它就是ognlroot)

如果从map中获取数据,需要使用#. (其实在struts2中的map--context其实就是ognlContext)

结论:

ValueStack它有两部分 List  Map

struts2List就是root   Map就是ognlContext.

默认情况下,在struts2中从valueStack获取数据从root中获取。

----------------------------------------------

问题3: 值栈对象的创建 ValueStack ActionContext 是什么关系 ?

 ActionContext ctx = ActionContext.getContext();

            if (ctx != null) {

                stack = ctx.getValueStack();

            }

valueStack是每一次请求时,都会创建.

ActionContext中持有了valueStack的引用。

-------------------------------------------------------

问题4:如何获得值栈对象?

对于valueStack获取有两种方式:

1.通过 request获取

ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

2.通过ActionContext获取.

ValueStack vs=ActionContext.getContext().getValueStack();

---------------------------------------------------------

问题5:向值栈保存数据 (主要针对 root

主要有两个方法

push(Object obj)------->底层就是 root.add(0,obj) 将数据存储到栈顶。

set(String name,Object obj);----->底层是将数据封装到HashMap中,在将这个HashMap通过push存储。

jsp中 通过 <s:debug /> 查看值栈的内容

------------------------------------------------------------

问题6: JSP中获取值栈的数据

root中数据不需要#,而context中数据需要#

1.如果栈顶是一个Map集合,获取时,可以直接通过Map集合的key来获取value.

<s:property  value="username"/>

2.如果栈顶数据不是一个Map,没有key值,可以使用序号来获取。

<s:property value="[0]">  0的位置向下查找所有。

<s:property value="[0].top"> 只查找0位置上数据。

如果获取OgnlContext中数据:

1.request数据    request.setAttribute()

2.session数据    session.setAttribute()

3.application数据  application.setAttribute()

4.attr   依次从request,session.application中查找

5.parameters 获取请求参数

--------------------------------------------------------------

ValueStack主流应用:就是解决将action数据携带到jsp页面。

问题:actionjsp携带数据,都是什么样的数据?

1.文本(字符串)

1.fieldError   校验数据错误信息提示

2.actionError 关于逻辑操作时错误信息(例如登录失败)

3.message 就是一个信息.

this.addFieldError("msg", "字段错误信息");

this.addActionError("Action全局错误信息");

this.addActionMessage("Action的消息信息");

* fieldError 针对某一个字段错误信息 (常用于表单校验)、actionError (普通错误信息,不针对某一个字段 登陆失败)actionMessage 通用消息

jsp中使用 struts2提供标签 显示消息信息

<s:fielderror fieldName="msg"/>

<s:actionerror/>

<s:actionmessage/>

2.复杂数据

可以使用valueStack存储.

action中存储数据:

List<User> users = new ArrayList<User>();

users.add(new User("tom", "123", 20, ""));

users.add(new User("james", "456", 21, ""));

users.add(new User("fox", "789", 26, ""));

vs.push(users);

在页面上获取数据:

使用了<s:iterator>标签来迭代集合。

<s:iterator value="[0].top" var="user"> 这是将集合中迭代出来每一个元素起个名称叫user,user是存储在context中,不在root.l

<s:iterator value="[0].top" var="user">

username:<s:property value="#user.username"/><br>

password:<s:property value="#user.password"/>

<hr>

</s:iterator>

注意:如果我们在使用<s:iterator>进行迭代时,没有给迭代出的元素起名.

<s:iterator value="[0].top">

username:<s:property value="username"/><br>

password:<s:property value="password"/>

<hr>

</s:iterator>

---------------------------------------------------

关于默认压入到valueStack中的数据.

1.访问的action对象会被压入到valueStack.

DefaultActionInvocation init方法 stack.push(action);

* Action如果想传递数据给 JSP,只有将数据保存到成员变量,并且提供get方法就可以了

2.ModelDriveInterceptor会执行下面操作

ModelDriven modelDriven = (ModelDriven) action;

ValueStack stack = invocation.getStack();

Object model = modelDriven.getModel();

if (model !=  null) {

stack.push(model);

}

将实现了ModelDrive接口的actiongetModel方法的返回值,也就是我们所说的model对象压入到了

valueStack.

--------------------------------------------------------------------------

问题7:为什么el表达式可以访问valueStack中数据?

struts2框架中所使用的request对象,是增强后的request对象。

${username}---->request.getAttribute("username");

增强后的request,会首先在request域范围查找,如果查找不到,会在valueStack中查找。

StrutsPreparedAndExecuteFilterdoFilter代码中 request = prepare.wrapRequest(request);

* Request对象进行了包装 ,StrutsRequestWrapper

* 重写requestgetAttribute

Object attribute = super.getAttribute(s);

if (attribute == null) {

   attribute = stack.findValue(s);

}

  访问request范围的数据时,如果数据找不到,去值栈中找

? request对象 具备访问值栈数据的能力 (查找root的数据)

--------------------------------------------------------------------------------

OGNL表达式常见使用($ % #)

1.#

用法一  # 代表 ActionContext.getContext() 上下文

  <s:property value="#request.name" />  ------------>  ActionContext().getContext().getRequest().get("name");

  #request

  #session

  #application

  #attr

  #parameters

用法二 不写# 默认在 值栈中root中进行查找

   <s:property value="name" /> root中查找name属性

* 查询元素时,从root的栈顶元素 开始查找, 如果访问指定栈中元素   

<s:property value="[1].name" />  访问栈中第二个元素name属性

* 访问第二个元素对象 <s:property value="[1].top" />

用法三 :进行投影映射 (结合复杂对象遍历

   1)集合的投影(只输出部分属性

<h1>遍历集合只要name属性</h1>

<s:iterator value="products.{name}" var="pname">

<s:property value="#pname"/>

</s:iterator>

   2)遍历时,对数据设置条件

<h1>遍历集合只要price大于1500商品</h1>

<s:iterator value="products.{?#this.price>1500}" var="product">

<s:property value="#product.name"/> --- <s:property value="#product.price"/>

</s:iterator>

   3)综合

   <h1>只显示价格大于1500 商品名称</h1>

<s:iterator value="products.{?#this.price>1500}.{name}" var="pname">

<s:property value="#pname"/>

</s:iterator>   

用法四: 使用#构造map集合

经常结合 struts2 标签用来生成 selectcheckboxradio

<h1>使用#构造map集合 遍历</h1>

<s:iterator value="#{'name':'aaa','age':'20', 'hobby':'sport' }" var="entry">

key : <s:property value="#entry.key"/> , value:  <s:property value="#entry.value"/> <br/>

</s:iterator>

--------------------------------------------------------

2.%

%作用:就是用于设定当前是否要解析其为 ognl表达式.

%{表达式}  当前表达式会被做为ognl解析.

%{'表达式'} 当前表达式不会被做为ognl解析。

<s:property value="表达式"> 对于s:property标签,它的value属性会被默认做为ognl.

以后,所有表达式如果想要让其是ognl  %{表达式}

----------------------------------------------------------

3.$

$作用:就是在配置文件中使用ognl表达式来获取valueStack中数据.

1.struts.xml

<result type="stream">

<param name="contentType">${contentType}</param>

</result>

2.在校验文件中使用

${min}  ${max}

${minLength} ${maxLength}

3.在国际化文件中使用

properties文件中

username=${#request.username}

jsp页面

<s:text name="username">

-----------------------------------------------------------------

总结: #就是用于获取数据  %就是用于设置是否是ognl表达式  $就是在配置文件中使用ognl.

==============================================================================================================

2.防止表单重复提交

问题:什么是表单重复提交?

regist.jsp----->RegistServlet

表单重复提交 危害: 刷票、 重复注册、带来服务器访问压力(拒绝服务)

解决方案:

在页面上生成一个令牌(就是一个随机字符串),将其存储到session中,并在表单中携带.

在服务器端,获取数据时,也将令牌获取,将它与session中存储的token对比,没问题,

session中令牌删除。

struts2中怎样解决表单重复提交:

struts2中解决表单重复提交,可以使用它定义的一个interceptor

 <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>

 

步骤:

1.在页面上需要使用一个token tag

在表单中添加一个标签 <s:token/>

就会在页面上生成一个令牌,并存在于表单中。

2.需要在action中引入token拦截器

<interceptor-ref name="token"/>

3.需要配置视图

<result name="invalid.token">/token.jsp</result>

 通过 <s:actionError/> 显示错误信息

 覆盖重复提交信息  struts.messages.invalid.token=您已经重复提交表单,请刷新后重试

 

===============================================================================================

3struts2json插件使用

1.struts2中怎样处理异步提交(ajax)

原始:

HttpServletResponse response = ServletActionContext.getResponse();

response.getWriter().write("hello " + msg);

response.getWriter().close();

还可以使用struts2中提供的json插件:

1.导入json插件包

struts2lib包下  struts2-json-plugin-2.3.15.1.jar

2.struts.xml文件中配置

1.<package extends="json-default">

2.设置视图<result type="json">

这样设置后,会将valueStack栈顶数据变成json

对于我们的程序,也就是会将action对象转换成json

<param name="root">p</param> 如果没有设置,可以理解成将整个action都转换成json的数据。也就是

 action中提供的getXxx方法,就是json中的一个属性。

 

如果设置了root,那么,只将指定数据转换成json.

--------------------------------------------------------

怎样设置转换成json的对象中不包含特定的属性?

1. @JSON(serialize=false) getXxx方法上设置

2. 还可以通过json插件的interceptor完成.

<param name="includeProperties">ps\[\d+\]\.name,ps\[\d+\]\.price,ps\[\d+\]\.count</param>




        

猜你喜欢

转载自blog.csdn.net/qq_22772465/article/details/80269657