我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring WebMVC初始化Controller流程详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring WebMVC初始化Controller流程详解

Spring WebMVC初始化Controller流程

此篇文章开始之前先向大家介绍一个接口 InitializingBean

这个接口的作用如果了解spring生命周期的应该知道 ,这个接口的作用就是在bean初始化之后会执行的init方法

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

当然能实现这种方式的方法spring还介绍了关于注解的@PostConstruct 和xml 的 init-method = "" 的两种方式。但是springmvc使用的是接口的方式。

这里要介绍的初始化Controller是指填充完HandlerMapping map<String,Method>即代表初始化Controller流程

我们再提一点小知识。声明一个controller类有哪些方式。(现在应该没有人用第二/三种方式吧)

  • 1.使用注解@Controller 和 请求路径@RequestMapping
  • 2.实现 Controller 接口 并将该类交给spring容器管理beanName为请求路径
  • 3.实现 HttpRequestHandler 接口并将该类交给spring容器管理beanName为请求路径

那么我们的map填充就从实现了InitializingBean 接口 的afterPropertiesSet这个方法开始。

源码中是这个类 AbstractHandlerMethodMapping(下面的代码有删减)

public void afterPropertiesSet() {
    this.initHandlerMethods();
}
protected void initHandlerMethods() {
    String[] var1 = this.getCandidateBeanNames();//1.获取容器初始化的所有beanName
    int var2 = var1.length;
    for(int var3 = 0; var3 < var2; ++var3) {
        String beanName = var1[var3];
        if (!beanName.startsWith("scopedTarget.")) {
            this.processCandidateBean(beanName);//2.获取所有声明为Controller类的beanName
        }
    }
    this.handlerMethodsInitialized(this.getHandlerMethods());
}

获取容器初始化的所有beanName(父子容器概念)

protected String[] getCandidateBeanNames() {
    return this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class) : this.obtainApplicationContext().getBeanNamesForType(Object.class);
}

获取所有声明为Controller类的beanName

protected void processCandidateBean(String beanName) {
    Class beanType = null;
    try {
        beanType = this.obtainApplicationContext().getType(beanName);//获取bean的类型
    } catch (Throwable var4) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
        }
    }
    if (beanType != null && this.isHandler(beanType)) {//获取所有声明为Controller类的beanName
        this.detectHandlerMethods(beanName);//1.开始处理这种类型的beanName
    }
}

开始处理这种类型的beanName

protected void detectHandlerMethods(Object handler) {
    Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
    if (handlerType != null) {
        Class<?> userType = ClassUtils.getUserClass(handlerType);
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
            try {
                return this.getMappingForMethod(method, userType);//1.获取到类类型下所有的方法
            } catch (Throwable var4) {
                throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
            }
        });
        if (this.logger.isTraceEnabled()) {
            this.logger.trace(this.formatMappings(userType, methods));
        }
        methods.forEach((method, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            this.registerHandlerMethod(handler, invocableMethod, mapping);//注册并填充map
        });
    }
}

获取到类类型下所有的方法和注册并填充map

第一个 MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap();

urlLookup.add(url, mapping);
eg:url = '/test/test.do' 

mapping是一个RequestMappingInfo 对象 RequestMappingInfo.patternsCondition = T --> /test/test.do

第二个 Map<T, AbstractHandlerMethodMapping.MappingRegistration<T>> registry = new HashMap();

eg:key = 'url'  value = 'method'

而我们的第二种和第三种的方式基本没有用了,因为会出现类爆炸,就像原始的servlet一样每一个方法都需要写一个类。

这两种方式是通过beanName为路径来实例化对象并执行通过该对象来执行里面的方法的。

源码中这两种map的填充方式是在bean的生命周期中通过实现beanFactory的applyBeanPostProcessorsBeforeInitialization方法来填充的。 

@Controller 类中初始化问题

在Controller类中常常遇到有些参数需要初始化,甚至有些只允许初始化一次,而Controller类不像servelet类可以调用init()函数进行初始化,这里想到的办法是设置标记值,让初始化部分只调用一次。

第一种方法

设置isStart值。

private static Boolean isStart = false;
if(!isStart){
  //进行初始化
  isStart=true;
}

第二种方法

使用注释@PostConstruct,该注释的类会在类初始化时进行调用。

@PostConstruct
private void init(){
//进行初始化
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring WebMVC初始化Controller流程详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

VueRouter 原理解读之初始化流程

这篇文章主要为大家介绍了VueRouter原理解读之初始化流程实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-19

详解Spring 延迟初始化遇到的问题

这篇文章主要介绍了我们在使用Spring延迟初始化容易遇到的问题,文中有详细的代码示例,具有一定的参考价值,需要的可以借鉴一下
2023-05-20

Java中实例初始化和静态初始化的过程详解

Java代码初始化块是Java语言中的一个非常重要的概念。初始化块负责在创建对象时进行一些必要的操作,例如设置对象的初始状态、初始化成员变量等。初始化块被分为实例初始化块和静态初始化块两种类型。本文详细介绍了初始化的过程,需要的朋友可以参考下
2023-05-18

Spring初始化与销毁顺序案例演示详解

这篇文章主要介绍了SpringBoot中的Bean的初始化与销毁顺序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-02-07

Spring Security基本架构与初始化操作流程是什么

这篇文章主要介绍“Spring Security基本架构与初始化操作流程是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Spring Security基本架构与初始化操作流程是什么”文章能帮助大
2023-07-05

spring初始化源码之关键类和扩展接口详解

Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给Spring管理,下面这篇文章主要给大家介绍了关于spring初始化源码之关键类和扩展接口的相关资料,需要的朋友可以参考下
2023-05-18

Spring手写简化版MVC流程详解

SpringMVC是SpringFramework的一部分,是基于Java实现MVC的轻量级Web框架。本文将通过简单示例带大家掌握SpringMVC简化版手写方法,感兴趣的可以了解一下
2022-11-16

Android4.X中SIM卡信息初始化过程详解

本文实例讲述了Android4.X中SIM卡信息初始化过程详解。分享给大家供大家参考,具体如下: Phone 对象初始化的过程中,会加载SIM卡的部分数据信息,这些信息会保存在IccRecords 和 AdnRecordCache 中。SI
2022-06-06

详解Spring 中如何控制2个bean中的初始化顺序

开发过程中有这样一个场景,2个 bean 初始化逻辑中有依赖关系,需要控制二者的初始化顺序。实现方式可以有多种,本文结合目前对 Spring 的理解,尝试列出几种思路。场景假设A,B两个 bean 都需要在初始化的时候从本地磁盘读取文件,其
2023-05-31

Golang并发编程之调度器初始化详解

这篇文章主要为大家详细介绍了Golang并发编程中关于调度器初始化的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
2023-03-22

一文详解spring注解配置bean的初始化方法和销毁方法

本篇我们讲解下spring项目中如何为bean指定初始化方法和销毁方法。当spring完成bean的属性赋值之后,就会执行bean的初始化方法,而当spring要销毁bean实例的时候,也会调用bean的销毁方法。文中有详细的代码实例,需要的朋友可以参考下
2023-05-18

详解SpringBoot初始教程之Tomcat、Https配置以及Jetty优化

1.介绍在SpringBoot的Web项目中,默认采用的是内置Tomcat,当然也可以配置支持内置的jetty,内置有什么好处呢? 1. 方便微服务部署。 2. 方便项目启动,不需要下载Tomcat或者Jetty在目前的公司已经把内置的Je
2023-05-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录