Spring Boot参数配置原理

  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值

1、HandlerAdapter

在这里插入图片描述
0 - 支持方法上标注@RequestMapping
1 - 支持函数式编程的
xxxxxx

2、执行目标方法

// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

	
mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法
//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

3、参数解析器-HandlerMethodArgumentResolver

作用

确定将要执行的目标方法的每一个参数的值是什么;
SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
在这里插入图片描述


参数解析器的接口:
在这里插入图片描述

  • 当前解析器是否支持解析这种参数
  • 支持就调用 resolveArgument

4、返回值处理器

在这里插入图片描述


5、如何确定目标方法每一个参数的值

============InvocableHandlerMethod==========================
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

	MethodParameter[] parameters = getMethodParameters();
	if (ObjectUtils.isEmpty(parameters)) {
		return EMPTY_ARGS;
	}

	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		args[i] = findProvidedArgument(parameter, providedArgs);
		if (args[i] != null) {
			continue;
		}
		if (!this.resolvers.supportsParameter(parameter)) {
			throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
		}
		try {
			args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
		}
		catch (Exception ex) {
			// Leave stack trace for later, exception may actually be resolved and handled...
			if (logger.isDebugEnabled()) {
				String exMsg = ex.getMessage();
				if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
					logger.debug(formatArgumentError(parameter, exMsg));
				}
			}
			throw ex;
		}
	}
	return args;
}


5.1、挨个判断所有参数解析器哪个支持解析这个参数

@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
	HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
	if (result == null) {
		for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
			if (resolver.supportsParameter(parameter)) {
				result = resolver;
				this.argumentResolverCache.put(parameter, result);
				break;
			}
		}
	}
	return result;
}

5.2、解析这个参数的值

调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可

执行流程
挨个遍历循环,匹配对应的参数解析器。

找到参数解析器是否支持当前参数解析,然后参数解析,然后从请求中取到值,给了解析到的变量

不同的注解有不同的解析器,循环遍历匹配,通过解析器获得参数值

5.3、自定义类型参数 封装POJO

ServletModelAttributeMethodProcessor 这个参数处理器支持
是否为简单类型。

源码:

public static boolean isSimpleValueType(Class<?> type) {
	return (Void.class != type && void.class != type &&
			(ClassUtils.isPrimitiveOrWrapper(type) ||
			Enum.class.isAssignableFrom(type) ||
			CharSequence.class.isAssignableFrom(type) ||
			Number.class.isAssignableFrom(type) ||
			Date.class.isAssignableFrom(type) ||
			Temporal.class.isAssignableFrom(type) ||
			URI.class == type ||
			URL.class == type ||
			Locale.class == type ||
			Class.class == type));
}
	
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
	
	Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
	Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
	
	String name = ModelFactory.getNameForParameter(parameter);
	ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
	if (ann != null) {
		mavContainer.setBinding(name, ann.binding());
	}
	
	Object attribute = null;
	BindingResult bindingResult = null;
	
	if (mavContainer.containsAttribute(name)) {
		attribute = mavContainer.getModel().get(name);
	} else {
		// Create attribute instance
		try {
			attribute = createAttribute(name, parameter, binderFactory, webRequest);
		}
		catch (BindException ex) {
			if (isBindExceptionRequired(parameter)) {
				// No BindingResult parameter -> fail with BindException
				throw ex;
			}
			// Otherwise, expose null/empty value and associated BindingResult
			if (parameter.getParameterType() == Optional.class) {
				attribute = Optional.empty();
			}
			bindingResult = ex.getBindingResult();
		}
	}
	
	if (bindingResult == null) {
		// Bean property binding and validation;
		// skipped in case of binding failure on construction.
		WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
		if (binder.getTarget() != null) {
			if (!mavContainer.isBindingDisabled(name)) {
				bindRequestParameters(binder, webRequest);
			}
			validateIfApplicable(binder, parameter);
			if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
				throw new BindException(binder.getBindingResult());
			}
		}
		// Value type adaptation, also covering java.util.Optional
		if (!parameter.getParameterType().isInstance(attribute)) {
			attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
		}
		bindingResult = binder.getBindingResult();
	}
	
	// Add resolved attribute and BindingResult at the end of the model
	Map<String, Object> bindingResultModel = bindingResult.getModel();
	mavContainer.removeAttributes(bindingResultModel);
	mavContainer.addAllAttributes(bindingResultModel);
	
	return attribute;
}

WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean – Integer)
byte – > file

@FunctionalInterface public interface Converter<S, T>


6、目标方法执行完成

将所有的数据都放在 ModelAndViewContainer;在这里插入图片描述
包含要去的页面地址View。还包含Model数据。


7、处理派发结果

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

内部资源视图
@Override
protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		
	// Expose the model object as request attributes.
	exposeModelAsRequestAttributes(model, request);
		
	// Expose helpers as request attributes, if any.
	exposeHelpers(request);
		
	// Determine the path for the request dispatcher.
	String dispatcherPath = prepareForRendering(request, response);
		
	// Obtain a RequestDispatcher for the target resource (typically a JSP).
	RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
	if (rd == null) {
		throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
			"]: Check that the corresponding file exists within your web application archive!");
	}
		
	// If already included or response already committed, perform include, else forward.
	if (useInclude(request, response)) {
		response.setContentType(getContentType());
		if (logger.isDebugEnabled()) {
			logger.debug("Including [" + getUrl() + "]");
		}
		rd.include(request, response);
	}
		
	else {
		// Note: The forwarded resource is supposed to determine the content type itself.
		if (logger.isDebugEnabled()) {
			logger.debug("Forwarding to [" + getUrl() + "]");
		}
		rd.forward(request, response);
	}
}

暴露模型作为请求域属性
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
	
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
		HttpServletRequest request) throws Exception {
	
    //model中的所有数据遍历挨个放在请求域中
	model.forEach((name, value) -> {
	if (value != null) {
		request.setAttribute(name, value);
	} else {
			request.removeAttribute(name);
		}
	});
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《史上最简单的Spring Cloud教程源码》 一、Spring Cloud Alibaba教程 Spring Cloud Alibaba教程:使用Nacos作为服务注册发现组件 Spring Cloud Alibaba教程:使用Nacos作为配置中心 Spring Cloud Alibaba教程:Sentinel的使用 二、Greenwich版本 Spring Cloud Consul 之Greenwich版本全攻略 spring cloud config将配置存储在数据库中 Spring Cloud Sleuth 之Greenwich版本全攻略 Spring Boot Admin 2.1.0 全攻略 阿里分布式事务框架GTS开源了! 三、Finchley版本 Spring Cloud Finchley; Spring Boot 2.0.3 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)(Finchley版本) 史上最简单的SpringCloud教程 | 第二篇: 服务消费者(rest+ribbon)(Finchley版本) 史上最简单的SpringCloud教程 | 第三篇: 服务消费者(Feign)(Finchley版本) 史上最简单的SpringCloud教程 | 第四篇:断路器(Hystrix)(Finchley版本) 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)(Finchley版本) 史上最简单的SpringCloud教程 | 第六篇: 分布式配置中心(Spring Cloud Config)(Finchley版本) 史上最简单的SpringCloud教程 | 第七篇: 高可用的分布式配置中心(Spring Cloud Config)(Finchley版本) 史上最简单的SpringCloud教程 | 第八篇: 消息总线(Spring Cloud Bus)(Finchley版本) 史上最简单的SpringCloud教程 | 第九篇: 服务链路追踪(Spring Cloud Sleuth)(Finchley版本) 史上最简单的SpringCloud教程 | 第十篇: 高可用的服务注册中心(Finchley版本) 史上最简单的SpringCloud教程 | 第十二篇: 断路器监控(Hystrix Dashboard)(Finchley版本) 史上最简单的SpringCloud教程 | 第十三篇: 断路器聚合监控(Hystrix Turbine)(Finchley版本) 史上最简单的SpringCloud教程 | 第十四篇: Spring Cloud Gateway初体验 史上最简单的SpringCloud教程 | 第十五篇: Spring Cloud Gateway 之Predict篇 史上最简单的SpringCloud教程 | 第十六篇: Spring Cloud Gateway 之filter篇 史上最简单的SpringCloud教程 | 第十七篇: Spring Cloud Gateway 之限流篇 史上最简单的SpringCloud教程 | 第十八篇: spring cloud gateway之服务注册与发现 四、源码篇: 深入理解Feign之源码解析 深入理解Eureka之源码解析 深入理解Ribbon之源码解析 深入理解Hystrix之文档翻译 深入理解Zuul之源码解析 五、进阶篇 Spring Cloud Sleuth超详细实战 拜托!面试请不要再问我Spring Cloud底层原理 微服务注册中心如何承载大型系统的千万级访问? 每秒上万并发下的Spring Cloud参数优化实战 六、D版本 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka) 史上最简单的SpringCloud教程 | 第二篇: 服务消费者(rest+ribbon) 史上最简单的SpringCloud教程 | 第三篇: 服务消费者(Feign) 史上最简单的SpringCloud教程 | 第四篇:断路器(Hystrix) 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul) 史上最简单的SpringCloud教程 | 第六篇: 分布式配置中心(Spring Cloud Config) 史上最简单的SpringCloud教程 | 第七篇: 高可用的分布式配置中心(Spring Cloud Config) 史上最简单的SpringCloud教程 | 第八篇: 消息总线(Spring Cloud Bus) 史上最简单的SpringCloud教程 | 第九篇: 服务链路追踪(Spring Cloud Sleuth) 史上最简单的SpringCloud教程 | 第十篇: 高可用的服务注册中心 史上最简单的SpringCloud教程 | 第十一篇:docker部署spring cloud项目 史上最简单的SpringCloud教程 | 第十二篇: 断路器监控(Hystrix Dashboard) 史上最简单的SpringCloud教程 | 第十三篇: 断路器聚合监控(Hystrix Turbine) 史上最简单的 SpringCloud 教程 | 第十四篇: 服务注册(consul) 七、番外篇: 如何使用MongoDB+Springboot实现分布式ID? 如何在springcloud分布式系统中实现分布式锁? 如何用Redlock实现分布式锁 如何在IDEA启动多个Spring Boot工程实例 JWT如何在Spring Cloud微服务系统中在服务相互调时传
Spring Boot的执行原理包括以下几个关键步骤: 1. 启动过程:Spring Boot的启动入口是`SpringApplication`类的`run()`方法。在启动过程中,它会创建一个`ApplicationContext`(应用上下文)对象,加载所有的配置信息,并进行必要的初始化工作。 2. 自动配置Spring Boot通过自动配置机制来简化开发者的工作。它会根据classpath中的依赖和配置文件,自动配置各种功能模块,如数据源、Web容器、日志、安全等。自动配置是通过条件注解和条件属性来实现的,只有满足特定条件的配置才会生效。 3. 命令行参数解析:Spring Boot支持从命令行参数中获取配置信息。它会解析命令行参数,并将其转化为Spring Boot应用的配置属性。 4. 启动器:Spring Boot提供了一系列的启动器(starters),可以简化依赖管理。启动器是一组预定义的依赖关系,可以一次性引入一组相关的依赖。例如,通过引入`spring-boot-starter-web`启动器,可以自动引入与Web开发相关的依赖。 5. 条件化加载:Spring Boot可以根据条件来选择性地加载某些组件。它使用条件注解和条件属性,根据特定的条件判断是否加载某个组件。这样可以根据不同的环境和配置,动态地决定加载哪些组件。 6. 外部化配置Spring Boot支持将配置信息外部化,可以通过配置文件、环境变量、命令行参数等方式来动态配置应用。它提供了一个统一的配置模型,并支持多种配置格式,如properties、YAML等。 7. 自定义启动过程:Spring Boot提供了一些扩展点和回调接口,可以让开发者在应用启动过程中插入自定义的逻辑。例如,可以通过实现`ApplicationRunner`和`CommandLineRunner`接口,在应用启动后执行一些初始化操作。 总体来说,Spring Boot的执行原理是通过自动配置、外部化配置和条件化加载等机制,简化了开发者的工作,使得应用可以快速启动,并按需加载所需的组件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值