Java web动态编程技术,经历了Model1和Model2时代。
Model1时代:整个Web应用几乎全部由JSP页面组成,JSP页面接收处理客户端请求,对请求处理后直接做出响应,用少量的JavaBean来处理数据库连接访问等操作。Model1的局限性非常明显,JSP页面同时担任View和Controller两种角色,将页面表现和逻辑处理混杂在一起,代码重用性极低,增加了扩展和维护难度。
Model2时代:已经采用了MVC的设计。在Model2架构中,Servlet作为Controller,负责接收用户请求,只包含控制逻辑,然后调用后端来进行具体的逻辑处理。最后转发到相应的JSP页面负责显示。
Model2(Servlet+JSP+JavaBean)项目示例
Struts是开源软件,使开发者能更深入的了解其内部实现机制。Struts开放源码框架的创建是为了使开发者在构建基于java Servlet和JavaServer Pages(jsp)技术的Web应用时更加容易。Struts框架为开放者提供了一个统一的标准框架,通过使用Struts作为基础,开发者能够更专注于应用程序的商业逻辑。Struts框架本身是使用Java Servlet和JavaServer Pages技术的一种Model-View-Controller(MVC)实现。
Struts2是基于MVC设计模式的流行和成熟的Web应用程序框架。Struts2并不只是Struts 1下一个版本,但它是一个完全重写的Struts架构。Struts2使用拦截器作为增强处理,以用户的逻辑控制器为目标,创建一个控制器代理,控制器代理回调业务控制器的execute方法处理用户请求,该方法的返回值决定struts2将怎样的视图资源呈现给用户。
Struts2 框架特性- POJO形式和POJO动作 - 已经摆脱了Struts2的动作表单的Struts框架的一个组成部分。Struts2可以使用任何的POJO接收的形式输入。同样的,你现在可以看到任何POJO的Action类。
- Tag支持 - Struts2的形式提高了标签的新标签让开发人员写更少的代码。
- AJAX支持- Struts2中已经认识到了Web2.0技术,并整合到产品中AJAX支持创建AJAX的标签,该功能非常相似的标准Struts2的标签。
- 容易整合 - 与其他框架如Spring,Tiles和SiteMesh的整合是现在更容易与各种集成Struts2。
- 模板支持 - 支持生成使用模板。
- 插件支持 - 核心Struts2行为,可以加强和扩大使用插件。大量的插件可供Struts2。
- 概要分析- Struts2的提供集成的配置,调试和分析应用程序。此外,Struts还提供了内置调试工具的帮助下,集成调试。
- 标签修改容易 - 在Struts2标签标记,可以调整使用FreeMarker模板。这并不需要JSP或Java知识。基本的HTML,XML和CSS知识是不够的修改的标签。
- 提升更少的配置 - Struts2的促进更少的配置与用于各种设置的帮助下,使用默认值。你没有的东西,除非它偏离由Struts2的默认设置的配置。
Struts2请求生命周期:
- 用户发送请求到服务器,用于请求对某些资源。
- 核心控制器StrutsPrepareAndExecuteFilter根据请求决定是否调用相应的Action。
- Struts2内置的拦截器链会自动对请求进行一些操,如验证,文件上传等。
- 回调Action的execute方法,执行操作。
- 配置拦截器做任何后期处理,如果需要的话。如Action会将处理结果存入stack Context中,并返回字符串,核心控制器根据字符串跳转到指定的视图资源。
- 视图资源会读取Stack Context中的信息,向用户生成响应数据。
Struts2的优点:
- Struts2实现MVC模式,结构清晰,使开发者只关注业务逻辑的实现。
- Struts2有丰富的标签可以用,Struts的标记库(Taglib),能大大提高开发效率。
- Struts2的页面导航。页面导航将是今后的一个发展方向,事实上,页面导航使系统的脉络更加清晰。通过一个配置文件,即可把握整个系统各部分之间的联系,这对于后期的维护有着非常大的好处。尤其是当新一批开发者接手这个项目时,这种优势体现得更加明显。
- Struts2提供Exception处理机制。
- Struts2数据库链接池管理。
- Struts2支持I18N。
Struts2的缺点:
- 转到展示层时,需要配置forward。每一次转到展示层,大多数都是直接转到jsp,而涉及到转向,需要配置forward。每次修改配置之后,都需要重新部署整个项目,并且如果使用tomcat服务器,还必须重新启动服务器。
- Struts2的Action必需是线程安全,它只允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。
- 测试不方便。Struts2的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,很难实现单元测试。不过Struts2 JUnit插件的StrutsTestCase可以实现它的单元测试。
- 类型的转换。Struts2的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。
- 对Servlet的依赖性过强。Struts2处理Action时必需要依赖ServletRequest和ServletResponse,所有它摆脱不了Servlet容器。
- 前端表达式语言方面。Struts2集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。
- 对Action执行的控制困难。Struts2创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。
- 对Action执行前和后的处理。Struts2处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。
- 对事件支持不够。在Struts2中,实际是一个表单Form对应一个Action类(或DispatchAction),即在Struts2中实际是一个表单只能对应一个事件,Struts2这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。
- Struts2重要的表单对象ActionForm是一种对象,它代表了一种应用,这个对象中至少包含几个字段,这些字段是Jsp页面表单中的input字段,因为一个表单对应一个事件,所以,当我们需要将事件粒度细化到表单中这些字段时,也就是说,一个字段对应一个事件时,单纯使用Struts就不太可能,当然通过结合JavaScript也是可以实现的。
Struts2环境的搭建
1. 开发环境: Eclipse Luna Release (4.4.0), JDK1.8, Maven(apache-maven-3.5.4), Struts(2.5.14.1), Tomcat(tomcat-8)
2. 创建Maven Web项目
3. 配置Struts2依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>angelia</groupId>
<artifactId>strutsmaven</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>strutsmaven Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<struts2.version>2.5.14.1</struts2.version>
<log4j2.version>2.10.0</log4j2.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>strutsmaven</finalName>
</build>
</project>
4. 配置Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Basic Struts2</display-name>
<welcome-file-list>
<welcome-file>index.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>
5. 创建log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
</Console>
</Appenders>
<Loggers>
<Logger name="com.opensymphony.xwork2" level="debug" />
<Logger name="org.apache.struts2" level="debug" />
<Root level="warn">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
6.创建view,index.jsp, login.jsp, error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!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>
欢迎来到Angelia的网站
<hr>
<c:if test="${username==null}">
<a href="${pageContext.request.contextPath}/gologin.action">登录</a>
</c:if>
<c:if test="${username!=null}">
欢迎:${username}
<a href="${pageContext.request.contextPath}/logout.action">注销</a>
</c:if>
</body>
</html>
<%@ 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="login" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="text" name="password" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="登录" />
<input type="reset" value="重置" /></td>
</tr>
</table>
</form>
</body>
</html>
<%@ 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>
<p>操作失败</p>
</body>
</html>
7. 创建Action
public class UserAction extends ActionSupport {
private static final long serialVersionUID = 1L;
public String execute(){
return SUCCESS;
}
public String login(){
HttpServletRequest request = ServletActionContext.getRequest();
String username = request.getParameter("username");
String password = request.getParameter("password");
if("angelia".equals(username) && "123456".equals(password)){
request.setAttribute("username", username);
return SUCCESS;
}else{
return ERROR;
}
}
public String logout(){
HttpServletRequest request = ServletActionContext.getRequest();
request.removeAttribute("username");
return SUCCESS;
}
}
8. 配置struts.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>
<constant name="struts.devMode" value="true" />
<package name="strutsmaven" extends="struts-default">
<action name="index">
<result>/index.jsp</result>
</action>
<action name="gologin" class="angelia.struts.action.UserAction">
<result name="success">login.jsp</result>
<result name="error">error.jsp</result>
</action>
<action name="login" class="angelia.struts.action.UserAction"
method="login">
<result name="success">index.jsp</result>
<result name="error">error.jsp</result>
</action>
<action name="logout" class="angelia.struts.action.UserAction"
method="logout">
<result name="success">index.jsp</result>
<result name="error">error.jsp</result>
</action>
</package>
</struts>
9. 启动项目:http://localhost:8080/strutsmaven/
10. 表单验证form-validation,Struts2有两种方法进行表单验证。一是action class添加validate方法,即validate()。二是使用Struts 2的XML,在Action对应路径下src/main/resources/angelia/struts/action生成ActionClassName-validation.xml文件。
validate method
public void validate(){
if (username.length() == 0) {
addFieldError("username", "First name is required.");
}
}
<s:form action="login">
<s:textfield name="username" label="用户名" />
<s:password name="password" label="密码"/>
<s:submit/>
</s:form>
XML Validator, 创建UserAction-validation.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.3//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<validator type="requiredstring">
<param name="fieldname">username</param>
<message>Username is required from XML.</message>
</validator>
</validators>
11. 添加Message Resource文件,在src/main/resources/angelia/struts/action添加ActionClassName.properties。而且 properties文件必须放在和Action class相同的包下。为了能使用properties文件里的key attribute, 这个要展示的页面必须是从刚执行的Struts Action class跳转来的。
创建UserAction.properties
username=用户名prop
修改jsp
<%-- <s:textfield name="username" label="用户名" /> --%>
<s:textfield key="username" />
修改struts.xml
<action name="loginInput" class="angelia.struts.action.UserAction" method="input">
<result name="input">/newlogin.jsp</result>
</action>
12 Package Level Properties和Global Properties
创建package.properties在src/main/resources/angelia/struts/action下,由此包下的所有Action跳转过去的页面都可使用package.properties里面的资源。
在文件src/main/resources
下创建myglobal.properties文件。
struts.xml文件添加:<constant name="struts.custom.i18n.resources" value="myglobal" />