- 前言
如果框架源码看多了,就会发现一个问题,AOP的思想是存在于整个框架思想的。无论是Spring,还是SpringMVC,或者Mybatis、Hibernate。为什么呢?因为AOP的存在让我们更好地去控制程序走向,而且不用人为的编写大量重复性代码。比如一个简单的权限认证,我们肯定不希望在每个用户请求抵达服务器时都需要在业务功能中加入重复性的权限验证代码。无疑,对所有的请求进行拦截,只放行权限认证通过的请求才是最佳的实践。这就是AOP思想。拦截器的本质也是一种AOP实践。
2.WebMvcConfigurationSupport 类的部分代码
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
private ServletContext servletContext;
private ApplicationContext applicationContext;
private List<Object> interceptors;
private ContentNegotiationManager contentNegotiationManager;
private List<HttpMessageConverter<?>> messageConverters;
private PathMatchConfigurer pathMatchConfigurer;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {}
protected final Object[] getInterceptors() {
if (interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
protected void addInterceptors(InterceptorRegistry registry) {
}
@Bean
public HandlerMapping viewControllerHandlerMapping() {
ViewControllerRegistry registry = new ViewControllerRegistry();
addViewControllers(registry);
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
handlerMapping.setPathMatcher(mvcPathMatcher());
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
handlerMapping.setInterceptors(getInterceptors());
return handlerMapping;
}
protected void addViewControllers(ViewControllerRegistry registry) {
}
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(2);
mapping.setInterceptors(getInterceptors());
return mapping;
}
@Bean
public HandlerMapping resourceHandlerMapping() {
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(
this.applicationContext, this.servletContext);
addResourceHandlers(registry);
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
handlerMapping.setPathMatcher(mvcPathMatcher());
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
return handlerMapping;
}
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
}
@Bean
public ResourceUrlProvider mvcResourceUrlProvider() {
}
@Bean
public HandlerMapping defaultServletHandlerMapping() {
DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);
configureDefaultServletHandling(configurer);
AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping();
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
return handlerMapping;
}
}
3.WebMvcConfigurationSupport 类的使用
通常为了使用拦截器,我们需要做两件事:
1).定义一个拦截器
public class MyInterceptor implements HandlerInterceptor{
//接口方法实现
}
2)..注册拦截器
public class MyInterceptorRegistry extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}}
4.WebMvcConfigurationSupport 注册拦截器的实现过程
WebMvcConfigurationSupport类中有这样两个方法:
protected final Object[] getInterceptors() {
//如果interceptors为空,说明拦截器还没有注册过,需要注册
if (interceptors == null) {
//构造一个拦截器注册器
InterceptorRegistry registry = new InterceptorRegistry();
//调用子类实现的addInterceptors()方法
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
//WebMvcConfigurationSupport中的空方法,由子类实现
protected void addInterceptors(InterceptorRegistry registry) {
}
可以看到,由getInterceptors()方法调用了我们自定义的MyInterceptor类中定义的addInterceptors()方法,从而将自定义拦截器注册到了WebMvcConfigurationSupport中。接下来呢,只要调用getInterceptors()方法,就能得到所有的拦截器了。我们可以看一看InterceptorRegistry类的内部:
public class InterceptorRegistry {
private final List<InterceptorRegistration> registrations = new ArrayList<InterceptorRegistration>();
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
registrations.add(registration);
return registration;
}
public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
InterceptorRegistration registration = new InterceptorRegistration(adapted);
registrations.add(registration);
return registration;
}
protected List<Object> getInterceptors() {
List<Object> interceptors = new ArrayList<Object>();
for (InterceptorRegistration registration : registrations) {
interceptors.add(registration.getInterceptor());
}
return interceptors ;
}
}
InterceptorRegistry类的内部维护了一个拦截器适配器的集合registrations ,我们的拦截器被转化为适配器对象来使用。但是getInterceptors()时得到的不是适配后的对象,而是拦截器本身对象的集合。
5.WebMvcConfigurationSupport 中拦截器集合的调用者
可以看到,WebMvcConfigurationSupport 类中的getInterceptors()方法被三个方法调用了。仔细观察,可以发现它们都在WebMvcConfigurationSupport类中。那么,我们来看看它们都是干什么用的:
@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
mapping.setOrder(2);
mapping.setInterceptors(getInterceptors());
return mapping;
}
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
PathMatchConfigurer configurer = getPathMatchConfigurer();
return handlerMapping;
}
@Bean
public HandlerMapping viewControllerHandlerMapping() {
ViewControllerRegistry registry = new ViewControllerRegistry();
addViewControllers(registry);
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping();
handlerMapping.setPathMatcher(mvcPathMatcher());
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
handlerMapping.setInterceptors(getInterceptors());
return handlerMapping;
}
发现没有,在Spring创建HandlerMapping类型的Bean事,getInterceptors()方法被调用了,然后把所有的拦截器加入了HandlerMapping对象中。
总结:SpringMVC的拦截器是如此,Mybatis的拦截器也是如此。有一条清晰的线索一直贯穿其中,先定义一个接口作为规范,允许编码人员实现此接口来完成自定义操作。再把所有符合条件的接口实现类添加到一个容器中,供系统使用。也算是一种比较巧妙的设计吧。
另一种思路是,在Spring容器完成所有的Bean的创建后,容器启动完成之前,获取实现了某种接口的所有实现类Bean,做一些特殊的事情。(ApplicationListener接口了解一下。。。)