问题描述:(jsp自定义标签代码片段中出现出来多个相同类型的标签<JB:ToobarCellComponent。那么jsp引擎是否实例化多个标签对象呢)那么大家可能就奇怪了,研究这个问题到底对我们项目研发有何好处呢?其实这个问题可以延伸出很多关于java自定义标签的一些高级应用以及自定义标签的解析原理,比如自定义标签按钮级别的权限控制【页面控件权值记录,初始化等】等。研究这些设计框架大有所益处,以下内容但愿对你有所帮助。
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="button" btnSelectName="新增" imgDisabled="plus.gif" imgEnabled="plus.gif" itemId="0" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="separator" imgDisabled="" imgEnabled="" itemId="1" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="button" btnSelectName="保存" imgDisabled="save.gif" imgEnabled="save.gif" itemId="2" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="separator" imgDisabled="" imgEnabled="" itemId="3" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="button" btnSelectName="删除" imgDisabled="delete.gif" imgEnabled="delete.gif" itemId="4" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="separator" imgDisabled="" imgEnabled="" itemId="5" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="button" btnSelectName="隐藏/关闭" imgDisabled="sysset.gif" imgEnabled="sysset.gif" itemId="6" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="separator" imgDisabled="" imgEnabled="" itemId="7" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="button" btnSelectName="刷新" imgDisabled="refresh.gif" imgEnabled="refresh.gif" itemId="8" optionItems=""></JB:ToolBarCellComponent>
<JB:ToolBarCellComponent id="skinSetting_Toolbar" cellType="separator" imgDisabled="" imgEnabled="" itemId="9" optionItems=""></JB:ToolBarCellComponent>
像以上这段jsp自定义标签代码片段中出现出来多个相同类型的标签<JB:ToobarCellComponent。那么jsp引擎是否实例化多个标签对象呢?这个问题是接下里需要考究的!
对于以上其中一个标签对应执行的相关操作我们可以通过查看jsp引擎编译整合jsp页面生成servlet类中一个方法,代码如下:
private boolean _jspx_meth_JB_005fToolBarCellComponent_005f6(javax.servlet.jsp.tagext.JspTag _jspx_th_JB_005fToolBarComponent_005f0, PageContext _jspx_page_context)
throws Throwable {
PageContext pageContext = _jspx_page_context;
JspWriter out = _jspx_page_context.getOut();
// JB:ToolBarCellComponent
org.jplogic.system.core.soa.component.taglib.ToolBarCellControl _jspx_th_JB_005fToolBarCellComponent_005f6 = (org.jplogic.system.core.soa.component.taglib.ToolBarCellControl) _005fjspx_005ftagPool_005fJB_005fToolBarCellComponent_0026_005foptionItems_005fitemId_005fimgEnabled_005fimgDisabled_005fid_005fcellType_005fbtnSelectName_005fnobody.get(org.jplogic.system.core.soa.component.taglib.ToolBarCellControl.class);
_jspx_th_JB_005fToolBarCellComponent_005f6.setPageContext(_jspx_page_context);
_jspx_th_JB_005fToolBarCellComponent_005f6.setParent((javax.servlet.jsp.tagext.Tag)_jspx_th_JB_005fToolBarComponent_005f0);
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = id type = null reqTime = true required = true fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setId("skinSetting_Toolbar");
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = cellType type = null reqTime = true required = false fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setCellType("button");
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = btnSelectName type = null reqTime = true required = false fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setBtnSelectName("隐藏/关闭");
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = imgDisabled type = null reqTime = true required = false fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setImgDisabled("sysset.gif");
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = imgEnabled type = null reqTime = true required = false fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setImgEnabled("sysset.gif");
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = itemId type = null reqTime = true required = false fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setItemId("6");
// /jsp/JpSystem/SystemParamConfigMngForm.jsp(342,6) name = optionItems type = null reqTime = true required = false fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
_jspx_th_JB_005fToolBarCellComponent_005f6.setOptionItems("");
int _jspx_eval_JB_005fToolBarCellComponent_005f6 = _jspx_th_JB_005fToolBarCellComponent_005f6.doStartTag();
if (_jspx_th_JB_005fToolBarCellComponent_005f6.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
_005fjspx_005ftagPool_005fJB_005fToolBarCellComponent_0026_005foptionItems_005fitemId_005fimgEnabled_005fimgDisabled_005fid_005fcellType_005fbtnSelectName_005fnobody.reuse(_jspx_th_JB_005fToolBarCellComponent_005f6);
return true;
}
_005fjspx_005ftagPool_005fJB_005fToolBarCellComponent_0026_005foptionItems_005fitemId_005fimgEnabled_005fimgDisabled_005fid_005fcellType_005fbtnSelectName_005fnobody.reuse(_jspx_th_JB_005fToolBarCellComponent_005f6);
return false;
}
通过以上代码你可以了解到在在jsp1.2规范中:(大家有兴趣可以去查看一下,可以很方便的让你了解到自定义标签技术的相关执行过程)
1:当容器创建一个新的标签实例后,通过setPageContext设置标签的页面上下文。
2:使用setParent方法设置这个标签的上一级标签。如果没有上一级嵌套,设置为空。
3:设置标签的属性。这个属性在标签库描述文件中定义。如果没有定义属性就不调用此类方法。
4:调用 doStartTag方法,这个方法可以返回EVAL_BODY_INCLUDE和SKIP_BODY。当返回EVAL_BODY_INCLUDE时,就计算标签的BODY,如果返回SKIP_BODY,就 不计算标签的BODY。
5:调用doEndTag方法,这个方法可以返回EVAL_PAGE或者SKIP_PAGE。当返回EVAL_PAGE时,容器将在标签结束时继续计算JSP页面的其他部分;如果返回SKIP_PAGE,容器将在标签结束时停止计算JSP页面的其他部分。
6:调用release方法释放标签程序占用的任何资源。
jsp引擎编译后的servlet类中看到标签对象引用:
private org.apache.jasper.runtime.TagHandlerPool _005fjspx_005ftagPool_005fJB_005fToolBarCellComponent_0026_005foptionItems_005fitemId_005fimgEnabled_005fimgDisabled_005fid_005fcellType_005fbtnSelectName_005fnobody;
当我们阅读这个有jsp引擎编译后的servlet的源代码可以看出,我们标签对象实例是通过标签处理对象池来获取,调用标签处理对象池TagHandlerPool类的get方法来获取相应的标签处理对象。入参为相应标签处理对象的class.。下面我们来看看这个jsp引擎编译后的servlet是如何通过TagHandlerPool来获取具体的标签处理对象的。
类TagHandlerPool内部代码如下:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jasper.runtime;
import javax.servlet.ServletConfig;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import org.apache.jasper.Constants;
import org.apache.tomcat.InstanceManager;
import org.jboss.logging.Logger;
/**
* Pool of tag handlers that can be reused.
*
* @author Jan Luehe
*/
public class TagHandlerPool {
private Tag[] handlers;
public static final String OPTION_TAGPOOL="tagpoolClassName";
public static final String OPTION_MAXSIZE="tagpoolMaxSize";
// index of next available tag handler
private int current;
protected InstanceManager instanceManager = null;
public static TagHandlerPool getTagHandlerPool( ServletConfig config) {
TagHandlerPool result=null;
String tpClassName=getOption( config, OPTION_TAGPOOL, null);
if( tpClassName != null ) {
try {
Class c=Class.forName( tpClassName );
result=(TagHandlerPool)c.newInstance();
} catch (Exception e) {
result = null;
}
}
<strong><span style="color:#ff0000;">if( result==null ) result=new PerThreadTagHandlerPool();</span>
<span style="color:#ff0000;"> result.init(config);</span></strong>
return result;
}
protected void init( ServletConfig config ) {
int maxSize=-1;
String maxSizeS=getOption(config, OPTION_MAXSIZE, null);
if( maxSizeS != null ) {
try {
maxSize=Integer.parseInt(maxSizeS);
} catch( Exception ex) {
maxSize=-1;
}
}
if( maxSize <0 ) {
maxSize=Constants.MAX_POOL_SIZE;
}
this.handlers = new Tag[maxSize];
this.current = -1;
instanceManager = InstanceManagerFactory.getInstanceManager(config);
}
/**
* Constructs a tag handler pool with the default capacity.
*/
public TagHandlerPool() {
// Nothing - jasper generated servlets call the other constructor,
// this should be used in future + init .
}
/**
* Constructs a tag handler pool with the given capacity.
*
* @param capacity Tag handler pool capacity
* @deprecated Use static getTagHandlerPool
*/
public TagHandlerPool(int capacity) {
this.handlers = new Tag[capacity];
this.current = -1;
}
/**
* Gets the next available tag handler from this tag handler pool,
* instantiating one if this tag handler pool is empty.
*
* @param handlerClass Tag handler class
*
* @return Reused or newly instantiated tag handler
*
* @throws JspException if a tag handler cannot be instantiated
*/
public Tag get(Class handlerClass) throws JspException {
Tag handler;
synchronized( this ) {
if (current >= 0) {
handler = handlers[current--];
return handler;
}
}
// Out of sync block - there is no need for other threads to
// wait for us to construct a tag for this thread.
try {
if (Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
return (Tag) instanceManager.newInstance(handlerClass);
} else {
Tag instance = (Tag) handlerClass.newInstance();
if (Constants.INJECT_TAGS) {
instanceManager.newInstance(instance);
}
return instance;
}
} catch (Exception e) {
throw new JspException(e.getMessage(), e);
}
}
/**
* Adds the given tag handler to this tag handler pool, unless this tag
* handler pool has already reached its capacity, in which case the tag
* handler's release() method is called.
*
* @param handler Tag handler to add to this tag handler pool
*/
public void reuse(Tag handler) {
synchronized( this ) {
if (current < (handlers.length - 1)) {
handlers[++current] = handler;
return;
}
}
// There is no need for other threads to wait for us to release
handler.release();
if (Constants.INJECT_TAGS || Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
try {
instanceManager.destroyInstance(handler);
} catch (Exception e) {
// Ignore
}
}
}
/**
* Calls the release() method of all available tag handlers in this tag
* handler pool.
*/
public synchronized void release() {
for (int i = current; i >= 0; i--) {
handlers[i].release();
if (Constants.INJECT_TAGS || Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
try {
instanceManager.destroyInstance(handlers[i]);
} catch (Exception e) {
// Ignore
}
}
}
}
protected static String getOption( ServletConfig config, String name, String defaultV) {
if( config == null ) return defaultV;
String value=config.getInitParameter(name);
if( value != null ) return value;
if( config.getServletContext() ==null )
return defaultV;
value=config.getServletContext().getInitParameter(name);
if( value!=null ) return value;
return defaultV;
}
}
通过以上源码可以看出
if
( result==
null
) result=
new
PerThreadTagHandlerPool();
result.init(config);
下面我们再来看看PerThreadTagHandlerPool类中的两个方法:
protected void init(ServletConfig config) {
instanceManager = InstanceManagerFactory.getInstanceManager(config);
maxSize = Constants.MAX_POOL_SIZE;
String maxSizeS = getOption(config, OPTION_MAXSIZE, null);
if (maxSizeS != null) {
maxSize = Integer.parseInt(maxSizeS);
if (maxSize < 0) {
maxSize = Constants.MAX_POOL_SIZE;
}
}
perThread = new ThreadLocal<PerThreadData>() {
protected PerThreadData initialValue() {
PerThreadData ptd = new PerThreadData();
ptd.handlers = new Tag[maxSize];
ptd.current = -1;
perThreadDataVector.addElement(ptd);
return ptd;
}
};
}
/**
* Gets the next available tag handler from this tag handler pool,
* instantiating one if this tag handler pool is empty.
*
* @param handlerClass Tag handler class
*
* @return Reused or newly instantiated tag handler
*
* @throws JspException if a tag handler cannot be instantiated
*/
public Tag get(Class handlerClass) throws JspException {
PerThreadData ptd = (PerThreadData)perThread.get();
if(ptd.current >=0 ) {
return ptd.handlers[ptd.current--];
} else {
try {
if (Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
return (Tag) instanceManager.newInstance(handlerClass);
} else {
Tag instance = (Tag) handlerClass.newInstance();
if (Constants.INJECT_TAGS) {
instanceManager.newInstance(instance);
}
return instance;
}
} catch (Exception e) {
throw new JspException(e.getMessage(), e);
}
}
}
从以上代码可以看出PerThreadTagHandlerPool继承类TagHandlerPool,jsp引擎编译后的servlet
首选通过PerThreadTagHandlerPool的get方法获取相应标签处理对象实例
(首先判断线程池中是否存在需要的标签处理对象实例,如果存在直接返回,
不存在的话,通过上下文以及反射来创建新的标签处理对象)。
那么以上提出的jsp自定义标签代码片段中出现出来多个相同类型的标签<JB:ToobarCellComponent。
那么jsp引擎是否实例化多个标签对象呢?问题的答案就很清楚了!
该篇文章回答http://www.iteye.com/problems/30505链接提出的问题。