springboot源码解析之自定义参数解析

springboot源码解析之自定义参数解析

有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家

标签:源码:springboot

自定义参数

@Data
public class Person {
    private String name;
    private Integer age;
    private Pet pet;
}

Controller代码

@RequestMapping("/savePerson")
@ResponseBody
public Object savePerson(Person person) {
    System.out.println("person = " + person);
    return person;
}

get请求(能收到)

/savePerson?name=李四&age=18&pet.petName=黑皇&pet.petAge=3

person = Person(name=李四, age=18, pet=Pet(petName=黑皇, petAge=3))

post请求-前端传参形式form-data(能收到)

image-20240310105949983

控制台输出

{
    "name": "张三",
    "age": 18,
    "pet": {
        "petName": "黑皇",
        "petAge": 3
    }
}

post请求-前端传参形式json(收不到)

为啥呢?因为不管是get请求还是post请求,传给后端的都是key-value对,json对象是一个整体

image-20240310110058133

person = Person(name=null, age=null, pet=null)

参数解析流程

先直接上结论吧,数据解析的过程太长了

  1. 从参数解析器组里拿到自定义参数的解析器ServletModelAttributeMethodProcessor(循环遍历参数解析器组,找到能支持的)
  2. 然后通过反射创建出一个空的参数对象(这里就是Person)
  3. 创建数据绑定器,数据绑定器里面封装了刚刚创建的对象Person,还有参数的名称person,以及请求(WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name)😉,在创建数据绑定器的过程中还会给数据绑定器设置数据转换器(http传输的key-value均为字符串,需要将字符串解析为参数所需要的类型,比如将age = "18"转为age=18)
  4. 通过数据绑定器将请求中的数据绑定到刚刚创建的Person对象里

26个默认参数解析器

  1. org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@78b6a2d
  2. org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@6e058e2e
  3. org.springframework.web.method.annotation.MapMethodProcessor@4739b98d
  4. org.springframework.web.method.annotation.ModelMethodProcessor@3ba3a6c
  5. org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@5a90bb5a
  6. org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@2b585515
  7. org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@1a758e21
  8. org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@19275a1e
  9. org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@1e07c615
  10. org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@2c2cd73f
  11. org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@7a9ffe46
  12. org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@680b4f35
  13. org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@4a73a9f5
  14. org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@4ba4c6f9
  15. org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@4c005168
  16. org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@2081310e
  17. org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@39d2ae1f
  18. org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@1d213998
  19. org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@1376fd7e
  20. org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@32cde714
  21. org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@54d87fc5
  22. org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@a7ba90b
  23. org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@21628d4d
  24. org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@578f7858
  25. org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@4f092a2e
  26. org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@275003f9

注意有两个ServletModelAttributeMethodProcessor对象org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@54d87fc5和org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@a7ba90b,一个负责解析ModelAttribute注解,一个负责解析自定义类型,且解析ModelAttribute注解注解的顺序在前面

自定义参数的解析器

ServletModelAttributeMethodProcessor

public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {

	// ...

}

ModelAttributeMethodProcessor

public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

   // ...

   private final boolean annotationNotRequired;


   @Override
   public boolean supportsParameter(MethodParameter parameter) {
       // 参数上有ModelAttribute注解或者没有ModelAttribute注解并且不是简单类型(在参数解析器组中有两个ServletModelAttributeMethodProcessor对象,一个annotationNotRequired == false,优先级高,一个annotationNotRequired == true 优先级低,自定义参数解析用的是annotationNotRequired == true 的)
      return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
            (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
   }
    
    @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");

        // 获取到参数的name
		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 {
                // 通过反射创建一个参数类型的空对象 测试代码会在这里创建出一个空Person对象 
				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.
            // 这里binder的具体类型为ExtendedServletRequestDataBinder
			WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
			if (binder.getTarget() != null) {
				if (!mavContainer.isBindingDisabled(name)) {
                    // 将请求中的数据 绑定到 binder中的target中,也就是 person对象上
					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;
	}
    // 调用绑定器的绑定
    protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
		((WebRequestDataBinder) binder).bind(request);
	}
   // ...
  

}

数据绑定器

用于将请求中的key-value数据通过类型转换,反射绑定到new 出来的参数对象上,数据绑定的流程还是很麻烦的

ExtendedServletRequestDataBinder

image-20240308143302663

ExtendedServletRequestDataBinder中重要属性说明

  1. target:要绑定的目标对象(传给方法的参数对象),也就是代码里面的attribute,这里就是Person对象

  2. objectName:参数名称

  3. conversionService:消息转换器,http请求,超文本传输请求,一切皆文本(也不知道这么理解对不对),收到的参数不管是数字还是日期,都是字符串的形式,需要通过转换器转为实际方法入参所需要的,默认有124个

    image-20240308144151810

数据绑定器的数据绑定过程

public class WebRequestDataBinder extends WebDataBinder {
    public void bind(WebRequest request) {
        // 这里就能拿到 请求参数中的key-value对,不管是get请求还是post请求的form-data格式的,都是key-value形式的
		MutablePropertyValues mpvs = new MutablePropertyValues(request.getParameterMap());
		if (isMultipartRequest(request) && request instanceof NativeWebRequest) {
			MultipartRequest multipartRequest = ((NativeWebRequest) request).getNativeRequest(MultipartRequest.class);
			if (multipartRequest != null) {
				bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
			}
			else {
				HttpServletRequest servletRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class);
				if (servletRequest != null) {
					bindParts(servletRequest, mpvs);
				}
			}
		}
        // 绑定数据
		doBind(mpvs);
	}
    @Override
	protected void doBind(MutablePropertyValues mpvs) {
		checkFieldDefaults(mpvs);
		checkFieldMarkers(mpvs);
		super.doBind(mpvs);
	}
}
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
    protected void doBind(MutablePropertyValues mpvs) {
		checkAllowedFields(mpvs);
		checkRequiredFields(mpvs);
        // 应用属性和属性的值
		applyPropertyValues(mpvs);
	}
    protected void applyPropertyValues(MutablePropertyValues mpvs) {
		try {
			// Bind request parameters onto target object.
			getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
		}
		catch (PropertyBatchUpdateException ex) {
			// Use bind error processor to create FieldErrors.
			for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
				getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
			}
		}
	}
    protected ConfigurablePropertyAccessor getPropertyAccessor() {
		return getInternalBindingResult().getPropertyAccessor();
	}
}
public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {
    @Override
	public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
			throws BeansException {

		List<PropertyAccessException> propertyAccessExceptions = null;
		List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
				((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
		for (PropertyValue pv : propertyValues) {
			try {
				// This method may throw any BeansException, which won't be caught
				// here, if there is a critical failure such as no matching field.
				// We can attempt to deal only with less serious exceptions.
                // pv里面就封装了key-value信息
				setPropertyValue(pv);
			}
			catch (NotWritablePropertyException ex) {
				if (!ignoreUnknown) {
					throw ex;
				}
				// Otherwise, just ignore it and continue...
			}
			catch (NullValueInNestedPathException ex) {
				if (!ignoreInvalid) {
					throw ex;
				}
				// Otherwise, just ignore it and continue...
			}
			catch (PropertyAccessException ex) {
				if (propertyAccessExceptions == null) {
					propertyAccessExceptions = new ArrayList<>();
				}
				propertyAccessExceptions.add(ex);
			}
		}

		// If we encountered individual exceptions, throw the composite exception.
		if (propertyAccessExceptions != null) {
			PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);
			throw new PropertyBatchUpdateException(paeArray);
		}
	}
    @Override
	public void setPropertyValue(PropertyValue pv) throws BeansException {
		setPropertyValue(pv.getName(), pv.getValue());
	}
}

巴拉巴拉又长又丑,中间还有一个数据类型的转换,如果请求过来的age = "18"是数字类型,调用转换服务转为实际参数需要的类型age = 18

BeanWrapperImpl

public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
    @Override
		public void setValue(final @Nullable Object value) throws Exception {
            // 拿到set方法 这里拿到了person对象的setAge方法
			final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
					((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
					this.pd.getWriteMethod());
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(writeMethod);
					return null;
				});
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
							writeMethod.invoke(getWrappedInstance(), value), acc);
				}
				catch (PrivilegedActionException ex) {
					throw ex.getException();
				}
			}
			else {
                // 设置方法为可访问的
				ReflectionUtils.makeAccessible(writeMethod);
                // 调用person的setAge方法给 person对象赋值
				writeMethod.invoke(getWrappedInstance(), value);
			}
		}
}

解析自定义参数的调用栈还是蛮长的

image-20240308151430825

自定义转换器Converter

当前端传的参数为"/savePerson?name=李四&age=18&pet=黑皇,3"时,spring试图将字符串"黑皇,3"转为Pet对象,默认的转换器里面没有一个将字符串转为pet对象的,所以就会报错

2024-03-10 11:40:58 - WARN - [io-8088-exec-4] .support.DefaultHandlerExceptionResolver. logException 199 : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'person' on field 'pet': rejected value [黑皇,3]; codes [typeMismatch.person.pet,typeMismatch.pet,typeMismatch.com.lxw.study.entity.Pet,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [person.pet,pet]; arguments []; default message [pet]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.lxw.study.entity.Pet' for property 'pet'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.lxw.study.entity.Pet' for property 'pet': no matching editors or conversion strategy found]]

添加自定义转换器

package com.lxw.study.config;

import com.lxw.study.entity.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig {
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                registry.addConverter(new Converter<String, Pet>() {
                    @Override
                    public Pet convert(String source) {
                        if (StringUtils.hasText(source)) {
                            Pet pet = new Pet();
                            pet.setPetName(source.split(",")[0]);
                            pet.setPetAge(Integer.parseInt(source.split(",")[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
        };
    }
}

再次请求就ok了,注意Converter的包是org.springframework.core.convert.converter.Converter

  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
手把手视频详细讲解项目开发全过程,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 视频简介: 目前业界最流行的微服务架构正在或者已被各种规模的互联网公司广泛接受和认可,业已成为互联网开发人员必备技术。无论是互联网、云计算还是大数据Java平台已成为全栈的生态体系,其重要性几乎不可替代。Spring Boot作为微服务的基础设施之一,背靠强大的Spring 生态社区,支撑Spring Cloud技术体系。本课程采用由浅入深,层层递进的讲解方式, 让你轻松掌握SpringBoot的快速构建Spring项目的方式,并且还深入剖析SpringBoot内部核心原理,如:自动配置原理,start原理,自定义start等, 让你知其然,知其所以然 讲解方式: 本课程采用由浅入深,层层递进的讲解方式, 让你轻松掌握SpringBoot的快速构建Spring项目的方式,并且还深入剖析SpringBoot内部核心原理,如:自动配置原理,start原理,自定义start等, 让你知其然,知其所以然 课程亮点: 1、课程由浅到深,由原理到实践,适合零基础入门学习。 2、课程中包含大量SpringBoot 原理讲解、源码分析。 3、课程中涉及很多SpringBoot 实用插件技术、监控技术; 适用人群: 1、有一定的Java基础以及SSM框架知识。 2、对目前职业有进一步提升要求,希望从事数据行业高薪工作的在职人员。 基础课程主讲内容包括: 阶段一:SpringBoot 快速入门 1. SpringBoot介绍 2. SpringBoot的核心功能 3. SpringBoot的优势 4. SpringBoot入门程序 5. SpringBoot配置文件类型 6. 配置文件与配置类的属性映射方式 7. SpringBoot整合Mybatis 8. SpringBoot整合Junit 9. SpringBoot整合Redis 阶段二: SpringBoot核心原理 1. 起步依赖原理分析 2. 自动配置原理解析 3. 自定义起步依赖并实现自动配置 4. 事件监听 5. 初始化流程 6. SpringBoot服务监控
【资源介绍】 课程实验基于Java实现的分布式存储系统源码+项目说明.tar 项目概述与技术点: * 参照**GFS**(Google File System)中心化思想自主完成系统架构设计,完成技术方案编写 * 基于文件元数据服务实现**自定义注册中心**,为文件分片冗余存储负载均衡的实现**奠定基础** * 基于 **SpringScheduled** 完成服务心跳定时推送、分片存储服务健康定时监测,保障服务的可用性 * 根据**约定大于配置理念**实现文件存储服务器选择器,便于负载均衡策略的拓展与调整 * 自定义文件元数据存储格式,自定义文件名生成规则保证文件在同一个时段内的**唯一性**,并基于该特性实现**断点续传** * 使用 **ReenTrantReadWriteLock** 降低同一文件读写冲突,提高文件IO并发度 ### 模块说明: **Client:** 与应用直接交互的服务,提供文件操作相关接口 **Meta:** 维持文件元数据,统一管理chunk信息 **chunk-server:** 实现文件存储的服务器 本系统采用Google的GFS的架构思想,按照一个元数据中心,多个分片服务,多个客户端进行设计 ### 项目所用技术栈:(作为手写分布式存储系统,固然少使用现成的框架与技术,尽可能采用手写的形式来完成系统开发) * SpringBoot * SpringData * SpringSchedule * MongDB ### 功能架构: * 项目工程化 * 自定义服务注册与发现 * 大文件的快速上传与下载 * 文件服务器容错保障 ### 服务注册设计: Meta 元数据中心兼任注册中心一职,chunk-server 需要在启动后向Meta中心注册,以便 Meta 中心进行文件分片存储位置分配 chunk-server 定期发送心跳(server info)到Meta服务,以确保 chunk-server 可用。Meta 定期检查 chunk-server 是否存活。如果 Meta 检查到 chunk-server 上次发送心跳与此时时间间隔过大,则认为该 chunk-server 已经宕机 #### 元数据设计: 1. 文件名 2. 文件后缀名 3. 文件大小 4. 文件存储桶 5. 文件分片数量 6. 每个chunk的详细信息 * 分片序号 * 分片存储桶 * 分片起始位置 * 分片文件大小 * 分片文件后缀 * 分片存储地址 * 分片文件的md5值 #### 上传流程: 1. 用户端直接和 Client 交互,用户提供文件的基本信息提交给 Client ,client 在发送请求给 Meta。 2. Meta 根据此信息生成 meta 元数据下发给 Client,Client 整理出分片的序号、起始位置和分片大小,Client 返回一个 chunk 列表。 3. 用户根据 chunk 信息,可以向 Client 上传文件分片,Client 转发给 chunk-server 完成分片文件上传。 4. 从用户端上传到 Client 时需要做md5校验以确保文件完整性。chunk-server 上传完成后,同样下发一个 md5 给用户端,并返回上传成功信息。用户端再次校验 md5 值确保文件完整。 #### 下载流程: 1. 用户通过下载链接发送下载请求到 Client,Client 根据链接解析出文件id,然后再从 Meta 中解析出文件元信息。 2. Meta把元数据返回给客户端 3. Client 根据文件信息请求不同的 chunk-server 得到每一个分片文件,最终到 Client 中奖分片进行拼装得到一个完整的文件,返回给用户端。 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,也适用于小白学习入门进阶。当然也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或者热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载,沟通交流,互相学习,共同进步!
### 回答1: Springboot源码解析PDF是一本深入解析Springboot框架的技术书籍,涵盖了Springboot的背景、原理、设计思路、运行机制、开发实践等方面。全书主要分为三部分:第一部分介绍Springboot的基础知识,包括Spring框架的常用注解、Springboot的配置、自动配置原理等;第二部分深入探讨Springboot的主要功能,如数据访问、Web开发、缓存、消息、安全等;第三部分着重介绍开发Springboot应用的最佳实践,包括Springboot与其他框架的结合使用、集成测试、监控与诊断等。 阅读Springboot源码解析PDF可以让开发者更深入理解Springboot的设计理念、技术实现以及应用场景,在实际项目开发中更加灵活、高效地使用Springboot。该书对于有一定JavaSpring框架基础的开发者来说是一本非常优秀的参考资料,也是Java开发者必不可少的技术读物。同时,该书也是借助源码解析的方式,让读者更加系统化地学习Springboot技术,具有很高的实用性和参考价值。总之,阅读Springboot源码解析PDF有助于开发者更好地掌握Springboot技术,提高应用开发效率和代码质量。 ### 回答2: Spring Boot源码解析pdf是一本介绍Spring Boot框架的开源书籍,该书的目的是帮助开发者深入了解Spring Boot的内部工作原理和实现细节。 该书首先从Spring Boot框架的源码结构和核心模块入手,详细介绍了Spring Boot的MVC、ORM、缓存、安全等核心功能的实现原理。同时,该书还介绍了Spring Boot对微服务的支持和整合Spring Cloud的方式,让开发者更深入了解Spring Boot在分布式架构中的应用。 在讲解源码实现原理的同时,该书还指出了一些常见的开发问题和易错点,并提供了相应的解决方案。此外,该书还通过一系列的案例,全面展示了Spring Boot的实际开发应用场景,帮助开发者更好地应用Spring Boot框架。 总的来说,Spring Boot源码解析pdf是一本非常实用的书籍,能够帮助开发者快速掌握Spring Boot框架的内部实现原理,提高开发效率和代码质量。同时,该书还可以作为学习Spring Boot的参考资料,对于想要深入学习和研究Spring Boot的开发者来说是非常有用的。 ### 回答3: Spring Boot 是一个很受欢迎的 Java 框架,它简化了开发者的工作,允许他们更快速地构建和部署应用程序。Spring Boot 的优点包括简洁的配置、内嵌的 Web 容器和现成的插件,让开发者可以更专注于业务逻辑。Spring Boot源码解析是学习它的关键。 Spring Boot源码解析Java初学者来说可能会比较复杂,但是它对于学习框架和原理是非常有益的。一个好的 Spring Boot 项目需要基于很好的基础,这就要求开发者了解其源码源码解析可以帮助开发者了解各种设计模式和组件的原理,有助于解决实际问题。 在 Spring Boot源码解析中,我们将会找到很多有用的信息。例如,我们可以看到 Spring Boot 如何通过注解处理器创建并配置 bean,这可以帮助我们更好地理解依赖注入和 IoC 容器。此外,我们还可以了解 Spring Boot 如何处理 HTTP 请求、创建模板、处理安全性等。这些知识可以帮助我们更好地理解 Spring Boot 内部的工作机制。 总之,Spring Boot源码解析是必不可少的一部分,它可以帮助开发者更好地了解和使用该框架,掌握在实践中所需的知识和技能。如果您是一名 Spring 开发者,那么深入了解 Spring Boot源码将会是一个很好的学习过程。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值