【小家Spring】如何证明Spring是存在父子容器的?顺便解决Spring MVC访问一直404问题(配置文件没问题)(上)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】如何证明Spring是存在父子容器的?顺便解决Spring MVC访问一直404问题(配置文件没问题)(上)

前言


各位老铁们是否遇曾经遇到过这样的疑惑:同样是Spring容器里的Bean,为何能够@Autowireservice进Controller里面,但是反之注入就报错呢?报找不到bean~


但是自己从容器里明明可以拿到这个Bean啊,怎么回事呢?

同样的我们发现,容器里面的属性值,容器之间也是不互通的?


环境准备



准备一个传统的Spring环境(注意,一定不能是Spring Boot环境),为了偷懒,项目环境各位移步此处:

【小家Spring】Spring注解驱动开发—Servlet 3.0整合Spring MVC(不使用web.xml部署描述符,全注解驱动)


如何证明Spring是存在父子容器的


我们现在的结论是,在Web环境中,是分为SpringMvc管理的子容器,和Spring管理的父容器。如何证明呢?


基于上面的项目环境(请参看项目环境,因为如果项目环境不对,得到的效果可能会被误导的,总之就是保证@Controller只被扫描一次)


我们分别让Controller和Service实现ApplicationContextAware,就可以证明出:


image.png


结论,虽然类型AnnotationConfigWebApplicationContext:但是显然是不一样的,从它从写的toString()方法里可以看出:


  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(getDisplayName());
    sb.append(": startup date [").append(new Date(getStartupDate()));
    sb.append("]; ");
    ApplicationContext parent = getParent();
    if (parent == null) {
      sb.append("root of context hierarchy");
    }
    else {
      sb.append("parent: ").append(parent.getDisplayName());
    }
    return sb.toString();
  }


==============备注:说一下Spring Boot环境下的:

结果我们发现(各位自己去试验哈),使用的是一模一样的(这里指的一模一样,就是一个,地址值都是一样的)AnnotationConfigEmbeddedWebApplicationContext,可以看出在boot环境中使用的是相同的容器管理的(无父子容器概念)。备注:该类在org.springframework.boot.context.embedded中这个包里面,属于Boot后来自己实现的


附上一个继承图谱:

image.png


注意:


我们的ApplicationContext以及BeanFactory都是可以直接@Autowired的,如下:

Controller注入:
    @Autowired
    private ApplicationContext applicationContext; WebApplicationContext for namespace 'dispatcher-servlet': startup date [Thu Mar 07 15:25:04 CST 2019]; parent: Root 
    @Autowired
    private BeanFactory beanFactory; // org.springframework.beans.factory.support.DefaultListableBeanFactory@41b6d6b3: defining beans [...
Service注入:
    @Autowired
    private ApplicationContext applicationContext; //Root WebApplicationContext: startup date [Thu Mar 07 15:25:02 CST 2019]; root of context hierarchy
    @Autowired
    private BeanFactory beanFactory; //rg.springframework.beans.factory.support.DefaultListableBeanFactory@747a2296: defining beans [...


由此可以看出,容器直接注入进来就行。但是,但是,但是如果存在父子容器的话,在不同的层,注入的对象也是不一样的,这点在了解了Spring容器的机制的情况下,是很好理解的~~~

如何证明Spring的父容器不能访问子容器的Bean


其实这个在上面的那篇博文里已经举例了。

比如,我在Web子容器的配置文件里注册一个Bean:

@ComponentScan(value = "com.fsx", useDefaultFilters = false,
        includeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}
)
@Configuration
public class AppConfig {
    @Bean
    public Child child() {
        return new Child();
    }
}


然后在Spring管理的Root父容器里注册一个Bean:


@ComponentScan(value = "com.fsx", excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
        //排除掉web容器的配置文件,否则会重复扫描
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AppConfig.class})
})
@Configuration
public class RootConfig {
    @Bean
    public Parent parent() {
        return new Parent();
    }
}


然后我们先在Controller里面注入这两个Bean,如下:


@Controller
@RequestMapping("/controller")
public class HelloController implements ApplicationContextAware {
    @Autowired
    private HelloService helloService;
    @Autowired
    private Parent parent;
    @Autowired
    private Child child;
    @ResponseBody
    @GetMapping("/hello")
    public String helloGet() {
        System.out.println(parent);
        System.out.println(child);
        System.out.println(helloService);
        System.out.println(helloService.hello());
        return "hello...Get";
    }
}

启动,访问。我们发现一切正常,并且都有值。


现在我在Service里注入这两个类如下:


@Service
public class HelloServiceImpl implements HelloService, ApplicationContextAware {
    @Autowired
    private Parent parent;
    @Autowired
    private Child child;
    @Override
    public Object hello() {
        System.out.println(parent);
        System.out.println(child);
        return "service hello";
    }
}

启动项目,我们就发现报错了,找不到Child这和Bean.


从上面这个例子,就可以看出。子容器是可以访问父容器里的Bean的,但是父容器不能访问子容器内的Bean。所以很显然,直接向Service里面@Autowire一个Controller,启动时候也是会报错的~


另外可以说一点,父子容器的初始化顺序为:先父容器,再子容器。所以web组件一般都是最后被初始化的(当然还存在循环嵌套的情况,另当别论了)。因为若使用web.xml配置Spring容器,是先执行ContextLoaderListener#contextInitialized启动Spring容器,再初始化DispatcherServlet来启动web容器的

相关文章
|
3月前
|
XML 安全 Java
|
5月前
|
XML Java 数据格式
Spring从入门到入土(xml配置文件的基础使用方式)
本文详细介绍了Spring框架中XML配置文件的使用方法,包括读取配置文件、创建带参数的构造对象、使用工厂方法和静态方法创建对象、对象生命周期管理以及单例和多例模式的测试。
235 7
Spring从入门到入土(xml配置文件的基础使用方式)
|
2月前
|
Java 测试技术 应用服务中间件
Spring Boot 配置文件总结
Spring Boot 提供全局配置文件 `application.properties` 和 `application.yml`,用于修改自动配置的默认值。前者使用键值对配置,后者使用缩进和冒号。不同环境(开发、测试、生产)可切换配置文件,通过 `spring.profiles.active` 指定。例如,开发环境端口为4790,测试环境为4791,生产环境为4792。配置示例展示了属性、List、Map定义及引用方法。
89 14
|
3月前
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
5月前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
4月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
4月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
87 0
|
5月前
|
Java 测试技术 Spring
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
这篇文章介绍了Spring Boot中配置文件的语法、如何读取配置文件以及如何通过静态工具类读取配置文件。
378 0
springboot学习三:Spring Boot 配置文件语法、静态工具类读取配置文件、静态工具类读取配置文件
|
5月前
|
网络协议 Ubuntu 前端开发
好好的容器突然起不来,经定位是容器内无法访问外网了?测试又说没改网络配置,该如何定位网络问题
本文记录了一次解决前端应用集成到主应用后出现502错误的问题。通过与测试人员的沟通,最终发现是DNS配置问题导致的。文章详细描述了问题的背景、沟通过程、解决方案,并总结了相关知识点和经验教训,帮助读者学习如何分析和定位网络问题。
218 0
|
6月前
|
消息中间件 NoSQL 安全
(转)Spring Boot加载 不同位置的 application.properties配置文件顺序规则
这篇文章介绍了Spring Boot加载配置文件的顺序规则,包括不同位置的application.properties文件的加载优先级,以及如何通过命令行参数或环境变量来指定配置文件的名称和位置。
183 0