源码分析技巧

55 篇文章 0 订阅
21 篇文章 1 订阅

源码分析的关键点

栈中方法的调用 + 方法调用图 + 类图 + debug方法栈

类图

IDEA 查看类的继承关系图形

方法调用

显示方法对应的调用树

调用栈 + 断点

找到栈中进入源码的第一个方法,依次进入下一个方法,分析需要什么,执行了什么

在这里插入图片描述

源码分析常见单词

源码中常见的英语单词一定要查,早晚要查,知道意思对看源码很有帮助
Candidate  候选的
Wrapper  包装器
Customizer  定制器    RedisCacheManagerBuilderCustomizer

其他技巧

idea 方法标识

在这里插入图片描述

根据源码自定义实现

几种方式

自定义 bean 注入
实现源码中的接口

自定义 redis cache 实现

关键部分

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {

	@Bean
	//可以自定义个 RedisCacheManager  注入,需要的代码直接 copy 源码
	// 不是 自定义 这里方法的参数 都可以实现自定义,具体要看逻辑
	RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
			ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
			ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
			RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
		RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
				determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
		List<String> cacheNames = cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
		}
		 //这里有个接口  RedisCacheManagerBuilderCustomizer ,可以实现这个接口自定义
		redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
		return cacheManagerCustomizers.customize(builder.build());
	}

自定义 bean 注入

@Configuration
public class CacheConfig{
	@Bean
	public RedisCacheConfiguration cacheConfiguration() {
	    return RedisCacheConfiguration.defaultCacheConfig()
	      .entryTtl(Duration.ofMinutes(60))
	      .disableCachingNullValues()
	      .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
	}
}
@Configuration
public class CacheConfig{
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
    	//这里相当于自定义实现了一个 RedisCacheManagerBuilderCustomizer
        return (builder) -> builder
                .withCacheConfiguration(DemoConstant.STATE_KEY_FORMAT,
                        RedisCacheConfiguration.defaultCacheConfig()
                                .entryTtl(Duration.ofMinutes(5))
                                .disableCachingNullValues()
                                //变双冒号为单冒号
                                .computePrefixWith(name -> name + ":")
                                .serializeValuesWith(SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(UserStateInfo.class))));

    }
}

实现源码中的接口

@Configuration
public class CacheConfig implements RedisCacheManagerBuilderCustomizer{

    @Override
    public void customize(RedisCacheManager.RedisCacheManagerBuilder builder) {
        builder.withCacheConfiguration(DemoConstant.STATE_KEY_FORMAT,
                        RedisCacheConfiguration.defaultCacheConfig()
                                .entryTtl(Duration.ofMinutes(5))
                                .disableCachingNullValues()
                                //变双冒号为单冒号
                                .computePrefixWith(name -> name + ":")
                                .serializeValuesWith(SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(UserStateInfo.class))));
    }
}

springboot请求映射源码分析

web应用访问一个请求路径,一定会从DispatcherServlet类中的doDispatcher方法开始

相关代码

前端欢迎页index.html
index.html放在 resources/static文件夹,即可作为欢迎页。注意不要自定义配置静态资源的访问前缀。否则导致 index.html不能被默认访问

spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效

  resources:
    static-locations: [classpath:/haha/]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>韩书哲,永远的韩书哲</h1>
<p>测试Rest风格</p>
<form action="/user" method="get">
    <input value="Rest-get 提交" type="submit">
</form>
<form action="/user" method="post">
    <input value="Rest-post 提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_m" type="hidden" value="delete">
    <input value="Rest-delete 提交" type="submit">
</form>
<form action="/user" method="post">
    <input name="_m" type="hidden" value="put">
    <input value="Rest-put 提交" type="submit">
</form>
</body>
</html>

MyConfig

@Configuration(proxyBeanMethods = false)
public class MyConfig {
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }
}

controller

@RestController
@RequestMapping("/user")
public class HelloController {
    @GetMapping
    public String get(){
        return "get";
    }
    @PostMapping
    public String post(){
        return "post";
    }
    @PutMapping
    public String put(){
        return "put";
    }
    @DeleteMapping
    public String delete(){
        return "delete";
    }

}

源码分析

springboot 底层还是 springmvc,所有的请求都会到 DispatcherServlet,它是所有请求的开始,打开 DispatcherServlet 的继承结构

在这里插入图片描述
一个Servlet必然会有doGet,doPost方法,我们在父类HttpServlet中找doGet,doPost方法,commend + F12 搜索方法
在这里插入图片描述
HttpServletBean中找doGet,doPost方法,发现没有,那么HttpServletBean的子类们可能重写了,在FrameworkServlet中找到了重写的方法

注意:向上的箭头表示重写,水平向右的箭头表示直接继承没有重写
在这里插入图片描述

再到底层DispatcherServlet中找,dispatcherServlet也没有重写,是直接使用的父类FrameworkServlet的doget方法
在这里插入图片描述
在这里插入图片描述

发现最底层FrameworkServlet的doget方法调用的是processRequest方法,点进去
在这里插入图片描述
发现核心的部分是doService,点进去
在这里插入图片描述
发现是一个抽象方法,没有实现,去子类里找实现方法,在子类中找到了实现方法
在这里插入图片描述
在这里插入图片描述
点进核心方法doDispatcher,才是真正有功能的方法,然后我们给这个方法打断点,再启动项目调试

在这里插入图片描述

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;//原生request
        HandlerExecutionChain mappedHandler = null;//执行链
        boolean multipartRequestParsed = false;//是不是一个文件上传请求,默认false
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);//请求有没有异步

        try {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);//检查是否文件上传请求
                    multipartRequestParsed = processedRequest != request;
                    // 找到当前请求使用哪个Handler(Controller的方法)处理
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = HttpMethod.GET.matches(method);
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

总结

在这里插入图片描述

SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet--------》doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 找到当前请求使用哪个Handler(Controller的方法)处理
				mappedHandler = getHandler(processedRequest);
                
                //HandlerMapping:处理器映射。/xxx->>xxxx

在这里插入图片描述
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则
在这里插入图片描述
所有的请求映射都在HandlerMapping中。

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
  • SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就看是不是下一个 HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值