JSF2 静态资源管理——组合、压缩

2023-12-19

有谁知道动态组合/缩小所有 h:outputStylesheet 资源,然后在渲染阶段组合/缩小所有 h:outputScript 资源的方法?混合/缩小的资源可能需要使用基于组合资源字符串或其他内容的键进行缓存,以避免过度处理。

如果这个功能不存在,我想开发它。有谁对实施此类事情的最佳方法有想法吗?我认为 Servlet 过滤器可以工作,但过滤器必须做比必要的更多的工作——基本上是检查整个渲染的输出并替换匹配项。在渲染阶段实现某些东西似乎会更好,因为所有静态资源都可用,而无需解析整个输出。

感谢您的任何建议!

Edit: 为了表明我并不懒惰,并且会在一些指导下真正解决这个问题,这里有一个存根,它捕获脚本资源名称/库,然后将它们从视图中删除。正如您所看到的,我对下一步该怎么做有一些疑问...我应该发出http请求并获取要组合的资源,然后组合它们并将它们保存到资源缓存中吗?

package com.davemaple.jsf.listener;

import java.util.ArrayList;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;

import org.apache.log4j.Logger;

/**
 * A Listener that combines CSS/Javascript Resources
 * 
 * @author David Maple<[email protected] /cdn-cgi/l/email-protection>
 *
 */
public class ResourceComboListener implements PhaseListener, SystemEventListener {

    private static final long serialVersionUID = -8430945481069344353L;
    private static final Logger LOGGER = Logger.getLogger(ResourceComboListener.class);

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
     */
    public void afterPhase(PhaseEvent event) {
        FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
     */
    public void beforePhase(PhaseEvent event) {
        //nothing here
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.SystemEventListener#isListenerForSource(java.lang.Object)
     */
    public boolean isListenerForSource(Object source) {
        return (source instanceof UIViewRoot);
    }

    /*
     * (non-Javadoc)
     * @see javax.faces.event.SystemEventListener#processEvent(javax.faces.event.SystemEvent)
     */
    public void processEvent(SystemEvent event) throws AbortProcessingException {
        FacesContext context = FacesContext.getCurrentInstance();
        UIViewRoot viewRoot = context.getViewRoot();
        List<UIComponent> scriptsToRemove = new ArrayList<UIComponent>();

        if (!context.isPostback()) {

            for (UIComponent component : viewRoot.getComponentResources(context, "head")) {
                if (component.getClass().equals(UIOutput.class)) {
                    UIOutput uiOutput = (UIOutput) component;

                    if (uiOutput.getRendererType().equals("javax.faces.resource.Script")) {
                        String library = uiOutput.getAttributes().get("library").toString();
                        String name = uiOutput.getAttributes().get("name").toString();

                        // make https requests to get the resources?
                        // combine then and save to resource cache?
                        // insert new UIOutput script?

                        scriptsToRemove.add(component);
                    }


                }
            }

            for (UIComponent component : scriptsToRemove) {
                viewRoot.getComponentResources(context, "head").remove(component);
            }

        }
    }

}

这个答案不包括缩小和压缩。最好将单个 CSS/JS 资源的最小化委托给构建脚本,例如YUI 压缩机 Ant 任务 http://code.google.com/p/javaflight-code/wiki/YuiCompressorAntTask。针对每个请求手动执行此操作的成本太高。压缩(我假设您指的是 GZIP?)最好委托给您正在使用的 servlet 容器。手动执行过于复杂。例如,在 Tomcat 上,只需添加一个compression="on"归因于<Connector>元素在/conf/server.xml.


The SystemEventListener http://docs.oracle.com/javaee/6/api/javax/faces/event/SystemEventListener.html已经是一个很好的第一步(除了一些PhaseListener不必要)。接下来,您需要实现一个自定义ResourceHandler http://docs.oracle.com/javaee/6/api/javax/faces/application/ResourceHandler.html and Resource http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html。这部分并不完全是微不足道的。如果您想独立于 JSF 实现,您需要进行大量的重新发明。

首先,在你的SystemEventListener http://docs.oracle.com/javaee/6/api/javax/faces/event/SystemEventListener.html,您想要创建新的UIOutput http://docs.oracle.com/javaee/6/api/javax/faces/component/UIOutput.html代表组合资源的组件,以便您可以使用添加它UIViewRoot#addComponentResource() http://docs.oracle.com/javaee/6/api/javax/faces/component/UIViewRoot.html#addComponentResource%28javax.faces.context.FacesContext,%20javax.faces.component.UIComponent,%20java.lang.String%29。你需要设置它的library归因于某事unique您的自定义资源处理程序可以理解它。您需要将组合资源存储在应用程序范围的变量中unique基于资源组合的名称(可能是 MD5 哈希?),然后将此密钥设置为name组件的属性。存储为应用程序范围的变量对于服务器和客户端来说都具有缓存优势。

像这样的事情:

String combinedResourceName = CombinedResourceInfo.createAndPutInCacheIfAbsent(resourceNames);
UIOutput component = new UIOutput();
component.setRendererType(rendererType);
component.getAttributes().put(ATTRIBUTE_RESOURCE_LIBRARY, CombinedResourceHandler.RESOURCE_LIBRARY);
component.getAttributes().put(ATTRIBUTE_RESOURCE_NAME, combinedResourceName + extension);
context.getViewRoot().addComponentResource(context, component, TARGET_HEAD);

然后按照你的习惯ResourceHandler http://docs.oracle.com/javaee/6/api/javax/faces/application/ResourceHandler.html实施,你需要实施createResource() http://docs.oracle.com/javaee/6/api/javax/faces/application/ResourceHandler.html#createResource%28java.lang.String,%20java.lang.String%29相应的方法来创建自定义Resource http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html每当库匹配所需值时执行:

@Override
public Resource createResource(String resourceName, String libraryName) {
    if (RESOURCE_LIBRARY.equals(libraryName)) {
        return new CombinedResource(resourceName);
    } else {
        return super.createResource(resourceName, libraryName);
    }
}

自定义的构造函数Resource http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html实现应该根据名称获取组合资源信息:

public CombinedResource(String name) {
    setResourceName(name);
    setLibraryName(CombinedResourceHandler.RESOURCE_LIBRARY);
    setContentType(FacesContext.getCurrentInstance().getExternalContext().getMimeType(name));
    this.info = CombinedResourceInfo.getFromCache(name.split("\\.", 2)[0]);
}

这个习俗Resource http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html实施必须提供适当的getRequestPath() http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html#getRequestPath%28%29方法返回一个 URI,然后该 URI 将包含在渲染中<script> or <link>元素:

@Override
public String getRequestPath() {
    FacesContext context = FacesContext.getCurrentInstance();
    String path = ResourceHandler.RESOURCE_IDENTIFIER + "/" + getResourceName();
    String mapping = getFacesMapping();
    path = isPrefixMapping(mapping) ? (mapping + path) : (path + mapping);
    return context.getExternalContext().getRequestContextPath()
        + path + "?ln=" + CombinedResourceHandler.RESOURCE_LIBRARY;
}

现在,HTML 渲染部分应该没问题了。它看起来像这样:

<link type="text/css" rel="stylesheet" href="/playground/javax.faces.resource/dd08b105bf94e3a2b6dbbdd3ac7fc3f5.css.xhtml?ln=combined.resource" />
<script type="text/javascript" src="/playground/javax.faces.resource/2886165007ccd8fb65771b75d865f720.js.xhtml?ln=combined.resource"></script>

接下来,您必须拦截浏览器发出的组合资源请求。这是最难的部分。首先,按照您的习惯ResourceHandler http://docs.oracle.com/javaee/6/api/javax/faces/application/ResourceHandler.html实施,你需要实施handleResourceRequest() http://docs.oracle.com/javaee/6/api/javax/faces/application/ResourceHandler.html#handleResourceRequest%28javax.faces.context.FacesContext%29相应的方法:

@Override
public void handleResourceRequest(FacesContext context) throws IOException {
    if (RESOURCE_LIBRARY.equals(context.getExternalContext().getRequestParameterMap().get("ln"))) {
        streamResource(context, new CombinedResource(getCombinedResourceName(context)));
    } else {
        super.handleResourceRequest(context);
    }
}

然后你必须完成实现自定义其他方法的全部工作Resource http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html相应实施,例如getResponseHeaders() http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html#getResponseHeaders%28%29它应该返回正确的缓存标头,getInputStream() http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html#getInputStream%28%29这应该返回InputStream单一资源中的组合资源InputStream and userAgentNeedsUpdate() http://docs.oracle.com/javaee/6/api/javax/faces/application/Resource.html#userAgentNeedsUpdate%28javax.faces.context.FacesContext%29它应该正确响应缓存相关的请求。

@Override
public Map<String, String> getResponseHeaders() {
    Map<String, String> responseHeaders = new HashMap<String, String>(3);
    SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);
    sdf.setTimeZone(TIMEZONE_GMT);
    responseHeaders.put(HEADER_LAST_MODIFIED, sdf.format(new Date(info.getLastModified())));
    responseHeaders.put(HEADER_EXPIRES, sdf.format(new Date(System.currentTimeMillis() + info.getMaxAge())));
    responseHeaders.put(HEADER_ETAG, String.format(FORMAT_ETAG, info.getContentLength(), info.getLastModified()));
    return responseHeaders;
}

@Override
public InputStream getInputStream() throws IOException {
    return new CombinedResourceInputStream(info.getResources());
}

@Override
public boolean userAgentNeedsUpdate(FacesContext context) {
    String ifModifiedSince = context.getExternalContext().getRequestHeaderMap().get(HEADER_IF_MODIFIED_SINCE);

    if (ifModifiedSince != null) {
        SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_RFC1123_DATE, Locale.US);

        try {
            info.reload();
            return info.getLastModified() > sdf.parse(ifModifiedSince).getTime();
        } catch (ParseException ignore) {
            return true;
        }
    }

    return true;
}

我在这里有一个完整的概念证明,但是代码太多,无法作为答案发布。以上只是部分内容,旨在帮助您朝正确的方向发展。我认为缺少的方法/变量/常量声明是不言自明的,足以编写您自己的声明,否则请告诉我。


Update:根据评论,您可以通过以下方式收集资源:CombinedResourceInfo:

private synchronized void loadResources(boolean forceReload) {
    if (!forceReload && resources != null) {
        return;
    }

    FacesContext context = FacesContext.getCurrentInstance();
    ResourceHandler handler = context.getApplication().getResourceHandler();
    resources = new LinkedHashSet<Resource>();
    contentLength = 0;
    lastModified = 0;

    for (Entry<String, Set<String>> entry : resourceNames.entrySet()) {
        String libraryName = entry.getKey();

        for (String resourceName : entry.getValue()) {
            Resource resource = handler.createResource(resourceName, libraryName);
            resources.add(resource);

            try {
                URLConnection connection = resource.getURL().openConnection();
                contentLength += connection.getContentLength();
                long lastModified = connection.getLastModified();

                if (lastModified > this.lastModified) {
                    this.lastModified = lastModified;
                }
            } catch (IOException ignore) {
                // Can't and shouldn't handle it here anyway.
            }
        }
    }
}

(上面的方法被调用reload()方法和 getter 取决于要设置的属性之一)

这是如何CombinedResourceInputStream看起来像:

final class CombinedResourceInputStream extends InputStream {

    private List<InputStream> streams;
    private Iterator<InputStream> streamIterator;
    private InputStream currentStream;

    public CombinedResourceInputStream(Set<Resource> resources) throws IOException {
        streams = new ArrayList<InputStream>();

        for (Resource resource : resources) {
            streams.add(resource.getInputStream());
        }

        streamIterator = streams.iterator();
        streamIterator.hasNext(); // We assume it to be always true; CombinedResourceInfo won't be created anyway if it's empty.
        currentStream = streamIterator.next();
    }

    @Override
    public int read() throws IOException {
        int read = -1;

        while ((read = currentStream.read()) == -1) {
            if (streamIterator.hasNext()) {
                currentStream = streamIterator.next();
            } else {
                break;
            }
        }

        return read;
    }

    @Override
    public void close() throws IOException {
        IOException caught = null;

        for (InputStream stream : streams) {
            try {
                stream.close();
            } catch (IOException e) {
                if (caught == null) {
                    caught = e; // Don't throw it yet. We have to continue closing all other streams.
                }
            }
        }

        if (caught != null) {
            throw caught;
        }
    }

}

Update 2:OmniFaces 提供了具体且可重用的解决方案。也可以看看CombinedResourceHandler展示页面 http://showcase.omnifaces.org/resourcehandlers/CombinedResourceHandler and API文档 http://omnifaces.org/docs/javadoc/current/org/omnifaces/resourcehandler/CombinedResourceHandler.html了解更多详情。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JSF2 静态资源管理——组合、压缩 的相关文章

  • 在应用程序服务器上存储 PDF 一段有限的时间并可供下载

    您好 我正在使用 PrimeFaces 5 JSF 2 和 tomcat 有人可以向我展示或给我一个关于如何在应用程序服务器上在有限时间内存储 pdf 的想法吗 我正在使用 tomcat 然后下载它 如果这是用户请求的 此功能与发票相关 因
  • 从 BroadcastReceiver 获取方法来更新 UI

    我正在尝试根据变量的变化更新用户界面BroadcastReceiver 因此 我需要调用一个扩展类的方法 以获取我提到的变量 BroadcastReceiver in MainActivity取决于但我无法以任何方式获得真正的返回值 扩展的
  • 默认情况下,JSF 生成不可用的 ID,这些 ID 与 Web 标准的 CSS 部分不兼容

    活跃的 JSF 或 Primefaces 用户能否解释一下为什么默认情况下会发生这种情况 为什么没有人对此采取任何措施
  • 如何在 中使用

    我想包括
  • JSF 不呈现自定义 HTML 标记属性

    我想向我的登录表单添加一些 iOS 特定的标签属性 如果我查看我的网页源代码 就会发现自动更正 自动大写和拼写检查属性不存在 这是什么原因呢 我正在使用 JSF 2 x
  • ES6 静态方法引用 self? [复制]

    这个问题在这里已经有答案了 我有两节课 存储库和用户存储库 我想在 Repository 中定义一个静态方法 该方法在运行时调用 UserRepository 中的静态函数 有什么干净的方法可以做到这一点吗 class Repository
  • JSF UIComponent 绑定、可序列化和视图范围

    我有一个视图范围的 bean 实现Serializable and a UIComponent通过绑定传入 ManagedBean ViewScoped public class ViewScopedBean implements Seri
  • 使用 boost 线程和非静态类函数

    所以我做了一些研究 发现你可以创建一个 boost thread 对象 并通过使用 this 和 boost bind 等以非静态类函数开头 这确实没有多大意义对我来说 我能找到的所有示例都在与其启动的函数相同的类中启动了 boost th
  • 可靠且方便的 JavaScript 压缩器 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 从 javascript 访问 JSF 资源

    我想访问 JSF 资源 在WebPages resources img 来自 JavaScript 函数 例如 function hideSpinner arguments i poster img sppiner png 我努力了argu
  • gulp-uglify 不会保留文件顺序

    当我使用吞咽丑化 https github com terinjokes gulp uglify为了缩小 Javascript 文件 顺序变得混乱 可以说我让这个任务按预期工作 var gulp require gulp var renam
  • 我应该为每个 Web 请求使用静态缓存的 ResourceManager 还是一个新实例?有关系吗?

    创建新的 NET 对性能 或其他 有何影响 如果有 ResourceManager根据每个请求new ResourceManger myResourceType FullName myResourceType Assembly 与在 Des
  • JSF“总”变量类似于 JSTL 中的 c:set

    我不喜欢 JSF 但我需要用它来解决这个问题 我正在 纯 JSF 中工作 所以这就是我基本上需要的 但我不知道如何用 JSF 来实现它
  • JSF - 实施受限页面过滤器

    我正在关注 BalusC 的回答JSF 2 0 如何获取在浏览器地址栏中输入的 URL https stackoverflow com questions 4105263 jsf 2 0 how to get the url that is
  • 丰富:数据表行跨度问题

    我需要创建一个 rich dataTable 甚至扩展 具有以下功能 我有一个公司类 其中包含产品对象的集合 我想展示下表 我仍然没有弄清楚如何使用子表执行此操作 在所有示例中 我发现子表具有与主表完全相同的列 据推测 我需要在前两列中使用
  • 如何在 h:outputText 中写入单引号和双引号

    如何在 value 属性中写入单引号和双引号h outputText 这应该适用于标准语法 JSP 和 XML 视图技术
  • JSF 2.x @EJB 依赖注入错误

    因此 正如编程中通常发生的那样 我提出了一个问题 而一个潜在的解决方案又引发了更多问题 和错误 我刚开始使用 servlet JSF 和 EJB 并且遇到了依赖注入错误 这是我原来的问题 JSF h 标签不显示 https stackove
  • 但是创建静态实用方法不应该被过度使用吗?如何避免呢? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 随着时间的推移 java项目中引入了许多实用方法来完成更复杂和简单的任务 当使用静态方法时 我们在代码中引入了紧密耦合 这使得我们的代
  • 如何显示/隐藏jsf组件

    在我的一个 JSF 应用程序中 顶部的标题部分包含 selectOneMenu 底部的内容部分显示过滤器组件 默认情况下 应用程序首先在顶部显示 selectOneMenu 数据 在底部显示相应的 Filter 信息 如果用户选择不同的se
  • Java加减法与金钱

    我正在尝试对美元和美分进行加法和减法 但在超过 100 美分和低于 0 美分时遇到困难 我的代码可以很好地添加任何内容 直到我需要将 100 美分转换为 1 美元 我无法将我的话转化为代码 但我知道需要做什么才能将美分转换成美元 仅供参考

随机推荐

  • Redis作为数据库

    我想使用Redis作为数据库 而不是缓存 根据我 有限 的理解 Redis 是一个内存数据存储 使用 Redis 有哪些风险 如何减轻这些风险 您可以通过多种不同方式将 Redis 用作权威存储 Turn on AOF 仅追加文件存储 请参
  • 使用 Amazon SES 的 Codeigniter SMTP 电子邮件

    我想昨天亚马逊宣布了对 SES 简单电子邮件服务 的 SMTP 支持 我尝试使用 Codeigniter 发送 SMTP 电子邮件 但没有成功 我有一个经过验证的发件人 一切看起来都很好 this gt load gt library em
  • 查找从开始日期到年底的每个星期五

    所以我又带着另一个令人困惑的日期时间问题回来了 在 C 中 我如何返回从开始日期 DateTime Now 到今年年底 例如 今天是 19 号星期五 它将返回 26 2 9 16 23 30 7 等 这有效吗 static IEnumera
  • 如何用 Dotrine 保存图像

    我想在我的文章实体中保留图像 Doctrine2 可以做到这一点吗 我该怎么做呢 此致 博多 使用带有 File 类型的 ODM 设置非常简单 对于 ORM 实体 我会考虑创建一个像这样的列 注意 使用此链接将 blob 类型添加到 Doc
  • 使用 multipart/form-data 和 JSON 进行 Curl 发布

    有没有办法使用curl来做multipart form data发布并指定数据是application json 它不一定是卷曲的 这就是我使用的 效果很好 curl v H Content Type multipart form data
  • 使用纱线工作区在 npm 上创建可用的库...

    我正在寻找有关我编码的模式的反馈一边修修补补 Context 我正在为我的公司创建一个包 该包将公开发布 DX 开发人员体验 对我们来说是最重要的 为此我选择了开发中的最新趋势 Typescript esm 等 我想提出多个模块并使导入易于
  • 无向图中,最简单的环必须有3个节点?

    我正在尝试写一个关于循环和无向图的证明 但我对某些事情感到困惑 如果我的图只有 2 个顶点和一条连接它们的边 那不是循环 不是吗 因此 我需要至少 3 个顶点 其中 2 个顶点与其中一个节点之间有 2 个连接 另外两个顶点之间有一个连接 以
  • Java程序使用javascript读取html页面并保存其内容

    我想使用javascript提取html内容 从网站中提取用户的评论 在chrome中另存为 Inspect Element 我试试这个 Java 程序读取 html 页面并将其 HTML 代码保存在文本文件中 https stackove
  • 这是 C# 中将分隔字符串转换为 int 数组的最佳方法吗?

    给出以下字符串 string str 1 2 3 这是否是将其转换为最佳扩展int array static class StringExtensions public static int ToIntArray this string s
  • 使用python查找CSV文件每列的最小值

    我创建了一个程序 可以查找 CSV 文件每一行的最小值 现在我想对每一列执行相同的操作 但我无法这样做 任何建议将不胜感激 谢谢 Import and convert csv import csv data with open file r
  • Java HashMap 中的冲突解决

    Java HashMap uses put插入K V对的方法HashMap 可以说我用过put方法和现在HashMap
  • 在cloudfoundry上发布项目时出现错误java.io.FileNotFoundException(log4j日志文件)

    我计划将 Web 应用程序与 mysql 服务一起迁移到 Cloud Foundry 但我遇到了以下错误 它与当地环境配合良好 日志文件也会生成 下面是错误日志 Jan 5 2013 7 35 59 AM org cloudfoundry
  • X-Forwarded-Proto 和 Flask

    我有完全相同的问题描述这个问题和答案 https stackoverflow com questions 19840051 mutating request base url in flask 这个问题的答案是一个很好的解决方法 但我不明白
  • 用c++做一个定时器?

    我正在用 C 开发一个简单的游戏 一种追逐点风格的游戏 你必须单击显示屏上的一个绘制的圆圈 然后每次单击都会跳到另一个随机位置 但我想让游戏在 60 后结束几秒钟左右 将分数写入文本文件 然后在启动程序时从文本文件中读取信息并将信息存储到数
  • 使用默认分隔符与用户定义分隔符分割字符串

    我尝试了一个使用字符串分割的简单示例 但出现了一些意外的行为 这是示例代码 def split string source splitlist for delim in splitlist source source replace del
  • 防止渐变叠加滚动

    我试图在滚动 div 的底部放置一个小渐变 我的解决方案基于已接受的答案这个所以线程 https stackoverflow com questions 9204450 how to apply a fade away effect not
  • Facebook Messenger API“无法验证 URL”

    我正在尝试设置 Facebook Messenger API 但在尝试添加 WebHook 时收到此错误 无法验证该 URL 回调验证失败 出现以下错误 curl errno 60 curl error SSL证书问题 无法获取本地颁发者证
  • init] 自动引用计数

    我知道我应该使用 ObjectClass tmpObject ObjectClass alloc init realObject tmpObject tmpObject release 初始化realObject where realObj
  • 如何遍历NLTK树对象?

    给定一个括号内的解析 我可以将其转换为 NLTK 中的 Tree 对象 如下所示 gt gt gt from nltk tree import Tree gt gt gt s ROOT S NP NNP Europe VP VBZ is P
  • JSF2 静态资源管理——组合、压缩

    有谁知道动态组合 缩小所有 h outputStylesheet 资源 然后在渲染阶段组合 缩小所有 h outputScript 资源的方法 混合 缩小的资源可能需要使用基于组合资源字符串或其他内容的键进行缓存 以避免过度处理 如果这个功