文档章节

SpringMVC4.x源码分析(三):解析Controllor和HandlerMethod

祖大俊
 祖大俊
发布于 2018/06/10 11:41
字数 1797
阅读 303
收藏 0

回顾本例springmvc-config.xml的配置:

<context:component-scan base-package="com.spring"/>
<mvc:annotation-driven />

component-scan:扫描指定包下被@Component,@Service@Controller@Repository,@Configuration注解所标注的class。实际上,都是通过@Component来完成的。

annotation-driven:注册SpringMVC的web组件,并完成HandlerMethod的解析。

名称空间处理器:NamespaceHandler

context:component-scan标签由ContextNamespaceHandler处理器处理。

mvc:annotation-driven标签由MvcNamespaceHandler处理器处理。

ContextNamespaceHandler处理器源码:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

@Override
public void init() {
	registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
	registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
	registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
	registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
	registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
	registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
	}

}

由此可见,component-scan是由具体的ComponentScanBeanDefinitionParser解析器解析,它实现了BeanDefinitionParser接口,完成扫描指定包路径下的class文件,并注册所有满足条件的BeanDefinition。

ComponentScanBeanDefinitionParser解析器

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
	String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
	basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
	String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

	ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    // 完成扫描并注册BeanDefinitions
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

	return null;
}

ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner父类,我们重点关注findCandidateComponents(String basePackage)方法。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
	try {
        // 首先将我们配置的com.spring包修改为classpath*:com/spring/**/*.class
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
		boolean traceEnabled = logger.isTraceEnabled();
		boolean debugEnabled = logger.isDebugEnabled();
		for (Resource resource : resources) {
			if (traceEnabled) {
				logger.trace("Scanning " + resource);
			}
			if (resource.isReadable()) {
				try {
					MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    // 重点关注该方法
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						sbd.setSource(resource);
						if (isCandidateComponent(sbd)) {
							//...
							candidates.add(sbd);
						}
						//...
	return candidates;
}

isCandidateComponent(metadataReader)方法,完成了@Component的筛选工作。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return false;
		}
	}
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, this.metadataReaderFactory)) {
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

includeFilters指定了筛选条件为:@Component注解的类。

由于我们并没有在springmvc-config.xml中给component-scan配置includeFilters标签过滤属性,所以includeFilters取的是默认值@Component注解

protected void registerDefaultFilters() {
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	//...
}

既然默认读取@Component标注的类,那么,那些仅仅标注了@Service@Controller@Repository,@Configuration注解的类是怎么被包含进来的呢?原理在于,直接或间接使用@Component标注的类,都会被包含进来。@Service@Controller@Repository,@Configuration都是被@Component所标注的。

AnnotationTypeFilter.matchSelf()源码:

@Override
protected boolean matchSelf(MetadataReader metadataReader) {
	AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
	return metadata.hasAnnotation(this.annotationType.getName()) ||
			(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

hasAnnotation:直接被@Component标注。

hasMetaAnnotation:间接被@Component标注,如@Service@Controller@Repository,@Configuration。

AnnotationDrivenBeanDefinitionParser解析器

public class MvcNamespaceHandler extends NamespaceHandlerSupport {

@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("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
	registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
	registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
	registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}

}

AnnotationDrivenBeanDefinitionParser.parse()方法很长:

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
	Object source = parserContext.extractSource(element);
	XmlReaderContext readerContext = parserContext.getReaderContext();

	CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
	parserContext.pushContainingComponent(compDefinition);

	RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);

	RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
	handlerMappingDef.setSource(source);
	handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	handlerMappingDef.getPropertyValues().add("order", 0);
	handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

	if (element.hasAttribute("enable-matrix-variables")) {
		Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
		handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
	}
	else if (element.hasAttribute("enableMatrixVariables")) {
		Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
		handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
	}

	configurePathMatchingProperties(handlerMappingDef, element, parserContext);
	readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);

	RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
	handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);

	RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
	RuntimeBeanReference validator = getValidator(element, source, parserContext);
	RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);

	RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
	bindingDef.setSource(source);
	bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	bindingDef.getPropertyValues().add("conversionService", conversionService);
	bindingDef.getPropertyValues().add("validator", validator);
	bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);

	ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
	ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
	ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
	String asyncTimeout = getAsyncTimeout(element);
	RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
	ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
	ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

	RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
	handlerAdapterDef.setSource(source);
	handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
	handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
	handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
	addRequestBodyAdvice(handlerAdapterDef);
	addResponseBodyAdvice(handlerAdapterDef);

	if (element.hasAttribute("ignore-default-model-on-redirect")) {
		Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
		handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
	}
	else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
		// "ignoreDefaultModelOnRedirect" spelling is deprecated
		Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
		handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
	}

	if (argumentResolvers != null) {
		handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
	}
	if (returnValueHandlers != null) {
		handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
	}
	if (asyncTimeout != null) {
		handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
	}
	if (asyncExecutor != null) {
		handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
	}

	handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
	handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
	readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);

	RootBeanDefinition uriContributorDef =
			new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
	uriContributorDef.setSource(source);
	uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
	uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
	String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
	readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);

	RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
	csInterceptorDef.setSource(source);
	csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
	RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
	mappedInterceptorDef.setSource(source);
	mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
	mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
	String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);

	RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
	methodExceptionResolver.setSource(source);
	methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
	methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
	methodExceptionResolver.getPropertyValues().add("order", 0);
	addResponseBodyAdvice(methodExceptionResolver);
	if (argumentResolvers != null) {
		methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
	}
	if (returnValueHandlers != null) {
		methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
	}
	String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);

	RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
	statusExceptionResolver.setSource(source);
	statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	statusExceptionResolver.getPropertyValues().add("order", 1);
	String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);

	RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
	defaultExceptionResolver.setSource(source);
	defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	defaultExceptionResolver.getPropertyValues().add("order", 2);
	String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);

	parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
	parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
	parserContext.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
	parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
	parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
	parserContext.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
	parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));

	// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
	MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

	parserContext.popAndRegisterContainingComponent();

	return null;
}

mvc:annotation-driven解析器,向Spring IOC容器注册了很多SpringMVC的web组件,我们重点关注下面几个组件,其余读者可自行了解:

1、RequestMappingHandlerMapping:负责解析HandlerMethod,并根据request请求返回HandlerExecutionChain,HandlerExecutionChain是HandlerMethod和方法拦截器的合体。

2、RequestMappingHandlerAdapter:负责处理request请求,并返回ModelAndView对象。

3、DefaultFormattingConversionService:类型转换服务类,如String to Date,Date to String。

4、HttpMessageConverter:消息转换器,如根据HTTP的MIME类型,JavaBean to Json,JavaBean to XML。

5、ConfigurableWebBindingInitializer:WebDataBinder的初始化器。

...

其余可自行查看。

RequestMappingHandlerMapping实现了InitializingBean接口,那么,在Spring IOC容器创建该bean对象时,会调用它的afterPropertiesSet()方法,完成HandlerMethod的解析工作。

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}

protected void initHandlerMethods() {
	//...
    // 从IOC容器中获得所有的beans
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
			getApplicationContext().getBeanNamesForType(Object.class));

	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			Class<?> beanType = null;
			try {
				beanType = getApplicationContext().getType(beanName);
			}
			catch (Throwable ex) {
				//...
			}
            // isHandler()检测是否有@Controller注解
			if (beanType != null && isHandler(beanType)) {
                // 从Controllor中解析出被@RequestMapping标注的方法
				detectHandlerMethods(beanName);
			}
		}
	}
    // 空方法
	handlerMethodsInitialized(getHandlerMethods());
}
protected void detectHandlerMethods(final Object handler) {
	Class<?> handlerType = (handler instanceof String ?
			getApplicationContext().getType((String) handler) : handler.getClass());
	final Class<?> userType = ClassUtils.getUserClass(handlerType);
    // T为RequestMappingInfo对象
	Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
			new MethodIntrospector.MetadataLookup<T>() {
				@Override
				public T inspect(Method method) {
					try {
						return getMappingForMethod(method, userType);
					}
					catch (Throwable ex) {
						//...
					}
				}
			});

	//...
	}
	for (Map.Entry<Method, T> entry : methods.entrySet()) {
		Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
		T mapping = entry.getValue();
		registerHandlerMethod(handler, invocableMethod, mapping);
	}
}
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // 方法上的@RequestMapping
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
        // 类上的@RequestMapping
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
            // 取二者的并集(合并)
			info = typeInfo.combine(info);
		}
	}
	return info;
}
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);
}

从源码中,的确看到了读取的是@RequestMapping注解标注的方法或类。

下面我们看看RequestMappingInfo的类结构:

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

private final PatternsRequestCondition patternsCondition;

private final RequestMethodsRequestCondition methodsCondition;

private final ParamsRequestCondition paramsCondition;

private final HeadersRequestCondition headersCondition;

private final ConsumesRequestCondition consumesCondition;

private final ProducesRequestCondition producesCondition;

private final RequestConditionHolder customConditionHolder;
}

我画一个图,大家就明白了。

紧接着,将RequestMappingInfo封装成HandlerMethod,注册到MappingRegistry中。

AbstractHandlerMethodMapping.MappingRegistry.register():

public void register(T mapping, Object handler, Method method) {
	this.readWriteLock.writeLock().lock();
	try {
        // T mapping即RequestMappingInfo对象
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		assertUniqueMethodMapping(handlerMethod, mapping);

		//...
		this.mappingLookup.put(mapping, handlerMethod);
        // 不带通配符*和?号的,直接加入urlLookup
		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
			this.urlLookup.add(url, mapping);
		}

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

		CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
		if (corsConfig != null) {
			this.corsLookup.put(handlerMethod, corsConfig);
		}

		this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}
class MappingRegistry {
//T为RequestMappingInfo对象
private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();

private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();

private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();

private final Map<String, List<HandlerMethod>> nameLookup =
		new ConcurrentHashMap<String, List<HandlerMethod>>();

private final Map<HandlerMethod, CorsConfiguration> corsLookup =
		new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();
}

MappingRegistry是RequestMappingHandlerMapping的属性,MappingRegistry对RequestMappingInfo进行了多种缓存映射,方便查找。

至此,context:component-scan、mvc:annotation-driven、RequestMappingInfo以及HandlerMethod的解析及工作原理,都已经基本明确了。

context:component-scan:主要完成bean的解析工作。

mvc:annotation-driven:主要完成从controllor bean中解析出RequestMappingInfo,封装为HandlerMethod对象。

原文出处:http://my.oschina.net/zudajun

© 著作权归作者所有

祖大俊
粉丝 802
博文 32
码字总数 52477
作品 0
昌平
私信 提问
SpringMVC源码解析(五)——视图处理

前言 本篇将分析一次请求从接收到处理的最终环节——视图处理,也是 SpringMVC 源码解析的最后一节。将涉及异常处理和视图转发两部分。 源码解读 承接上篇,来看 “processDispatchResult” ...

MarvelCode
2018/07/07
53
0
如何实现@ResponseBody,把Json字符串转换为指定类型

1.问题 spring 是如何把 http中的body,转换为指定类的,里面的难点其实在于泛型的处理。 2.Spring的处理 2.1 HandlerMethod 这个类Spring对Method的封装,例如使用@RequestMapping注解方法,...

atheva
2018/08/09
0
0
你真的了解Spring MVC处理请求流程吗?

前言 阅读本文章大概需要8分钟左右。相信会让你对Spring MVC的理解更加深刻,更上一层楼。 SpringMVC图解 粒度很粗的图解 粒度比较粗的图解 粒度一般的图解 FrameworkServlet FrameworkServ...

cmazxiaoma
2018/06/30
0
0
SpringMVC源码解析(三)——HandlerAdapter

前言 上篇讲的是,HandlerMapping 初始化过程中,是如何将 Handler 与 请求的 url 建立起映射的,我们可以假想一下,“ http://localhost/test ” 的请求过来了,通过映射关系我们找到了 Ha...

MarvelCode
2018/06/30
86
0
SpringMvc 源码分析-注册Mapping

SpringBoot 默认开启扫描@Configuration,所以DelegatingWebMvcConfiguration默认会被扫进来,因为继承WebMvcConfigurationSupport,所以也会被实例化 在WebMvcConfigurationSupport里面声明...

数齐
2017/09/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

nginx学习笔记

中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。 是连接两个独立应用程序或独立系统的软件。 web请求通过中间件可以直接调用操作系统,也可以经过中间件把请求分发到多...

码农实战
今天
5
0
Spring Security 实战干货:玩转自定义登录

1. 前言 前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(Authentication),认证...

码农小胖哥
今天
11
0
JAVA 实现雪花算法生成唯一订单号工具类

import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import java.util.Calendar;/** * Default distributed primary key generator. * * <p> * Use snowflake......

huangkejie
昨天
12
0
PhotoShop 色调:RGB/CMYK 颜色模式

一·、 RGB : 三原色:红绿蓝 1.通道:通道中的红绿蓝通道分别对应的是红绿蓝三种原色(RGB)的显示范围 1.差值模式能模拟三种原色叠加之后的效果 2.添加-颜色曲线:调整图像RGB颜色----R色增强...

东方墨天
昨天
11
1
将博客搬至CSDN

将博客搬至CSDN

算法与编程之美
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部