简介:Struts2.0是基于Struts1.x改进的Java Web MVC框架,本手册深入探讨其核心概念、配置及实践方法。内容包括Action和结果类型处理、拦截器的灵活性、OGNL表达式的使用、XML和注解配置选项、丰富的插件生态、全局异常处理、国际化支持以及单元测试友好的特点。开发人员通过阅读,可深入了解Struts2.0的高级特性并掌握实际项目开发的最佳实践。
1. Struts2.0 MVC框架概述
Struts2.0是一个经典的Java Web应用框架,它基于MVC(Model-View-Controller)设计模式,是企业级Java应用开发的重要组成部分。Struts2.0在继承了Struts1.x的核心优势同时,也引入了WebWork框架的特性,从而成为一个更加灵活、功能强大的框架。
1.1 Struts2.0框架的起源与发展
1.1.1 从Struts1.x到Struts2.0的演变
Struts2.0是Struts1.x的后继者。在1.x版本中,开发者遇到了诸多限制,如对Action的耦合度高、难以维护和扩展。Struts2.0应运而生,采纳了WebWork的拦截器和值栈等概念,显著提升了框架的性能、灵活性和可维护性。
1.1.2 Struts2.0的核心特点与优势
Struts2.0的显著特点是其轻量级的拦截器机制。拦截器提供了一个简便的方式来进行请求处理的预处理和后处理。Struts2.0还拥有强大的数据类型转换、丰富的结果类型、易用的验证框架和与Ajax的无缝整合等优势。
在学习和使用Struts2.0时,开发者会发现它对资源文件的良好组织、对主题和模板的内建支持以及对国际化(i18n)和本地化(l10n)的全面支持,这都极大地促进了国际化Web应用的开发。
2. Action与结果类型的工作机制
2.1 Action的概念与生命周期
2.1.1 Action接口的作用与实现方式
在Struts2框架中,Action是控制器(Controller)的核心组件,它负责接收用户的输入并将其转化为业务逻辑的执行。Action接口是所有Action类的父接口,它定义了一个执行方法 execute()
。当用户提交表单或者请求一个操作时,Struts2框架会创建Action实例,并调用这个 execute()
方法。开发者通常不直接实现Action接口,而是继承一个类,如 ActionSupport
,该类已经提供了 execute()
方法的默认实现以及许多辅助方法和属性。
public class MyAction extends ActionSupport {
private String name;
private int age;
@Override
public String execute() {
// 实现业务逻辑
return SUCCESS;
}
}
2.1.2 Action生命周期各阶段详解
Action的生命周期包括实例化、初始化、执行 execute()
方法和销毁四个阶段:
- 实例化 :Struts2容器使用无参构造器创建Action的实例。
- 初始化 :如果Action有
@PostConstruct
注解的方法或者实现了Initializable
接口,则此阶段会调用它们。 - 执行 :Struts2容器调用Action的
execute()
方法。此方法返回一个字符串,通常是“success”、“error”或者指定的其他结果名称。 - 销毁 :在Web应用关闭或者服务器停止时,如果Action实现了
DisposableBean
接口或者有@PreDestroy
注解的方法,它们将被调用。
2.2 结果类型的选择与配置
2.2.1 常见结果类型介绍:dispatcher、redirect等
在Struts2中,结果类型定义了当Action执行完成后视图(View)如何被呈现。最常见的结果类型包括:
- dispatcher :这是默认的结果类型,用于将请求转发到视图组件,如JSP页面。
- redirect :用于重定向用户到另一个URL,这类似于Servlet中的
HttpServletResponse.sendRedirect
方法。 - chain :用于继续执行另一个Action的操作。
- redirectAction :此结果类型允许重定向到另一个Action,同时可以传递参数。
配置结果类型时,可以在Action类的 execute()
方法中返回结果名称,然后在struts.xml中定义对应的结果名称和类型。
2.2.2 结果类型配置的最佳实践
配置结果类型时,一个最佳实践是使用 <result>
元素来指定视图名称和结果类型,如:
<action name="myAction" class="com.example.MyAction">
<result name="success">/pages/success.jsp</result>
<result name="error" type="redirectAction">
<param name="actionName">login</param>
<param name="executionContext">ERROR</param>
</result>
</action>
在这个例子中,成功执行 execute()
方法将结果转发到 success.jsp
页面,而在错误情况下则重定向到 login
Action。
2.3 实现Action与视图的交互
2.3.1 Action与JSP页面的数据传递
Struts2提供了多种方式在Action和JSP页面之间传递数据。最简单的方式是将Action对象的属性设置为私有,并提供公共的getter和setter方法。在 execute()
方法中,可以通过这些方法设置数据,然后在JSP页面中通过EL表达式访问它们。
public class MyAction extends ActionSupport {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String execute() {
setMessage("Hello World!");
return SUCCESS;
}
}
在JSP页面中,可以通过 ${message}
访问这个属性。
2.3.2 数据校验和验证框架集成
为了保证数据质量,Struts2提供了内建的数据校验机制。开发者可以通过在Action类中定义校验规则来实现这一功能。此外,Struts2还集成了Jakarta Commons Validator来支持更复杂的校验需求。
public class MyAction extends ActionSupport {
private String username;
private String 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 validate() {
if (username == null || username.isEmpty()) {
addFieldError("username", "Username cannot be empty!");
}
if (password == null || password.isEmpty()) {
addFieldError("password", "Password cannot be empty!");
}
return super.validate();
}
@Override
public String execute() {
return SUCCESS;
}
}
在这个例子中,如果用户名或密码为空,相应的错误信息将被捕获,并且用户将被带回到输入页面。
这样,我们就完成了第二章的详细内容,关于Action与结果类型的工作机制,涵盖了Action的基本概念、结果类型的配置以及与视图的交互等多个方面。在下一章中,我们将探索Struts2框架中的拦截器,并了解如何自定义和优化拦截器行为。
3. 拦截器的配置与自定义
拦截器是Struts2.0框架中重要的组成部分,它是框架提供的一种强大机制,用于在请求处理流程中的不同点插入用户自定义的行为。拦截器可以在请求被处理之前进行预处理操作,或在处理之后进行清理和附加操作,从而实现代码的复用和职责的分离。
3.1 拦截器的工作原理与应用场景
3.1.1 拦截器与过滤器的区别
拦截器和过滤器都是用于控制HTTP请求和响应的组件,但是它们在Web应用架构中的位置和作用有所不同。过滤器属于Servlet API的一部分,它拦截请求和响应在进入或离开Servlet之前,能够对几乎所有的资源进行过滤,包括静态资源如图片、HTML页面和JavaScript文件等。
而拦截器是Struts2.0框架特有的,它只对框架内部的请求处理过程起作用。拦截器工作在Action执行前后,可以访问到值栈中的对象,与框架的其他部分如Action、Result等紧密集成。
3.1.2 拦截器在Struts2.0中的作用
拦截器在Struts2.0中扮演了非常关键的角色,主要有以下几点作用:
- 日志记录 :可以记录用户请求的详细信息,帮助追踪和分析问题。
- 验证 :可以在Action执行之前对用户输入的数据进行验证。
- 权限检查 :检查用户是否有权访问请求的资源。
- 数据封装 :封装请求数据到值栈中,便于后续处理。
- 性能监控 :在请求处理前后记录时间,用于性能监控和分析。
3.2 配置Struts2.0的内建拦截器
3.2.1 常见内建拦截器的功能与配置
Struts2.0提供了大量内建的拦截器,每个拦截器都有其特定的功能和默认配置。以下是一些常用的内建拦截器:
- PrepareInterceptor :在Action执行之前,为Action准备数据。
- ParamsInterceptor :读取请求参数,并根据Action的属性名映射到值栈。
- ValidationInterceptor :执行验证逻辑,如果验证失败,将返回到表单页面。
- ConversionErrorInterceptor :处理类型转换错误。
- ProfilingInterceptor :提供性能监控数据。
要在Struts2.0配置文件中启用这些拦截器,你需要将它们添加到默认的拦截器栈中,如下所示:
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<!-- 自定义拦截器 -->
<interceptor-ref name="myCustomInterceptor" />
</interceptor-stack>
</interceptors>
<action name="example" class="com.example.ExampleAction">
<interceptor-ref name="myStack" />
<!-- 其他配置 -->
</action>
3.2.2 拦截器栈的创建与应用
拦截器栈是拦截器的集合,它允许你将多个拦截器按顺序组织起来,形成一个拦截链。在处理请求时,拦截器栈中的拦截器会依次执行。
创建拦截器栈的步骤如下:
- 定义拦截器栈。
- 在栈中添加拦截器。
- 指定栈应用到特定的Action或全局配置。
例如,创建一个名为 myStack
的拦截器栈,并将 PrepareInterceptor
和 ValidationInterceptor
加入其中:
<interceptors>
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="validation"/>
</interceptor-stack>
</interceptors>
然后,可以在Action中引用这个栈:
<action name="example" class="com.example.ExampleAction">
<interceptor-ref name="myStack"/>
<!-- 其他配置 -->
</action>
3.3 自定义拦截器的开发与应用
3.3.1 自定义拦截器的基本步骤
开发自定义拦截器需要遵循以下步骤:
- 创建拦截器类,继承
AbstractInterceptor
或MethodFilterInterceptor
。 - 实现
intercept
方法或覆盖doIntercept
方法。 - 在方法中编写自定义逻辑。
- 在
struts.xml
中配置拦截器。 - 将拦截器加入到拦截器栈中。
以下是创建一个简单的自定义拦截器的代码示例:
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MyCustomInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
// 在请求处理之前执行的代码
System.out.println("Before Action");
// 执行下一个拦截器或Action
String result = invocation.invoke();
// 在请求处理之后执行的代码
System.out.println("After Action");
return result;
}
}
配置这个拦截器需要在 struts.xml
中进行:
<interceptors>
<interceptor name="myCustomInterceptor" class="com.example.MyCustomInterceptor"/>
<!-- 其他配置 -->
</interceptors>
3.3.2 拦截器链的构造与优化策略
构造拦截器链时,需要考虑拦截器的执行顺序对整体处理流程的影响。拦截器通常按照配置文件中声明的顺序执行,但在使用过滤器时,顺序可能不同。为了优化性能,应该尽量减少不必要的拦截器调用,只在需要的时候调用特定的拦截器。
可以通过以下策略进行优化:
- 过滤不必要的请求 :使用过滤器或拦截器预检请求,减少对非目标请求的处理。
- 异步处理 :对于执行时间较长的操作,可以考虑使用异步方式。
- 缓存结果 :如果拦截器的某些结果可以预知,那么可以考虑缓存这些结果以避免重复计算。
- 日志优化 :合理安排日志记录的级别和内容,避免不必要的I/O操作。
自定义拦截器赋予了开发者强大的灵活性来控制请求的处理逻辑,使得Struts2.0的应用程序更加模块化和易于维护。通过上述方法,开发者可以根据实际需求进行高效的拦截器开发和配置,从而优化应用程序的性能和用户体验。
4. OGNL表达式语言的使用
4.1 OGNL表达式语言基础
4.1.1 OGNL的语法特点与规则
OGNL,即 Object-Graph Navigation Language,是一种功能强大的表达式语言,它允许在Java对象的图中进行导航、数据访问和数据操作。在Struts2.0框架中,OGNL用于处理请求参数、访问动作类的属性以及执行动态方法调用等。
OGNL 表达式通常包含以下几种类型的元素: - 属性访问 :使用点号( .
)来访问对象的属性。例如, user.name
可以访问 user
对象的 name
属性。 - 方法调用 :使用圆括号( ()
)来调用对象的方法。例如, user.getName()
可以调用 user
对象的 getName
方法。 - 数组和集合索引 :使用方括号( []
)来访问数组或集合中的元素。例如, users[0]
可以访问 users
集合的第一个元素。 - 逻辑运算符 :如 &&
(逻辑与)、 ||
(逻辑或)、 !
(逻辑非)等。 - 数学运算符 :如 +
(加)、 -
(减)、 *
(乘)、 /
(除)等。
4.1.2 OGNL在Struts2.0中的应用范围
在Struts2.0中,OGNL不仅可以用于在动作类和视图之间传递数据,还用于动态表达式计算、类型转换和动作链的定义。
举例来说,当一个动作类返回一个字符串结果,可以通过OGNL表达式直接在页面上显示:
<result name="success" type="dispatcher">/success.jsp?message=${message}</result>
此例中 ${message}
就是一个OGNL表达式,用于在视图中显示动作类中 message
属性的值。
OGNL还被用来配置动态方法调用,支持通过一个字符串参数来调用动作类中对应的方法,如下所示:
<action name="userAction" class="com.example.UserAction" method="execute">
<result name="success">/user_success.jsp</result>
<result name="error">/user_error.jsp</result>
</action>
在上述配置中, method
属性可以是OGNL表达式,使得方法调用动态化。
4.2 OGNL的高级功能与技巧
4.2.1 集合和Map的操作
OGNL提供了强大的集合操作功能。可以对集合进行过滤、映射和排序等操作。例如,可以使用OGNL表达式筛选出用户列表中的女性用户:
userList.{? #this.gender == 'female'}
在这个表达式中, userList
是用户的集合, #this.gender == 'female'
是过滤条件,表示筛选出性别为女性的用户。
同样,可以对用户列表进行排序:
userList.{#this.name}
此表达式将根据用户的 name
属性进行自然排序。
4.2.2 类型转换和动态方法调用
OGNL允许对对象的类型进行转换。例如,可以将字符串转换为整数:
${"123".parseInt()}
在实际应用中,这样的类型转换经常用于处理表单提交的数据。
OGNL也支持动态方法调用,允许在动作类中根据字符串参数调用不同的方法:
${actionInstance.doSomeAction("arg1", "arg2")}
这里的 doSomeAction
方法可以是动态传入的,通过OGNL解析这个方法名字符串并调用实际的方法。
4.3 OGNL在安全和性能上的考量
4.3.1 OGNL的安全隐患及防范措施
OGNL本身是非常强大的,但在使用时也要注意安全问题。OGNL表达式可以访问和修改任何对象的属性,因此如果用户输入未经严格验证,就有可能造成安全漏洞。
为防范这种情况,可以采取如下措施: - 限制OGNL访问 :通过配置安全相关的拦截器,例如 struts2ognlProvideValueStackAccess
,限制OGNL对值栈的访问。 - 使用安全拦截器 :使用如 struts2-prevent-ognl
等拦截器,阻止潜在的OGNL注入攻击。 - 输入验证 :在应用层面进行严格的输入验证,确保用户输入的内容不包含恶意代码。
4.3.2 性能调优技巧和最佳实践
由于OGNL在解析和执行表达式时会消耗资源,因此在性能方面也需要注意以下调优技巧: - 使用静态方法调用 :静态方法不需要创建对象实例,执行更快,应尽可能使用。 - 避免复杂表达式 :复杂表达式会消耗更多资源,在可能的情况下进行简化。 - 缓存结果 :对于重复使用的复杂表达式,可以考虑将其结果缓存起来,以减少重复计算。
下面是一个具体的代码块示例,展示了如何利用OGNL在Struts2.0中设置值栈的属性,并在视图中访问该属性。
// 在Action中设置值栈属性
ActionContext.getContext().setValue("message", "Hello, OGNL!");
<!-- 在JSP页面中访问OGNL表达式 -->
<p>Message from OGNL: ${message}</p>
在这个例子中,我们通过ActionContext将一个消息设置到值栈中,并在JSP页面中通过OGNL表达式访问这个值。需要注意的是,这个表达式在执行前会通过OGNL解析器进行解析,它会从值栈中检索 message
属性的值并显示出来。
总结来说,OGNL在Struts2.0框架中扮演了非常重要的角色,它的强大功能和灵活性使得数据处理和流程控制变得非常方便。但与此同时,正确且安全地使用OGNL也是每个开发者需要注意的问题。通过遵循最佳实践和采取必要的防范措施,我们可以最大化OGNL的效用,同时最小化安全风险。
5. 实际项目开发中的最佳实践
5.1 代码组织与项目结构优化
在实际开发中,良好的代码组织和项目结构能够提升开发效率,便于团队协作,同时也是系统维护和扩展的关键。以下是针对Struts2.0项目进行代码组织与结构优化的几个建议:
5.1.1 模块化开发和包结构设计
模块化开发是将大型项目拆分为更小、更易于管理的部分的过程。在Struts2.0中,我们通常根据功能和职责来划分模块。比如,我们可以创建如下包结构:
-
com.example.action
:存放所有的Action类。 -
com.example.dao
:存放数据访问对象(DAO)。 -
com.example.util
:存放工具类和通用模块。 -
com.example.model
:存放业务对象(Java Beans)。
在每个包内,我们还可以进一步细分,比如根据功能模块划分子包,如 com.example.action.user
和 com.example.action.product
。这样的结构清晰,有助于代码的维护和理解。
5.1.2 多环境配置与管理
在开发过程中,我们通常需要区分不同的环境,如开发环境、测试环境和生产环境。为了更好地管理不同环境下的配置,我们可以采用如下策略:
- 在
src/main/resources
目录下创建环境相关的配置文件,如development.properties
、test.properties
和production.properties
。 - 利用Maven或Gradle的profiles功能,通过构建脚本切换不同环境下的配置文件。
例如,使用Maven时,可以在 pom.xml
中配置不同的profiles,如下所示:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>development</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>production</env>
</properties>
</profile>
</profiles>
5.2 与现代技术栈的整合策略
Struts2.0是一个成熟的MVC框架,但在现代的项目中,可能需要与其他技术栈整合,以充分利用现有技术的优势。下面介绍如何将Struts2.0与Spring、Hibernate进行整合以及如何集成响应式编程和Web服务。
5.2.1 整合Spring和Hibernate
整合Spring框架可以为Struts2.0提供依赖注入、事务管理等功能,而整合Hibernate则可以提供一个轻量级的ORM解决方案。
整合步骤大致如下:
- 添加Spring和Hibernate的依赖到项目中。
- 创建Spring的配置文件,配置数据源、session工厂以及事务管理器。
- 在Struts的配置文件中引用Spring的配置文件。
- 利用Spring的依赖注入功能,在Action中注入所需的DAO类。
这里是一个简化的整合示例:
<!-- Spring配置文件(applicationContext.xml) -->
<beans>
<!-- 数据源配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 数据库连接参数 -->
</bean>
<!-- Session工厂配置 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- Hibernate属性配置 -->
</bean>
<!-- 事务管理器配置 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- Action中注入DAO -->
<bean id="userAction" class="com.example.action.UserAction">
<property name="userService" ref="userService"/>
</bean>
<bean id="userService" class="com.example.service.UserService">
<!-- 注入DAO等 -->
</bean>
</beans>
5.2.2 响应式编程与Web服务集成
响应式编程是近年来非常热门的一个概念,能够帮助开发者创建异步的、非阻塞的,并且基于事件的系统。与Web服务集成则能够使得我们的应用能够被外部应用调用。
集成策略可以包括:
- 使用如Spring WebFlux这样的响应式编程框架来处理Web层请求。
- 利用RESTful API或SOAP Web服务来暴露内部数据和功能。
整合示例代码可能如下:
// Spring WebFlux控制器
@RestController
public class ReactiveController {
@GetMapping("/users")
public Flux<User> getAllUsers() {
// 返回用户列表的响应式流
return userService.getAllUsers();
}
}
5.3 性能优化与维护策略
性能优化和维护是项目生命周期中不可或缺的环节,它们能够确保应用的高性能、高可用性和长期稳定性。
5.3.1 常见性能瓶颈分析与优化
性能瓶颈分析通常涉及以下几个方面:
- 数据库查询优化:索引优化、查询语句优化。
- 会话管理优化:会话持久化、分布式会话。
- 并发处理优化:使用线程池、异步处理。
性能优化方法例如:
- 对于数据库查询,可以使用专业的分析工具来发现慢查询,并进行优化。
- 通过配置Struts2.0的
struts.concurrency-Control
参数来限制并发访问。 - 使用缓存机制,如使用EHCache或Redis来减少数据库访问。
5.3.2 代码重构与系统升级的最佳实践
随着技术的发展和业务的变化,系统升级和代码重构是不可避免的。以下是一些最佳实践:
- 保持代码的高内聚低耦合,便于模块替换和扩展。
- 定期进行代码审查,避免技术债务累积。
- 使用自动化测试和持续集成工具来保证重构不会引入新问题。
举个简单的代码重构例子:
// 糟糕的代码,函数职责不清晰
public String getUserList() {
// 业务逻辑过于复杂
// 这里可能涉及到获取用户数据、格式化输出等多个职责
}
// 重构后的代码,函数职责单一
public List<User> getUsers() {
// 简单地获取用户数据,其它逻辑交给其它方法处理
}
public String getUserList() {
List<User> users = getUsers();
// 对users进行格式化并返回
}
以上就是对实际项目开发中,代码组织、模块化、技术栈整合以及性能优化与维护策略的一些建议和最佳实践。通过这些方法,可以确保在不断变化的业务和技术环境中保持应用的稳定和高效。
简介:Struts2.0是基于Struts1.x改进的Java Web MVC框架,本手册深入探讨其核心概念、配置及实践方法。内容包括Action和结果类型处理、拦截器的灵活性、OGNL表达式的使用、XML和注解配置选项、丰富的插件生态、全局异常处理、国际化支持以及单元测试友好的特点。开发人员通过阅读,可深入了解Struts2.0的高级特性并掌握实际项目开发的最佳实践。