Spring拦截机制之后端国际化心得

需求

前端请求的header里带有Prefer_Lang参数,向后端传递国际化信息,后端需要在处理业务之前(建立拦截机制),将Prefer_Lang保存于线程上下文。

思路分析

初次接收该需求时,为了不改变既有代码而捕获request中的Prefer_Lang参数,脑海中第一反应是拦截器的概念。

方案一

Servlet的javax.servlet.Filter 或起源于Spring Security的org.springframework.web.filter.DelegatingFilterProxy

要点分析

  • 这种Filter在web.xml中配置,表面上能满足需求,但Filter与web容器耦合,不利于将国际化模块提供给第三方使用

方案二

SpringMVC的org.springframework.web.servlet.HandlerInterceptor

具体实施

  • 在spring mvc的xml中配置,基于request-response模型进行拦截,与方案一Filter类似
<mvc:interceptors>
    <mvc:interceptor>
    <mvc:mapping path="/**" />
    <bean class="com.shjv.tdscdma.omc.server.platform.web.interceptor.LanguageInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

要点分析

  • 仔细探讨需求后,发现我们仅需要对Controller的调用进行拦截即可,只有Controller的调用才与后端国际化逻辑有关
  • 该方案对包括html、js等等与后端国际化逻辑无关的任意请求皆进行拦截,无意义地消耗大量系统资源
  • 虽然request-response模型上的拦截机制不可取,但是拦截也是AOP概念中的一部分,故有更优方案

方案三

Spring古老的AOP技术org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator

具体实施

  • 在spring mvc的xml中配置,为所有beanNameController为后缀的bean生成代理对象
<bean id="languageInterceptor"
      class="com.shjv.tdscdma.omc.server.platform.web.interceptor.LanguageInterceptor"></bean>
<bean
    class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames">
    <list>
    <value>*Controller</value>
    </list>
    </property>
    <property name="interceptorNames">
    <list>
    <value>languageInterceptor</value>
    </list>
    </property>
</bean>

要点分析

  • 表面上,该方案很好地实现了对所有beanNameController为后缀的bean进行拦截,没有方案二浪费系统资源的弊端。
  • 但是,无法保证开发人员自行编写的其他bean就不会以Controller为后缀
  • 同时,无法保证开发人员编写的Controller类就一定是以ControllerbeanName后缀
  • 我们知道,编写的Controller能为Spring MVC调度,是因为从代码结构上,强制了Controller类必须标注@Controller的注解,故有更优方案

方案四

Spring AOP的XML配置

具体实施

  • 在spring mvc的xml中配置,拦截所有标注@Controller的注解的Controller
<bean id="languageInterceptor"
      class="com.shjv.tdscdma.omc.server.platform.web.interceptor.LanguageInterceptor"></bean>
<aop:config>
    <aop:aspect ref="languageInterceptor">
        <aop:before method="trapLanguage"
                    pointcut="execution(* *(..)) and within(@org.springframework.stereotype.Controller *)"/>
    </aop:aspect>
</aop:config>

要点分析

  • 目前看来,该方案已经近乎完美地满足了我们的需求,通过within(@org.springframework.stereotype.Controller *)只拦截Spring MVC中的Controller的public方法
    做到。
  • 如果你与我一样是个XML Hater,我们还有更优雅的实现方式。

方案五

Spring AOP的Annotation配置。

具体实施

  • 在此,我们更进一步,只拦截Controller下标注了@RequestMapping的公共方法,JAVA代码如下
@Component
@Aspect
public class LanguageInterceptor {
    @Pointcut("execution(@org.springframework.web.bind.annotation.RequestMapping * *(..)) && within(@org.springframework.stereotype.Controller *)")
    public void controllerMethod() {
    }

    @Before("controllerMethod()")
    public void trapLanguage() {
        final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        LanguageOption.setPreferLang(request == null ? null : request.getHeader("Prefer_Lang"));
    }
}
  • 在spring mvc的xml中配置如下
<aop:aspectj-autoproxy />
posted @ 2017-01-04 13:14  SupremeHover  阅读(1857)  评论(0编辑  收藏  举报