- 浏览: 26522 次
-
最新评论
最近在对一个web系统做性能优化.
而对用到的静态资源文件的压缩整合则是前端性能优化中很重要的一环.
好处不仅在于能够减小请求的文件体积,而且能够减少浏览器的http请求数.
因为是基于java的web系统,并且使用的是nginx+tomcat做为服务器.
最后考虑用wro4j和maven plugin在编译期间压缩静态资源.
优化前:
基本上所有的jsp都引用了这一大坨静态文件:
Html代码 收藏代码
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/skin.css"/>
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/jquery-ui-1.8.23.custom.css"/>
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/validationEngine.jquery.css"/>
<script type="text/javascript">var GV = {ctxPath: '${ctxPath}',imgPath: '${ctxPath}/css'};</script>
<script type="text/javascript" src="${ctxPath}/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery-ui-1.8.23.custom.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.validationEngine.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.validationEngine-zh_CN.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.fixedtableheader.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/roll.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.pagination.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.rooFixed.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.ui.datepicker-zh-CN.js"></script>
<script type="text/javascript" src="${ctxPath}/js/json2.js"></script>
<script type="text/javascript" src="${ctxPath}/js/common.js"></script>
引用的文件很多,并且文件体积没有压缩,导致页面请求的时间非常长.
另外还有一个问题,就是为了能够充分利用浏览器的缓存,静态资源的文件名称最好能够做到版本化控制.
这样前端web服务器就可以放心大胆的开启缓存功能而不用担心缓存过期问题,因为如果一旦静态资源文件有修改的话,
会重新生成一个文件名称.
下面我根据自己项目的经验,来介绍下如何较好的解决这两个问题.
分两步进行.
第一步:引入wro4j,在编译时期将上述分散的多个文件整合成少数几个文件,并且将文件最小化.
第二步:在生成的静态资源文件的文件名称上加入时间信息
这是两步优化之后的引用情况:
Html代码 收藏代码
${platform:cssFile("/wro/basic") }
<script type="text/javascript">var GV = {ctxPath: '${ctxPath}',imgPath: '${ctxPath}/css'};</script>
${platform:jsFile("/wro/basic") }
${platform:jsFile("/wro/custom") }
只引用了1个css文件,2个js文件.http请求从10几个减少到3个,并且整体文件体积缩小了近一半.
下面介绍优化流程.
第一步:合并并且最小化文件.
1.添加wro4j的maven依赖
Xml代码 收藏代码
<wro4j.version>1.6.2</wro4j.version>
...
<dependency>
<groupId>ro.isdc.wro4j</groupId>
<artifactId>wro4j-core</artifactId>
<version>${wro4j.version}</version>
<exclusions>
<exclusion>
<!-- 因为项目中的其他jar包已经引入了不同版本的slf4j,所以这里避免jar重叠所以不引入 -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
2.添加wro4j maven plugin
Xml代码 收藏代码
<plugin>
<groupId>ro.isdc.wro4j</groupId>
<artifactId>wro4j-maven-plugin</artifactId>
<version>${wro4j.version}</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<targetGroups>basic,custom</targetGroups>
<!-- 这个配置是告诉wro4j在打包静态资源的时候是否需要最小化文件,开发的时候可以设成false,方便调试 -->
<minimize>true</minimize>
<destinationFolder>${basedir}/src/main/webapp/wro/</destinationFolder>
<contextFolder>${basedir}/src/main/webapp/</contextFolder>
<!-- 这个配置是第二步优化需要用到的,暂时忽略 -->
<wroManagerFactory>com.rootrip.platform.common.web.wro.CustomWroManagerFactory</wroManagerFactory>
</configuration>
</plugin>
如果开发环境是eclipse的话,可以下载m2e-wro4j这个插件.
下载地址:http://download.jboss.org/jbosstools/updates/m2e-wro4j/
这个插件的主要功能是能够帮助我们在开发环境下修改对应的静态文件,或者pom.xml文件的时候能够自动生成打包好的js和css文件.
对开发来说就会方便很多.只要修改源文件就能看见修改后的结果.
3.在WEB-INF目录下添加wro.xml文件,这个文件的作用就是告诉wro4j需要以怎样的策略打包jss和css文件.
Java代码 收藏代码
<?xml version="1.0" encoding="UTF-8"?>
<groups xmlns="http://www.isdc.ro/wro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd">
<group name="basic">
<css>/css/basic.css</css>
<css>/css/skin.css</css>
<css>/css/jquery-ui-1.8.23.custom.css</css>
<css>/css/validationEngine.jquery.css</css>
<js>/js/jquery-1.7.2.min.js</js>
<js>/js/jquery-ui-1.8.23.custom.min.js</js>
<js>/js/jquery.validationEngine.js</js>
<js>/js/jquery.fixedtableheader.min.js</js>
<js>/js/roll.js</js>
<js>/js/jquery.pagination.js</js>
<js>/js/jquery.rooFixed.js</js>
<js>/js/jquery.ui.datepicker-zh-CN.js</js>
<js>/js/json2.js</js>
</group>
<group name="custom">
<js>/js/jquery.validationEngine-zh_CN.js</js>
<js>/js/common.js</js>
</group>
</groups>
官方文档:http://code.google.com/p/wro4j/wiki/WroFileFormat
其实这个配置文件很好理解,如果不愿看官方文档的朋友我在这简单介绍下.
上面这样配置的目的就是告诉wro4j要将
<css>/css/basic.css</css>
<css>/css/skin.css</css>
<css>/css/jquery-ui-1.8.23.custom.css</css>
<css>/css/validationEngine.jquery.css</css>
这四个文件整合到一起,生成一个叫basic.css的文件到指定目录(wro4j-maven-plugin里配置的),将
<js>/js/jquery-1.7.2.min.js</js>
<js>/js/jquery-ui-1.8.23.custom.min.js</js>
<js>/js/jquery.validationEngine.js</js>
<js>/js/jquery.fixedtableheader.min.js</js>
<js>/js/roll.js</js>
<js>/js/jquery.pagination.js</js>
<js>/js/jquery.rooFixed.js</js>
<js>/js/jquery.ui.datepicker-zh-CN.js</js>
<js>/js/json2.js</js>
这几个文件整合到一起,生成一个叫basic.js的文件到指定目录.
最后将
<js>/js/jquery.validationEngine-zh_CN.js</js>
<js>/js/common.js</js>
这两个文件整合到一起,,生成一个叫custom.js的文件到指定目录.
第一步搞定,这时候如果你的开发环境是eclipse并且安装了插件的话,应该就能在你工程的%your webapp%/wor/目录下看见生成好的
basic.css,basic.js和custom.js这三个文件了.
然后你再将你的静态资源引用路径改成
Html代码 收藏代码
<link rel="stylesheet" type="text/css" href="${ctxPath}/wro/basic.css"/>
<script type="text/javascript" src="${ctxPath}/wro/basic.js"></script>
<script type="text/javascript" src="${ctxPath}/wro/custom.js"></script>
就ok了.每次修改被引用到的css或js文件的时候,这些文件都将重新生成.
如果开发环境是eclipse但是没有安装m2e-wro4j插件的话,pom.xml可能需要额外配置.
请参考:https://community.jboss.org/en/tools/blog/2012/01/17/css-and-js-minification-using-eclipse-maven-and-wro4j
第二步:给生成的文件名称中加入时间信息并通过el自定义函数引用脚本文件.
1. 创建DailyNamingStrategy类
Java代码 收藏代码
public class DailyNamingStrategy extends TimestampNamingStrategy {
protected final Logger log = LoggerFactory.getLogger(DailyNamingStrategy.class);
@Override
protected long getTimestamp() {
String dateStr = DateUtil.formatDate(new Date(), "yyyyMMddHH");
return Long.valueOf(dateStr);
}
}
2.创建CustomWroManagerFactory类
Java代码 收藏代码
//这个类就是在wro4j-maven-plugin里配置的wroManagerFactory参数
public class CustomWroManagerFactory extends
DefaultStandaloneContextAwareManagerFactory {
public CustomWroManagerFactory() {
setNamingStrategy(new DailyNamingStrategy());
}
}
上面这两个类的作用是使用wro4j提供的文件命名策略,这样生成的文件名就会带上时间信息了.
例如:basic-2013020217.js
但是现在又会发现一个问题:如果静态资源文件名称不固定的话,那怎么样引用呢?
这时候就需要通过动态生成<script>与<link>来解决了.
因为项目使用的是jsp页面,所以通过el自定义函数来实现标签生成.
3.创建PlatformFunction类
Java代码 收藏代码
public class PlatformFunction {
private static Logger log = LoggerFactory.getLogger(PlatformFunction.class);
private static ConcurrentMap<String, String> staticFileCache = new ConcurrentHashMap<>();
private static AtomicBoolean initialized = new AtomicBoolean(false);
private static final String WRO_Path = "/wro/";
private static final String JS_SCRIPT = "<script type=\"text/javascript\" src=\"%s\"></script>";
private static final String CSS_SCRIPT = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">";
private static String contextPath = null;
/**
* 该方法根据给出的路径,生成js脚本加载标签
* 例如传入参数/wro/custom,该方法会寻找webapp路径下/wro目录中以custom开头,以js后缀结尾的文件名称名称.
* 然后拼成<script type="text/javascript" src="${ctxPath}/wro/custom-20130201.js"></script>返回
* 如果查找到多个文件,返回根据文件名排序最大的文件
* @param str
* @return
*/
public static String jsFile(String filePath) {
String jsFile = staticFileCache.get(buildCacheKey(filePath, "js"));
if(jsFile == null) {
log.error("加载js文件失败,缓存中找不到对应的文件[{}]", filePath);
}
return String.format(JS_SCRIPT, jsFile);
}
/**
* 该方法根据给出的路径,生成css脚本加载标签
* 例如传入参数/wro/custom,该方法会寻找webapp路径下/wro目录中以custom开头,以css后缀结尾的文件名称名称.
* 然后拼成<link rel="stylesheet" type="text/css" href="${ctxPath}/wro/basic-20130201.css">返回
* 如果查找到多个文件,返回根据文件名排序最大的文件
* @param str
* @return
*/
public static String cssFile(String filePath) {
String cssFile = staticFileCache.get(buildCacheKey(filePath, "css"));
if(cssFile == null) {
log.error("加载css文件失败,缓存中找不到对应的文件[{}]", filePath);
}
return String.format(CSS_SCRIPT, cssFile);
}
public static void init() throws IOException {
if(initialized.compareAndSet(false, true)) {
ServletContext sc = Platform.getInstance().getServletContext();
if(sc == null) {
throw new PlatformException("查找静态资源的时候的时候发现servlet context 为null");
}
contextPath = Platform.getInstance().getContextPath();
File wroDirectory = new ServletContextResource(sc, WRO_Path).getFile();
if(!wroDirectory.exists() || !wroDirectory.isDirectory()) {
throw new PlatformException("查找静态资源的时候发现对应目录不存在[" + wroDirectory.getAbsolutePath() + "]");
}
//将wro目录下已有文件加入缓存
for(File file : wroDirectory.listFiles()) {
handleNewFile(file);
}
//监控wro目录,如果有文件生成,则判断是否是较新的文件,是的话则把文件名加入缓存
new Thread(new WroFileWatcher(wroDirectory.getAbsolutePath())).start();
}
}
private static void handleNewFile(File file) {
String fileName = file.getName();
Pattern p = Pattern.compile("^(\\w+)\\-\\d+\\.(js|css)$");
Matcher m = p.matcher(fileName);
if(!m.find() || m.groupCount() < 2) return;
String fakeName = m.group(1);
String fileType = m.group(2);
//暂时限定只能匹配/wro/目录下的文件
String key = buildCacheKey(WRO_Path + fakeName, fileType);
if(staticFileCache.putIfAbsent(key, fileName) != null) {
synchronized(staticFileCache) {
String cachedFileName = staticFileCache.get(key);
if(fileName.compareTo(cachedFileName) > 0) {
staticFileCache.put(key, contextPath + WRO_Path + fileName);
}
}
}
}
private static String buildCacheKey(String fakeName, String fileType) {
return fakeName + "-" + fileType;
}
static class WroFileWatcher implements Runnable {
private static Logger log = LoggerFactory.getLogger(WroFileWatcher.class);
private String wroAbsolutePathStr;
public WroFileWatcher(String wroPathStr) {
this.wroAbsolutePathStr = wroPathStr;
}
@Override
public void run() {
Path path = Paths.get(wroAbsolutePathStr);
File wroDirectory = path.toFile();
if(!wroDirectory.exists() || !wroDirectory.isDirectory()) {
String message = "监控wro目录的时候发现对应目录不存在[" + wroAbsolutePathStr + "]";
log.error(message);
throw new PlatformException(message);
}
log.warn("开始监控wro目录[{}]", wroAbsolutePathStr);
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
path.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
while (true) {
WatchKey key = null;
try {
key = watcher.take();
} catch (InterruptedException e) {
log.error("", e);
continue;
}
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> e = (WatchEvent<Path>) event;
Path filePath = e.context();
handleNewFile(filePath.toFile());
}
if (!key.reset()) {
break;
}
}
} catch (IOException e) {
log.error("监控wro目录发生错误", e);
}
log.warn("停止监控wro目录[{}]", wroAbsolutePathStr);
}
}
}
对应的tld文件就不给出了,根据方法签名编写就行了.
其中的cssFile和jsFile方法分别实现引用css和js文件.
在页面使用的时候类似这样:
${platform:cssFile("/wro/basic") }
${platform:jsFile("/wro/custom") }
这个类的主要功能就是使用jdk7的WatchService监控wro目录的新增文件事件,
一旦有新的文件加到目录里,判断这个文件是不是最新的,如果是的话则使用这个文件名称引用.
这样一旦有新加的资源文件放到wro目录里,则能够自动被引用,不需要做任何代码上的修改,并且基本不影响性能.
到此为止功能已经实现.
但是我考虑到还有两个问题有待完善:
1.因为生成的文件名称精确到小时,如果这个小时之内有多次代码修改,生成的文件名都完全一样.
这样就算线上的代码有修改,对于已经有该文本缓存的浏览器来说,不会重新请求文件,也就看不到文件变化.
不过一般来说线上代码不会如此频繁改动,对于大多数应用来说影响不大.
2.在开发环境开发一段时间之后,wro目录下会生成一大堆的文件(因为m2e-wro4j插件在生成新的文件的时候不会删除旧文件,如果文件名相同会覆盖掉以前的文件),
这时候就需要手动删除时间靠前的旧文件,虽然系统会忽略旧文件,但是我相信大多数程序员和我一样是有些许洁癖的吧.
解决办法还是不少,比如可以写脚本定期清理掉旧文件.
时间有限,有些地方考虑的不是很完善,欢迎拍砖.
参考资料:
http://meri-stuff.blogspot.sk/2012/08/wro4j-page-load-optimization-and-lessjs.html#Configuration
https://community.jboss.org/en/tools/blog/2012/01/17/css-and-js-minification-using-eclipse-maven-and-wro4j
http://code.google.com/p/wro4j/wiki/MavenPlugin
http://code.google.com/p/wro4j/wiki/WroFileFormat
http://java.dzone.com/articles/using-java-7s-watchservice
而对用到的静态资源文件的压缩整合则是前端性能优化中很重要的一环.
好处不仅在于能够减小请求的文件体积,而且能够减少浏览器的http请求数.
因为是基于java的web系统,并且使用的是nginx+tomcat做为服务器.
最后考虑用wro4j和maven plugin在编译期间压缩静态资源.
优化前:
基本上所有的jsp都引用了这一大坨静态文件:
Html代码 收藏代码
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/skin.css"/>
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/jquery-ui-1.8.23.custom.css"/>
<link rel="stylesheet" type="text/css" href="${ctxPath}/css/validationEngine.jquery.css"/>
<script type="text/javascript">var GV = {ctxPath: '${ctxPath}',imgPath: '${ctxPath}/css'};</script>
<script type="text/javascript" src="${ctxPath}/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery-ui-1.8.23.custom.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.validationEngine.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.validationEngine-zh_CN.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.fixedtableheader.min.js"></script>
<script type="text/javascript" src="${ctxPath}/js/roll.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.pagination.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.rooFixed.js"></script>
<script type="text/javascript" src="${ctxPath}/js/jquery.ui.datepicker-zh-CN.js"></script>
<script type="text/javascript" src="${ctxPath}/js/json2.js"></script>
<script type="text/javascript" src="${ctxPath}/js/common.js"></script>
引用的文件很多,并且文件体积没有压缩,导致页面请求的时间非常长.
另外还有一个问题,就是为了能够充分利用浏览器的缓存,静态资源的文件名称最好能够做到版本化控制.
这样前端web服务器就可以放心大胆的开启缓存功能而不用担心缓存过期问题,因为如果一旦静态资源文件有修改的话,
会重新生成一个文件名称.
下面我根据自己项目的经验,来介绍下如何较好的解决这两个问题.
分两步进行.
第一步:引入wro4j,在编译时期将上述分散的多个文件整合成少数几个文件,并且将文件最小化.
第二步:在生成的静态资源文件的文件名称上加入时间信息
这是两步优化之后的引用情况:
Html代码 收藏代码
${platform:cssFile("/wro/basic") }
<script type="text/javascript">var GV = {ctxPath: '${ctxPath}',imgPath: '${ctxPath}/css'};</script>
${platform:jsFile("/wro/basic") }
${platform:jsFile("/wro/custom") }
只引用了1个css文件,2个js文件.http请求从10几个减少到3个,并且整体文件体积缩小了近一半.
下面介绍优化流程.
第一步:合并并且最小化文件.
1.添加wro4j的maven依赖
Xml代码 收藏代码
<wro4j.version>1.6.2</wro4j.version>
...
<dependency>
<groupId>ro.isdc.wro4j</groupId>
<artifactId>wro4j-core</artifactId>
<version>${wro4j.version}</version>
<exclusions>
<exclusion>
<!-- 因为项目中的其他jar包已经引入了不同版本的slf4j,所以这里避免jar重叠所以不引入 -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
2.添加wro4j maven plugin
Xml代码 收藏代码
<plugin>
<groupId>ro.isdc.wro4j</groupId>
<artifactId>wro4j-maven-plugin</artifactId>
<version>${wro4j.version}</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<configuration>
<targetGroups>basic,custom</targetGroups>
<!-- 这个配置是告诉wro4j在打包静态资源的时候是否需要最小化文件,开发的时候可以设成false,方便调试 -->
<minimize>true</minimize>
<destinationFolder>${basedir}/src/main/webapp/wro/</destinationFolder>
<contextFolder>${basedir}/src/main/webapp/</contextFolder>
<!-- 这个配置是第二步优化需要用到的,暂时忽略 -->
<wroManagerFactory>com.rootrip.platform.common.web.wro.CustomWroManagerFactory</wroManagerFactory>
</configuration>
</plugin>
如果开发环境是eclipse的话,可以下载m2e-wro4j这个插件.
下载地址:http://download.jboss.org/jbosstools/updates/m2e-wro4j/
这个插件的主要功能是能够帮助我们在开发环境下修改对应的静态文件,或者pom.xml文件的时候能够自动生成打包好的js和css文件.
对开发来说就会方便很多.只要修改源文件就能看见修改后的结果.
3.在WEB-INF目录下添加wro.xml文件,这个文件的作用就是告诉wro4j需要以怎样的策略打包jss和css文件.
Java代码 收藏代码
<?xml version="1.0" encoding="UTF-8"?>
<groups xmlns="http://www.isdc.ro/wro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd">
<group name="basic">
<css>/css/basic.css</css>
<css>/css/skin.css</css>
<css>/css/jquery-ui-1.8.23.custom.css</css>
<css>/css/validationEngine.jquery.css</css>
<js>/js/jquery-1.7.2.min.js</js>
<js>/js/jquery-ui-1.8.23.custom.min.js</js>
<js>/js/jquery.validationEngine.js</js>
<js>/js/jquery.fixedtableheader.min.js</js>
<js>/js/roll.js</js>
<js>/js/jquery.pagination.js</js>
<js>/js/jquery.rooFixed.js</js>
<js>/js/jquery.ui.datepicker-zh-CN.js</js>
<js>/js/json2.js</js>
</group>
<group name="custom">
<js>/js/jquery.validationEngine-zh_CN.js</js>
<js>/js/common.js</js>
</group>
</groups>
官方文档:http://code.google.com/p/wro4j/wiki/WroFileFormat
其实这个配置文件很好理解,如果不愿看官方文档的朋友我在这简单介绍下.
上面这样配置的目的就是告诉wro4j要将
<css>/css/basic.css</css>
<css>/css/skin.css</css>
<css>/css/jquery-ui-1.8.23.custom.css</css>
<css>/css/validationEngine.jquery.css</css>
这四个文件整合到一起,生成一个叫basic.css的文件到指定目录(wro4j-maven-plugin里配置的),将
<js>/js/jquery-1.7.2.min.js</js>
<js>/js/jquery-ui-1.8.23.custom.min.js</js>
<js>/js/jquery.validationEngine.js</js>
<js>/js/jquery.fixedtableheader.min.js</js>
<js>/js/roll.js</js>
<js>/js/jquery.pagination.js</js>
<js>/js/jquery.rooFixed.js</js>
<js>/js/jquery.ui.datepicker-zh-CN.js</js>
<js>/js/json2.js</js>
这几个文件整合到一起,生成一个叫basic.js的文件到指定目录.
最后将
<js>/js/jquery.validationEngine-zh_CN.js</js>
<js>/js/common.js</js>
这两个文件整合到一起,,生成一个叫custom.js的文件到指定目录.
第一步搞定,这时候如果你的开发环境是eclipse并且安装了插件的话,应该就能在你工程的%your webapp%/wor/目录下看见生成好的
basic.css,basic.js和custom.js这三个文件了.
然后你再将你的静态资源引用路径改成
Html代码 收藏代码
<link rel="stylesheet" type="text/css" href="${ctxPath}/wro/basic.css"/>
<script type="text/javascript" src="${ctxPath}/wro/basic.js"></script>
<script type="text/javascript" src="${ctxPath}/wro/custom.js"></script>
就ok了.每次修改被引用到的css或js文件的时候,这些文件都将重新生成.
如果开发环境是eclipse但是没有安装m2e-wro4j插件的话,pom.xml可能需要额外配置.
请参考:https://community.jboss.org/en/tools/blog/2012/01/17/css-and-js-minification-using-eclipse-maven-and-wro4j
第二步:给生成的文件名称中加入时间信息并通过el自定义函数引用脚本文件.
1. 创建DailyNamingStrategy类
Java代码 收藏代码
public class DailyNamingStrategy extends TimestampNamingStrategy {
protected final Logger log = LoggerFactory.getLogger(DailyNamingStrategy.class);
@Override
protected long getTimestamp() {
String dateStr = DateUtil.formatDate(new Date(), "yyyyMMddHH");
return Long.valueOf(dateStr);
}
}
2.创建CustomWroManagerFactory类
Java代码 收藏代码
//这个类就是在wro4j-maven-plugin里配置的wroManagerFactory参数
public class CustomWroManagerFactory extends
DefaultStandaloneContextAwareManagerFactory {
public CustomWroManagerFactory() {
setNamingStrategy(new DailyNamingStrategy());
}
}
上面这两个类的作用是使用wro4j提供的文件命名策略,这样生成的文件名就会带上时间信息了.
例如:basic-2013020217.js
但是现在又会发现一个问题:如果静态资源文件名称不固定的话,那怎么样引用呢?
这时候就需要通过动态生成<script>与<link>来解决了.
因为项目使用的是jsp页面,所以通过el自定义函数来实现标签生成.
3.创建PlatformFunction类
Java代码 收藏代码
public class PlatformFunction {
private static Logger log = LoggerFactory.getLogger(PlatformFunction.class);
private static ConcurrentMap<String, String> staticFileCache = new ConcurrentHashMap<>();
private static AtomicBoolean initialized = new AtomicBoolean(false);
private static final String WRO_Path = "/wro/";
private static final String JS_SCRIPT = "<script type=\"text/javascript\" src=\"%s\"></script>";
private static final String CSS_SCRIPT = "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">";
private static String contextPath = null;
/**
* 该方法根据给出的路径,生成js脚本加载标签
* 例如传入参数/wro/custom,该方法会寻找webapp路径下/wro目录中以custom开头,以js后缀结尾的文件名称名称.
* 然后拼成<script type="text/javascript" src="${ctxPath}/wro/custom-20130201.js"></script>返回
* 如果查找到多个文件,返回根据文件名排序最大的文件
* @param str
* @return
*/
public static String jsFile(String filePath) {
String jsFile = staticFileCache.get(buildCacheKey(filePath, "js"));
if(jsFile == null) {
log.error("加载js文件失败,缓存中找不到对应的文件[{}]", filePath);
}
return String.format(JS_SCRIPT, jsFile);
}
/**
* 该方法根据给出的路径,生成css脚本加载标签
* 例如传入参数/wro/custom,该方法会寻找webapp路径下/wro目录中以custom开头,以css后缀结尾的文件名称名称.
* 然后拼成<link rel="stylesheet" type="text/css" href="${ctxPath}/wro/basic-20130201.css">返回
* 如果查找到多个文件,返回根据文件名排序最大的文件
* @param str
* @return
*/
public static String cssFile(String filePath) {
String cssFile = staticFileCache.get(buildCacheKey(filePath, "css"));
if(cssFile == null) {
log.error("加载css文件失败,缓存中找不到对应的文件[{}]", filePath);
}
return String.format(CSS_SCRIPT, cssFile);
}
public static void init() throws IOException {
if(initialized.compareAndSet(false, true)) {
ServletContext sc = Platform.getInstance().getServletContext();
if(sc == null) {
throw new PlatformException("查找静态资源的时候的时候发现servlet context 为null");
}
contextPath = Platform.getInstance().getContextPath();
File wroDirectory = new ServletContextResource(sc, WRO_Path).getFile();
if(!wroDirectory.exists() || !wroDirectory.isDirectory()) {
throw new PlatformException("查找静态资源的时候发现对应目录不存在[" + wroDirectory.getAbsolutePath() + "]");
}
//将wro目录下已有文件加入缓存
for(File file : wroDirectory.listFiles()) {
handleNewFile(file);
}
//监控wro目录,如果有文件生成,则判断是否是较新的文件,是的话则把文件名加入缓存
new Thread(new WroFileWatcher(wroDirectory.getAbsolutePath())).start();
}
}
private static void handleNewFile(File file) {
String fileName = file.getName();
Pattern p = Pattern.compile("^(\\w+)\\-\\d+\\.(js|css)$");
Matcher m = p.matcher(fileName);
if(!m.find() || m.groupCount() < 2) return;
String fakeName = m.group(1);
String fileType = m.group(2);
//暂时限定只能匹配/wro/目录下的文件
String key = buildCacheKey(WRO_Path + fakeName, fileType);
if(staticFileCache.putIfAbsent(key, fileName) != null) {
synchronized(staticFileCache) {
String cachedFileName = staticFileCache.get(key);
if(fileName.compareTo(cachedFileName) > 0) {
staticFileCache.put(key, contextPath + WRO_Path + fileName);
}
}
}
}
private static String buildCacheKey(String fakeName, String fileType) {
return fakeName + "-" + fileType;
}
static class WroFileWatcher implements Runnable {
private static Logger log = LoggerFactory.getLogger(WroFileWatcher.class);
private String wroAbsolutePathStr;
public WroFileWatcher(String wroPathStr) {
this.wroAbsolutePathStr = wroPathStr;
}
@Override
public void run() {
Path path = Paths.get(wroAbsolutePathStr);
File wroDirectory = path.toFile();
if(!wroDirectory.exists() || !wroDirectory.isDirectory()) {
String message = "监控wro目录的时候发现对应目录不存在[" + wroAbsolutePathStr + "]";
log.error(message);
throw new PlatformException(message);
}
log.warn("开始监控wro目录[{}]", wroAbsolutePathStr);
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
path.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
while (true) {
WatchKey key = null;
try {
key = watcher.take();
} catch (InterruptedException e) {
log.error("", e);
continue;
}
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> e = (WatchEvent<Path>) event;
Path filePath = e.context();
handleNewFile(filePath.toFile());
}
if (!key.reset()) {
break;
}
}
} catch (IOException e) {
log.error("监控wro目录发生错误", e);
}
log.warn("停止监控wro目录[{}]", wroAbsolutePathStr);
}
}
}
对应的tld文件就不给出了,根据方法签名编写就行了.
其中的cssFile和jsFile方法分别实现引用css和js文件.
在页面使用的时候类似这样:
${platform:cssFile("/wro/basic") }
${platform:jsFile("/wro/custom") }
这个类的主要功能就是使用jdk7的WatchService监控wro目录的新增文件事件,
一旦有新的文件加到目录里,判断这个文件是不是最新的,如果是的话则使用这个文件名称引用.
这样一旦有新加的资源文件放到wro目录里,则能够自动被引用,不需要做任何代码上的修改,并且基本不影响性能.
到此为止功能已经实现.
但是我考虑到还有两个问题有待完善:
1.因为生成的文件名称精确到小时,如果这个小时之内有多次代码修改,生成的文件名都完全一样.
这样就算线上的代码有修改,对于已经有该文本缓存的浏览器来说,不会重新请求文件,也就看不到文件变化.
不过一般来说线上代码不会如此频繁改动,对于大多数应用来说影响不大.
2.在开发环境开发一段时间之后,wro目录下会生成一大堆的文件(因为m2e-wro4j插件在生成新的文件的时候不会删除旧文件,如果文件名相同会覆盖掉以前的文件),
这时候就需要手动删除时间靠前的旧文件,虽然系统会忽略旧文件,但是我相信大多数程序员和我一样是有些许洁癖的吧.
解决办法还是不少,比如可以写脚本定期清理掉旧文件.
时间有限,有些地方考虑的不是很完善,欢迎拍砖.
参考资料:
http://meri-stuff.blogspot.sk/2012/08/wro4j-page-load-optimization-and-lessjs.html#Configuration
https://community.jboss.org/en/tools/blog/2012/01/17/css-and-js-minification-using-eclipse-maven-and-wro4j
http://code.google.com/p/wro4j/wiki/MavenPlugin
http://code.google.com/p/wro4j/wiki/WroFileFormat
http://java.dzone.com/articles/using-java-7s-watchservice
发表评论
-
开发中用到的工具
2015-02-28 16:16 5091、画原型工具:Axure RP Pro 7.0 2、数据库之 ... -
ubuntu设置系统时间
2014-09-18 15:30 551ubuntu设置时区,网上同步时间 Linux默认情况下使用U ... -
随 笔
2013-10-10 16:41 414还是没把这点嗜好坚持下去,或许是还不习惯吧,没事,再来。每天的 ... -
不定义JQuery插件,不要说会JQuery
2013-10-10 16:33 492一:导言 有些WEB ... -
下决心
2013-09-30 09:25 431从今天开始写博客,理由如下: 1、可以把自己成长的经历记录下来 ... -
java程序员修炼之道
2013-09-25 12:00 454大牛告诉我们应该好好 ... -
JS开发者经常忽略或误用的7个基础知识点
2013-08-22 14:48 715JavaScript 本身可以算是一门简单的语言,但我们也不断 ... -
js省市区联动
2013-05-21 17:30 19711、先投入js: <script type=" ...
相关推荐
WRO4J 是一个用于优化 Web 资源(如 CSS、JavaScript 文件)的工具,它可以合并、压缩和缓存这些资源,以提高网页加载速度和减少网络请求。 【标签】"开源项目" 表明这两个软件都是遵循开源许可证的,允许用户自由...
介绍这是一个单模块 maven 项目,演示了如何集成以下技术: Spring Boot(基于Spring MVC的项目的单jar部署) Sass Maven 插件(Sass 转 CSS) Wro4J(用于构建时 javascript 组合/缩小) Handlebars.java(服务器端...
Wro4j是一个Web资源优化工具,它可以合并、压缩和最小化JavaScript和CSS文件,以提高网页加载速度。在"icampus-console"项目中,wro4j被用来优化AngularJS的资源,减少HTTP请求的数量,从而提升用户体验。通过...
2. **用户界面**:前端可能使用HTML、CSS和JavaScript,配合React、Angular或Vue.js等现代前端框架,为用户提供交互式的体验。 3. **API设计**:后端可能提供RESTful API供前端调用,处理业务逻辑和数据操作。 4. **...
【AI】从头到脚详解如何创建部署Azure Web App的OpenAI项目源码
人脸识别项目实战
人工智能-人脸识别代码,采用cnn的架构识别代码
汽车配件制造业企业信息化整体解决方案
短期风速预测模型,IDBO-BiTCN-BiGRU-Multihead-Attention IDBO是,网上复现 评价指标:R方、MAE、MAPE、RMSE 附带测试数据集运行(风速数据) 提示:在MATLAB2024a上测试正常 ,短期风速预测模型; IDBO-BiTCN-BiGRU-Multihead-Attention; 评价指标: R方、MAE、MAPE、RMSE; 复现; 测试数据集; MATLAB 2024a,短期风速预测模型:IDBO-BiTCN-BiGRU-Attention集成模型
手势识别项目实战
在智慧园区建设的浪潮中,一个集高效、安全、便捷于一体的综合解决方案正逐步成为现代园区管理的标配。这一方案旨在解决传统园区面临的智能化水平低、信息孤岛、管理手段落后等痛点,通过信息化平台与智能硬件的深度融合,为园区带来前所未有的变革。 首先,智慧园区综合解决方案以提升园区整体智能化水平为核心,打破了信息孤岛现象。通过构建统一的智能运营中心(IOC),采用1+N模式,即一个智能运营中心集成多个应用系统,实现了园区内各系统的互联互通与数据共享。IOC运营中心如同园区的“智慧大脑”,利用大数据可视化技术,将园区安防、机电设备运行、车辆通行、人员流动、能源能耗等关键信息实时呈现在拼接巨屏上,管理者可直观掌握园区运行状态,实现科学决策。这种“万物互联”的能力不仅消除了系统间的壁垒,还大幅提升了管理效率,让园区管理更加精细化、智能化。 更令人兴奋的是,该方案融入了诸多前沿科技,让智慧园区充满了未来感。例如,利用AI视频分析技术,智慧园区实现了对人脸、车辆、行为的智能识别与追踪,不仅极大提升了安防水平,还能为园区提供精准的人流分析、车辆管理等增值服务。同时,无人机巡查、巡逻机器人等智能设备的加入,让园区安全无死角,管理更轻松。特别是巡逻机器人,不仅能进行360度地面全天候巡检,还能自主绕障、充电,甚至具备火灾预警、空气质量检测等环境感知能力,成为了园区管理的得力助手。此外,通过构建高精度数字孪生系统,将园区现实场景与数字世界完美融合,管理者可借助VR/AR技术进行远程巡检、设备维护等操作,仿佛置身于一个虚拟与现实交织的智慧世界。 最值得关注的是,智慧园区综合解决方案还带来了显著的经济与社会效益。通过优化园区管理流程,实现降本增效。例如,智能库存管理、及时响应采购需求等举措,大幅减少了库存积压与浪费;而设备自动化与远程监控则降低了维修与人力成本。同时,借助大数据分析技术,园区可精准把握产业趋势,优化招商策略,提高入驻企业满意度与营收水平。此外,智慧园区的低碳节能设计,通过能源分析与精细化管理,实现了能耗的显著降低,为园区可持续发展奠定了坚实基础。总之,这一综合解决方案不仅让园区管理变得更加智慧、高效,更为入驻企业与员工带来了更加舒适、便捷的工作与生活环境,是未来园区建设的必然趋势。
相亲交友系统源码 V10.5支持婚恋相亲、媒婆返利、红娘系统、商城系统等等 这款交友系统功能太多了,适合婚恋相亲,还有媒婆婚庆等等支持 PC和 H5还有小程序,可封装红年、APP,里面带安装教程
本资源《单片机也能玩双核之你想不到的C技巧系列——嵌入式实战》涵盖 双核单片机开发、C语言高级技巧、嵌入式系统优化 等核心内容,结合 实战案例与视频教程,帮助开发者深入理解并掌握高效编程技巧。 适用人群: 适合 嵌入式开发工程师、单片机开发者、电子信息相关专业学生,以及希望提升 C语言编程能力 和 嵌入式项目经验 的技术人员。 能学到什么: 双核单片机开发思路,提高并行处理能力。 C语言高级技巧,提升代码优化与执行效率。 嵌入式系统调试方法,掌握实际项目中的调试策略。 实战案例解析,学习如何在实际工程中应用双核技术。 阅读建议: 建议 先学习基础知识,再结合 示例代码与视频教程 进行实操,重点关注 代码优化、调试技巧与双核应用模式,通过实战演练提高嵌入式开发能力。
人脸识别项目源码实战
人脸识别项目源码实战
c语言学习
红外光伏缺陷目标检测模型,YOLOv8模型 基于红外光伏缺陷目标检测数据集训练,做了必要的数据增强处理,以达到缺陷类别间的平衡 可检测大面积热斑,单一热斑,二极管短路和异常低温四类缺陷 测试集指标如图所示 ,核心关键词:红外光伏缺陷目标检测模型; YOLOv8模型; 数据增强处理; 缺陷类别平衡; 大面积热斑; 单一热斑; 二极管短路; 异常低温。,基于YOLOv8的红外光伏缺陷检测模型
基于PLC的自动浇花控制系统 西门子1200PLC博途仿真,提供HMI画面,接线图,IO分配表,演示视频,简单讲解视频 博图15.1及以上版本均可使用 ,核心关键词: PLC自动浇花控制系统; 西门子1200PLC博途仿真; HMI画面; 接线图; IO分配表; 演示视频; 简单讲解视频; 博图15.1及以上版本。,基于PLC的自动浇花系统:西门子1200PLC博途仿真实践教程
在智慧园区建设的浪潮中,一个集高效、安全、便捷于一体的综合解决方案正逐步成为现代园区管理的标配。这一方案旨在解决传统园区面临的智能化水平低、信息孤岛、管理手段落后等痛点,通过信息化平台与智能硬件的深度融合,为园区带来前所未有的变革。 首先,智慧园区综合解决方案以提升园区整体智能化水平为核心,打破了信息孤岛现象。通过构建统一的智能运营中心(IOC),采用1+N模式,即一个智能运营中心集成多个应用系统,实现了园区内各系统的互联互通与数据共享。IOC运营中心如同园区的“智慧大脑”,利用大数据可视化技术,将园区安防、机电设备运行、车辆通行、人员流动、能源能耗等关键信息实时呈现在拼接巨屏上,管理者可直观掌握园区运行状态,实现科学决策。这种“万物互联”的能力不仅消除了系统间的壁垒,还大幅提升了管理效率,让园区管理更加精细化、智能化。 更令人兴奋的是,该方案融入了诸多前沿科技,让智慧园区充满了未来感。例如,利用AI视频分析技术,智慧园区实现了对人脸、车辆、行为的智能识别与追踪,不仅极大提升了安防水平,还能为园区提供精准的人流分析、车辆管理等增值服务。同时,无人机巡查、巡逻机器人等智能设备的加入,让园区安全无死角,管理更轻松。特别是巡逻机器人,不仅能进行360度地面全天候巡检,还能自主绕障、充电,甚至具备火灾预警、空气质量检测等环境感知能力,成为了园区管理的得力助手。此外,通过构建高精度数字孪生系统,将园区现实场景与数字世界完美融合,管理者可借助VR/AR技术进行远程巡检、设备维护等操作,仿佛置身于一个虚拟与现实交织的智慧世界。 最值得关注的是,智慧园区综合解决方案还带来了显著的经济与社会效益。通过优化园区管理流程,实现降本增效。例如,智能库存管理、及时响应采购需求等举措,大幅减少了库存积压与浪费;而设备自动化与远程监控则降低了维修与人力成本。同时,借助大数据分析技术,园区可精准把握产业趋势,优化招商策略,提高入驻企业满意度与营收水平。此外,智慧园区的低碳节能设计,通过能源分析与精细化管理,实现了能耗的显著降低,为园区可持续发展奠定了坚实基础。总之,这一综合解决方案不仅让园区管理变得更加智慧、高效,更为入驻企业与员工带来了更加舒适、便捷的工作与生活环境,是未来园区建设的必然趋势。
大型集团用户画像系统化标准化数字化用户主数据管理项目规划方案