Struts2 是基于MVC的WEB框架
经过六年多的发展,Struts1已经成为了一个高度成熟的框架,不管是稳定性还是可靠性都得到了广泛的证明。市场占有率超过20%,拥有丰富的开发人群,几乎已经成为了事实上的工业标准。但是随着时间的流逝,技术的进步,Struts1的局限性也越来越多地暴露出来,并且制约了Struts1的继续发展。
对于Struts1框架而言,由于与JSP/Servlet耦合非常紧密,因而导致了一些严重的问题。首先,Struts1支持的表现层技术单一。由于Struts1出现的年代比较早,那个时候没有FreeMarker、Velocity等技术,因此它不可能与这些视图层的模版技术进行整合。其次,Struts1与Servlet API的严重耦合,使应用难于测试。最后,Struts1代码严重依赖于Struts1 API,属于侵入性框架。
从目前的技术层面上看,出现了许多与Struts1竞争的视图层框架,比如JSF、Tapestry和spring MVC等。这些框架由于出现的年代比较近,应用了最新的设计理念,同时也从Struts1中吸取了经验,克服了很多不足。这些框架的出现也促进了Struts的发展。
Struts已经分化成了两个框架:第一个是在传统的Struts1的基础上,融合了另外的一个优秀的Web框架WebWork的Struts2。Struts 2虽然是在Struts1的基础上发展起来的,但是实质上是以WebWork为核心的。Struts2为传统的Struts1注入了WebWork的先进的设计理念,统一了Struts1和WebWork两个框架。
官网下的jar包
打开看见这是apache和opensymphony的WebWork结合的一个产品
找到struts-2.3.16.3\apps路径下的struts2-blank.war这个包参考写代码
1参考上面的war包创建Web动态工程导入相关jar包
参考xml。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<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>
</web-app>
然后在src下放一个struts.xml,参考得来下面的
这是最简单的struts.xml配置
其效果是当访问index路径的时候,服务端跳转到index.jsp
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="index">
<result>index.jsp</result>
</action>
</package>
</struts>
2开始写HelloWorld
修改一下inde.jsp页面
运行产生效果
通过这个过程体会到
相当于实现了servlet的服务端跳转到index.jsp的功能
3整个流程
4 继续 struts 显示数据到JSP
Model层使用一个简单的Product用于存放数据
public class Product {
int id;
String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ProductAction用于控制
import com.tzy.struts.Product;
public class ProductAction {
private Product product;
public String show() {
product = new Product();
product.setName("xiaomi8");
return "show";
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
}
在struts.xml中配置跳转
该配置表示 访问路径/showProduct会调用 ProductAction 类的 show 方法
服务端跳转到show.jsp
<action name="showProduct" class="com.tzy.struts.action.ProductAction" method="show">
<result name="show">show.jsp</result>
</action>
show.jsp
在web目录下创建show.jsp文件
通过EL表达式,取出product的name
注: ${product}会访问对应的Action的 getProduct()方法
注: 严格地说,是Struts通过getProudct()方法返回product对象,然后再把product对象通过request.setAttribute("product", product) 放在"product" 这个key上,这样就方便EL表达式去取出来了
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${product.name}
</body>
</html>
5整个流程
- 访问路径 /showProduct
- 所有访问都被struts的filter所拦截,并进入struts的工作流程
- 根据配置文件struts.xml, 会执行ProductAction的show方法
- 在show方法中,将实例属性product指向一个新的对象,并设置名称为xiaomi8
- 服务端跳转到show.jsp
- 在show.jsp中,访问ProductAction.getProduct() 获取实例属性product,并显示其名称xiaomi8
6 struts 提交数据到ACTION
准备提交数据的jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>submit</title>
</head>
<body>
<form action="addProduct">
<input type="text" name="product.name">
<br/>
<input type="submit" value="submit">
</form>
</body>
</html>
为addProduct路径配置对应的action和方法以及返回
<action name="addProduct" class="com.tzy.struts.action.ProductAction" method="add">
<result name="show">show.jsp</result>
</action>
在ProductAction添加add方法
public String add(){
System.out.println("product.name:"+product.getName());
return "show";
}
7 原理图
- 访问路径 /addProduct
- 所有访问都被struts的filter所拦截,并进入struts的工作流程
- 根据配置文件struts.xml, 会执行ProductAction的add方法
- 在add方法执行之前,Struts生成一个新的product对象,并把页面传递过来的name设置在该对象上,接着把该对象通过setProduct()方法,注入ProductAction
- 服务端跳转到show.jsp
- 在show.jsp中,访问ProductAction.getProduct() 获取注入的product,并显示其名称
8 struts 中文问题
Struts的中文问题,由3部分组成
- jsp提交数据的时候,必须是UTF-8编码的
- struts拿到数据后进行UTF-8解码
- 服务端跳转到jsp进行显示的时候,要指定浏览器使用UTF-8进行显示
UTF-8可以换成GBK或者GB2312,但是必须统一,不能混用
使用上一步的操作传入啦啦啦
修改提交页面
修改展示页面
修改struts.xml文件
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
再次测试
8 struts 启动失败,出现错误ERROR FILTERSTART 应该如何调试
在tomcat启动struts web应用的时候,如果出现了struts配置上的错误,你可能只能看到一个 Error FilterStart的提示,而看不到详细的错误原因。
这样就加大了定位和解决问题的难度
这是因为默认配置下,struts把日志输出关闭了
为了把日志输出开启便于调试,需要增加log4j.xml这个配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration PUBLIC "-//log4j/log4j Configuration//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p %c.%M:%L - %m%n"/>
</layout>
</appender>
<!-- specify the logging level for loggers from other libraries -->
<logger name="com.opensymphony">
<level value="DEBUG" />
</logger>
<logger name="org.apache.struts2">
<level value="DEBUG" />
</logger>
<!-- for all other loggers log only debug and above log messages -->
<root>
<priority value="INFO"/>
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
这样struts就会启动失败,虽然启动失败了,但是tomcat给出的错误信息非常有限,很不利于调试。 无法知道到底哪里写错了。
然后重新启动tomcat即可看到具体struts启动失败错误原因
如图所示,可以看到很明确的错误原因,ProductAction类的ClassNotFoundException
关闭log4j.xml日志输出
有了log4j.xml日志输出可以帮助调试,但是也会导致struts的启动变慢。
关闭log4j.xml日志输出很简单,直接把log4j.xml命名成其他文件即可,以后要用再把名字改回来,很方便
9 在struts中也可以获取servlet包中的request和response对象
修改ProductAction的show方法,获取request和response对象
public String show(){
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
System.out.println("request:\t" + request);
System.out.println("response:\t" + response);
Product product = new Product();
product.setName("xiaomi8");
return "show";
}
在Tomcat的控制台输出 可以看到Struts使用类StrutsRequestWrapper对HttpServletRequest进行了封装
10 struts 获取SESSION对象
- struts中的Session有两个
- 一个是传统的servlet包下的HttpSession
- 另一个是Struts中自己定义的Session
- 传统的servlet包下的session的获取办法是:
- ServletActionContext.getRequest().getSession();
- 使用该方法,需要在eclipse的项目中导入servlet-api.jar,可以在右边下载
- 新的Session的获取办法是
- Map m = ActionContext.getContext().getSession();
- 这个session以Map类的形式出现,其中的值和HttpSession中的值是同步的
修改ProductAction的add方法中访问Session
public String add(){
Map m = ActionContext.getContext().getSession();
m.put("name", product.getName());
return "show";
}
在show.jsp中把通过EL表达式把session中的数据取出来
通过
http://127.0.0.1:8080/addProduct.jsp
增加数据
在show.jsp中就可以看到session中的name