1. 简介
Apache Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2是Struts1和WebWork的技术基础上进行了合并的全新的Struts2框架。其全新的Struts2的体系结构与Struts1的体系结构差别巨大。Struts2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设也是的业务逻辑控制器能够ServletAPI完全脱离开。
1.1 Struts2的优点
- 在软件设计上,Struts2没有像struts1那样和Servlet API和struts API有着紧密的耦合,Struts2的应用可以不依赖于Servlet API和Struts2 API,属于无侵入式设计。
- Struts2提供了拦截器,利用拦截器可以进行AOP(面向切面编程),实现注入权限拦截等功能。
- Struts2提供了类型转换器,可以把特殊的请求参数转换为需要的类型。
- Struts2提供支持多种表现层技术,如JSP,freeMarker, Velocity等
- Struts2的输入校验可以对指定方法进行校验
- 提供了全局范围,包范围和Action范围的国际化资源文件管理实现
1.2 Web项目中的三层框架
1.2.1 表示层
Struts2框架就是工作在这里
1.2.2 业务逻辑层
Service层,处理业务逻辑,比如判断用户名是否存在,用户名和密码是否匹配,权限检查等。为一些执行某一个操作条件的判断。
1.2.3 数据访问层
dao层,专门处理和数据库进行交换的业务。jdbc/hibernate在这里使用。MVC架构是项目中三层架构的一部分,三层架构的表示层可以使用MVC架构的struts2框架来实现。
2. web项目中引入struts2框架
2.1 开发环境
- IDE:Intellij IDEA 2017.2
- Server:Tomcat 9.0
- JDK:JDK 8
2.2 新建项目
File-New-Project
创建JavaEE项目,勾选WebApplication和Struts2选项。
2.3 目录结构
- Project Name
- .idea
- lib
- out
- src
- web
- xx.iml
需要注意将lib移动到web目录下面的Web-INF文件夹下,并在moudule中添加路径,不然部署过程中会报ClassNotFoundException。因为部署过程中只会读取web下的lib, 外部的lib是不会读取的。
2.4 struts2配置文件
src目录下面会生成struts.xml文件,默认内容如下:
2.5 web.xml文件中配置struts2框架的过滤器
|
|
这个过滤器的作用是拦截struts2框架中的action
3 Action
3.1 Action定义
- struts2框架中有一种Java类叫做Action。struts2框架楼底层封装了Servlet的相关内容而实现出来的。替代之前Servlet的类在Struts2中成为Action。比Servlet功能更加强大,操作更加简单。原因在于拦截器的设计,拦截器可以拦截访问Action的请求,拦截请求之后,可以给Action添加很多丰富的功能,而Action专注于业务逻辑的处理就可以。
3.2 实现一个Action
3.2.1 类中只要有一个固定的方法:
|
|
不需要实现或继承任何接口或者父类。execute方法一定要返回String类型的对象,每个字符串都可以对应一个跳转的页面。(字符串是可以自己随便定义的,字符串对应的哪一个跳转到页面也是自己定义的,在struts.xml文件中定义这些内容)
3.2.2 实现一个接口:com.opensyymphony.xwork2.Action
|
|
该接口有一个抽象方法execute()和五个静态属性:ERROR,SUCCESS, INPUT NONE, LOGIN
3.2.3 继承一个指定的父类ActionSupport
|
|
这种方法是最常用的,继承ActionSupport类,重写execute()方法。实际上ActionSupport实现了Action接口。
3.3 在struts.xml中进行配置
配置之后,才可以通知struts2框架我们写的这个类是一个Action,将来struts2框架中要给这个类创建对象,调用方法以及这个Action加入更多丰富的功能,注册的实例如下:
<package>
: 一个struts.xml文件中可以配置多个<package>
标签,一个<package>
标签里面可以配置多个<action>
标签,一个<action>
标签里面可以配多个<result>
标签- name=”front” 表示给当前package起一个名字,唯一标示当前这个package,方便package之间的继承
- extends=”struct-default” 表示当前这个package继承了另外一个名字叫做struct-default的package。这个package定义在core包中的struts-default.xml中定义。所有的package都会直接或者间接的继承struts-default这个包,类似java中的Object类。
- namespace=”/“ 表示当前package的命名空间为/,以后这个package里面所有的action被访问时候,路径里面都要加上这个命名空间
<action>标签属性及其子标签
:- name=”index” 表示当前配置这个action的名字为index,这个名字是自己定义的,可以和action类的名字相同,同时将来浏览器中的地址栏里面就要出现这个名字来访问当前这个action类
- class=”com.struts2.front.action.IndexAction1” 表示当前配置的action对应的是IndexAction1这个类,
<result>
标签表示action访问完成之后,根据action返回的字符串的值,跳转不同的页面,默认结果为’success’, 可以省略,一般都是Action接口中的那5个返回值,也可以自己定义。
3.4 动态方法调用DMI
struts2默认调用的是action中的execute方法,实际上我们也可以让其调用其他的方法,首先定义一个类,让其继承ActionSupport类,配置的时候配置其方法,或者不写具体的方法,用DMI的方式进行调用:
对其进行配置struts2.xml:
其中需要注意的是:
- struts.enable.DynamicMethodInvocation 默认为false,将其设置为true
- 配置action的时候,
- 浏览器中的访问方法为:../user/user!add 动态方法调用, 或者../user/userAdd 都可以成功访问
3.5 使用通配符来配置action
从之前的学习中可以发现,多个action名字或者类的名字是有一定的规律的,比如我们要写两个action, StudentAction, TeacherAction, 每个action有两个方法,add和execute方法,配置的时候可以使用通配符来简化配置:
其中{1}代表的是前面name属性中的第一个*号的值。这样可以简化配置,如果约定的好的话,整个配置文件可以只配置一个action。但是需要记住一点: 约定优于配置!!
3.6 action 的特点及其访问
3.6.1 特点:
- Servlet 是线程不安全的,因为Servlet是单例
- struts2 框架中的action是线程安全的,因为每次访问都会创建一个新的Action对象,所有的action里面可以随意定义成员变变量(只有成员变量才有线程安全的问题)
3.6.2 访问:
默认情况下,namespace/actionName.action或者namespace/actionName即可访问到action。访问成功会跳转到相应的jsp页面。由于这样会影响对相关Servlet的访问,所有最好默认action访问使用后缀名.action - 访问action的时候,action中方法的调用
- 默认情况下,访问action的 时候会调用action的execute()方法,这个方法执行完返回一个字符串,根据字符串进行跳转
- 可以在action标签里面添加一个method属性来指明将来访问这个action的时候会调用的方法
- 地址栏中动态指定调用的方法
3.7 Action接收参数
3.7.1 同名参数
例如通过页面将name=a&age=8这个参数传送给action,这时候需要:
- action里面定义两个成员变量;name,age
- action里面需要提供get/set方法
- 当页面将参数传送过来的时候,struts2框架会自动帮我们把这两个参数的值放在action中的属性里面,并自动进行了类型转换。从而完成了参数的初始化,实际上这个是有defaultStack这个拦截器栈里面的拦截器来完成的
index.jsp:
struts.xml:
UserAction:
3.7.2 DomainModel 域模型
在接收到的页面传值的时候,可以让struts2框架直接帮我们把这些接收到的值封装到一个JavaBean对象里面:
- Action中定义一个User类型的变量user,User类中有name,age两个成员变量及其get/set方法
- UserAction中提供这个User的get/set方法
- 页面向Action传递参数的名称要求user.name=1&user.age=23
接收到这个参数之后,struts2框架会帮我们去创建一个User队形,并且将所传的参数封装到对象的三个属性中去,最后将这个封装好的对象放到Action中的user属性中去。
3.7.3 模型驱动
实现ModelDriven接口,没怎么接触,不常用!创建一个User类有name,age两个成员变量,以及一个UserAction类,
3.8 简单数据验证
Action可以进行简单数据验证,这种验证一般不使用UI标签就可以完成,添加addFieldError到页面:
3.9 访问Web元素
访问Web元素有四种方法,Map类型的和原始类型的各两种:
Map类型 依赖于容器:Action初始化的时候生成三个Action容器,获取网页元素
12345public LoginAction1() {request = (Map)ActionContext.getContext().get("request");session = ActionContext.getContext().getSession();application = ActionContext.getContext().getApplication();}Map类型 IOC,Action实现了RequestAware,SessionAware, ApplicationAware三个接口,有三个Map类型的成员变量,并重写了成员变量的set方法,struts会自动调用set方法将元素注入到Action中。
12345678910111213141516171819202122232425262728public class LoginAction2 extends ActionSupport implements RequestAware, SessionAware, ApplicationAware{private Map<String, Object> request;private Map<String, Object> session;private Map<String, Object> application;public String execute() {request.put("r1", "r1");session.put("s1", "s1");application.put("a1", "a1");return SUCCESS;}@Overridepublic void setApplication(Map<String, Object> application) {this.application = application;}@Overridepublic void setRequest(Map<String, Object> request) {this.request = request;}@Overridepublic void setSession(Map<String, Object> session) {this.session = session;}}原始类型,IOC,实现了ServletRequestAware接口
12345678910111213141516171819public class LoginAction3 extends ActionSupport implements ServletRequestAware{private HttpServletRequest request;private HttpSession session;private ServletContext application;public String execute() {request.setAttribute("r1", "r1");session.setAttribute("s1", "s1");application.setAttribute("a1", "a1");return SUCCESS;}@Overridepublic void setServletRequest(HttpServletRequest httpServletRequest) {this.request = request;this.session = request.getSession();this.application = session.getServletContext();}}原始类型,依赖容器
123456789101112131415161718public class LoginAction4 extends ActionSupport{private HttpServletRequest request;private HttpSession session;private ServletContext application;public LoginAction4() {request = ServletActionContext.getRequest();session = request.getSession();application = session.getServletContext();}public String execute() {request.setAttribute("r1", "r1");session.setAttribute("s1", "s1");application.setAttribute("a1", "a1");return SUCCESS;}}
- 原类型和map类型的关系
- 使用Map类型的对象,可以降低对ServletAPI的依赖
- 我们使用元类型大多时候也是存值和取值,原来性里面本身也封装了Map对象,使用Map类型对象也可以完成存值和取值
- map类型的request对象里面的k-v值是复制原类型request对象的键值对的,元类型的request对象中还包含其他的属性和方法。
- 原类型和Map类型的Request对象是相同的 ,可以互相读取值,session和application也一样。
3.10 包含文件配置
struts2支持配置文件之间的继承,通过include标签完成,相当于将include内容复制到include的位置:
struts.xml:
1234<struts><constant name="struts.devMode" value="true" /><include file="login.xml" /></struts>login.xml:
1234567<struts><package name="login" extends="struts-default" namespace="/login"><action name="login*" class="com.bjsxt.struts2.user.action.LoginAction{1}"><result>/user_login_success.jsp</result></action></package></struts>
3.11 默认Action处理
当struts中没有找到请求的Action的时候会报错,我们可以设置一个默认的Action,当找不到请求的action的时候访问这个特定的Action,使得用户体验更加友好。在package中配置一条语句就可以完成:
<default-action-ref name="index"></default-action-ref>