Spring启动流程(原理)详解--结合web.xml加载配置分析

本文详细探讨了Spring框架的启动流程,从ServletContext上下文开始,讲解了Spring应用上下文的创建,以及SpringMVC的启动过程。重点在于如何通过web.xml配置加载Spring的初始化过程,包括容器启动、ServletContext监听器、Spring上下文的创建和刷新,以及DispatcherServlet的初始化。文章适合对Spring框架原理感兴趣的开发者阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:文章内容有点多,可以先总体大概浏览一遍,心中有个整体类目关系构图,再详细看,不至于迷失在细节中,最好本地也使用开发工具打开源码对着看。

引言:

Spring框架已经成为目前JavaEE企业应用的主流框架,它提供了一个全面的编程和配置模型,适用于任何类型的部署平台。

Spring的一个关键元素是应用程序级别的基础设施支持:Spring专注于企业应用程序的“管道”,这样我们开发人员就可以专注于应用程序的业务逻辑开发,而不用关注于底层对象的管理与环境配置等。

说到Spring,我们基本都能说出它的两大特性:IoC、AOP,但底层的实现原理、启动流程等就不一定有所深入了解。

Spring 特性:

  • 核心技术:依赖注入、事件、资源、i18n、验证、数据绑定、类型转换、SpEL、AOP。
  • 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
  • 数据访问:事务、DAO支持、JDBC、ORM、编组XML。
  • Spring MVC和Spring WebFlux web框架。
  • 集成:远程调用、JMS、JCA、JMX、电子邮件、任务、调度、缓存。
  • 语言:Kotlin、Groovy、动态语言。

Spring架构:

Spring框架是一个分层架构,总共包含20多个模块,包含一系列的功能要素,由 1300 多个不同的文件构成。所有组件被分别整合在核心容器(Core Container)AOP(Aspect Oriented Programming) 、Aspects(切面)、设备支持(Instrmentation)数据访问及集成(Data Access/Integeration)Web报文发送(Messaging)Test 8个模块集合中,Spring 架构图如下:

注:篇幅有限,这块内容也不是本文重点,每一个架构模块,可参考其他资料。

下面将结合源码分析Spring的启动流程:

Spring的启动是建立在servlet/web容器(Tomcat、JBoss、Jetty等)之上的,一个常规的Spring应用,在web容器启动时,默认会先去加载/WEB-INF/web.xml,它配置了:servletContext上下文、监听器(Listener)、过滤器(Filter)、Servlet等

常规的web.xml示例:

<!--spring资源上下文定义,在指定路径加载spring的xml配置文件-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
<!--spring的上下文监听器-->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>


<!--编码过滤器,防止请求中文乱码-->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Springmvc的核心控制器,DispatcherServlet请求分发器-->
    <servlet>
        <servlet-name>dispatchServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatchServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

web.xml 加载顺序为: context-param < listener < filter < servlet

当然,也可以自定义自己的监听器、过滤器、拦截器等。这里只是截取主要的常用配置,下面将结合web.xml详细介绍它的启动流程。

一、ServletContext上下文

JavaEE标准规定,servlet容器需要在应用项目启动时,给应用程序初始化一个ServletContext作为公共环境容器存放公共信息,ServletContext中的信息都是由容器提供的。servlet规范当中,使用了Listener监听器机制来进行web容器相关组件的生命周期管理以及Event事件监听器来实现组件之间的交互

其中一个重要的生命周期监听器是 ServletContextListener 。web容器在创建和初始化 ServletContext 的时候,会产生一个ServletContextEvent 事件,其中 ServletContextEvent 包含该 ServletContext 的引用。然后交给在web.xml中配置的,注册到这个ServletContext 的监听器 ServletContextListener。ServletContextListener 在其 contextInitialized 方法中定义处理逻辑,源码如下:

package javax.servlet;

import java.util.EventListener;

public interface ServletContextListener extends EventListener {
    void contextInitialized(ServletContextEvent var1);

    void contextDestroyed(ServletContextEvent var1);
}

从 contextInitialized 方法的注释可知:通知所有的ServletContextListeners,当前的web应用正在启动,而且这些ServletContextListeners是在Filters和Servlets创建之前接收到通知的。所以在这个时候,web应用还不能接收请求,故可以在这里完成底层处理请求的组件的加载,这样等之后接收请求的Filters和Servlets创建时,则可以使用这些创建好的组件了。spring相关的bean就是这里所说的底层处理请求的组件,如数据库连接池,数据库事务管理器等。

举例:

通过自定义contextListener获取web.xml中配置的参数,需要实现 ServletContextListener 接口。

1.容器启动时,优先会加载web.xml 配置文件<context-param>标签中的内容,作为键值对放到ServletContext中。

2.然后找到对应的<listener>标签 ,容器调用指定的监听器的 contextInitialized(ServletContextEvent event) 方法,执行其中的操作。

例如:在web.xml中配置

<context-param>
   <param-name>key</param-name>
   <param-value>value</param-value>
</context-param>
<listener> 
   <listener-class>com.stwen.spring.listener.MyContextListener</listener-class>
</listener>

其中,MyContextListener 是自定义实现ServletContextListener 接口的类:

package com.stwen.spring.listener;
 
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
 
public class MyContextListener implements ServletContextListener {
    
    @Override
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("===========销毁自定义的监听器:MyContextListener=============");
    }
    
    @Override
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("===========初始化自定义的监听器:MyContextListener===========");
        ServletContext servletContext = event.getServletContext();
        System.out.println("key:"+servletContext.getInitParameter("key"));
    }
    
}

web.xml中可以定义两种参数:

  •     一个是全局参数(ServletContext),通过<context-param></context-param>定义,如上;
  •     一个是servlet参数,通过在servlet中声明:
  <init-param>
    <param-name>param1</param-name>
    <param-value>avalible in servlet init()</param-value>   
 </init-param> 

第一种参数在servlet里面可以通过 getServletContext().getInitParameter("context/param") 得到;

第二种参数只能在Servlet的 init() 方法中通过 this.getInitParameter("param1")取得:

package javax.servlet;

import java.io.IOException;

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值