详解JSP 2.0下的动态内容缓存

本文介绍了一种利用JSP2.0表达式语言优化Web应用缓存的技术。通过<jc:cache>和<jc:dynamic>标签,实现了高效的内容缓存,即使在动态内容变化时也能保持良好的性能。
在Web应用中,内容缓存是最普通的优化技术之一,并且能够很容易地实现。例如,可以使用一个自定义地JSP标签——我们将之命名为<jc: cache>——由<jc:cache>和</jc:cache>将每一个需要被缓存的页面片段封装起来。任何自定义标签 可以控制它所包含部分 (也即预先封装的页面片段)在何时执行,并且动态输出结果可以被捕获。<jc:cache>标签使得JSP容器(例如Tomcat)只生成内 容一次,作为应用程序范围内的JSP变量,来存储每一个缓存片段。每次JSP页面被执行时,自定义标签将缓存页面片段载入而无需再次执行JSP代码来生成 输出结果。作为Jakarta工程的一个部分,标签库的开发使用了这项技术。当被缓存内容无需被每一个用户或者请求所定制的时候,它工作的十分良好。
  
   这篇文章对上面描述的技术做了改进,通过使用JSP 2.0表达式语言(EL),允许JSP页面为每一个请求和用户定制缓存内容。缓存页面片段可以包含未被JSP容器赋值的JSP表达式,在每一次页面被执行 时,由自定义标签来确定这些表达式的值。因此,动态内容的建立被最优化,但是缓存片段可以含有部分由每一个请求使用本机JSP表达式语言产生的内容。通过 JSP 2.0 EL API的帮助,Java开发者可以用表达式语言来使之成为可能。
  
   内容缓存VS数据缓存
  
   内容缓存不是唯一的选择。例如, 从数据库中提取的数据同样可以被缓存。事实上,由于存储的信息中不包含HTML markup,以及要求较少的内存,数据缓存可能更加高效率。然而在很多情况下,内存缓存更容易实现。假设在某个案例总,一个应用由大量事务对象,占用重 要的CPU资源,产生复杂的数据,并且用JSP页面来呈现这些数据。工作一切良好,直到某天突然地服务器的负载增加,需要一个紧急解决方案。这时在事务对 象和呈现表达层之间建立一个缓存层,时一个非常不错和有效的方案。但是必须非常快速和流畅地修改缓存动态内容的JSP页面。相对于简单的JSP页面编辑, 应用程序的业务逻辑变化通常要求更多的工作量和测试;另外,如果一个页面从多个复合源聚合信息时,Web层仅有少量的改变。问题在于,当缓存信息变得失去 时效时,缓存空间需要被释放,而事务对象应该知道何时发生这种情况。然而,选择实现内容缓存还是数据缓存,或者其他的优化技术,有很多不得不考虑的因素, 有时是所开发的程序所特殊要求的。
  
   数据缓存和内容缓存没有必要互相排斥,它们可以一起使用。例如,在数据库驱动的应用中;从数据库中提取出来的数据,和呈现该数据的HTML分别被缓存起 来。这与使用JSP实时生成的模板有些相似。这篇文章中讨论的基于EL API技术说明如何使用JSP EL来将数据载入到呈现模板中。
  
   使用JSP变量缓存动态内容
  
   每当实现一个缓存机制是,都需要一个存储缓存对象的方法,在这篇文章中涉及的是String类型的对象。 一种选择是使用一个对象——缓存框架结构,或者使用Java maps来实现自定义的缓存方案。JSP已经拥有了称为“scoped attributes”或“JSP variables”来提供ID——object映射,这正是缓存机制所需要的。对于使用page或者request scope,这是没有意义的,而在应用范围内,这是一个很好的存储缓存内容的位置, 因为它被所有的用户和页面共享。当每一个用户需要单独缓存时,Session scope也可以被使用,但这不是很有效率。JSTL标签库可以被是与那个来缓存内容,通过使用JSP变量正如下例所示:
  
   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c:if test="${empty cachedFragment}">
   <c:set var="cachedFragment" scope="application">
   ...
   </c:set></c:if>
  
   缓存页面片段用下列语句输出结果:
   ${applicationScope.cachedFragment}
  
   当缓存片段需要被每一个请求所定制的时候,到底发生了什么?例如,如果希望包含一个计数器,需要缓存两个片段:
   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><c:if test="${sessionScope.counter == null}">  <c:set var="counter" scope="session" value="0"/></c:if><c:set var="counter" value="${counter+1}" scope="session"/><c:if test="${empty cachedFragment1}">
   <c:set var="cachedFragment1" scope="application">
   ...
   </c:set></c:if><c:if test="${empty cachedFragment2}">
   <c:set var="cachedFragment2" scope="application">
   ...
   </c:set></c:if>
  
   可以使用下面语句输出缓存内容:
   ${cachedFragment1} ${counter} ${cachedFragment2}
  
   通过专门的标签库的帮助,需要定制的页面片段的缓存变得异常容易了。上面已经提及,缓存内容可以被开始标签(<jc:cache>)和结尾标 签(</jc:cache>)封装起来。而每一个定制可以使用另一个标签(<jc:dynamic expr="..."/>)输出一个JSP表达式(${...})来表现。动态内容用JSP表达式缓存并在每一次缓存内容被输出时赋值。在下面的部 分可以看到这是如何实现的。Counter.jsp缓存了一个包含计数器的页面片段,当每一次用户刷新这个页面的时候计数器会自动+1。
   <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="jc" uri="http://devsphere.com/articles/jspcache" %><c:if test="${sessionScope.counter == null}">
   <c:set var="counter" scope="session" value="0"/></c:if><c:set var="counter" value="${counter+1}" scope="session"/><jc:cache id="cachedFragmentWithCounter">
   ... <jc:dynamic expr="sessionScope.counter"/>
   ...</jc:cache>
  
   JSP 变量易于使用,对于简单的Web apps,这是一个不错的内容缓存方案。然而,如果应用程序产生大量的动态内容,没有对缓存大小的控制无疑是一个问题。一种专用的缓存框架结构能够提供一 个更加有力的方案,允许对缓存的监视,限制缓存大小,控制缓存策略,等等……
  
   使用JSP 2.0表达式语言API
  
   JSP容器(例如Tomcat)对应用EL API的JSP页面中的表达式予以赋值,并且可以被Java代码所使用。这允许在Web页面外应用JSP EL作开发,例如,对XML文件、基于文本的资源以及自定义脚本。当需要控制何时对Web页面中的表达式进行赋值或者书写与之相关的表达式时,EL API同样是有用的。例如,缓存页面片段可以包含自定义JSP表达式,并且当每一次缓存内容被输出时,EL API将用来给这些表达式赋值或者重新赋值。
  
   文章提供了一个例子程序(参见文末资源部分),这个应用程序包含了一个Java类(JspUtils)和类中包含一个方法eval(),这个方法有三个参 数:JSP表达式、表达式的期望类型和一个JSP context对象。Eval()方法从JSP context中取得ExpressionEvaluator并且调用evaluate()方法,通过表达式、表达式的期望类型、和一个从JSP congtext中获得的变量。JspUtils.eval()方法返回表达式的值。
   package com.devsphere.articles.jspcache;
   import javax.servlet.jsp.JspContext;
   import javax.servlet.jsp.JspException;
   import javax.servlet.jsp.PageContext;
   import javax.servlet.jsp.el.ELException;
   import javax.servlet.jsp.el.ExpressionEvaluator;
   import java.io.IOException;public class JspUtils {
   public static Object eval(
   String expr, Class type, JspContext jspContext)
   throws JspException {
   try {
   if (expr.indexOf("${") == -1)
   return expr;
   ExpressionEvaluator evaluator
   = jspContext.getExpressionEvaluator();
   return evaluator.evaluate(expr, type,
   jspContext.getVariableResolver(), null);
   } catch (ELException e) {
   throw new JspException(e);
   }
   }
   ...}
  
   注意:JspUtils.eval()主要封装了标准的ExpressionEvaluator。如果expr不包含${,JSP EL API不被调用,因为没有JSP表达式。
  
   创建标签库描述符(TLD)文件
  
   JSP标签库需要一个标签库描述符(TLD)文件来自定义标签的命名,它们的属性,以及操作该标签的Java类。jspcache.tld描述了两个自定 义标签,<jc:cache>拥有两个属性:缓存页面片段的id和JSP scope—JSP页面总需要被储存的内容范围。<jc:dynamic>只有一个属性,即JSP表达式必须在每一次缓存片段被输出时被赋 值。TLD文件将这两个自定义标签映射到CacheTag和DynamicTag类,如下所示:
   <?xml version="1.0" encoding="UTF-8" ?><taglib 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 web-jsptaglibrary_2_0.xsd"
   version="2.0">
   <tlib-version>1.0</tlib-version>
   <short-name>jc</short-name>
   <uri>http://devsphere.com/articles/jspcache</uri>
   <tag>
   <name>cache</name>
   <tag-class>com.devsphere.articles.jspcache.CacheTag</tag-class>
   <body-content>scriptless</body-content>
   <attribute>
   <name>id</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
   </attribute>
   <attribute>
   <name>scope</name>
   <required>false</required>
   <rtexprvalue>false</rtexprvalue>
   </attribute>
   </tag>
   <tag>
   <name>dynamic</name>
   <tag-class>com.devsphere.articles.jspcache.DynamicTag</tag-class>
   <body-content>empty</body-content>
   <attribute>
   <name>expr</name>
   <required>true</required>
   <rtexprvalue>false</rtexprvalue>
   </attribute>
   </tag></taglib>
  
   TLD文件包含在Web应用描述符文件(web.xml)中,这五个文件同样包含一个初始参数指出cache是否可用。
   <?xml version="1.0" encoding="ISO-8859-1"?><web-app 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 web-app_2_4.xsd"
   version="2.4">
   <context-param>
   <param-name>com.devsphere.articles.jspcache.enabled</param-name>
   <param-value>true</param-value>
   </context-param>
   <jsp-config>
   <taglib>
   <taglib-uri>http://devsphere.com/articles/jspcache</taglib-uri>
   <taglib-location>/WEB-INF/jspcache.tld</taglib-location>
   </taglib>
   </jsp-config></web-app>
  
   理解<jc:cache>的工作机理
  
   JSP容器为JSP页面中的每一个<jc:cache>标签创建一个CacheTag实例,来对其处理。JSP容器负责调用setJsp ()、setParent()和setJspBody()方法,这是CacheTag类从SimpleTagSupport继承而来。JSP容器同事还为 所操作标签的每一个属性调用setter方法。SetId()和setScope()方法存储属性值到私有域,这个值已经用CacheTag()构造函数 用缺省值初始化。
  
   package com.devsphere.articles.jspcache;
   import javax.servlet.ServletContext;
   import javax.servlet.jsp.JspContext;
   import javax.servlet.jsp.JspException;
   import javax.servlet.jsp.PageContext;
   import javax.servlet.jsp.tagext.SimpleTagSupport;
   import java.io.IOException;import java.io.StringWriter;
   public class CacheTag extends SimpleTagSupport {
   public static final String CACHE_ENABLED
   = "com.devsphere.articles.jspcache.enabled";
   private String id;
   private int scope;
   private boolean cacheEnabled;  public CacheTag() {
   id = null;    scope = PageContext.APPLICATION_SCOPE;
   }  public void setId(String id) {
   this.id = id;
   }  public void setScope(String scope) {
   this.scope = JspUtils.checkScope(scope);
   }
   ...}
  
   setScope()方法调用JspUtils.checkScope()来校验已经从String转换为int类型的scope的属性值。
   ...public class JspUtils {
   ...
   public static int checkScope(String scope) {
   if ("page".equalsIgnoreCase(scope))
   return PageContext.PAGE_SCOPE;
   else if ("request".equalsIgnoreCase(scope))
   return PageContext.REQUEST_SCOPE;
   else if ("session".equalsIgnoreCase(scope))
   return PageContext.SESSION_SCOPE;
   else if ("application".equalsIgnoreCase(scope))
   return PageContext.APPLICATION_SCOPE;
   else
   throw new IllegalArgumentException(
   "Invalid scope: " + scope);
   }}
  
   一旦CacheTag实例准备对标签进行操作,JSP容器调用doTag()方法,用getJspContext()来获得JSP context。这个对象被造型为PageContext,从而可以调用getServletContext()方法。servlet context用来获取初始化参数的值,这个值标明缓存机制是否被启用。如果缓存被启用,doTag()尝试使用id和scope属性值来获得缓存页面片 段。如果页面片段还没有被缓存,doTag()使用getJspBody().invoke()来执行由<jc:cache>和< /jc:cache>封装的JSP代码。由JSP body产生的输出结果缓冲在StringWriter并且被toStirng()方法获得。这样,doTag()调用JSP context的setAttribute()方法新建一个JSP变量,这个变量控制可能包含JSP表达式(${…})的缓存内容。这些表达式在用 jspContext.getOut().print()输出内容前,被JspUtils.eval()赋值。只有当缓存被启用的时候,这些行为才发生。 否则,doTag()只是通过getJspBody().invoke(null)执行JSP body并且输出结果不被缓存。
   ...public class CacheTag extends SimpleTagSupport {
   ...
   public void doTag() throws JspException, IOException {
   JspContext jspContext = getJspContext();
   ServletContext application
   = ((PageContext) jspContext).getServletContext();
   String cacheEnabledParam
   = application.getInitParameter(CACHE_ENABLED);
   cacheEnabled = cacheEnabledParam != null
   && cacheEnabledParam.equals("true");
   if (cacheEnabled) {
   String cachedOutput
   = (String) jspContext.getAttribute(id, scope);
   if (cachedOutput == null) {
   StringWriter buffer = new StringWriter();
   getJspBody().invoke(buffer);
   cachedOutput = buffer.toString();
   jspContext.setAttribute(id, cachedOutput, scope);
   }      String evaluatedOutput = (String) JspUtils.eval(
   cachedOutput, String.class, jspContext);
   jspContext.getOut().print(evaluatedOutput);
   } else
   getJspBody().invoke(null);
   }
   ...}
  
   注意一个单独的JspUtils.eval()调用给所有的${…} 表达式赋值。因为一个包含了大量的${…}结构的text也是一个表达式。每一个缓存片段都可以被当作一个复杂的JSP表达式来进行处理。
  
   IsCacheEnabled()方法返回cacheEnabled的值,这个值已经被doTag()初始化。
   ...public class CacheTag extends SimpleTagSupport {
   ...  public boolean isCacheEnabled() {
   return cacheEnabled;
   }}
  
   <jc:cache>标签允许页面开发者自主选择缓存页面片段的ID。这使得缓存一个页面片段可以被多个JSP页面共享,当需要重用JSP代 码时,这是很有用处的。但是仍然需要一些命名协议来避免可能的冲突。通过修改CacheTag类来在自动ID内部包含URL可以避免这种副作用。
  
   理解<jc:dynamic>在做什么
  
   每一个<jc:dynamic>被一个DynamicTag类的实例处理,setExpr()方法将expr属性值存储到一个私有域。 DoTag()方法创建JSP表达式,在expr属性值加上${前缀和}后缀。然后,doTag()使用findAncestorWithClass() 来查找含有<jc:dynamic>标签元素的<jc:cache>的CacheTag handler。如果没有查找到或者缓存被禁用,JSP表达式被JspUtils.eval()赋值并且值被输出。否则,doTag()输出无值表达式。
  
   package com.devsphere.articles.jspcache;
   import javax.servlet.jsp.JspException;
   import javax.servlet.jsp.tagext.SimpleTagSupport;
   import java.io.IOException;
   public class DynamicTag extends SimpleTagSupport {
   private String expr;
   public void setExpr(String expr) {
   this.expr = expr;
   }  public void doTag() throws JspException, IOException {
   String output = "${" + expr + "}";
   CacheTag ancestor = (CacheTag) findAncestorWithClass(
   this, CacheTag.class);
   if (ancestor == null || !ancestor.isCacheEnabled())
   output = (String) JspUtils.eval(
   output, String.class, getJspContext());
   getJspContext().getOut().print(output);
   }}
  
   分析以上代码,可以注意到<jc:cache>和<jc:dynamic>合作来实现一个尽可能有效率的方案。如果缓存可用,页 面片段和由<jc:dynamic>生成并被CacheTag赋值的JSP表达式一起放入缓冲器。如果缓存被禁用,缓冲变得没有意义, <jc:cache>只是执行其JSP body部分,而让DynamicTag给JSP表达式赋值。禁用缓存有时候是必要的,特别是在开发过程期间出现内容的改变和JSP页面被重新编译的时 候。当然,在开发完毕的成品环境中缓存必须被启用。
  
   总结
  
    对于开发大型企业级应用,则该考虑使用支持更好的缓存机制的框架结构,而不仅是使用JSP变量。但是了解基于EL API的定制技术无疑是不无裨益的。 
标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值