Java 资源管理与优化策略
1. 让外观模式更通用
传统的外观模式虽然能将缓存代码隔离在一处,但在向应用程序添加更多边界类时不够灵活。为了解决这个问题,我们利用 Java 的反射 API 创建了一个更通用的边界外观模式。
首先,要确保所有边界类都继承自同一个基类。这里我们有一个
BoundaryBase
类,它封装了数据库连接池与边界子类的关联。以下是
BoundaryBase
类的代码:
package com.nealford.art.facade.emotherearth.boundary;
import com.nealford.art.facade.emotherearth.util.DBPool;
public class BoundaryBase {
private DBPool dBPool;
public BoundaryBase() {
}
public DBPool getDBPool() {
return dBPool;
}
public void setDBPool(DBPool dBPool) {
this.dBPool = dBPool;
}
}
知道所有边界类都是基类的特殊版本后,我们可以在外观模式中编写一个方法,以基类的形式返回边界对象。这样,外观模式就成为了一个根据特定实体返回边界对象的工厂。以下是通用的
borrowBoundary()
方法:
public BoundaryBase borrowBoundary(HttpSession session,
Class boundaryClass) {
ServletContext sc = session.getServletContext();
boolean cacheInBoundaryInSession =
Boolean.valueOf(sc.getInitParameter(
CACHE_BOUNDARY_IN_SESSION)).
booleanValue();
BoundaryBase boundary = null;
if (cacheInBoundaryInSession)
boundary = (BoundaryBase) session.getAttribute(
boundaryClass.getName());
if (boundary == null) {
try {
GenericKeyedObjectPool boundaryPool =
(GenericKeyedObjectPool) sc.getAttribute(
BOUNDARY_POOL);
boundary = (BoundaryBase) boundaryPool.
borrowObject(boundaryClass);
if (cacheInBoundaryInSession &&
boundary instanceof Cacheable)
session.setAttribute(boundaryClass.getName(),
boundary);
} catch (Exception x) {
session.getServletContext().log("Pool error", x);
}
boundary.setDBPool((DBPool) sc.getAttribute(DB_POOL));
borrowedObjects.add(boundary);
}
return boundary;
}
borrowBoundary()
方法利用 Java 的反射 API,将一个类作为参数传入。例如,从目录控制器调用该方法的示例如下:
BoundaryFacade facade = BoundaryFacade.getInstance();
ProductDb products = (ProductDb) facade.borrowBoundary(session,
ProductDb.class);
该方法首先从 Servlet 上下文中收集状态信息,以确定如何处理边界缓存。如果启用了缓存,它会先尝试从用户会话中检索边界对象。如果会话中没有,则从对象池中获取。
2. 缓存策略
在使用对象池和用户会话进行对象缓存时需要谨慎。例如,当对象池中有 60 个对象,且启用了会话缓存,有 60 个并发用户时,每个用户会从池中取出一个对象并存储在会话缓存中,导致对象池为空。解决这个问题的方法有:
- 关闭会话缓存:这会稍微减慢每个用户的交互速度,但可以降低整个应用程序的内存需求,支持更多并发用户。
- 创建自动增长的对象池:确保对象池永远不会耗尽边界对象。
3. Cacheable 接口
borrowBoundary()
方法中还使用了一个名为
Cacheable
的接口。这是一个标记接口,用于标记哪些边界类需要在应用程序中进行缓存。以下是
Cacheable
接口的代码:
package com.nealford.art.facade.emotherearth.util;
public interface Cacheable {
}
要指定一个类需要缓存,只需让该类实现这个接口,例如:
public class ProductDb extends BoundaryBase implements Cacheable {
borrowBoundary()
方法会同时检查 Servlet 上下文的部署标志和
Cacheable
接口的存在,以确定从池中借用的边界对象是否应放入会话中。
4. 通用返回边界对象
我们还修改了
returnBoundaries()
方法,以适应通用的边界外观模式。以下是新的
returnBoundaries()
方法:
public void returnBoundaries(HttpSession session,
boolean preserveCachedBoundaries) {
GenericKeyedObjectPool boundaryPool =
(GenericKeyedObjectPool) session.getServletContext().
getAttribute(BOUNDARY_POOL);
boolean cacheInBoundaryInSession =
Boolean.valueOf(session.getServletContext().
getInitParameter(CACHE_BOUNDARY_IN_SESSION)).
booleanValue();
Iterator borrowedObject = borrowedObjects.iterator();
while (borrowedObject.hasNext()) {
Object o = borrowedObject.next();
if (o instanceof BoundaryBase)
if (cacheInBoundaryInSession &&
preserveCachedBoundaries &&
o instanceof Cacheable)
break;
else {
try {
boundaryPool.returnObject(o.getClass(), o);
} catch (Exception x) {
session.getServletContext().log(
"Pool return exception: " +
x.getMessage());
} finally {
borrowedObject.remove();
}
}
}
}
该方法接收用户会话和一个布尔标志,用于指示是否保留缓存的边界对象。它会根据缓存设置、调用者是否希望保留缓存以及对象的可缓存性,有条件地将会话中的对象返回给对象池。
5. 框架中的资源管理
本章介绍的两种设计模式与 Model 2 框架配合良好。这些策略主要适用于边界类和实体类,框架则负责基础设施。不过,Tapestry 框架可能无法从这些模式中受益,因为该框架本身已经内置了许多对象池和缓存功能。
以下是不同框架与设计模式的适用性表格:
| 框架 | 是否适合设计模式 | 原因 |
| ---- | ---- | ---- |
| Model 2 框架 | 是 | 策略适用于边界和实体类,框架负责基础设施 |
| Tapestry | 否 | 框架本身内置了对象池和缓存功能 |
| InternetBeans Express | 否 | 虽初始构建快,但限制多,不够灵活 |
6. 资源管理流程
graph TD;
A[开始] --> B[检查会话中是否有缓存的边界对象];
B --> C{是否找到};
C -- 是 --> D[使用会话中的对象];
C -- 否 --> E[从对象池借用对象];
E --> F{对象是否可缓存};
F -- 是 --> G[将对象存入会话];
F -- 否 --> H[不存入会话];
D --> I[使用对象];
G --> I;
H --> I;
I --> J[完成使用];
J --> K{是否保留缓存};
K -- 是 --> L[不返回对象到池];
K -- 否 --> M[返回对象到池];
L --> N[结束];
M --> N;
7. 其他需要管理的资源
除了内存资源,我们还需要管理其他外部资源(如应用服务器的 JNDI 查找上下文)和内部资源(如各种集合)。
7.1 有效使用 JNDI
如果使用 JNDI 处理数据库连接池或 Enterprise JavaBeans,需要管理为用户维护的连接。JNDI 与应用服务器的连接类似于客户端/服务器应用程序与数据库的连接,建立连接都需要较长时间。
通常的策略是在用户登录时为其建立 JNDI 连接,并将上下文存储在用户会话中。以下是一个示例代码:
package com.nealford.art.ejbsched.controller;
import java.io.IOException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.nealford.art.ejbsched.model.ScheduleBean;
public class ViewSchedule extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws
ServletException, IOException {
Context c = establishContext(request);
forwardToView(request, response, populateModel(c));
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws
ServletException, IOException {
doGet(request, response);
}
private void forwardToView(HttpServletRequest request,
HttpServletResponse response,
ScheduleBean scheduleBean) throws
ServletException, IOException {
request.setAttribute("scheduleBean", scheduleBean);
RequestDispatcher rd = request.getRequestDispatcher(
"/ScheduleView.jsp");
rd.forward(request, response);
}
private ScheduleBean populateModel(Context c) {
ScheduleBean scheduleBean = new ScheduleBean();
scheduleBean.setContext(c);
try {
scheduleBean.populate();
} catch (Exception x) {
getServletContext().log(
"Error: ScheduleBean.populate()");
}
return scheduleBean;
}
private Context establishContext(HttpServletRequest request) {
HttpSession session = request.getSession(true);
Context c = (Context) session.getAttribute("context");
if (c == null) {
c = getInitialContext();
session.setAttribute("context", getInitialContext());
}
return c;
}
private Context getInitialContext() {
Context c = null;
try {
c = new InitialContext();
} catch (NamingException ex) {
ex.printStackTrace();
}
return c;
}
}
establishContext()
方法会检查会话中是否已经存在上下文,如果不存在则创建并存储在会话中。
7.2 使用懒加载
懒加载是一种资源分配策略,它允许我们在真正需要资源时才创建它们。这种方法适用于那些可能不需要的重量级资源,如管理模块。虽然它可以节省资源,但在需要资源时会增加响应时间。
7.3 处理 Web 集合
Java 开发者应该根据任务的需求选择最合适的属性集合,避免过度依赖会话集合。通常,应该选择作用域最小的集合来完成任务,以避免资源浪费。
8. 会话管理员的职责
在资源密集型的 Web 应用程序开发团队中,设立“会话管理员”是个不错的主意。会话管理员的职责包括:
- 确保使用正确的集合。
- 尽快清理集合属性。
- 防止开发者在内存中放置会影响应用程序可扩展性的大型数据结构。
- 要求开发者为将对象放入集合提供合理的理由。
当开发者在集合使用上出现冲突时,会话管理员应及时向项目技术负责人发出警告。
9. 总结
Java Web API 为资源管理提供了丰富的机会。缓存是一种有效的资源管理方式,Flyweight 和 Façade 设计模式为创建缓存提供了很好的选择。同时,我们还需要管理其他资源,如 JNDI 连接和标准集合。合理使用这些资源可以提高应用程序的性能和可扩展性。设立“会话管理员”可以帮助复杂应用程序优雅地管理资源,及时发现问题。
在后续的开发中,我们还需要关注调试和日志记录等方面,以确保应用程序的稳定性和可维护性。
Java 资源管理与优化策略
10. 资源管理的注意事项
在进行资源管理时,有一些关键的注意事项需要牢记,这些要点对于确保应用程序的性能和稳定性至关重要。
- 缓存一致性 :当使用缓存时,要确保缓存中的数据与实际数据保持一致。例如,当数据库中的数据发生更新时,需要及时更新或清除相关的缓存,避免出现数据不一致的问题。
- 资源释放 :对于使用的各种资源,如数据库连接、JNDI 上下文等,要确保在使用完毕后及时释放。否则,可能会导致资源泄漏,影响应用程序的性能和稳定性。
- 并发控制 :在多用户并发访问的情况下,要注意对资源的并发控制。例如,在使用对象池时,要确保多个用户不会同时访问和修改同一个对象,避免出现数据竞争和错误。
11. 不同资源管理策略的对比
为了更好地理解各种资源管理策略的优缺点,我们可以通过以下表格进行对比:
| 策略 | 优点 | 缺点 | 适用场景 |
| ---- | ---- | ---- | ---- |
| 缓存策略 | 提高响应速度,减少资源重复创建 | 可能导致内存占用过高,需要处理缓存一致性问题 | 频繁访问的数据或资源 |
| 懒加载策略 | 节省资源,只在需要时创建资源 | 增加资源使用时的响应时间 | 可能不需要的重量级资源 |
| 预创建和池化策略 | 提高性能,减少资源创建时间 | 需要预先分配一定的资源,可能造成资源浪费 | 经常使用的资源,如数据库连接 |
12. 资源管理的最佳实践
结合前面介绍的各种资源管理策略和注意事项,我们可以总结出以下资源管理的最佳实践:
1.
合理使用缓存
:根据数据的访问频率和更新频率,选择合适的缓存策略。对于频繁访问且不经常更新的数据,可以使用缓存来提高性能。
2.
及时释放资源
:在使用完资源后,要确保及时释放,避免资源泄漏。可以使用
try-with-resources
语句来自动管理资源的释放。
3.
优化集合使用
:选择合适的集合来存储数据,避免使用过大的集合或不必要的集合。同时,要及时清理集合中的无用数据。
4.
使用设计模式
:利用 Flyweight 和 Façade 等设计模式来优化资源管理,提高代码的可维护性和可扩展性。
5.
监控和调整
:定期监控应用程序的资源使用情况,根据实际情况调整资源管理策略。例如,如果发现内存占用过高,可以考虑减少缓存的使用或增加内存分配。
13. 资源管理的流程图
graph TD;
A[开始资源管理] --> B{是否需要缓存};
B -- 是 --> C[选择合适的缓存策略];
B -- 否 --> D{是否使用懒加载};
C --> E[设置缓存参数];
D -- 是 --> F[实现懒加载逻辑];
D -- 否 --> G[使用预创建和池化策略];
E --> H[使用资源];
F --> H;
G --> H;
H --> I{资源使用完毕};
I -- 是 --> J[释放资源];
I -- 否 --> H;
J --> K{是否需要调整策略};
K -- 是 --> L[调整资源管理策略];
K -- 否 --> M[结束资源管理];
L --> H;
14. 总结
Java 资源管理是一个复杂而重要的任务,涉及到内存、外部资源(如 JNDI)和内部资源(如集合)等多个方面。通过合理使用缓存、懒加载、预创建和池化等策略,以及遵循资源管理的最佳实践,可以有效地提高应用程序的性能和可扩展性。
同时,设立“会话管理员”可以帮助团队更好地管理资源,避免资源浪费和冲突。在实际开发中,要根据应用程序的具体需求和特点,选择合适的资源管理策略,并不断监控和调整,以确保应用程序的稳定性和高效性。
在未来的开发中,随着技术的不断发展和应用场景的不断变化,资源管理也需要不断地优化和改进。我们需要持续关注新的技术和方法,不断提升自己的资源管理能力,以应对日益复杂的应用需求。
超级会员免费看

被折叠的 条评论
为什么被折叠?



