前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringFramework之@Controller/@RequestMapping解析

SpringFramework之@Controller/@RequestMapping解析

作者头像
克虏伯
发布2019-07-01 18:10:11
6450
发布2019-07-01 18:10:11
举报
文章被收录于专栏:软件开发-青出于蓝

  分析版本Spring5.0.9.release,Springboot2.0.3.release

    spring-webmvc的META-INFO/spring.handles文件中,有MvcNamespaceHandler,这是用来解析标签的,来看下MvcNamespaceHandler的init(),如下List-1,我们暂时只关注AnnotationDrivenBeanDefinitionParser。

List-1

代码语言:javascript
复制
@Override
public void init() {
    registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
    registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
    registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
    registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
    registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
    registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
    registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
    registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
    registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
    registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}

    AnnotationDrivenBeanDefinitionParser的parse方法里面有很多代码,但是我们只关注俩个,如下List-2:

  1. 用RootBeanDefinition将RequestMappingHandlerMapping注册到spring容器中。
  2. MvcNamespaceUtils.registerDefaultComponents中,将BeanNameUrlHandlerMapping注册到spring容器中、将HttpRequestHandlerAdapter注册到spring容器中、将SimpleControllerHandlerAdapter注册到spring容器中、将HandlerMappingIntrospector注册到容器中。

List-2

代码语言:javascript
复制
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
    ...
    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
    ...
    // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
    MvcNamespaceUtils.registerDefaultComponents(context, source);
    return null;
}

    RequestMappingHandlerMapping的继承图如下图1所示:

                                                                          图1

    图1中,AbstractHandlerMethodMapping实现了接口InitializingBean,实现了afterPropertiesSet(),如果了解SpringIOC,应该知道这个方法意味着什么,Spring在创建Bean的时候会调用这个方法。

List-3

代码语言:javascript
复制
@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

    由List-3知道afterPropertiesSet调用了initHandlerMethods(),如下List-4,首先从ApplicationContext或者所有的beanName,之后循环它们。isHandler方法由子类RequestMappingHandlerMapping实现,判断类上是否有Controller或者RequestMapping注解,所以即使不加上@Controller,只是用@RequestMapping注解也可以。List-4中的handlerMethodsInitialized是空方法,不用管。

List-4

代码语言:javascript
复制
protected void initHandlerMethods() {
	if (logger.isDebugEnabled()) {
		logger.debug("Looking for request mappings in application context: " + getApplicationContext());
	}
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));

	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			Class<?> beanType = null;
			try {
				beanType = obtainApplicationContext().getType(beanName);
			}
			catch (Throwable ex) {
				// An unresolvable bean type, probably from a lazy bean - let's ignore it.
				if (logger.isDebugEnabled()) {
					logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
				}
			}
			if (beanType != null && isHandler(beanType)) {
				detectHandlerMethods(beanName);
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

    来看List-4中的方法detectHandlerMethods,传入的是String类型的bean name,如下List-5,首先获取bean name对应的Class,getMappingForMethod在RequestMappingHandlerMapping中实现,如List-6所示,判断方法上是否有RequestMapping注解,如果有,则获取参数、path、header等信息,用builder模式构造出RequestMappingInfo。MethodIntrospector.selectMethods会遍历类上的方法,所以整体上对类的所有方法进行是否有RequestMapping的检查。需要注意的是,类似GetMapping等是组合注解,它们还是基于RequestMapping,所以方法上有GetMapping/PostMapping等都会会扫描并创建出对应的RequestMappingInfo。

List-5

代码语言:javascript
复制
protected void detectHandlerMethods(Object handler) {
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						throw new IllegalStateException("Invalid mapping on handler class [" +
								userType.getName() + "]: " + method, ex);
					}
				});
		if (logger.isDebugEnabled()) {
			logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

List-6

代码语言:javascript
复制
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
	}
	return info;
}

@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

    List-5中的registerHandlerMethod,会将得到的Mapper信息注册到mappingRegistry到,这个mappingRegistry后面在DispatcherServlet中使用到。注意传入到registerHandlerMethod方法的handler是String类型的bean name。

    Springboot中,RequestMappingHandlerMapping是通过@Configuration方式注入的,如下List-7

List-7

代码语言:javascript
复制
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
...

Reference

  1. 源码
  2. https://www.cnblogs.com/lucas2/p/9419147.html

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Reference
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档