IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringMVC之父子容器启动原理(一) -> 正文阅读

[Java知识库]SpringMVC之父子容器启动原理(一)

SpringMVC之父子容器启动原理(一)

回顾

SpringMVC的具体执行流程

详细链接: https://www.processon.com/view/link/62659c39e0b34d4baed5bf01

SpringMVC整体请求流程.png

Spring整合SpringMVC

官网地址: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc

mvc context hierarchy

说到Spring整合SpringMVC唯一体现就是父子容器

  • 通常我们会设置父容器Spring管理Service、Dao层的Bean,子容器SpringMVC管理Controller的Bean
  • 子容器可以访问父容器的Bean,父容器无法访问当子容器的Bean

XML实现方式

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
		 version="4.0">
	<!--spring 基于web应用的启动-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!--全局参数:spring配置文件-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-core.xml</param-value>
	</context-param>
	<!--前端调度器servlet-->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!--设置配置文件的路径-->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
		<!--设置启动即加载-->
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app> 

注解实现方式

  • @WebServlet
  • @WebFilter
  • @WebListener

这种方式不利于扩展,并且如果编写在jar包中tomcat是无法感知的

SPI的方式

Servlet-3-1的规范手册中,就提供了一种更加易于扩展可用于共享库可插拔的一种方式.

也就是在META-INF/services路径下放一个javax.servlet.ServletContainerInitailizer

什么是SPI

SPI就是Service Provider Interface,服务提供商接口,我们叫它服务接口扩展.

其实这个是根据Servlet厂商提供要求的一个接口,在固定的目录(META-INF/services)放上以接口全类名为命名的文件,

文件中放入接口的实现的全类名,该类由我们自己实现,按照这种约定的方式,服务提供商会调用文件中实现类的方法,从而完成扩展.

SPI例子

  • 定义一个接口

    public interface IUserDao {
    	void save();
    }
    
  • 在固定的目录放上接口的文件名

image.png

  • 文件中放入实现类(该实现类由你实现)
public class UserDaoImpl implements IUserDao {
	@Override
	public void save() {
		System.out.println("UserDaoImpl.save...");
	}
}
  • 通过java.util.ServiceLoader提供的ServiceLoader就可以完成SPI实现类的加载

    public class App {
    	public static void main(String[] args) {
    		ServiceLoader<IUserDao> daos = ServiceLoader.load(IUserDao.class);
    		for (IUserDao dao : daos) {
    			dao.save();
    		}
    	}
    }
    

小结

  • Tomcat在启动时会通过SPI注册ContextLoaderListener和DispatcherServlet对象

    • 同时创建父子容器
      • 分别创建在ContextLoaderListener初始化时创建父容器设置配置类
      • 在DispatcherServlet初始化时创建子容器,也就是2个ApplicationContext实例设置配置类
  • Tomcat在启动时执行ContextLoaderListener和DispatcherServlet对象的初始化方法,执行refresh进行加载

  • 在子容器加载时创建SpringMVC所需要的Bean和预准备的数据: 通过配置类+@EnableWebMVC配置(DelegatingWebMvcConfiguration),可实现WebMvcConfigurer进行定制扩展

    • RequestMappingHandlerMapping,它会处理@RequestMapping 注解

    • RequestMappingHandlerAdapter,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值。

    • HandlerExceptionResolver 错误视图解析器

    • addDefaultHttpMessageConverters 添加默认的消息转换器(解析json、解析xml)

  • 子容器需要注入父容器的Bean时比如Controller需要@Autowired Service的Bean,会先从子容器中找,没找到会去父容器中找

    AbstractBeanFactory#doGetBean

    protected <T> T  doGetBean(
    		String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    		throws BeansException {
      ....
    
    // Check if bean definition exists in this factory.
    BeanFactory parentBeanFactory = getParentBeanFactory();
    // 因为单例池没有找到Bean,如果有父BeanFactory,就从父BeanFactory的BeanDefinitionMap中找是否存在这个bean
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    	// Not found -> check parent.
    	// 如果从父BeanFactory找到了
    	// &&&&xxx---->&xxx
    	String nameToLookup = originalBeanName(name);
    	if (parentBeanFactory instanceof AbstractBeanFactory) {
    		return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
    				nameToLookup, requiredType, args, typeCheckOnly);
    	}
    	else if (args != null) {
    		// Delegation to parent with explicit args.
    		return (T) parentBeanFactory.getBean(nameToLookup, args);
    	}
    	else if (requiredType != null) {
    		// No args -> delegate to standard getBean method.
    		return parentBeanFactory.getBean(nameToLookup, requiredType);
    	}
    	else {
    		return (T) parentBeanFactory.getBean(nameToLookup);
    	}
    }
    ...
    }
    

    总结

    Spring和SpringMVC为什么需要父子容器?不要不行吗?

    就实现层面来说不用父子容器也是可以实现功能的,因为我们可以参考SpringBoot就没有使用父子容器.

    • 父子容器的主要作用应该是早期Spring为了划分框架分界.有点单一职责的味道.service、dao层我们一般使用spring框架来管理,controller层交给SpringMVC来管理
    • 规范整体架构,使父容器sevice无法访问子容器controller,而子容器controller可以访问父容器service
    • 方便子容器的切换.如果现在我们想把web层从Spring MVC替换成Struts,那么只需要将Spring MVC的配置文件spring-mvc.xml替换成Struts的配置文件structs.xml就可以了,而Spring的配置文件spring-core.xml不需要改变
    • 为了节省重复Bean的创建

    是否可以把所有Bean都通过Spring容器来管理(Spring的配置文件applicationContext.xml中配置全局扫描)

    不可以,这样会导致我们请求接口的时候产生404.

    如果所有的Bean都交给父容器,SpringMVC在初始化HandlerMethods的时候(initHandlerMethods)无法根据Controller的handler方法注册HandlerMethod,并没有去查找父容器的Bean.

    也就是无法根据请求URI获取到HandlerMethod来进行匹配

    是否可以把我们所需的Bean都放入Spring MVC子容器里面来管理(SpringMVC的配置文件的spring-servlet.xml中配置全局扫描)

    可以,因为父容器的提现无非是为了获取子容器不包含的Bean,如果全部包含在子容器完全用不到父容器了,所以是可以全部放在SpringMVC子容器来管理的

    虽然可以这么做不过一般应该是不推荐这么去做的.如果项目里有用到事务,或者AOP需要把这部分配置放到SpringMVC子容器的配置文件里来,不然一部分内容在子容器和一部分在父容器,可能导致事务或者AOP不生效.

    所以如果AOP和事务不生效也有可能是通过父容器去增强子容器,也就无法增强

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:28:38  更:2022-04-26 11:31:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:06:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码