3

我使用 Tapestry 4,每当我们推送一个更改任何资产(图像、样式表、JS 库)的版本时,我们都会遇到问题,因为用户的浏览器缓存中仍然有旧版本的资产。我想设置一些简单的方法来允许缓存,但在我们更新应用程序时强制下载新的资产。简单地完全禁止对资产进行缓存不是一个可接受的解决方案。

我看不到执行此操作的任何现有机制,但我认为可能有某种方法可以告诉 Tapestry 将内部版本号添加到 URL,如下所示:

http://www.test.com/path/to/the/asset/asset.jpg?12345

这样,每一个新的构建都会使它看起来像最终用户的不同资产。

Tapestry 是否提供了一种简单的方法来解决我不知道的缓存问题?如果不是,如何修改 Tapestry 生成的 URL?负责执行此操作的代码将如何获得内部版本号?(例如,我可以将内部版本号放入 Spring bean 中,但是新的 URL 构建机制将如何获取它?)

4

1 回答 1

6

这个问题纠结了很久,最终还是自己解决了。此解决方案假定您的项目中有Tapestry-spring 库

就我而言,我有一个 Spring bean,其中包含我的应用程序的一些全局属性:

package myapp;

public class AppProperties {
    private String build;

    public String getBuild() {
        return build;
    }

    public void setBuild(String build) {
        this.build = build;
    }

    // other properties
}

在你的 Spring 配置中声明这个 bean:

<bean id="appProperties" class="myapp.AppProperties">
    <property name="build" value="@BUILD_NUMBER@"/>
</bean>

您可以设置您的 Ant 构建脚本以替换@BUILD_NUMBER@为实际数字(有关详细信息,请参阅 Ant 手册中的Copy任务)。

现在创建一个将包装IAssets 并将内部版本号附加到 URL 的类:

package myapp;

import java.io.InputStream;

import org.apache.hivemind.Location;
import org.apache.hivemind.Resource;
import org.apache.tapestry.IAsset;

public class BuildAwareAssetWrapper implements IAsset {
    private IAsset wrapped;
    private String build;

    public BuildAwareAssetWrapper(IAsset wrapped, String build) {
        this.wrapped = wrapped;
        this.build = build;
    }

    public String buildURL() {
        return addParam(wrapped.buildURL(), "build", build);
    }

    public InputStream getResourceAsStream() {
        return wrapped.getResourceAsStream();
    }

    public Resource getResourceLocation() {
        return wrapped.getResourceLocation();
    }

    public Location getLocation() {
        return wrapped.getLocation();
    }

    private static String addParam(String url, String name, String value) {
        if (url == null) url = "";
        char sep = url.contains("?") ? '&' : '?';
        return url + sep + name + '=' + value;
    }
}

接下来,我们需要让 Tapestry 用我们的包装器包装所有资产。该类AssetSourceImpl负责为IAssetTapestry 提供实例。我们将扩展这个类并覆盖该findAsset()方法,以便我们可以用包装类包装创建的资产:

package myapp;

import java.util.Locale;

import org.apache.hivemind.Location;
import org.apache.hivemind.Resource;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.asset.AssetSourceImpl;

public class BuildAwareAssetSourceImpl extends AssetSourceImpl {
    private AppProperties props;

    @Override
    public IAsset findAsset(Resource base, String path, Locale locale, Location location) {
        IAsset asset = super.findAsset(base, path, locale, location);
        return new BuildAwareAssetWrapper(asset, props.getBuild());
    }

    public void setAppProperties(AppProperties props) {
        this.props = props;
    }
}

请注意,实现有一个可以接受我们的 Spring bean 的 setter。最后一步是让 Tapestry 用于BuildAwareAssetSourceImpl创建资产,而不是AssetSourceImpl. 我们通过覆盖相应的服务点来做到这一点hivemodule.xml

<!-- Custom asset source -->
<implementation service-id="tapestry.asset.AssetSource">
    <invoke-factory service-id="hivemind.BuilderFactory" model="singleton">
        <construct class="myapp.BuildAwareAssetSourceImpl">
            <set-object property="appProperties" value="spring:appProperties"/>
            <set-configuration property="contributions" configuration-id="tapestry.asset.AssetFactories"/>
            <set-service property="lookupAssetFactory" service-id="tapestry.asset.LookupAssetFactory"/>
            <set-service property="defaultAssetFactory" service-id="tapestry.asset.DefaultAssetFactory"/>
        </construct>
    </invoke-factory>
</implementation>

就是这样。如果您运行应用程序并查看使用资产的任何页面的源代码,您将看到 URL 上将包含新build参数。

于 2009-02-25T05:39:02.650 回答