maven 打包生产环境 压缩 JS 利用SpirngMVC扩展解决减少资源的请求数

转:http://huangpengpeng.iteye.com/blog/2092189

1.首先利用maven压缩js 和css 等资源文件

Xml代码   收藏代码
  1. <build>  
  2.                             <plugin>  
  3.                     <groupId>net.alchim31.maven</groupId>  
  4.                     <artifactId>yuicompressor-maven-plugin</artifactId>  
  5.                     <version>1.4.0</version>  
  6.                     <executions>  
  7.                         <execution>  
  8.                             <!-- 在真正的打包之前,执行一些准备打包压缩操作的操作  -->  
  9.                             <phase>prepare-package</phase>  
  10.                             <goals>  
  11.                                 <goal>compress</goal>  
  12.                             </goals>  
  13.                         </execution>  
  14.                     </executions>  
  15.                     <configuration>  
  16.                         <encoding>UTF-8</encoding>  
  17.                         <!-- 忽视 js 错误警告 -->  
  18.                         <jswarn>false</jswarn>  
  19.                         <nosuffix>true</nosuffix>  
  20.                         <linebreakpos>-1</linebreakpos>  
  21.                         <!-- 压缩的文件 工程里面所有的 js css 后缀的都会压缩 -->  
  22.                         <includes>  
  23.                             <include>**/*.js</include>  
  24.                             <include>**/*.css</include>  
  25.                         </includes>  
  26.                         <!-- 不需要压缩的文件 -->  
  27.                         <excludes>  
  28.                             <exclude>**/style.css</exclude>  
  29.                         </excludes>  
  30.                         <failOnWarning>false</failOnWarning>  
  31.                     </configuration>  
  32.                 </plugin>  
  33.                 <!-- 当压缩没有填写输出目录 或者 输出目录和压缩目录是同一路径时 一定要配合下面的使用  -->  
  34.                 <plugin>  
  35.                     <groupId>org.apache.maven.plugins</groupId>  
  36.                     <artifactId>maven-war-plugin</artifactId>  
  37.                     <configuration>  
  38.                         <!--   
  39.                             如果不增加此配置   src/main/webapp 下面的内容 会重新复制到target输出目录 覆盖掉编译后的内容  
  40.                             这样编译的还是未压缩过的内容,增加上此过滤  打war包就不会内容覆盖  
  41.                          -->  
  42.                         <warSourceExcludes>**/*.js,**/*.css</warSourceExcludes>  
  43.                     </configuration>  
  44.                 </plugin>  
  45.             </plugins>  
  46.         </build>  

 2.通过SpringMvc扩展通过一个请求获取多个JS文件的功能

    2.1 spring MVC 配置

Xml代码   收藏代码
  1. <bean id="simpleUrlHandlerMapping"    
  2. class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">    
  3. <property name="urlMap">    
  4. <map>    
  5. <!-- 静态资源处理器 -->    
  6. <entry key="/r/**/**">    
  7. <!-- 自己扩展的SpingMVC ResourceHttpRequestHandler 类,增加了类是与淘宝CDN通过逗号(,)隔开      
  8. 访问多个js的效果 此功能不能压缩JS 如果想实现压缩功能可通过maven 或者 ant 在编译的时候进行压缩 -->    
  9. <bean class="com.yoro.core.springmvc.ResourceHttpRequestHandler">    
  10. <property name="locations">    
  11. <list>    
  12. <!-- 只有相同目录的JS文件才能实现淘宝CDN通过逗号(,)隔开 访问多个js的效果 如 /r/ 目录下的js|css 文件      
  13. 就能实现 /r/js/ 目录下的js文件也可以有同样的效果 但 /a/css 下面的文件 和 /r/css 就不能实现 有点小遗憾,因为时间关系待以后升级 -->    
  14. <value>/r/</value>    
  15. </list>    
  16. </property>    
  17. <!-- 启用静态资源浏览器缓存一个月 -->    
  18. <!-- 通过浏览器进行的缓存 根据可浏览器实现方式不同有所差异 按刷新按扭缓存不会起 当是页面跳转或者地址栏输入则缓存会起作用 -->    
  19. <!-- 更 过浏览器缓存的资料和特效 可 搜索 cachecontrol 设置   cacheSeconds 缓存时间单位是秒  2592000 表示缓存 30天 因为我自己每次新版本发布都会都js css 文件增加版本号 所以缓存时间我设置的比较长 -->    
  20. <property name="cacheSeconds" value="2592000"></property>    
  21. <property name="useExpiresHeader" value="true"></property>    
  22. <property name="useCacheControlNoStore" value="true"></property>    
  23. </bean>    
  24. </entry>    
  25. <entry key="/thirdparty/**/**">    
  26. <bean class="com.yoro.core.springmvc.ResourceHttpRequestHandler">    
  27. <property name="locations">    
  28. <list>    
  29. <value>/thirdparty/</value>    
  30. </list>    
  31. </property>    
  32. <!-- 启用静态资源浏览器缓存一个月 -->    
  33. <property name="cacheSeconds" value="2592000"></property>    
  34. <property name="useExpiresHeader" value="true"></property>    
  35. <property name="useCacheControlNoStore" value="true"></property>    
  36. </bean>    
  37. </entry>    
  38. </map>    
  39. </property>    
  40. <property name="order" value="1"></property>    
  41. </bean>    

 2.2 ResourceHttpRequestHandler  扩展类代码

Java代码   收藏代码
  1. package com.yoro.core.springmvc;     
  2.     
  3. import java.io.IOException;     
  4. import java.io.InputStream;     
  5. import java.io.OutputStream;     
  6. import java.util.ArrayList;     
  7. import java.util.Iterator;     
  8. import java.util.List;     
  9.     
  10. import javax.activation.FileTypeMap;     
  11. import javax.activation.MimetypesFileTypeMap;     
  12. import javax.servlet.ServletException;     
  13. import javax.servlet.http.HttpServletRequest;     
  14. import javax.servlet.http.HttpServletResponse;     
  15.     
  16. import org.apache.commons.logging.Log;     
  17. import org.apache.commons.logging.LogFactory;     
  18. import org.springframework.beans.factory.InitializingBean;     
  19. import org.springframework.core.io.ClassPathResource;     
  20. import org.springframework.core.io.Resource;     
  21. import org.springframework.http.MediaType;     
  22. import org.springframework.util.Assert;     
  23. import org.springframework.util.ClassUtils;     
  24. import org.springframework.util.CollectionUtils;     
  25. import org.springframework.util.StreamUtils;     
  26. import org.springframework.util.StringUtils;     
  27. import org.springframework.web.HttpRequestHandler;     
  28. import org.springframework.web.context.request.ServletWebRequest;     
  29. import org.springframework.web.servlet.HandlerMapping;     
  30. import org.springframework.web.servlet.support.WebContentGenerator;     
  31.     
  32. public class ResourceHttpRequestHandler      
  33. extends WebContentGenerator implements HttpRequestHandler, InitializingBean  {     
  34.     
  35.          
  36.     private static final boolean jafPresent =     
  37.             ClassUtils.isPresent("javax.activation.FileTypeMap", ResourceHttpRequestHandler.class.getClassLoader());     
  38.     
  39.     private final static Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);     
  40.     
  41.     
  42.     private List<Resource> locations;     
  43.     
  44.     
  45.     public ResourceHttpRequestHandler() {     
  46.         super(METHOD_GET, METHOD_HEAD);     
  47.     }     
  48.     
  49.     /**   
  50.      * Set a {@code List} of {@code Resource} paths to use as sources   
  51.      * for serving static resources.   
  52.      */    
  53.     public void setLocations(List<Resource> locations) {     
  54.         Assert.notEmpty(locations, "Locations list must not be empty");     
  55.         this.locations = locations;     
  56.     }     
  57.     
  58.     @Override    
  59.     public void afterPropertiesSet() throws Exception {     
  60.         if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {     
  61.             logger.warn("Locations list is empty. No resources will be served");     
  62.         }     
  63.     }     
  64.     
  65.     @Override    
  66.     public void handleRequest(HttpServletRequest request,     
  67.             HttpServletResponse response) throws ServletException, IOException {     
  68.     
  69.         checkAndPrepare(request, response, true);     
  70.     
  71.         // check whether a matching resource exists     
  72.         List<Resource> resources = getResources(request);     
  73.         if (resources == null || resources.isEmpty()) {     
  74.             logger.debug("No matching resource found - returning 404");     
  75.             response.sendError(HttpServletResponse.SC_NOT_FOUND);     
  76.             return;     
  77.         }     
  78.     
  79.         // check the resource's media type     
  80.         MediaType mediaType = getMediaType((String)request     
  81.                 .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));     
  82.         if (mediaType != null) {     
  83.             if (logger.isDebugEnabled()) {     
  84.                 logger.debug("Determined media type '" + mediaType + "' for "    
  85.                         + resources.get(0));     
  86.             }     
  87.         } else {     
  88.             if (logger.isDebugEnabled()) {     
  89.                 logger.debug("No media type found for " + resources.get(0)     
  90.                         + " - not sending a content-type header");     
  91.             }     
  92.         }     
  93.     
  94.         for (Resource resource : resources) {     
  95.             // header phase     
  96.             if (!new ServletWebRequest(request, response)     
  97.                     .checkNotModified(resource.lastModified())) {     
  98.                 logger.debug("Resource not modified - returning 304");     
  99.                 break;     
  100.             }     
  101.             return;     
  102.         }     
  103.     
  104.         setHeaders(response, resources, mediaType);     
  105.     
  106.         // content phase     
  107.         if (METHOD_HEAD.equals(request.getMethod())) {     
  108.             logger.trace("HEAD request - skipping content");     
  109.             return;     
  110.         }     
  111.         writeContent(response, resources);     
  112.     }     
  113.          
  114.     protected MediaType getMediaType(String filename) {     
  115.         MediaType mediaType = null;     
  116.         String mimeType = getServletContext().getMimeType(filename);     
  117.         if (StringUtils.hasText(mimeType)) {     
  118.             mediaType = MediaType.parseMediaType(mimeType);     
  119.         }     
  120.         if (jafPresent && (mediaType == null || MediaType.APPLICATION_OCTET_STREAM.equals(mediaType))) {     
  121.             MediaType jafMediaType = ActivationMediaTypeFactory.getMediaType(filename);     
  122.             if (jafMediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(jafMediaType)) {     
  123.                 mediaType = jafMediaType;     
  124.             }     
  125.         }     
  126.         return mediaType;     
  127.     }     
  128.     
  129.     protected void setHeaders(HttpServletResponse response,     
  130.             List<Resource> resources, MediaType mediaType) throws IOException {     
  131.         long length = 0;     
  132.         //Calculation of multiple file length     
  133.         Iterator<Resource> iter = resources.iterator();     
  134.         while (iter.hasNext()) {     
  135.             length += iter.next().contentLength();     
  136.         }     
  137.         if (length > Integer.MAX_VALUE) {     
  138.             throw new IOException(     
  139.                     "Resource content too long (beyond Integer.MAX_VALUE)");     
  140.         }     
  141.         response.setContentLength((int) length);     
  142.         if (mediaType != null) {     
  143.             response.setContentType(mediaType.toString());     
  144.         }     
  145.     }     
  146.     
  147.     protected void writeContent(HttpServletResponse response,     
  148.             List<Resource> resourcess) throws IOException {     
  149.         OutputStream out = response.getOutputStream();     
  150.         InputStream in = null;     
  151.         try {     
  152.             for (Resource resource : resourcess) {     
  153.                 try {     
  154.                     in = resource.getInputStream();     
  155.                     StreamUtils.copy(in, out);     
  156.                 } finally {     
  157.                     try {     
  158.                         in.close();     
  159.                     } catch (IOException ex) {     
  160.                     }     
  161.                 }     
  162.             }     
  163.         } finally {     
  164.             try {     
  165.                 out.close();     
  166.             } catch (IOException ex) {     
  167.             }     
  168.         }     
  169.     }     
  170.     
  171.     protected List<Resource> getResources(HttpServletRequest request) {     
  172.         String path = (String) request     
  173.                 .getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);     
  174.         if (path == null) {     
  175.             throw new IllegalStateException("Required request attribute '"    
  176.                     + HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE     
  177.                     + "' is not set");     
  178.         }     
  179.     
  180.         if (!StringUtils.hasText(path) || isInvalidPath(path)) {     
  181.             if (logger.isDebugEnabled()) {     
  182.                 logger.debug("Ignoring invalid resource path [" + path + "]");     
  183.             }     
  184.             return null;     
  185.         }     
  186.     
  187.         for (Resource location : this.locations) {     
  188.             try {     
  189.                 if (logger.isDebugEnabled()) {     
  190.                     logger.debug("Trying relative path [" + path     
  191.                             + "] against base location: " + location);     
  192.                 }     
  193.                 List<Resource> rs = new ArrayList<Resource>();     
  194.                 String[] paths = path.split(",");     
  195.                 for (String url : paths) {     
  196.                     Resource resource = location.createRelative(url);     
  197.                     if (resource.exists() && resource.isReadable()) {     
  198.                         rs.add(resource);     
  199.                     }     
  200.                 }     
  201.                 return rs;     
  202.             } catch (IOException ex) {     
  203.                 logger.debug(     
  204.                         "Failed to create relative resource - trying next resource location",     
  205.                         ex);     
  206.             }     
  207.         }     
  208.         return null;     
  209.     }     
  210.          
  211.     /**   
  212.      * Validates the given path: returns {@code true} if the given path is not a valid resource path.   
  213.      * <p>The default implementation rejects paths containing "WEB-INF" or "META-INF" as well as paths   
  214.      * with relative paths ("../") that result in access of a parent directory.   
  215.      * @param path the path to validate   
  216.      * @return {@code true} if the path has been recognized as invalid, {@code false} otherwise   
  217.      */    
  218.     protected boolean isInvalidPath(String path) {     
  219.         return (path.contains("WEB-INF") || path.contains("META-INF") || StringUtils.cleanPath(path).startsWith(".."));     
  220.     }     
  221.     
  222.     /**   
  223.      * Inner class to avoid hard-coded JAF dependency.   
  224.      */    
  225.     private static class ActivationMediaTypeFactory {     
  226.     
  227.         private static final FileTypeMap fileTypeMap;     
  228.     
  229.         static {     
  230.             fileTypeMap = loadFileTypeMapFromContextSupportModule();     
  231.         }     
  232.     
  233.         private static FileTypeMap loadFileTypeMapFromContextSupportModule() {     
  234.             // see if we can find the extended mime.types from the context-support module     
  235.             Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types");     
  236.             if (mappingLocation.exists()) {     
  237.                 InputStream inputStream = null;     
  238.                 try {     
  239.                     inputStream = mappingLocation.getInputStream();     
  240.                     return new MimetypesFileTypeMap(inputStream);     
  241.                 }     
  242.                 catch (IOException ex) {     
  243.                     // ignore     
  244.                 }     
  245.                 finally {     
  246.                     if (inputStream != null) {     
  247.                         try {     
  248.                             inputStream.close();     
  249.                         }     
  250.                         catch (IOException ex) {     
  251.                             // ignore     
  252.                         }     
  253.                     }     
  254.                 }     
  255.             }     
  256.             return FileTypeMap.getDefaultFileTypeMap();     
  257.         }     
  258.     
  259.         public static MediaType getMediaType(String filename) {     
  260.             String mediaType = fileTypeMap.getContentType(filename);     
  261.             return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);     
  262.         }     
  263.     }     
  264. }    

 2.3 资源文件目录结构



 2.4  浏览器缓存效果

 

 2.5

 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

通过逗号(,)分割他们现在就实现了通过一个请求加载多个资源文件的效果了

猜你喜欢

转载自tw-wangzhengquan.iteye.com/blog/2092652
今日推荐