Spring管理Controller可行性原理示例分析

 更新时间:2023年07月18日 09:45:34   作者:江南一点雨  
这篇文章主要为大家介绍了Spring管理Controller可行性原理示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Spring 容器中的父子容器

上篇文章和小伙伴们聊了 Spring 容器中的父子容器问题,也和小伙伴们梳理了 Spring 容器和 SpringMVC 容器之间的关系,其中,Spring 容器是父容器,SpringMVC 是子容器,子容器可以访问父容器中的 Bean,但是父容器无法访问子容器中的 Bean。

在一个 SSM 项目中,你可以单纯使用 SpringMVC 容器,这个没问题,项目可以正常运行。但是,有的小伙伴可能要问了,如果把所有的 Bean 都扫描到 Spring 容器中行不行?

先来说结论:可以!但是需要额外配置。

阅读本文需要先了解 Spring 容器的父子容器哦,如果还不了解的话建议先阅读上篇文章

为什么不能把所有 Bean 都注册到 Spring 容器中呢?按照我们上篇文章中的分析,所有 Bean 都注册到 Spring 容器之后,Spring 容器作为父容器,SpringMVC 作为子容器,按理说,由于子容器可以访问父容器中的 Bean,所以 SpringMVC 是可以正常访问 Spring 容器中的 Bean 的,所以,似乎把所有的 Bean 都扫描到 Spring 容器应该是没有问题的?

其实不然!

问题就出在 SpringMVC 容器查找 Controller 的方式上,SpringMVC 容器查找 Controller,默认情况下,只在当前容器中查找,并不会去父容器中查找,所以如果把 Controller 都扫描到父容器的话,对于 SpringMVC 来说,相当于系统中就没有 Controller 了,所以你一访问,直接就 404 了。

接下来,我结合源码和小伙伴们分析一下。

SpringMVC 如何查找 Controller 

首先,小伙伴们知道,在 SpringMVC 中,当请求到达服务端之后,需要由处理器映射器 HandlerMapping 来确定这个请求应该由哪个处理器来处理,所以,按理说,HandlerMapping 中就会记录所有的处理器信息,也就是 Controller 的信息。一般我们在 SpringMVC 中使用的 HandlerMapping 都是 RequestMappingHandlerMapping,所以这里我们就通过 RequestMappingHandlerMapping 的初始化来看一下,SpringMVC 到底是如何查找 Controller 的。

在 RequestMappingHandlerMapping#afterPropertiesSet 方法中,调用了父类的 afterPropertiesSet 方法,我们来看下:

AbstractHandlerMethodMapping#afterPropertiesSet:

@Override
public void afterPropertiesSet() {
    initHandlerMethods();
}
protected void initHandlerMethods() {
    for (String beanName : getCandidateBeanNames()) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            processCandidateBean(beanName);
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

initHandlerMethods 方法就是初始化处理器的方法,也就是在这个方法中,去尝试找到所有的 Controller,并且把每一个接口方法都封装成 HandlerMethod 对象。

我们来看下 getCandidateBeanNames 方法,这个方法用来找到所有的候选的 Bean:

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

关键点就在这了,这里首先去判断 detectHandlerMethodsInAncestorContexts 变量的值,如果这个变量为 true,则调用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去查询 Bean,这个方法在上篇文章中松哥和大家分享过,用来查找 Bean 的名称,包括父容器中的 Bean 都会查找到并返回;

如果 detectHandlerMethodsInAncestorContexts 变量为 false,则调用 getBeanNamesForType 方法去查找 Bean,getBeanNamesForType 方法我们上篇文章也讲过,这个方法只找当前容器的 Bean,不会去父容器中查找。

SpringMVC 容器查找 Bean

所以现在问题的关键就在于 detectHandlerMethodsInAncestorContexts 变量了,这个变量默认是 false,即,默认情况下,只去当前容器(SpringMVC 容器)查找 Bean。

这里找到的 beanName 是当前容器中所有的 beanName,所以接下来还要去 processCandidateBean 方法走一圈,这个方法会去判断这个 Bean 是否是一个 Controller,如果是就将之收集到一起:

protected void processCandidateBean(String beanName) {
    Class<?> beanType = null;
    beanType = obtainApplicationContext().getType(beanName);
    if (beanType != null && isHandler(beanType)) {
        detectHandlerMethods(beanName);
    }
}
@Override
protected boolean isHandler(Class<?> beanType) {
    return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
}

可以看到,只有这类上有 @Controller 注解,这个类才会被留下来。

好啦,剩下的逻辑我们就不看了。

现在大家已经了解到这样一个情况:

SpringMVC 容器在初始化 HandlerMapping 的时候,会去查找所有的 Controller 并完成初始化,但是在默认情况下,只会去当前容器中查找,并不会去父容器中查找。

所以,如果把 Controller 让 Spring 容器扫描并管理,那么就会导致在默认情况下,SpringMVC 容器找不到 Controller,进而导致所有的请求 404。

在前面的讲解中,松哥都强调了默认情况,意思就是说这个事情还有转圜的余地,看了前面源码的小伙伴应该也发现了,只要我们把 detectHandlerMethodsInAncestorContexts 变量改为 true,那么 HandlerMapping 就会去父容器中查找 Bean,这样即使被 Spring 容器扫描并管理的 Bean,也就能够查找到了。

修改方式

spring-servlet.xml:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="detectHandlerMethodsInAncestorContexts" value="true"/>
</bean>

在 Spring 容器中直接扫描所有 Bean:

<context:component-scan base-package="org.javaboy.web"/>

web.xml 中加载这两个配置文件:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-servlet.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

这样配置之后,就可以把所有 Bean 都扫描到 Spring 容器中了。

好啦,今天这篇文章目的不是为了让小伙伴们去在 Spring 容器中管理 Controller,只是想借这样一个契机,一起来捋一捋 SpringMVC 中 HanderMapping 的原理。

如果感觉本文阅读有点吃力,可以先看看上篇文章

以上就是Spring管理Controller可行性原理示例分析的详细内容,更多关于Spring管理Controller的资料请关注脚本之家其它相关文章!

相关文章

  • java多线程之线程,进程和Synchronized概念初解

    java多线程之线程,进程和Synchronized概念初解

    这篇文章主要介绍了java多线程之线程,进程和Synchronized概念初解,涉及进程与线程的简单概念,实现多线程的方式,线程安全问题,synchronized修饰符等相关内容,具有一定借鉴价值,需要的朋友可以参考下。
    2017-11-11
  • Spring 使用注解存储和读取 Bean对象操作方法

    Spring 使用注解存储和读取 Bean对象操作方法

    在 Spring 中,要想更加简单的实现对 Bean 对象的储存和使用,其核心就是使用 注解 ,本文主要就是演示如何使用注解实现对 Bean 对象的存取操作,感兴趣的朋友跟随小编一起看看吧
    2023-08-08
  • Springboot创建时常用的依赖详解

    Springboot创建时常用的依赖详解

    本文介绍了Spring Boot项目中常用依赖的配置及作用,涵盖了父依赖、Web应用、测试、数据库、MyBatis、连接池、JSON处理、Lombok、AOP、校验、监控、工具包、打包配置、多配置文件以及热部署等
    2025-03-03
  • SpringBoot如何使用Scala进行开发的实现

    SpringBoot如何使用Scala进行开发的实现

    这篇文章主要介绍了SpringBoot如何使用Scala进行开发的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Spring Boot 集成Redisson实现分布式锁详细案例

    Spring Boot 集成Redisson实现分布式锁详细案例

    这篇文章主要介绍了Spring Boot 集成Redisson实现分布式锁详细案例,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-08-08
  • 解决SecureRandom.getInstanceStrong()引发的线程阻塞问题

    解决SecureRandom.getInstanceStrong()引发的线程阻塞问题

    这篇文章主要介绍了解决SecureRandom.getInstanceStrong()引发的线程阻塞问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringBoot对静态资源的映射规则详解

    SpringBoot对静态资源的映射规则详解

    在Web应用中会涉及到大量的静态资源,例如 JS、CSS和HTML等,我们知道,Spring MVC 导入静态资源文件时,需要配置静态资源的映射,但在 SpringBoot 中则不再需要进行此项配置,因为SpringBoot已经默认完成了这一工作,本文给大家介绍了SpringBoot对静态资源的映射规则详
    2024-12-12
  • 给@Value设置默认值以及为static变量赋值问题

    给@Value设置默认值以及为static变量赋值问题

    在Spring框架中,@Value注解用于属性注入,可将配置文件中的值赋给变量,未指定默认值时,若配置文件缺少相应属性,程序启动会报错,可通过设定默认值防止此问题,对于静态变量,由于@Value无法直接注入,需通过Set方法赋值,该方法也支持默认值设置
    2024-09-09
  • Java中基于DeferredResult的异步服务详解

    Java中基于DeferredResult的异步服务详解

    这篇文章主要介绍了Java中基于DeferredResult的异步服务详解,DeferredResult字面意思是"延迟结果",它允许Spring MVC收到请求后,立即释放(归还)容器线程,以便容器可以接收更多的外部请求,提升吞吐量,需要的朋友可以参考下
    2023-12-12
  • logback的UNDEFINED_PROPERTY属性源码执行流程解读

    logback的UNDEFINED_PROPERTY属性源码执行流程解读

    这篇文章主要为大家介绍了logback的UNDEFINED_PROPERTY属性源码执行流程解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论