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


 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 资源文件目录结构


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


 2.4  浏览器缓存效果

 
maven 打包生产环境 压缩 JS 利用SpirngMVC扩展解决减少资源的请求数_第2张图片
 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

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

 

你可能感兴趣的:(maven,springMVC)