是否可以关闭对 Tomcat 8 中引用静态字段和方法的支持,该支持是作为统一表达式语言 3.0 的一部分添加的。
我们的应用程序中有 ~4K JSP,有许多${undefined}
(未指定范围)表达式,迁移到 Tomcat 8 会导致性能显著下降,因为这些表达式的评估是合法的"空"值。我们不再将JSP技术用于较新的页面,但旧页面不会很快消失。
有问题的代码位于javax.servlet.el.ScopedAttributeELResolver类中,该类尝试解析来自ImportHandler的表达式,该表达式进行了许多Class.forName查找,这主要是由于ClassNotFoundException而失败的。
@Override
public Object getValue(ELContext context, Object base, Object property) {
if (context == null) {
throw new NullPointerException();
}
Object result = null;
if (base == null) {
context.setPropertyResolved(base, property);
if (property != null) {
String key = property.toString();
PageContext page = (PageContext) context
.getContext(JspContext.class);
result = page.findAttribute(key);
if (result == null) {
// This might be the name of an imported class
ImportHandler importHandler = context.getImportHandler();
if (importHandler != null) {
Class<?> clazz = importHandler.resolveClass(key);
if (clazz != null) {
result = new ELClass(clazz);
}
if (result == null) {
// This might be the name of an imported static field
clazz = importHandler.resolveStatic(key);
if (clazz != null) {
try {
result = clazz.getField(key).get(null);
} catch (IllegalArgumentException | IllegalAccessException |
NoSuchFieldException | SecurityException e) {
// Most (all?) of these should have been
// prevented by the checks when the import
// was defined.
}
}
}
}
}
}
}
return result;
}
更新
Tomcat 8 打开了一个错误 - 使用无范围的可选属性时出现性能问题,该错误因无法修复而关闭。我以为他们可能会添加一些属性来禁用性能消耗代码,但现在他们不会,因为:
- 这将是雄猫特定的
- 它必须是系统属性,因为这是一个规范类
- 它不能应用于每个应用程序 - 它会影响在该实例上运行的所有应用程序
请告知谢谢
禁用新行为的一种方法是利用 Tomcat 的类加载机制。公共类装入器包含对 Tomcat 内部类和所有 Web 应用程序可见的其他类。此类装入器搜索的位置由 $CATALINA_BASE/conf/catalina.properties 中的 common.loader 属性定义。默认设置将按列出顺序搜索以下位置:
- $CATALINA_BASE/lib 中解压缩的类和资源
- $CATALINA_BASE/lib 中的 JAR 文件
- 解压缩的类和资源 $CATALINA_HOME/lib
- $CATALINA_HOME/lib 中的 JAR 文件
我创建了一个带有一个类的新jar:javax.servlet.jsp.el.ScopedAttributeELResolver,这个类与原始类相同(来自jsp-api.jar),除了执行静态解析的getValue方法(tomcat代码在Apache 2许可证下,因此补丁是合法的)。放置在 $CATALINA_BASE/lib 文件夹下的 jar。
新方法:
@Override
public Object getValue(ELContext context, Object base, Object property) {
if (context == null) {
throw new NullPointerException();
}
Object result = null;
if (base == null) {
context.setPropertyResolved(base, property);
if (property != null) {
String key = property.toString();
PageContext page = (PageContext) context
.getContext(JspContext.class);
result = page.findAttribute(key);
// if (result == null) {
// // This might be the name of an imported class
// ImportHandler importHandler = context.getImportHandler();
// if (importHandler != null) {
// Class<?> clazz = importHandler.resolveClass(key);
// if (clazz != null) {
// result = new ELClass(clazz);
// }
// if (result == null) {
// // This might be the name of an imported static field
// clazz = importHandler.resolveStatic(key);
// if (clazz != null) {
// try {
// result = clazz.getField(key).get(null);
// } catch (IllegalArgumentException | IllegalAccessException |
// NoSuchFieldException | SecurityException e) {
// // Most (all?) of these should have been
// // prevented by the checks when the import
// // was defined.
// }
// }
// }
// }
// }
}
}
return result;
}
将加载此类而不是原始类,并绕道性能问题。
优势:
- 通过简单的 jar 删除轻松回滚
弊:
- 每次雄猫升级都需要维护
Tomcat 8.0.33 似乎解决了这个问题,性能提高了 10 倍https://bz.apache.org/bugzilla/show_bug.cgi?id=57583
几年后,我们在Tomcat 8.5上运行的系统出现了巨大的内存分配问题,我们实施了与上述类似的解决方法 https://stackoverflow.com/a/35679744/8068546),但是我们没有完全禁用类的解析,而是仅将其保留为以大写首字母命名的属性(如类应该是)。
真正的中期解决方案当然是按照 Tomcat 迁移指南中的说明确定我们的页面属性范围......