是否可以禁用在Tomcat 8中引用JSP 2.3的静态字段和方法的支持

时间:2016-02-18 08:09:04

标签: java performance jsp el tomcat8

是否可以关闭在Tomcat 8中引用静态字段和方法的支持,它是作为统一表达式语言3.0的一部分添加的。

我们的应用程序中有~4K JSP,有许多${undefined}(没有指定范围)表达式,迁移到Tomcat 8会导致性能显着下降,因为对这些表达式的评估是合法的“null”值。我们不再将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打开了一个错误 - performance problems when using scopeless optional attributes,该错误已关闭,因为无法修复。我认为他们可能会添加一些属性来禁用性能消耗代码,但是现在他们不会,因为:

  • 这将是特定于Tomcat的
  • 它必须是系统属性,因为这是一个规范类
  • 无法按应用程序应用 - 它会影响在该实例上运行的所有应用程序

请告知谢谢

3 个答案:

答案 0 :(得分:4)

禁用新行为的一种方法是利用Tomcat的类加载机制。公共类加载器包含对Tomcat内部类和所有Web应用程序都可见的其他类。此类加载器搜索的位置由$ CATALINA_BASE / conf / catalina.properties中的common.loader属性定义。默认设置将按列出的顺序搜索以下位置:

  1. $ CATALINA_BASE / lib
  2. 中解压缩的类和资源
  3. $ CATALINA_BASE / lib
  4. 中的JAR文件
  5. $ CATALINA_HOME / lib
  6. 中未解压缩的类和资源
  7. $ CATALINA_HOME / lib
  8. 中的JAR文件

    我创建了一个包含一个类的新jar: javax.servlet.jsp.el.ScopedAttributeELResolver ,这个类与原始类(来自jsp-api.jar)相同,但 getValue除外执行静态解析的方法(tomcat代码在Apache 2许可下,因此补丁是合法的)。该jar放在 $ CATALINA_BASE / lib 文件夹下。

    新方法:

        @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升级

答案 1 :(得分:2)

似乎Tomcat 8.0.33解决了这个问题,性能提升速度提高了10倍 https://bz.apache.org/bugzilla/show_bug.cgi?id=57583

答案 2 :(得分:0)

几年后,由于在Tomcat 8.5上运行的系统存在巨大的内存分配问题,我们实现了与上述https://stackoverflow.com/a/35679744/8068546相似的变通方法,但我们并未完全禁用类的解析,而是保留了它仅适用于以首字母大写命名的属性(应具有类)。

真正的中期解决方案当然是按照Tomcat迁移指南中的说明来定义页面属性的范围。