蓝凌EKP产品:JSP性能优化及JSTL/EL要点核查指南
Spring Filter实现Redis功能与JSP内置对象作用域性能优化
在蓝凌的办公自动化系统里,其功能丰富的门户系统、流程审批、报表统计以及消息与公告模块十分突出。门户访问能够应对高并发状况,审批环节需要查询大量权限和节点信息,致使数据库查询压力较大,报表查询的数据量也颇为可观(例如考勤统计、流程统计、费用报表等)。用户可能会在短时间内频繁刷新报表页面。消息数量和公告列表属于“热门数据”,被频繁访问,但它们的更新频率并不高(比如公告每天更新数次)。这些场景都需要借助缓存redis来进行数据的存储与页面数据的临时存放,从而让传统的JSP办公自动化系统在性能和架构方面,向“高并发、分布式、用户体验优良”的现代平台靠拢。因此,蓝凌针对办公自动化系统的这些痛点,深入研究,不放过任何可以改进的细节,决定从以下两个方面解决缓存的性能问题。
一、Spring Filter对请求的拦截及Redis功能应用
1. 背景情况
在实际业务当中,我们可能有如下需求:
– 对请求进行限流
– 用户访问缓存
– 统计接口调用次数
这些功能通常借助 Redis 来实现。为了拦截所有请求并统一处理,可以使用 Filter 。
2. 存在的问题
普通的Filter由 Servlet容器进行管理(比如Tomcat),无法直接使用Spring容器管理的Bean(如RedisTemplate),若直接进行@Autowired操作会失败。
3. 解决办法
方案A:DelegatingFilterProxy(推荐使用)
Spring提供了DelegatingFilterProxy,其原理如下:
Tomcat FilterRegistry
|
v
DelegatingFilterProxy
|
v
Spring管理的业务Filter Bean(可依赖RedisTemplate)
- Tomcat负责调用入口
- Spring管理业务Filter,支持依赖注入
- 可安全使用RedisTemplate、Service等Spring Bean
示例:
public class KmssSessionRepositoryFilter extends SessionRepositoryFilter<MapSession> implements ContextLoadedListener {
//自行查找蓝凌实现。
}
在web.xml或Spring Boot配置中进行注册:
<!--
filter:springSessionRepositoryFilter
filter-name必须是这个,并且在 sys\authentication\spring.xml中有对应的bean
-->
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如此,Filter便可安全使用Redis功能,同时支持Spring注入。
二、JSP内置对象作用域产生性能问题的原因
在JSP页面中,我们常使用<c:set>设置变量:
<c:set var="canPrint" value="false" scope="page" />
1. JSP的作用域类型
JSP支持四种作用域:
| Scope | 生命周期 | 典型用途 |
|---|---|---|
| page | 单个页面请求 | 临时变量,用于当前页面渲染 |
| request | 单次HTTP请求 | 在JSP或Servlet间传递数据 |
| session | 用户会话级别 | 用户登录状态、会话数据 |
| application | 应用级别 | 全局共享数据 |
2. 性能问题原理
若临时页面变量使用 request、session或application :
– Request/Session范围的对象可能需序列化(尤其分布式环境)
– 大量临时变量占内存,增加GC压力
– Request范围对象过多,增加每次请求查找成本
而page作用域的变量:
– 仅在当前JSP页面有效
– 生命周期短,JSP渲染完即释放
– 无需序列化或跨请求传递
– 性能最优
3. 正确示例
<!-- 临时渲染变量,使用page作用域 --> <c:set var="canPrint" value="false" scope="page" />
错误示例(会增加性能负担):
<!-- 临时渲染变量却用session作用域 --> <c:set var="canPrint" value="false"
scope="session" />
三、Filter与JSP优化结合说明
实际项目中流程通常如下:
1. Filter拦截请求 → 使用 Redis缓存/统计请求
2. Controller返回数据 → 转发至JSP页面
3. JSP渲染页面 → 使用<c:set>或其他内置对象
优化要点:
– Filter负责业务逻辑(如Redis功能)
– JSP页面仅用page作用域存临时变量
– 避免将Filter逻辑产生的临时数据放入Session或Request,减少内存和序列化压力
原理图:
请求 —> Filter(DelegatingFilterProxy, 进行Redis操作)
|
v
Controller
|
v
JSP渲染(page作用域临时变量)
EKP正确指导实现
1. JSTL正确写法
推荐写法:
<c:set var="canPrint" value="false" scope="page" />
避免默认作用于session导致Redis写入。
2. 容器对象提前声明
<%
if (request.getAttribute("nodeAdditionalMap") == null) {
pageContext.setAttribute("nodeAdditionalMap", new HashMap());
}
%>
//防止xxx.abc触发全类扫描。
3 pageContext.removeAttribute显式指定scope
pageContext.removeAttribute("canPrint", PageContext.PAGE_SCOPE);
防止错误修改session。
四、总结EL容器对象提前声明
- Spring Filter + Redis
- 使用
DelegatingFilterProxy或SpringBeanUtils.getBean()获取Spring Bean - Filter由Tomcat管理,业务逻辑由Spring管理
- 支持Redis缓存、限流、统计等功能
- 使用
- JSP内置对象作用域优化
- 临时页面变量用
page作用域 - 避免用request/session/application存短期临时数据
- 提升性能,降低内存压力
- 临时页面变量用
