1.1、properties
同以前的properties用法
1.2、yaml 1.2.1、简介YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML
的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。
非常适合用来做以数据为中心的配置文件
1.2.2、基本语法1.2.3、数据类型key: value;kv之间有空格
大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格
缩进的空格数不重要,只要相同层级的元素左对齐即可
'#‘表示注释
字符串无需加引号,如果要加,’'与""表示字符串内容 会被 转义/不转义
字面量:单个的、不可再分的值。date、boolean、string、number、null
k: v
对象:键值对的集合。map、hash、set、
行内写法: k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
● 数组:一组按次序排列的值。array、list、queue
行内写法: k: [v1,v2,v3] #或者 k: - v1 - v2 - v31.2.4、示例
@Data
public class Person {
private String userName;
private Boolean boss;
private Date birth;
private Integer age;
private Pet pet;
private String[] interests;
private List animal;
private Map score;
private Set salarys;
private Map> allPets;
}
@Data
public class Pet {
private String name;
private Double weight;
}
# yaml表示以上对象
person:
userName: zhangsan
boss: false
birth: 2019/12/12 20:12:33
age: 18
pet:
name: tomcat
weight: 23.4
interests: [篮球,游泳]
animal:
- jerry
- mario
score:
english:
first: 30
second: 40
third: 50
math: [131,140,148]
chinese: {first: 128,second: 136}
salarys: [3999,4999.98,5999.99]
allPets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: mario,weight: 47}]
2、配置yaml语言打字提示
自定义的类和配置文件绑定一般没有提示。
org.springframework.boot spring-boot-configuration-processor true
以下配置是不把yml配置文件打包到jar
05、Web开发org.springframework.boot spring-boot-maven-plugin org.springframework.boot spring-boot-configuration-processor
技术点
官方可找
2、简单功能分析 2.1、静态资源访问 1、静态资源目录只要静态资源放在类路径(resources)下: /static or /public or /resources or/meta-INF/resources 就可访问
访问路径 : 当前项目根路径/ + 静态资源名
http://localhost:8080/xxx.png
原理: 默认静态映射 private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; 3、欢迎页的处理规则
HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
return welcomePageHandlerMapping;
}
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext, Optional welcomePage, String staticPathPattern) {
if (welcomePage.isPresent() && "
@Data
public class Person {
private String userName;
private Integer age;
private Date birth;
private Pet pet;
}
@Data
public class Pet {
private String name;
private String age;
}
result
2、POJO封装过程
3、参数处理原理页面传的参数是怎么直接封装到类里面的,就是通过这个类实现的ServletModelAttributeMethodProcessor
1、HandlerAdapterHandlerMapping中找到能处理请求的Handler(Controller.method()) 为当前Handler 找一个适配器HandlerAdapter; RequestMappingHandlerAdapter 适配器执行目标方法并确定方法参数的每一个值
2、执行目标方法0 - 支持方法上标注@RequestMapping 1 - 支持函数式编程的 xxxxxx
// 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目标方法能写多少种参数类型。取决于参数解析器。
4、返回值处理器 5、如何确定目标方法每一个参数的值当前解析器是否支持解析这种参数 支持就调用 resolveArgument
============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、解析这个参数的值
5.3、自定义类型参数 封装POJO调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可
首先分析是如何校验
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));
}
接下来是如何封装
会给我们new一个空的POJO(person)对象,然后用web数据绑定器将参数绑定到POJO对象,循环100多个绑定器看哪一个能把前台
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面 WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中,如下图 GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean -- Integer) byte -- > file @FunctionalInterfacepublic interface Converter
@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 bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
4、数据响应与内容协商
1、响应JSON
1.1、jackson.jar+@ResponseBody
web场景自动引入了json场景 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-json 2.3.4.RELEASE compile
给前端自动返回json数据;
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
RequestResponseBodyMethodProcessor
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
// 使用消息转换器进行写出操作
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
1.1.2、返回值解析器原理
1、返回值处理器判断是否支持这种类型返回值 supportsReturnType 2、返回值处理器调用 handleReturnValue 进行处理 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。 1. 利用 MessageConverters 进行处理 将数据写为json 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型) 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据, 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理? 1、得到MappingJackson2HttpMessageConverter可以将对象写为json 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。1.2、SpringMVC到底支持哪些返回值
ModelAndView Model View ResponseEntity ResponseBodyEmitter StreamingResponseBody HttpEntity HttpHeaders Callable DeferredResult ListenableFuture CompletionStage WebAsyncTask 有 @ModelAttribute 且为对象类型的 @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;1.3、HTTPMessageConverter原理 1.3.1、MessageConverter规范
1.3.2、默认的MessageConverterHttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例子:Person对象转为JSON。或者 JSON转为Person
0 - 只支持Byte类型的 1 - String 2 - String 3 - Resource 4 - ResourceRegion 5 - DOMSource.class SAXSource.class) StAXSource.class StreamSource.class Source.class 6 - MultiValueMap 7 - true 8 - true 9 - 支持注解方式xml处理的。
最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)



