转:http://huangpengpeng.iteye.com/blog/2092189
1.首先利用maven压缩js 和css 等资源文件
- <build>
- <plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>yuicompressor-maven-plugin</artifactId>
- <version>1.4.0</version>
- <executions>
- <execution>
- <!-- 在真正的打包之前,执行一些准备打包压缩操作的操作 -->
- <phase>prepare-package</phase>
- <goals>
- <goal>compress</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <encoding>UTF-8</encoding>
- <!-- 忽视 js 错误警告 -->
- <jswarn>false</jswarn>
- <nosuffix>true</nosuffix>
- <linebreakpos>-1</linebreakpos>
- <!-- 压缩的文件 工程里面所有的 js css 后缀的都会压缩 -->
- <includes>
- <include>**/*.js</include>
- <include>**/*.css</include>
- </includes>
- <!-- 不需要压缩的文件 -->
- <excludes>
- <exclude>**/style.css</exclude>
- </excludes>
- <failOnWarning>false</failOnWarning>
- </configuration>
- </plugin>
- <!-- 当压缩没有填写输出目录 或者 输出目录和压缩目录是同一路径时 一定要配合下面的使用 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-war-plugin</artifactId>
- <configuration>
- <!--
- 如果不增加此配置 src/main/webapp 下面的内容 会重新复制到target输出目录 覆盖掉编译后的内容
- 这样编译的还是未压缩过的内容,增加上此过滤 打war包就不会内容覆盖
- -->
- <warSourceExcludes>**/*.js,**/*.css</warSourceExcludes>
- </configuration>
- </plugin>
- </plugins>
- </build>
2.通过SpringMvc扩展通过一个请求获取多个JS文件的功能
2.1 spring MVC 配置
- <bean id="simpleUrlHandlerMapping"
- class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="urlMap">
- <map>
- <!-- 静态资源处理器 -->
- <entry key="/r/**/**">
- <!-- 自己扩展的SpingMVC ResourceHttpRequestHandler 类,增加了类是与淘宝CDN通过逗号(,)隔开
- 访问多个js的效果 此功能不能压缩JS 如果想实现压缩功能可通过maven 或者 ant 在编译的时候进行压缩 -->
- <bean class="com.yoro.core.springmvc.ResourceHttpRequestHandler">
- <property name="locations">
- <list>
- <!-- 只有相同目录的JS文件才能实现淘宝CDN通过逗号(,)隔开 访问多个js的效果 如 /r/ 目录下的js|css 文件
- 就能实现 /r/js/ 目录下的js文件也可以有同样的效果 但 /a/css 下面的文件 和 /r/css 就不能实现 有点小遗憾,因为时间关系待以后升级 -->
- <value>/r/</value>
- </list>
- </property>
- <!-- 启用静态资源浏览器缓存一个月 -->
- <!-- 通过浏览器进行的缓存 根据可浏览器实现方式不同有所差异 按刷新按扭缓存不会起 当是页面跳转或者地址栏输入则缓存会起作用 -->
- <!-- 更 过浏览器缓存的资料和特效 可 搜索 cachecontrol 设置 cacheSeconds 缓存时间单位是秒 2592000 表示缓存 30天 因为我自己每次新版本发布都会都js css 文件增加版本号 所以缓存时间我设置的比较长 -->
- <property name="cacheSeconds" value="2592000"></property>
- <property name="useExpiresHeader" value="true"></property>
- <property name="useCacheControlNoStore" value="true"></property>
- </bean>
- </entry>
- <entry key="/thirdparty/**/**">
- <bean class="com.yoro.core.springmvc.ResourceHttpRequestHandler">
- <property name="locations">
- <list>
- <value>/thirdparty/</value>
- </list>
- </property>
- <!-- 启用静态资源浏览器缓存一个月 -->
- <property name="cacheSeconds" value="2592000"></property>
- <property name="useExpiresHeader" value="true"></property>
- <property name="useCacheControlNoStore" value="true"></property>
- </bean>
- </entry>
- </map>
- </property>
- <property name="order" value="1"></property>
- </bean>
2.2 ResourceHttpRequestHandler 扩展类代码
- package com.yoro.core.springmvc;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.List;
- import javax.activation.FileTypeMap;
- import javax.activation.MimetypesFileTypeMap;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.core.io.Resource;
- import org.springframework.http.MediaType;
- import org.springframework.util.Assert;
- import org.springframework.util.ClassUtils;
- import org.springframework.util.CollectionUtils;
- import org.springframework.util.StreamUtils;
- import org.springframework.util.StringUtils;
- import org.springframework.web.HttpRequestHandler;
- import org.springframework.web.context.request.ServletWebRequest;
- import org.springframework.web.servlet.HandlerMapping;
- import org.springframework.web.servlet.support.WebContentGenerator;
- public class ResourceHttpRequestHandler
- extends WebContentGenerator implements HttpRequestHandler, InitializingBean {
- private static final boolean jafPresent =
- ClassUtils.isPresent("javax.activation.FileTypeMap", ResourceHttpRequestHandler.class.getClassLoader());
- private final static Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);
- private List<Resource> locations;
- public ResourceHttpRequestHandler() {
- super(METHOD_GET, METHOD_HEAD);
- }
- /**
- * Set a {@code List} of {@code Resource} paths to use as sources
- * for serving static resources.
- */
- public void setLocations(List<Resource> locations) {
- Assert.notEmpty(locations, "Locations list must not be empty");
- this.locations = locations;
- }
- @Override
- public void afterPropertiesSet() throws Exception {
- if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
- logger.warn("Locations list is empty. No resources will be served");
- }
- }
- @Override
- public void handleRequest(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- checkAndPrepare(request, response, true);
- // check whether a matching resource exists
- List<Resource> resources = getResources(request);
- if (resources == null || resources.isEmpty()) {
- logger.debug("No matching resource found - returning 404");
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- // check the resource's media type
- MediaType mediaType = getMediaType((String)request
- .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
- if (mediaType != null) {
- if (logger.isDebugEnabled()) {
- logger.debug("Determined media type '" + mediaType + "' for "
- + resources.get(0));
- }
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("No media type found for " + resources.get(0)
- + " - not sending a content-type header");
- }
- }
- for (Resource resource : resources) {
- // header phase
- if (!new ServletWebRequest(request, response)
- .checkNotModified(resource.lastModified())) {
- logger.debug("Resource not modified - returning 304");
- break;
- }
- return;
- }
- setHeaders(response, resources, mediaType);
- // content phase
- if (METHOD_HEAD.equals(request.getMethod())) {
- logger.trace("HEAD request - skipping content");
- return;
- }
- writeContent(response, resources);
- }
- protected MediaType getMediaType(String filename) {
- MediaType mediaType = null;
- String mimeType = getServletContext().getMimeType(filename);
- if (StringUtils.hasText(mimeType)) {
- mediaType = MediaType.parseMediaType(mimeType);
- }
- if (jafPresent && (mediaType == null || MediaType.APPLICATION_OCTET_STREAM.equals(mediaType))) {
- MediaType jafMediaType = ActivationMediaTypeFactory.getMediaType(filename);
- if (jafMediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(jafMediaType)) {
- mediaType = jafMediaType;
- }
- }
- return mediaType;
- }
- protected void setHeaders(HttpServletResponse response,
- List<Resource> resources, MediaType mediaType) throws IOException {
- long length = 0;
- //Calculation of multiple file length
- Iterator<Resource> iter = resources.iterator();
- while (iter.hasNext()) {
- length += iter.next().contentLength();
- }
- if (length > Integer.MAX_VALUE) {
- throw new IOException(
- "Resource content too long (beyond Integer.MAX_VALUE)");
- }
- response.setContentLength((int) length);
- if (mediaType != null) {
- response.setContentType(mediaType.toString());
- }
- }
- protected void writeContent(HttpServletResponse response,
- List<Resource> resourcess) throws IOException {
- OutputStream out = response.getOutputStream();
- InputStream in = null;
- try {
- for (Resource resource : resourcess) {
- try {
- in = resource.getInputStream();
- StreamUtils.copy(in, out);
- } finally {
- try {
- in.close();
- } catch (IOException ex) {
- }
- }
- }
- } finally {
- try {
- out.close();
- } catch (IOException ex) {
- }
- }
- }
- protected List<Resource> getResources(HttpServletRequest request) {
- String path = (String) request
- .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
- if (path == null) {
- throw new IllegalStateException("Required request attribute '"
- + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
- + "' is not set");
- }
- if (!StringUtils.hasText(path) || isInvalidPath(path)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Ignoring invalid resource path [" + path + "]");
- }
- return null;
- }
- for (Resource location : this.locations) {
- try {
- if (logger.isDebugEnabled()) {
- logger.debug("Trying relative path [" + path
- + "] against base location: " + location);
- }
- List<Resource> rs = new ArrayList<Resource>();
- String[] paths = path.split(",");
- for (String url : paths) {
- Resource resource = location.createRelative(url);
- if (resource.exists() && resource.isReadable()) {
- rs.add(resource);
- }
- }
- return rs;
- } catch (IOException ex) {
- logger.debug(
- "Failed to create relative resource - trying next resource location",
- ex);
- }
- }
- return null;
- }
- /**
- * Validates the given path: returns {@code true} if the given path is not a valid resource path.
- * <p>The default implementation rejects paths containing "WEB-INF" or "META-INF" as well as paths
- * with relative paths ("../") that result in access of a parent directory.
- * @param path the path to validate
- * @return {@code true} if the path has been recognized as invalid, {@code false} otherwise
- */
- protected boolean isInvalidPath(String path) {
- return (path.contains("WEB-INF") || path.contains("META-INF") || StringUtils.cleanPath(path).startsWith(".."));
- }
- /**
- * Inner class to avoid hard-coded JAF dependency.
- */
- private static class ActivationMediaTypeFactory {
- private static final FileTypeMap fileTypeMap;
- static {
- fileTypeMap = loadFileTypeMapFromContextSupportModule();
- }
- private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
- // see if we can find the extended mime.types from the context-support module
- Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types");
- if (mappingLocation.exists()) {
- InputStream inputStream = null;
- try {
- inputStream = mappingLocation.getInputStream();
- return new MimetypesFileTypeMap(inputStream);
- }
- catch (IOException ex) {
- // ignore
- }
- finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- }
- catch (IOException ex) {
- // ignore
- }
- }
- }
- }
- return FileTypeMap.getDefaultFileTypeMap();
- }
- public static MediaType getMediaType(String filename) {
- String mediaType = fileTypeMap.getContentType(filename);
- return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);
- }
- }
- }
2.3 资源文件目录结构
2.4 浏览器缓存效果
5.实现淘宝CDN JS 请求例子
JS例子:http://127.0.0.1/r/js/alert.js,/js/application.js,/js/bootstrap.js
CSS例子:http://127.0.1.1/r/css/activity_style.css,/css/bootstrap_responsive.css
通过逗号(,)分割他们现在就实现了通过一个请求加载多个资源文件的效果了