SpringMVC纯注解配置+原理详解【请求流程预备点】

SpringMVC系列这里都基于纯注解的方式。而要实现纯注解,就是要替代原本的web.xmlspringmvc.xml两个配置文件的内容。

一、替代web.xml

1.1 spring配置

不扫描Controller类

@ComponentScan(value = "com.wml",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class SpringRootConfig {
}

1.2 springmvc配置

扫描Controller类

@ComponentScan(value = "com.wml",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class WebMvcConfig {
}

这里相当于web.xml,后面会加载到。

public class WebAppInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    //Spring父容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{SpringRootConfig.class};
    }

    //SpringMVC配置子容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{WebMvcCOnfig.class};
    }

    //DispatcherServlet的映射信息
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected Filter[] getServletFilters() {
        return super.getServletFilters();
    }
}

二、替代springmvc.xml

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurationSupport {

    @Autowired
    private MyInterceptor userInterceptor;

    //配置视图解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/items/**").excludePathPatterns("/items/get/**");
        super.addInterceptors(registry);
    }
    
    //还有其他的一些配置,如转换器等


}

拦截器

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("======preHandle=========");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("========postHandle======");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("========afterCompletion======");
    }

}

这样就可以完全取代xml配置了,还有Controller省略没写。

三、原理

解析如何取代web.xml

在Servlet 3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果能发现的话,就会用它来配置Servlet容器。

在setvlet容器启动时,会根据SPI服务发现的设计,在spring-web-5.1.3.RELEASE/META-INF/services/javax.servlet.ServletContainerInitializer中,加载里面的类:

org.springframework.web.SpringServletContainerInitializer,而该类实现了ServletContainerInitializer接口,如下:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer{}

因此,该类就会在启动时进行实例化,调用到其唯一的onStartup方法,但是,其类上还有个注解@HandlesTypes(WebApplicationInitializer.class),因此会在调用方法前,会先收集该实现该注解中的接口的类,也就是上面我们自己定义的WebAppInit类,该类顶级父类就是WebApplicationInitializer,会在onStartup方法中作为参数传入。

如下:

参数webAppInitializerClasses就是我们的WebAppInit

3.1 onStartup

public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					.....
				}
			}
		}

		。。。。
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
            //调用WebApplicationInitializer实现类的onStartup方法
			initializer.onStartup(servletContext);
		}
	}

这个方法最终就会调用WebApplicationInitializer实现类的onStartup方法:

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
		//注册DispatcherServlet
		registerDispatcherServlet(servletContext);
	}

这里会先调用其父类的onStartup方法,

@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

在该类中完成监听器的创建。

注册监听器

protected void registerContextLoaderListener(ServletContext servletContext) {

		//代码1:创建spring上下文对象
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
			//创建监听器上下文,并将spring上下文添加进去
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
            //将监听器上下文加入servlet上下文
			servletContext.addListener(listener);
		}
		else {
			logger.debug("No ContextLoaderListener registered, as " +
					"createRootApplicationContext() did not return an application context");
		}
	}

createRootApplicationContext创建Spring上下文

protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            //注册到上下文
			context.register(configClasses);
			return context;
		}
		else {
			return null;
		}
	}

在该方法中,通过调用getRootConfigClasses就可以拿到我们在WebAppInit类中实现的该方法,返回SpringRootConfig类class对象,该类中我们配置了扫描的包,然后就通过基于注解的上下文对象注册到Spring上下文中。

接着会将Spring的上下文对象注册到监听器上下文中,再将监听器上下文注册到servlet上下文中。

回到上面,调用完super.onStartup后,接着调用registerDispatcherServlet,在该方法中注册DispatcherServlet

注册和实例化DispatcherServlet

看后面具体流程分析

protected void registerDispatcherServlet(ServletContext servletContext) {
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return null or empty");

		//代码1:创建springmvc的上下文,注册了MvcContainer类
		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

		//代码2:创建DispatcherServlet
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

    //代码3:将dispatcherServlet添加到servlet上下文
		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		if (registration == null) {
			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
					"Check if there is another servlet registered under the same name.");
		}

		//如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载
		registration.setLoadOnStartup(1);
    	//这里会调用到我们实现的方法获取DispatcherServlet的映射信息
		registration.addMapping(getServletMappings());
		registration.setAsyncSupported(isAsyncSupported());

    	// 获取过滤器,该方法默认为空,可重写它加入我们自己的过滤器
		Filter[] filters = getServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
			for (Filter filter : filters) {
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

代码1:createServletApplicationContext

这里会调用到我们自己实现的getServletConfigClasses方法,如下:

 protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{WebMvcConfig.class};
    }

WebMvcConfig类中,我们配置了扫描带Controller注解的类,在这里将该类注册到上下文中。

protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			context.register(configClasses);
		}
		return context;
	}

代码2:createDispatcherServlet

然后创建DispatcherServlet,将mvc的上下文传进去,再将DispatcherServlet对象添加到servlet上下文中.

到这里onStartup方法就执行完毕了,完成了向servlet容器中添加监听器ContextLoaderListenerDispatcherServlet的任务。

3.2 监听器启动创建Spring上下文

监听器上下文就是上面的ContextLoaderListener,因为实现了ServletContextListener,servlet容器启动时,就会触发它的contextInitialized方法:

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

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	......

		try {
	
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
				
					if (cwac.getParent() == null) {
						
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
                    //主要看这里
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
//将上下文对象WebApplicationContext添加到servletContext中
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}


			return this.context;
		}
		....
	}

这里主要关注configureAndRefreshWebApplicationContext方法,然后就是会将WebApplicationContext上下文对象设置到servletContext。

该方法只要看最后,就是调用了refresh方法,该方法我们都知道,会初始化Spring容器,实例化,依赖注入等,就会将前面配置监听器时,要扫描的非Controller注解的类进行实例化注册,完成Spring上下文的创建。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		...........

		wac.setServletContext(sc);
		....
		
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);
		wac.refresh();
	}

3.3DispatcherServlet的启动

前面配置DispatcherServlet时,会在servlet容器启动时启动,触发生命周期中的init方法,位于HttpServletBean类中:

public final void init() throws ServletException {

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		............
		//调用子类FrameworkServlet的实现
		initServletBean();
	}
protected final void initServletBean() throws ServletException {
		...

		try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		...
	}
//看这里
protected WebApplicationContext initWebApplicationContext() {
		//代码1:刚刚通过servletContext.setAttribute将Listener监听器加载的spring容器添加到了servlet容器,这里就可以获得到了
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// DispatcherServlet的spring上下文
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					
					if (cwac.getParent() == null) {
						//这里将spring容器作为自己的父容器,这样当需要注入某个bean时就可以从父上下文中获取
						cwac.setParent(rootContext);
					}
					//代码2:这里dispatcherServlet启动子容器
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
    ....
        if (!this.refreshEventReceived) {
		
			synchronized (this.onRefreshMonitor) {
                //这里会对相关组件进行初始化
				onRefresh(wac);
			}
		}
		。。。。。
		return wac;
	}

这部分主要做了两个工作:

  1. 获取前面加入到servlet容器中的spring容器上下文,将其作为自己parent,这样就可以直接从其父容器【spring上下文中】获取bean,这里详见spring的getBean流程。
  2. 再启动dispatcherServlet的子容器,调用refresh刷新spring上下文。

到这里取代web.xml的配置解析就结束了。

接着看看如何取代springmvc.xml的:

解析如何取代springmvc.xml

我们在AppConfig类中,定义了EnableWebMvc注解:

@Configuration
@EnableWebMvc
public class AppConfig extends WebMvcConfigurationSupport {}

如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

在该类中引入了DelegatingWebMvcConfiguration配置类:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();


	@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			//拿到AppConfig类:AppConfig$$EnhancerBySpringCGLIB..
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}


	@Override
	protected void configurePathMatch(PathMatchConfigurer configurer) {
		this.configurers.configurePathMatch(configurer);
	}

	@Override
	protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		this.configurers.configureContentNegotiation(configurer);
	}

	@Override
	protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
		this.configurers.configureAsyncSupport(configurer);
	}

	@Override
	protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		this.configurers.configureDefaultServletHandling(configurer);
	}

	@Override
	protected void addFormatters(FormatterRegistry registry) {
		this.configurers.addFormatters(registry);
	}

	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
		this.configurers.addInterceptors(registry);
	}

	@Override
	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
		this.configurers.addResourceHandlers(registry);
	}

	@Override
	protected void addCorsMappings(CorsRegistry registry) {
		this.configurers.addCorsMappings(registry);
	}

	@Override
	protected void addViewControllers(ViewControllerRegistry registry) {
		this.configurers.addViewControllers(registry);
	}

	@Override
	protected void configureViewResolvers(ViewResolverRegistry registry) {
		this.configurers.configureViewResolvers(registry);
	}

	@Override
	protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		this.configurers.addArgumentResolvers(argumentResolvers);
	}

	@Override
	protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
		this.configurers.addReturnValueHandlers(returnValueHandlers);
	}

	@Override
	protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		this.configurers.configureMessageConverters(converters);
	}

	@Override
	protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		this.configurers.extendMessageConverters(converters);
	}

	@Override
	protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
	}

	@Override
	protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
	}

	@Override
	@Nullable
	protected Validator getValidator() {
		return this.configurers.getValidator();
	}

	@Override
	@Nullable
	protected MessageCodesResolver getMessageCodesResolver() {
		return this.configurers.getMessageCodesResolver();
	}

}

该类都是通过WebMvcConfigurerComposite对象configurers调用实现,而该对象在该方法中获取:

@Autowired(required = false)
	public void setConfigurers(List<WebMvcConfigurer> configurers) {
		if (!CollectionUtils.isEmpty(configurers)) {
			//拿到AppConfig类:AppConfig$$EnhancerBySpringCGLIB..
			this.configurers.addWebMvcConfigurers(configurers);
		}
	}

这个参数configurers就是我们的配置类AppConfig,而该方法是通过@Autowired(required = false)注入的,因此依赖注入时,会先检查依赖的WebMvcConfigurer,对其进行实例化,这时就是解析我们的配置类AppConfig进行实例化依赖注入。

DelegatingWebMvcConfiguration继承自WebMvcConfigurationSupport,在该类中,就完成了许多组件的bean实例化,RequestMappingHandlerMappingPathMatcherHandlerMappingHandlerAdapterBeanNameUrlHandlerMapping等等,每个组件都是通过@Bean注入的:

在实例化时会调用许多我们实现的钩子方法,如拦截器、跨域配置等。

@Bean
	public RequestMappingHandlerMapping requestMappingHandlerMapping() {
		RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
		mapping.setOrder(0);
        //代码1:添加拦截器
		mapping.setInterceptors(getInterceptors());
		mapping.setContentNegotiationManager(mvcContentNegotiationManager());
		mapping.setCorsConfigurations(getCorsConfigurations());

		PathMatchConfigurer configurer = getPathMatchConfigurer();

		......

		return mapping;
	}

如代码1:添加拦截器

protected final Object[] getInterceptors() {
		if (this.interceptors == null) {
			InterceptorRegistry registry = new InterceptorRegistry();
			//这里是个钩子方法,会调用到我们自己定义的添加拦截器的方法【AppConfig中定义】
			addInterceptors(registry);
			registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
			registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
			this.interceptors = registry.getInterceptors();
		}
		return this.interceptors.toArray();
	}

SpringMVC纯注解配置+原理详解【请求流程预备点】_第1张图片

等等就不列举了,但这里需要注意的是,当我们发送请求时,要定位到某个具体的方法,而url和方法的映射关系肯定不会在发送请求的时候才建立,一定是在启动的时候就建立完毕的了。

RequestMappingHandlerMapping的顶级接口实现了InitializingBean接口,该类的afterPropertiesSet方法会在bean实例化后调用【bean声明周期的知识】,而对url和方法的映射关系建立就是在这里进行的。

建立url和method的映射

AbstractHandlerMethodMapping#afterPropertiesSet

public void afterPropertiesSet() {
		initHandlerMethods();
	}

	//这里会遍历上下文中的beanName,进行注册映射关系
	protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                //主要看这里
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

processCandidateBean

protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			beanType = obtainApplicationContext().getType(beanName);
		}
		....
		//如果类上面有@Controller注解或者@RequestMapping注解
		if (beanType != null && isHandler(beanType)) {
			//建立uri和method的映射关系
			detectHandlerMethods(beanName);
		}
	}
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || //如果有Controller注解
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));//如果有RequestMapping注解
	}	

detectHandlerMethods

找到有ControllerRequestMapping注解的bean,因为这两个注解的类中才有 url 开始进行具体的映射建立。

protected void detectHandlerMethods(Object handler) {
    //拿到class对象
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);

			//这里的map,key就是类中所有有RequestMappint注解的方法对象
			// value就是RequestMapping注解属性封装的对象
			//也就是将这两者建立了映射关系
            //代码1:selectMethods
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						...
					});
		
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				//建立uri和方法的各种映射关系,反正一条,根据uri要能够找到method对象
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

代码1:selectMethods

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
		final Map<Method, T> methodMap = new LinkedHashMap<>();
		Set<Class<?>> handlerTypes = new LinkedHashSet<>();
		Class<?> specificHandlerType = null;

		......
		handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

		for (Class<?> currentHandlerType : handlerTypes) {
			final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);

			//doWithMethods中会拿到当前Controller类中所有的方法,然后遍历调用方法的doWith方法,也就是这里的lambada表示
			ReflectionUtils.doWithMethods(currentHandlerType, method -> {
				//拿到当前方法
				Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
				//如果方法上面有@RequestMapping注解,则进行封装返回,result就是封装后的注解对象,调用外层的lambada表达式
				T result = metadataLookup.inspect(specificMethod);
				if (result != null) {
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
					if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
						//将方法对象和注解封装对象简历映射关系
						methodMap.put(specificMethod, result);
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}

		return methodMap;
	}

该方法主要就是遍历当前类中的所有方法,然后调用metadataLookup.inspect,也就是外层的lambada表达式,对有RequestMapping注解的方法,封装其注解的属性信息返回,得到T result,然后将方法对象和其封装的注解属性对象,放到map中建立关系映射,返回该map。

外层的lambada表达式会调用getMappingForMethod

如果该方法有@RequestMapping注解,就将注解信息封装成RequestMappingInfo中,如果类中也有该注解,则同样的方式封装类上的注解信息,然后将这两个信息合并,因为我们知道一个url,是将其类上的url和方法上的url拼接成的。

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		//判断是否有@RequestMapping注解,然后注解里面的内容封装成对象
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
			//类上面的@RequestMapping注解也封装成对象
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
				//把方法上面的注解属性结合到类上面的RequestMappingInfo对象中
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).build().combine(info);
			}
		}
		return info;
	}

这里可以看一下其封装注解属性的实现:

用到了建造者设计模式一一将其属性进行构造封装

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();
	}

回到上面的detectHandlerMethods方法,接着会调用下面的循环:

	methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				//建立uri和方法的各种映射关系,反正一条,根据uri要能够找到method对象
				registerHandlerMethod(handler, invocableMethod, mapping);
			});

具体会调用register方法:

  1. 首先将beanName和具体的某个方法封装到HandlerMethod
  2. 再将mapping,也就是前面封装的注解属性对象RequestingMappingInfo,和HandlerMethod建立映射
  3. 再将具体的url字符串和RequestingMappingInfo进行映射
  4. 再将跨域的注解封装成对象和RequestingMappingInfo建立映射
public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				//创建HandlerMethod对象,其实
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);
				//建立uri对象和handlerMethod的映射关系
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					//url和RequestMappingInfo进行映射
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				//将CrossOrigin注解属性封装成CorsConfiguration
				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					//建立映射关系
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

总之就是将各种信息进行映射,包括

url和handlerMethod的映射、
url和requestMapping的映射、
handlerMethod和跨域配置对象的映射等。

但我们只要知道容器启动时,这些url和方法的映射以及各种映射已经建立完毕,在SpingMVC调用时,就可以直接拿到。

后面我们会讲SpringMVC的调用流程。SpringMVC流程总结、源码详解

你可能感兴趣的:(Spring)