Struts2
A.Model2模型
1.Model1模型
所谓的Model1模型,其实就是纯JSP页面
在JSP页面上既写Java代码,又写HTML代码
2.Model2模型
在java web开发中,基于Servlet+JSP+JavaBean的开发模型称为Model2模型
该模型完全遵守MVC模式:
JavaBean
模型层,既可以封装数据,又可作为业务逻辑模型
接收到控制器传递的更新请求后,执行逻辑处理返回响应的执行结果
JSP
表现层,负责提供用户数据显示页面
并在适当时候(提交表单)向控制器请求模型更新
Servlet
控制器,接收用户请求,然后获取数据并转换为业务模型需要的模型
然后调用模型的业务方法,请求模型更新
同时根据执行结果选择返回的视图页面
3.基本流程
a.用户发出一个request请求,会被控制器servlet接收到
b.servlet将请求的数据转换成数据模型javabean,然后调用模型的逻辑方法
c.servlet把业务逻辑模型返回的结果放到合适的地方,譬如request的属性里
d.根据逻辑模型返回结果,servlet选择合适的jsp视图
e.jsp视图展示数据给用户
4.缺点
a.流程凌乱
servlet既要处理请求,还要处理页面流程,功能不单一
无法把握整个系统的页面流程
b.数据传递无序
使用javabean传递数据比较繁琐,有时候无法满足需求
c.缺乏辅助功能
没有统一的分发调度,验证框架 ,国际化,异常处理等等功能
B.Struts2
1.概述
基于mvc的轻量级web应用框架
所谓框架就是完成了一定功能的半成品软件
在框架的基础上开发,效率和质量更高
struts2应用场景是web应用,对web服务器资源消耗少,运行速度快
struts2致力于在mode view controller的各个部分为开发者提供帮助
2.功能
a.POJO表单及POJO操作
可以用任何一POJO来接收表单输入,可以把任一POJO视为一个Action
b.标签支持
改进了标签表单,新标签可减少代码编写量
c.AJAX支持
把AJAX支持整合进其结果中
d.易于整合
Struts有多种整合方式可使用
如Spring、Tiles、SiteMesh之类的,整合更为容易了
e.模板支持
支持使用模板生成视图
f.插件支持
有大量的插件增强和扩大Struts2 核心行为
g.性能分析
Struts2 为调试和配置应用程序提供综合的性能分析
此外也以嵌入调试工具的形式提供集成调试
h.易于修改标签
可使用Freemarker的模板对标签标记进行调整
基本的HTML、XML和CSS知识就足够了
i.促进减少配置
Struts2使用各种设置默认值减少配置
j.视图技术
Struts2 为多种视图选项(JSP、Freemarker、Velocity、XSLT等)提供支持
3.Sturts2和MVC
a.控制器:StrutsPrepareAndExecuteFilter
用户请求首先到达该前端控制器
该过滤器负责根据用户提交的URL和struts.xml中的配置
来选择合适的动作(Action),让这个Action来处理用户的请求
该控制器其实是一个过滤器(Filter,servlet规范中的一种web组件)
是Struts2内置类,只是要在项目的web.xml中配置一下即可
b.动作:Action模型
在用户请求经过核心过滤器处理之后,被分发到了合适的动作Action对象
Action负责把用户请求中的参数组装成合适的数据模型
并调用相应的业务逻辑进行真正的功能处理
获取下一个视图展示所需要的数据,实现了与Servlet API的解耦,
不需要再直接引用和使用HttpServletRequest与HttpServletResponse等接口
使得Action的单元测试更加简单
而且强大的类型转换也使得我们少做了很多重复的工作
c.视图:Result
视图结果用来把动作中获取到的数据展现给用户
在Struts2中有多种优秀的结果展示方式
常规的jsp,模板 freemarker、velocity
还有各种其它专业的展示方式
如图表jfreechart、报表JasperReports、将XML转化为 HTML的XSLT等等
而且各种视图结果在同一个工程里面可以混合出现
C.使用步骤
1.从官网下载struts-2.5.10.1-all.zip
2.创建web项目
拷贝必须jar文件,放置在WEB-INF下的lib目录中
(可以从官网下载struts-2.5.10.1-min-lib.zip)
3.在web.xml中添加核心控制器配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>Struts2</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <!-- 过滤器 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <!-- 过滤所有文件 --> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
4.创建对应jsp文件
a.login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>登陆页面</title> </head> <body> <form action="loginAction"> 用户名:<input type="text" name="username" /><br /> 密 码:<input type="password" name="password" /><br /> <input type="submit" value="提交" /> </form> <span><font style="color: red;font-size: 20px;">${ msg }</font></span> </body> </html>
b.success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>登陆成功</title> </head> <body> <span><font style="color: green;font-size: 20px;">${ username },欢迎您!!!</font></span> </body> </html>
5.创建对应的Action模型类
LoginAction
package org.xxxx.web; import org.apache.struts2.ServletActionContext; public class LoginAction { // 与jsp中的name一致 private String username; private String password; public LoginAction() { super(); } public LoginAction(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String login() { if ("zhangsan".equals(username) && "123456".equals(password)) { // 调取域对象 ServletActionContext.getRequest().setAttribute("username", username); return "success"; } else { ServletActionContext.getRequest().setAttribute("msg", "用户名或密码错误!"); return "fail"; } } }
6.在项目的src目录下创建struts.xml文件
在struts2框架中使用包来管理Action
包的作用和java中的类包是非常类似的
它主要用于管理一组业务功能相关的action
在实际应用中,我们应该把一组业务功能相关的Action放在同一个包下
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="struts2" extends="struts-default"> <!-- name和jsp的action相一致 --> <!-- method可以不写,默认调用name中的execute方法 --> <action name="loginAction" class="org.xxxx.web.LoginAction" method="login"> <!-- 判断返回值 --> <result name="success">/success.jsp</result> <result name="fail">/login.jsp</result> </action> </package> </struts>
启动tomcat服务器,运行
7.架构图
D.Action的使用
1.Action
代表一次请求或调用,对应于一个Action类
在Struts2里面充当着MVC中模型的角色
既封装了业务数据,又要处理业务功能
但在实际的JavaEE开发中,逻辑部分会放到逻辑层去实现
Action只是去调用逻辑层来进行业务逻辑的处理
Action中方法实现的功能,是MVC中控制器部分的功能
因为过滤器作为核心控制器,Action常看做model层
2.Action的实现方式
在Struts2中,Action可以不实现任何特殊的接口或者继承特殊的类
仅仅是一个POJO(Plain Old Java Object,简单的Java对象)就可以
但要有一个公共的无参的构造方法
还可以有一个返回String类型的execute或其他方法
SUCCESS:表示Action执行成功,显示结果视图给用户,值为字符串"success"
NONE:表示Action执行成功,不需要显示视图给用户,值为字符串"none"
ERROR:表示Action执行失败,显示错误页面给用户,值为字符串"error"
INPUT:表示执行Action需要更多的输入信息,回到input页面,值为字符串"input"
LOGIN:表示因用户没有登陆而没有正确执行,返回该登陆视图,值为字符串"login"
也可以自己定义的字符串,只要你在Action里面返回的字符串
跟在struts.xml里面配置的result的name属性值一样就可以了。
如果struts.xml配置文件中Action没有method属性,则execute方法是必须的
但实际开发的时候,Action实现方式
a.实现Action接口
仅仅定义了前面的execute方法
除外还有一系列预定义的字符串常量
可以用于返回一些预定的result
User类
package org.xxxx.web; import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private String username; private String password; public User() { super(); // TODO Auto-generated constructor stub } public User(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Action
package org.xxxx.web; import com.opensymphony.xwork2.Action; public class OrtherAction implements Action { private User user; public OrtherAction() { super(); } public OrtherAction(User user) { super(); this.user = user; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String execute() throws Exception { if (user.getUsername().equals("zhangsan") && user.getPassword().equals("123456")) { return SUCCESS; } return ERROR; } }
b.继承ActionSupport类
除实现了Action接口,还提供更多辅助功能,推荐使用
package org.xxxx.web; import java.util.List; import com.opensymphony.xwork2.ActionSupport; public class AnotherAction extends ActionSupport { private static final long serialVersionUID = 1L; private String username; private String password; // 如果前台有复选框,用集合或数组接收 private List<String> hobby; public AnotherAction() { super(); } public AnotherAction(String username, String password, List<String> hobby) { super(); this.username = username; this.password = password; this.hobby = hobby; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<String> getHobby() { return hobby; } public void setHobby(List<String> hobby) { this.hobby = hobby; } @Override public String execute() throws Exception { // 查看数组 System.out.println(hobby); if (username.equals("zhangsan") && password.equals("123456")) { return SUCCESS; } return ERROR; } }
3.Action获取页面数据
在Struts2中,页面的数据传递给Action有两种基本方式
属性驱动(FieldDriven)和模型驱动(ModelDriven)。
属性驱动又有两种情况:(代码参考上一条两段代码)
a.基本数据类型的属性对应:控件的name属性,和Action的属性对应
b.JavaBean风格的域对象对应: 控件的name属性是域对象.属性名方式
4.后台Action处理复选框
成员变量名必须和web页面name相同(代码参考上一条两段代码)
方案1: 定义一个私有String数组类型的属性,提供相应的getter/setter方法
方案2: 定义一个私有集合类型的属性,比如List类型的,提供相应的getter/setter方法
E.Struts.xml配置
1.constant常量
a.所有匹配*.action的请求都由struts2处理
<constant name="struts.action.extension" value="action" />
b.是否启用开发模式
使用开发者模式,会显示错误信息
在项目提交发布前,应该为非开发者模式
<constant name="struts.devMode" value="true" />
c.struts配置文件改动后,是否重新加载
<constant name="struts.configuration.xml.reload" value="true" />
d.指定Web应用的默认Locale
<constant name="struts.locale" value="zh_CN"/>
e.请求参数的编码方式
<constant name="struts.i18n.encoding" value="utf-8" />
f.每次HTTP请求系统都重新加载资源文件,有助于开发
<constant name="struts.i18n.reload" value="true" /
2.package
<package>元素把逻辑上相关的一组Action等元素封装起来
形成一个独立的模块,可以继承其他的package
也可以作为父包被其他的package继承
核心属性:
name:包的名称,必须配置
extends:配置的是被继承的包名称,可选
3.action
<action>元素是<package>元素的子元素,应该配置在<package>元素里面
<action>元素通常需要配置name和class属性,其中name是必须的
method属性对应Action实现类中对应方法名
<action>元素可以包含其他的子元素:比如<param>、<result>
4.result
<result>元素可有name属性和type属性,都是可选的
name属性:默认值success
Action运行后跳转的下一个页面名,可以是任意字符串
但必须和Action方法返回值一致
type属性:默认值dispatcher
在struts-default.xml中所定义的<result-type>的name属性的值或合法的自定义值
F.Interceptor拦截器
1.概述
是Struts2最强大的特性之一
是一种可以在Action执行之前和Result执行之后进行一些功能处理的机制
2.优点
a.简化Action的实现
把很多功能从Action中独立出来,大量减少了Action的代码
b.功能更单一
把功能从Action中分离出来,分散到不同的拦截器
这样每个拦截器以及Action的功能都更单一了
c.通用代码模块化
拦截器能把一些在多个Action中通用的代码进行模块化
封装在一个或几个拦截器里面
d.提高重用性
拦截器实现了代码模块化过后,就可以对不同的Action
根据功能需要来配置相同的拦截器了
e.拦截器和Filter
有相似之处,但是Interceptor相比于Filter具有更强大的功能
比如拦截器与Servlet的API无关,比如拦截器可以访问到值栈等等
3.预定义拦截器
把用户请求中的参数值和Action的属性做了一个对应
并且把请求中的参数赋值到了Action的属性上
这个功能就是由缺省配置的拦截器来实现的
Struts2的预定义拦截器都定义在struts-default.xml文件的struts-default包内
例如params拦截器,把请求参数设置到相应的Action的属性,并自动进行类型转换
4.自定义拦截器
开发人员实现的拦截器, 实现接口com.opensymphony.xwork2.interceptor.Interceptor
核心方法intercept() 定义拦截器执行的处理方法,即要实现的功
invocation.invoke():继续执行其他拦截器方法
其之前代码,会在Action运行之前执行
之后代码,会在Result运行之后执行
其返回值是要返回的Result字符串
拦截器
package org.xxxx.web; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; // 实现接口 public class MyInterceptor implements Interceptor { private static final long serialVersionUID = 1L; @Override public void destroy() { System.out.println("拦截器被销毁"); } @Override public void init() { System.out.println("拦截器初始化"); } @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("action执行之前"); // 放行 String result = invocation.invoke(); System.out.println("result运行之后"); return result; } }
Action
package org.xxxx.web; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private static final long serialVersionUID = 1L; private String username; private String password; public LoginAction() { super(); // TODO Auto-generated constructor stub } public LoginAction(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String execute() throws Exception { if ("zhangsan".equals(username) && "123456".equals(password)) { // 调取域对象 ServletActionContext.getRequest().setAttribute("username", username); return SUCCESS; } else { ServletActionContext.getRequest().setAttribute("msg", "用户名或密码错误!"); return ERROR; } } }
Sturts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="struts2" extends="struts-default"> <!-- 拦截器 --> <interceptors> <interceptor name="myInterceptor" class="org.xxxx.web.MyInterceptor"></interceptor> </interceptors> <action name="loginAction" class="org.xxxx.web.LoginAction"> <result name="success">/success.jsp</result> <result name="error">/login.jsp</result> <!-- 添加拦截器 --> <interceptor-ref name="myInterceptor"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action> </package> </struts>
启动tomcat服务器,查看控制台信息
执行,查看控制台信息
5.应用
使用拦截器实现登录检查,没有登录直接返回登录页面
分析:
要实现登录检查的功能
很明显是在Action运行之前
就要判断用户是否登陆了
判断方式是看session里面是否有相关信息
如果有,则继续操作;如果没有,则要跳转到预先制定好的登录页面
只有部分Action需要登陆录检查,另外一些Action(登陆)不需要
对于大多数Action都要进行登录检查的包
可以在包的默认拦截器引用上设置登录检查
而对于少数不需要登陆检查的Action
可以让它们直接引用默认的defaultStack拦截器栈
在之前基础上添加以下代码
LoginAction
package org.xxxx.web; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private static final long serialVersionUID = 1L; private String username; private String password; public LoginAction() { super(); } public LoginAction(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String execute() throws Exception { if ("zhangsan".equals(username) && "123456".equals(password)) { // 封装信息 LoginAction la = new LoginAction(username, password); // 存入session域中 ServletActionContext.getRequest().getSession().setAttribute("la", la); return SUCCESS; } else { ServletActionContext.getRequest().setAttribute("msg", "用户名或密码错误!"); return ERROR; } } }
AnotherAction.java
package org.xxxx.web; import com.opensymphony.xwork2.ActionSupport; public class AnotherAction extends ActionSupport { private static final long serialVersionUID = 1L; @Override public String execute() throws Exception { return SUCCESS; } }
another.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>其他页面</title> </head> <body> 其他的展示信息页面 </body> </html>
拦截器
package org.xxxx.web; import java.util.Map; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; // 实现接口 public class MyInterceptor implements Interceptor { private static final long serialVersionUID = 1L; @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocation invocation) throws Exception { // Struts2对Session封装为Map格式 // 获取session对象 Map<String, Object> map = invocation.getInvocationContext().getSession(); // la键值对是否存在 if (map.get("la") == null) { // 不存在,跳转登录页面 return "login"; } else { // 已登陆,放行 return invocation.invoke(); } } }
struts.xml
启动tomcat,先在地址栏访问/anotherAction,跳转到登录页面<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <!-- 开发者模式 --> <constant name="struts.devMode" value="true"></constant> <package name="struts2" extends="struts-default"> <!-- 过滤器 --> <interceptors> <!-- 自定义拦截器 --> <interceptor name="myInterceptor" class="org.xxxx.web.MyInterceptor"></interceptor> <!-- 将自定义拦截器和预定义拦截器合在一起,调用方便 --> <interceptor-stack name="myStack"> <interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name="myInterceptor"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 默认拦截器声明,对package范围Action均生效 --> <default-interceptor-ref name="myStack"></default-interceptor-ref> <!-- package级别的Result,对所有Action均生效 --> <global-results> <!-- 没有登陆,都去登录页面 --> <result name="login">/login.jsp</result> </global-results> <!-- 没有method属性,则默认调用Action的execute方法 --> <action name="loginAction" class="org.xxxx.web.LoginAction"> <result>/success.jsp</result> <result name="error">/login.jsp</result> <!-- 登陆Action不应被自定义拦截,只被预定义拦截 --> <interceptor-ref name="defaultStack"></interceptor-ref> </action> <!-- 其他页面 --> <action name="anotherAction" class="org.xxxx.web.AnotherAction"> <result>/another.jsp</result> </action> </package> </struts>
如果先登录成功,session域中就有数据
就无法验证拦截,需要重启浏览器和服务器
G.ONGL
1.概述
Object-Graph Navigation Language对象图导航语言
是一种功能强大的表达式语言,通过简单的语法
可以任意存取对象的属性或者调用对象的方法
能够遍历整个对象的结构图,实现对象属性类型的转换等功能
OGNL的计算是围绕OGNL上下文进行的
上下文实际上是一个Map对象,里面可以存放很多个JavaBean对象
OGNL上下文它有一个根对象
上下文中的根对象可以直接使用名称来访问它的属性值
否则要加前缀“#key”
2.Struts
a.ActionContext
Action的上下文,是数据中心,充当OGNL的context
b.ValueStack
值栈,本质是一个ArrayList,充当ognl的root
当Struts2接受一个请求时
会迅速创建ActionContext,ValueStack,action
然后把action存放进ValueStack
OGNL会设定一个根对象,在Struts2中根对象就是ValueStack
要访问ValueStack中对象的属性,直接访问即可
访问上下文(Context)中的对象需要使用#符号标注命名空间
如#application、#session
改变上面的LoginAction和success.jsp代码
LoginAction
package org.xxxx.web; import java.util.ArrayList; import java.util.List; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private static final long serialVersionUID = 1L; private String username; private String password; public LoginAction() { super(); } public LoginAction(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String execute() throws Exception { List<LoginAction> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { LoginAction la = new LoginAction(); la.setUsername("user" + i); la.setPassword("pass" + i); list.add(la); } ServletActionContext.getRequest().setAttribute("la", list); return SUCCESS; } }
success.jsp
别忘了添加taglib标签
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>登陆成功</title> </head> <body> <center> <table> <s:iterator value="#request.la" var="u"> <tr> <td style="border: 1px solid black;"><s:property value="#u.username" /></td> <td style="border: 1px solid black;"><s:property value="#u.password" /></td> </tr> </s:iterator> </table> </center> </body> </html>
struts.xml(上面的最近的一个struts.xml拿来就能用)
运行
H.Maven搭建Struts2项目
依赖dependency坐标
<groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.10.1</version>