Struts2
概念:是一个mvc框架
Servlet的缺点
1、 在web.xml文件中需要配置很多行代码,维护起来很不方便,不利于团队合作
2、 一个servlet的入口只有一个doPost或者doGet方法,如果在一个servlet中写好几个方法,怎么办?
这样会导致代码结构很乱
3、 servlet类与servlet容器高度耦合,每个方法中都有两个参数request,response。如果服务器不启动,这两个参数没有办法初始化。
4、 如果在servlet中的一个方法中,有很多功能,这个时候,会导致该方法比价复杂,以致于不利于维护
用户注册完成了4件事情,所以整个方法比较杂乱
5、 如果一个servlet类中有很多方法,浏览器对这些方法进行请求,url写起来很麻烦
6、在servlet中如果要获取页面上表单中的数据,那么在方法中会写很多行
Servlet的重构
目的
1、 在web.xml文件中只写一个过滤器
2、 用action处理业务逻辑
3、 在过滤器中动态的调用action中的方法处理业务逻辑
类的设计
技术路线总图:
1、 监听器
1、 准备一个map
2、 把所有的action的key,value放入到map中
3、 把map放入到application域中
2、 过滤器
1、 获取application域中的map
2、 解析url
3、 根据解析的url从map中把value提取出来
4、 根据java的反射机制动态调用action
5、 根据action返回的方法跳转到相应的页面
3、执行action 的execute方法,该方法返回一个字符串
实现
1、 写监听器
2、 过滤器
3、 写action
Struts2的历史
1、 servlet
2、 struts1
1、 写action
2、 写了一个中控的servlet
3、 actionForm 和页面上表单中的内容一致
3、webwork
1、使得action与servlet容器完全松耦合
2、属性驱动和模型驱动获取页面上表单中的数据
3、利用了拦截器的概念把servlet容器的第4个缺点克服掉了
4、struts1+webwork=struts2
Struts2的第一个例子
步骤
1、 创建一个web project
2、 导入jar包
3、 编写web.xml文件
4、 写一个action
5、 编码struts.xml文件
6、 运行
解析
struts.xml文件的内容
上图为加载流程
注意:
1、 struts.xml文件必须放在classpath的根目录下
2、 名字必须为struts.xml文件
3、 因为整个加载过程写在了过滤器中的init方法中,所以tomcat启动的时候就把该文件加载了
Package
1、 用意:用来管理action的
从上图可以看出在system模块下有三个action
2、 name属性
为包的名称,是唯一的
3、 namespace
为命名空间,是针对url的
上述的命名空间针对的是:itheima09_struts2_helloworld/hello
当浏览器提交一个url:
上述的url直接从项目的根目录查找,所以找不到
该路径和上述的url是对应的
先找hello/a下的action,如果找不到,则查找上一层,再找不到再找上一层,直到找到,如果最上层找不到,则报错
如果我们的namespace设的是/a 那么:工程名/a/xx/xx/xx.action可以由右向左找到此目录,但是如果在工程名后面写不存在的目录则会报错:工程名xx/xx/a/xx/xx/xx.action这个样就报错了,可以理解为在url中工程名与namespace地址是紧密关联的,
上述的命名空间针对的是:itheima09_struts2_helloworld
只要命名空间加一层,最后跳转到相应的jsp以后,也会加上相应的路径
这样的体系不好。
4、 extends
1、 在tomcat启动的时候,不仅加载了struts.xml文件,而且还加载了struts-default.xml文件,而这个文件在classpath下。针对该文件的路径在
在一个配置文件中:
说明helloworld拥有package的名称为struts-default包的所有的功能
案例:
Action
action元素代表一个类
class为action的类的全名,可以写,也可以不写
如果不写class属性,则默认执行ActionSupport中的execute方法
该方法什么都没有做,仅仅返回了一个success字符串
如图:
Result
代表一种结果集
Type 为结果集的类型
Name 属性的值和action中某一个方法的返回值一致
type属性不写,则默认和struts-default中的结果集中的default="true"的结果集保持一致
为dispatcher,转发
result标签中的内容就是要转发到的页面
在struts-default.xml文件中
Name属性也可以不写,如果不写,则默认值为”success”
Struts2基本用法的其他方法
Include
在struts.xml文件中
就可以把struts-helloworld.xml文件包括进来了
Action的写法
简单的javabean
实现action接口
继承ActionSupport
Actin的模式
在action的构造器中输出一句话,在浏览器中多次请求,可以看到构造器执行了好几次,所以action是多例的。
结果集
转发
重定向
重定向到action
通配符映射
第一种
将执行UrlPatternAction中的execute方法
第二种
缺点:action中有几个方法就得在配置文件中写几个action元素
第三种
第四种
第五种
该模型的好处:如果在action中增加了一个方法,配置文件是不需要改变的,在写url时
urlPatternAction_后面的内容变成要请求的方法的名称就可以了
第六种
第七种(不推荐)
这么写不好,覆盖范围太大,很有可能出现和其他的action 的配置冲突的情况
第八种
强制让url中的_后面的内容和方法保持一致,跳转到的jsp页面的名称和方法的名称也保持一致。这么写带有一定的规范性
Struts2与servlet容器的交互
这种方法可以交互,但是这种方法把ServletAction与servlet容器耦合性变高了,不利于测试。
可以通过ServletActionContext把servlet容器相关的类调出来
该写法使得action与servlet容器的耦合性不是很强。
总结
1、 sturts2的配置文件中用了package的机制,这样可以分模块
name是唯一的名称,extends采用了继承的机制
2、 写的action与servlet容器完全松耦合了
3、 通配符映射解决:很容器就把一个url映射到一个action的方法中了
4、 Include保证了可以写多个配置文件
5、 结果集的封装
值栈(重要)和ognl表达式
1、 只要是一个mvc框架,必须解决数据的存和取的问题
2、 Struts2利用值栈来存数据,所以值栈是一个存储数据的内存结构
3、 把数据存在值栈中,在页面上利用ognl表达式显示出来
讨论的问题
1、 讨论值栈的生命周期
2、 值栈的内存结构
3、 通过什么样的方法把数据放入到值栈中
4、 显示出来
值栈的生命周期
一次请求
值栈的内存结构
获取值栈的路径
说明:
1、 值是一样的,说明只有一个对象
2、 因为有一种是从request域中获取的,所以是一次请求
内存结构
1、 大致图:
2、 上面图中的context的放大
说明:
_root:(CompoundRoot)
_values:(HashMap)
在这里存放了request,response,session,application等servlet容器的内容
3、_root的放大
说明:
和ValueStack中的root是一致的
值栈的内存总图
说明:
从上图中可以看出valueStack总共分为两个部分:
对象栈:root
Map栈:_values
对象栈的存放
1、 Push方法
2、 add方法
对象栈的提取
从上图中可以看出,peek方法为获取对象栈的栈顶的元素
这行代码也可以获取对象栈的栈顶的元素
对象栈的元素的弹出
操作对象栈中的对象
说明:
可以利用valueStack.setParameter方法改变对象栈中对象的属性的值
Map栈的存放
通过该方法可以把一个字符串放入到map栈中的request域中
通过该方法可以把一个字符串放入到map栈中的application域中
通过该方法可以把一个字符串放入到map栈中
Ognl表达式
Debug标签(重要)
在页面上引入标签库
标签库的位置:
可以通过网页的形式把valueStack中值提取出来
关于对象栈:
从上图可以看出来,对象栈的属性来自于对象栈中的每一个对象的属性的get方法
把一个对象放入到对象栈中
在s:debug中
如果在对象栈中有两个相同的属性:
上述的两个类中都同时有name属性,而且当前请求的action在对象栈中
利用该方法就把person对象放入到栈顶了
可以利用上述的方法改变对象栈中的name属性,但是从上往下找,只要找到一个,改变了值就完事了。
Property标签(重要)
介绍
输出标签
value 指定要输出的内容
如果在对象栈中,直接指定属性
也可以直接调用方法
如果在map栈中,用#指定
如果value属性不写,则默认输出栈顶的元素
页面上:
把一个对象放入到对象栈中,可以利用s:prototype标签的value属性直接访问
该对象的属性。如果对象栈中出现两个相同的属性,则prototype标签的value属性会
从栈顶往下查找,直到找到为止。赋值的为第一个。
把一个对象放入到map中,再把map放入到对象栈中,在页面上可以利用
可以利用方法来访问
利用该标签访问map栈中的元素
Iterator标签(重要)
1、 用途:主要用于迭代,可以迭代List,Set,Map,Array
2、 案例
1、 把集合放入到对象栈中
2、把list放入到map中
3、 把list放入到request域中
4、 把map放入到map栈中
5、把List<Map<String,Person>>放入到map栈中
总结:
1、 如果要迭代一个集合,当前迭代的元素在栈顶
2、 如果要迭代一个map,则当前迭代的元素是entry
3、 Iterator标签中有一个属性status
Ui标签
1、 struts2提供了一套标签机制,由struts2框架把struts2的标签解析成html标签。
2、 在struts2的配置文件中,增加
在struts2的配置文件中加入如下的内容:
由struts2容器翻译过程来的html代码和struts2标签的代码就能保持一致。
Select标签
Checkbox:
回显
1、 如果把要回显的数据放入到map栈中,则必须根据value进行回显
Value=”%{ognl表达式}”
2、 一般情况下,应该把回显的数据放入到对象栈中,因为页面上可以直接根据name进行回显
3、 因为当前请求的action在对象栈中,所以可以利用action中的属性进行回显,action中的属性必须有get方法。
4、 Checkbox的回显
上述图表示要回显的id值
根据name属性进行回显
拦截器
需求
如果要访问某一个action类中的某一个方法的内容,如果是admin用户,则访问,如果不是admin用户,则不能访问。
实现
步骤
1、 编写interceptor
2、 写一个PvilegeSuperAction
3、 在配置文件中进行配置
详细解析
说明:
通过该图可以看出:当提交一个url请求时,先执行所有的拦截器(按照声明的从上到下的顺序),再执行action。
声明一个拦截器
声明一个拦截器栈
既可以引用一个拦截器
也可以引用一个拦截器栈
真正的做法:
如果p包继承了privilege包就把所有的新的拦截器栈继承过来了,如果没有继承,则执行默认的拦截器栈
缺点
把权限判断的代码和业务逻辑的代码混合在一起了
利用拦截器改进
拦截器的优点
1、 把项目中一些经常用到的业务逻辑从action中分离出来,写到拦截器中
这样可以做到这些常用的逻辑和真正的逻辑的松耦合
2、 拦截器和action是真正的松耦合了
3、 Struts2把开发过程中的一些常用的业务已经封装到各个拦截器中了
案例2
在执行action的方法之前,
1、 开启日志
2、 权限的检查
声明两个拦截器
在执行的拦截器栈中,按照执行的先后顺序引入拦截器
拦截器的应用
属性驱动
注意事项:
1、 必须使用struts2默认的拦截器栈中的ParametersInterceptor
2、 Action中的属性和表单中的name属性的值保持一致
3、 利用valueStack.setValue方法可以赋值了
模型驱动
从上图可以看出,ModelDriverInterceptor有两个作用:
1、 当前请求的action必须实现ModelDriver接口
2、 把model对象放入到了栈顶
Threadlocal
1、 本地线程类
2、 可以存放一个对象
3、 Threadlocal对象只能当前线程访问,其他的线程是不能访问的
4、 传递参数
案例:
参数的传递
说明:通过参数的传递,一个字符串很容易的从客户端传递到tl1中的tl1方法,再传递到tl2中的tl2方法
说明:
在TL1Super中用到一个字符串
在TL2Super中用到同样的字符串,但是这两个类是完全松耦合,这个时候要用
Threadlocal传递数据,因为不管是否是送耦合的,但是可以肯定在同一个线程中。所以可以得到同一个线程threadlocal对象中的数据