【spring】ApplicationListener传递参数到页面(解决静态+动态资源路径+静态文件的缓存控制)

一.相对路径还是绝对路径的问题

       前端页面加载资源或者请求的时候到底是使用相对路径还是绝对路径,想必大家都很清楚,用的是当然是相对路径,因为这样增加了项目的灵活性,不需要经常的改动。那既然是相对路径就需要在页面中小心使用了,一旦使用错误,讨厌的404就会来了,相当讨人厌。

二.相对路径的获取

       相对路径的获取办法也有好多种

              1. 一种是在jsp页面利用<%%>来拼凑路径,然后配置base路径,代码如下

<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServeName()+":"+request.getServePort()+"/"+path+"/";
%>
<head>
<base href="<%basePath%>">
</head>

              2.这里为大家介绍另一种办法就是通过spring的Applicationlistener接口来传递相对路径的参数,可以直接在页面中使用,同时可以通过此方法来解决静态文件更新后的缓存问题。框架【spring+springmvc】

             步骤:

                   ①.引入spring及其他的相关jar包,此处省略

                   ②.配置相关配置文件

  spring的配置文件ApplicationContext.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd">

    <context:component-scan base-package="com.raipeng.work.spring.model"/>
    <context:component-scan base-package="com.raipeng.work.spring.listener"/>
   <!--加载自定义的配置文件-->
    <context:property-placeholder location="classpath:config.properties"/>
</beans>

 config.properties

git.version =1.0.0

 web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:test-servlet.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>test</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

test.servlet.xml    (spring mvc 配置文件)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">


    <context:component-scan base-package="com.xxx.work.spring.controller"/>

    <mvc:default-servlet-handler/>

   <mvc:annotation-driven/>
    <!--视图解析配置-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

 webConfig.java

package com.xxx.work.spring.model;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Created by 111 on 2015/11/24.
 */
//需要加入注解并扫描该文件,用于后期的自动注入
@Component public class WebConfig { private String resource;//静态资源文件 private String context;//WEB应用上下文 private String revision;//CSS、js版本号,防止缓存的问题 public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public String getContext() { return context; } public void setContext(String context) { this.context = context; } public String getRevision() { return revision; }
//加载配置文件中的值 @Value(
"${git.version}") public void setRevision(String revision) { this.revision = revision; } }

WebApplicationContextListener.java

package com.xxx.work.spring.listener;

import com.raipeng.work.spring.model.WebConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;

import javax.annotation.Resource;
import javax.servlet.ServletContext;

/**
 * Created by 111 on 2015/11/24.
 */

//需要注解,并扫描,在程序启动的时候就自动加载
@Component
public class WebApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    private Logger logger = LogManager.getLogger(WebApplicationListener.class);

    private WebConfig webConfig;
    
    //资源注入,也可以直接在变量上用autowired
    @Resource
    public void setWebConfig(WebConfig webConfig) {
        this.webConfig = webConfig;
    }

     //覆盖ApplicationListener的方法,重写自己的业务逻辑
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        WebApplicationContext webApplicationContext = (WebApplicationContext)applicationContext;
        ServletContext servletContext = webApplicationContext.getServletContext();
        webConfig.setContext(servletContext.getContextPath());
        webConfig.setResource(webConfig.getContext()+"/public");
        servletContext.setAttribute("context",webConfig.getContext());
        servletContext.setAttribute("resource",webConfig.getResource());
        servletContext.setAttribute("revision",webConfig.getRevision());
        logger.debug("context:{},resource:{},revision:{}",webConfig.getContext(),webConfig.getResource(),webConfig.getRevision());
    }
}

index.jsp

<%--
  Created by IntelliJ IDEA.
  User: 111
  Date: 2015/11/24
  Time: 15:51
  To change this template use File | Settings | File Templates.
--%>
<!--jsp有的版本默认el表达式关闭,如果遇到el表达式没解析,可以试试加上这个-->
<%@ page isELIgnored="false"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
    <link rel="stylesheet" href="${resource}/css/index.css?revision=${revision}">
    <title></title>
</head>
<body>
<img src="${resource}/image/image.png"/>
<a href="${context}/test/home">点击</a>
</body>
</html>

目录结构

【spring】ApplicationListener传递参数到页面(解决静态+动态资源路径+静态文件的缓存控制)忽略boot.jsp

浏览器中的效果:

【spring】ApplicationListener传递参数到页面(解决静态+动态资源路径+静态文件的缓存控制)

三.原理解析(暂略)

       这里从spring的流程进行分析,

       首先启动Tomcat服务器

       ——>根据web.xml中配置的contextLoaderListener初始化容器(ContextLoadListener.java 实现了ServletContextListener)

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}

       ——>实际上是在父类ContextLoader中初始化,在ContextLoader中为容器例示一个根webApplicationContext(Instantiate the root WebApplicationContext for this loader),方法为

if (this.context == null) {
      this.context = createWebApplicationContext(servletContext);
}

接着如果有父上下文加载父上下文(这里parentContext为空)

四.js页面的相对路径解决(使用tiles布局,定义在general.jsp的公共页面):

 <script type="text/javascript" src="${resource}/lib/amd/require.js"
            data-main="${resource}/lib/amd/config.js?revision=${revision}"
            data-app="${resource}/js/general.js,<tiles:insertAttribute name="app" ignore="true"/>"
            data-context="${context}" data-revision="${revision}" data-resource="${resource}" defer
            async="true"></script>

在config.js(amd的配置文件)中

(function () {
    var scripts = document.getElementsByTagName('script');
    for (var i = 0; i < scripts.length; i++) {
        if (scripts[i].getAttribute('data-main')) {
            var context = scripts[i].getAttribute('data-context');
            var revision = scripts[i].getAttribute('data-revision');
            var resource = scripts[i].getAttribute('data-resource');
            var config = {
                context: context,
                revision: revision,
                resource: resource
            };
            window.config = config;
            break;
        }
    }
})(window);

调用时使用:url:config.context  静态页面:config.resource+"/lib"+..