YUI3中,为了避免js文件过大,各个功能模块是拆分的。它有一个“种子”的概念:先下载一个小的核心的js文件到浏览器端,再通过这个小的js文件去加载其它所需的模块。

这种按需加载虽然解决了单个js过大的问题,但是随之带来另外一个问题:如果一个页面使用了YUI的a、b、c功能,那么浏览器就要向服务器请求a.js、b.js、c.js三个文件,这样增加了浏览器向服务器的沟通次数。

为了解决后面的问题,YUI3又有一个combine的概念,预先下载的那个核心的js,把页面上需要的a、b、c模块合并成一个请求发给服务器,类似这样:http://mydomain.com/conbine?a.js&b.js&c.js。

这要求服务器接收到http://mydomain.com/conbine请求后,将参数取出来,找到对应的a、b、c的js文件,合并成一个js文件,返回给客户端。Combo Handler是Yahoo!开发的一个Apache模块,专门来干这个事情的。

如果只用java的web容器,可以把这项工作委托给servlet:

1
2
3
4
<servlet>
    <servlet-name>yuicombo</servlet-name>
    <servlet-class>org.siqisource.mozo.servlets.YuiCombineServlet</servlet-class>
</servlet>

对应的YUI配置为:

1
2
3
4
5
YUI.GlobalConfig = {
    combine: true,
    comboBase: '<mz:webRoot/>/yuicombo?',
    root: 'uilibrary/yui/',
};

在servlet代码中,我使用了YUI的yuicompressor来压缩js和css文件,下面是maven配置。

1
2
3
4
5
<dependency>
    <groupId>com.yahoo.platform.yui</groupId>
    <artifactId>yuicompressor</artifactId>
    <version>2.4.7</version>
</dependency>

其中的Path类只是为了获得web应用物理路径,在是用的时候替换一下即可。

目前已知的缺陷:对于css的按需加载,浏览器会请求客户端两次,目前不清楚是不是YUI3(测试版本:3.7.2)的问题。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package org.siqisource.mozo.servlets;
 
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.io.FileUtils;
import org.siqisource.mozo.context.Path;
 
import com.yahoo.platform.yui.compressor.CssCompressor;
import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
 
public class YuiCombineServlet extends HttpServlet {
 
    private static Map<String, String> cachedResources = new HashMap<String, String>();
 
    private String cacheContextPath = "uilibrary/yui/cache/";
 
    private String cacheDir = Path.getPhysicalPath() + cacheContextPath;
 
    int linebreakpos = -1;
    boolean munge = true;
    boolean verbose = false;
    boolean preserveAllSemiColons = false;
    boolean disableOptimizations = false;
 
    @Override
    public void init() throws ServletException {
 
        // 重置缓存文件夹
        File catchedDir = new File(cacheDir);
 
        if (catchedDir.exists()) {
            FileDeleteStrategy strategy = FileDeleteStrategy.FORCE;
            try {
                strategy.delete(catchedDir);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        catchedDir.mkdirs();
 
        super.init();
    }
 
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
 
        String queryString = request.getQueryString();
        String resourcePath = cachedResources.get(queryString);
        // 已缓存
        if (resourcePath == null) {
 
            String[] resources = queryString.split("&");
            String firstResource = resources[0];
 
            String fileName = UUID.randomUUID().toString();
 
            if (firstResource.endsWith(".js")) {
 
                fileName += ".js";
                Writer writer = new FileWriter(cacheDir + fileName);
                for (String resource : resources) {
                    Reader reader = new FileReader(Path.getPhysicalPath()
                            + resource);
                    JavaScriptCompressor compressor = new JavaScriptCompressor(
                            reader, null);
                    compressor.compress(writer, linebreakpos, munge, verbose,
                            preserveAllSemiColons, disableOptimizations);
                    reader.close();
                }
                writer.flush();
                writer.close();
 
            } else if (resources[0].endsWith(".css")) {
                fileName += ".css";
                Writer writer = new FileWriter(cacheDir + fileName);
                for (String resource : resources) {
                    Reader reader = new FileReader(replacedUrlFile(resource));
                    CssCompressor compressor = new CssCompressor(reader);
                    compressor.compress(writer, linebreakpos);
                    reader.close();
                }
                writer.flush();
                writer.close();
            }
 
            resourcePath = cacheContextPath + fileName;
            cachedResources.put(queryString, resourcePath);
        }
        request.getRequestDispatcher(resourcePath).forward(request, response);
        return;
    }
     
    public String replacedUrlFile(String fileName) throws IOException {
 
        String cssfilePath = Path.getPhysicalPath() + fileName;
        File cssFile = new File(cssfilePath);
 
        String tempCssFilePath = cacheDir + "tmp-css-" + cssFile.getName();
        File tempCssFile = new File(tempCssFilePath);
        if (tempCssFile.exists()) {
            return tempCssFilePath;
        }
 
        // 判断是否需要替换
        String css = FileUtils.readFileToString(cssFile);
        int maxIndex = css.length() - 1;
        int appendIndex = 0;
        Pattern p = Pattern.compile("url\\(\\s*([\"']?)");
        if (!p.matcher(css).find()) {
            return cssfilePath;
        }
 
        // 真的需要替换
        Matcher m = p.matcher(css);
        String url = fileName.substring(0, fileName.lastIndexOf('/'));
        url = Path.getContextPath() + "/" + url + "/";
 
        StringBuffer replacedUrlCss = new StringBuffer();
 
        while (m.find()) {
            int startIndex = m.start() + 4; // "url(".length()
            String terminator = m.group(1); // ', " or empty (not quoted)
 
            if (terminator.length() == 0) {
                terminator = ")";
            }
 
            boolean foundTerminator = false;
 
            int endIndex = m.end() - 1;
            while (foundTerminator == false && endIndex + 1 <= maxIndex) {
                endIndex = css.indexOf(terminator, endIndex + 1);
 
                if ((endIndex > 0) && (css.charAt(endIndex - 1) != '\\')) {
                    foundTerminator = true;
                    if (!")".equals(terminator)) {
                        endIndex = css.indexOf(")", endIndex);
                    }
                }
            }
 
            // Enough searching, start moving stuff over to the buffer
            replacedUrlCss.append(css.substring(appendIndex, m.start()));
 
            if (foundTerminator) {
                String token = css.substring(startIndex, endIndex);
                token = token.replaceAll("\\s+", "");
                String preserver = "url('" + url + token + "')";
                replacedUrlCss.append(preserver);
 
                appendIndex = endIndex + 1;
            } else {
                // No end terminator found, re-add the whole match. Should we
                // throw/warn here?
                replacedUrlCss.append(css.substring(m.start(), m.end()));
                appendIndex = m.end();
            }
        }
        FileUtils.writeStringToFile(tempCssFile, replacedUrlCss.toString());
        return tempCssFilePath;
 
    }
}

java版的YUI3 combine服务-Combo Handler的更多相关文章

  1. ArcGIS Server 10 Java 版的Rest服务手动配置方法

    Java版的Manager中发布的服务默认只发布了该服务的SOAP接口,而REST接口需要用户在信息服务器,如Tomcat. Apache.WebLogic等中手工配置.由于在Java版的Server ...

  2. ArcGIS Server 10 Java 版的Rest服务的部署方法

    使用ArcGIS Server 10 Java版发布GIS服务,当使用ArcGIS Manager创建好服务后,然后打开“ArcGIS Services Directory”的链接时发现网页报出了找不 ...

  3. java版gRPC实战之二:服务发布和调用

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. java版gRPC实战之三:服务端流

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. java版gRPC实战之六:客户端动态获取服务端地址

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  6. 第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...

  7. 第五篇 :微信公众平台开发实战Java版之如何获取公众号的access_token以及缓存access_token

    一.access_token简介 为了使第三方开发者能够为用户提供更多更有价值的个性化服务,微信公众平台 开放了许多接口,包括自定义菜单接口.客服接口.获取用户信息接口.用户分组接口.群发接口等, 开 ...

  8. 第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备

    相信很多人或多或少听说了微信公众平台的火热.但是开发还是有一点门槛,鉴于挺多朋友问我怎么开发,问多了,自己平时也进行以下总结.所以下面给大家分享一下我的经验: 微信公众号是什么? 官网的介绍:再小的个 ...

  9. Oracle Berkeley DB Java 版

    Oracle Berkeley DB Java 版是一个开源的.可嵌入的事务存储引擎,是完全用 Java 编写的.它充分利用 Java 环境来简化开发和部署.Oracle Berkeley DB Ja ...

随机推荐

  1. iOS应用日志:开始编写日志组件与异常日志

    应用日志(一):开始编写日志组件 对于那些做后端开发的工程师来说,看 LOG解Bug应该是理所当然的事,但我接触到的移动应用开发的工程师里面,很多人并没有这个意识,查Bug时总是一遍一遍的试图重现,试 ...

  2. 【NOI2015】软件包管理器

    NOI难得的水题,话说还是T2诶……又学到了线段树的一种新的魔性使用 看sxysxy大神的代码才写出来的,sxysxy_orz 原题: Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包 ...

  3. web开发注意的问题

    1.<input type="submit" value="提交">    将表单提交<form action="login.jsp ...

  4. Linux下PHP+MySQL+CoreSeek中文检索引擎配置

    说明: 操作系统:CentOS 5.X 服务器IP地址:192.168.21.127 Web环境:Nginx+PHP+MySQL 站点根目录:/usr/local/nginx/html 目的:安装co ...

  5. Quarzt.NET 任务调度框架

      Quartz.NET是一个开源的作业调度框架,是 OpenSymphony 的 Quartz API 的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性 ...

  6. 什么是Spring的命名空间及使用Spring 的命名空间p 装配属性

    这个就要从XML说了,Spring的配置管理可以利用XML方式进行配置,而XML里面就有命名空间这个概念..实际上就和标签的意思有点像 你给一个命名空间以后,这个XML文件里面就可以用那个命名空间上下 ...

  7. 题目1049:字符串去特定字符——九度OJ

    题目1049:字符串去特定字符 http://ac.jobdu.com/problem.php?pid=1049 时间限制:1 秒 内存限制:32 兆 题目描述: 输入字符串s和字符c,要求去掉s中所 ...

  8. 通过CSS禁用页面模块的复制和粘贴功能

    样式代码: -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; -khtml-user-select: ...

  9. 利用Swift之协议语法实现页面间的传值功能

    随着Swift 新开发语言的发布,又随着Xcode6.0.1的正式发布,利用swift编写iOS代码迫在眉睫,笔者在使用Objective-C开发近三年以来,对这种优雅的语法深感赞叹,下面我将对比式的 ...

  10. js停止冒泡和阻止浏览器默认行为

    停止冒泡通用方法: function stopBubble(e) { //如果提供了事件对象,是非IE浏览器 if ( e && e.stopPropagation ) //使用W3C ...