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中配置,为所有beanName以Controller为后缀的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>
要点分析
- 表面上,该方案很好地实现了对所有beanName以Controller为后缀的bean进行拦截,没有方案二浪费系统资源的弊端。
- 但是,无法保证开发人员自行编写的其他bean就不会以Controller为后缀
- 同时,无法保证开发人员编写的Controller类就一定是以Controller为beanName后缀
- 我们知道,编写的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 />