Spring WebMVC初始化Controller流程详解

 更新时间:2022年02月22日 10:11:18   作者:拟梦  
这篇文章主要介绍了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(){
//进行初始化
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring @EventListener 异步中使用condition的问题及处理

    Spring @EventListener 异步中使用condition的问题及处理

    这篇文章主要介绍了Spring @EventListener 异步中使用condition的问题及处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java实现的mysql事务处理操作示例

    Java实现的mysql事务处理操作示例

    这篇文章主要介绍了Java实现的mysql事务处理操作,结合实例形式较为详细的分析了Java基于JDBC操作mysql数据库实现事务处理的相关概念、操作技巧与注意事项,需要的朋友可以参考下
    2018-08-08
  • 在CentOS上配置Java环境变量的教程

    在CentOS上配置Java环境变量的教程

    这篇文章主要介绍了在CentOS上配置Java环境变量的教程,同时适用于Fedora等其他RedHat系的Linux系统,需要的朋友可以参考下
    2015-06-06
  • Java 中向 Arraylist 添加对象的示例代码

    Java 中向 Arraylist 添加对象的示例代码

    本文介绍了如何在 Java 中向 ArrayList 添加对象,并提供了示例和注意事项,通过掌握这些知识,读者可以在自己的 Java 项目中有效地使用 ArrayList 来存储和操作对象,需要的朋友可以参考下
    2023-11-11
  • 关于mybatis遇到Integer类型的参数时动态sql需要注意条件

    关于mybatis遇到Integer类型的参数时动态sql需要注意条件

    这篇文章主要介绍了关于mybatis遇到Integer类型的参数时动态sql需要注意条件,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • 详解Springboot事务管理

    详解Springboot事务管理

    本篇文章主要介绍了详解Springboot事务管理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 详解IDEA 中使用Maven创建项目常见错误和使用技巧(推荐)

    详解IDEA 中使用Maven创建项目常见错误和使用技巧(推荐)

    这篇文章主要介绍了详解IDEA 中使用Maven创建项目常见错误和使用技巧(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Maven依赖冲突原因以及解决方法

    Maven依赖冲突原因以及解决方法

    依赖冲突是指项目依赖的某一个 jar 包,有多个不同的版本,因而造成类包版本冲突依赖冲突很经常是类包之间的间接依赖引起的,本文将给大家介绍Maven依赖冲突原因以及解决方法,需要的朋友可以参考下
    2023-12-12
  • 详解MyBatis的Dao层实现和配置文件深入

    详解MyBatis的Dao层实现和配置文件深入

    这篇文章主要为大家详细介绍了MyBatis的Dao层实现和配置文件深入,文中的示例代码讲解详细,感兴趣的小伙伴快来跟随小编一起学习一下
    2022-07-07
  • java数组中的异常类型整理

    java数组中的异常类型整理

    在本篇文章里小编给各位分享的是一篇关于java数组中的异常类型整理内容,有兴趣的朋友们可以学习下。
    2021-02-02

最新评论