HttpServlet,ServletContext,Listener它仨的故事

1.HttpServlet

听起来是不是感觉像是个上古词汇,是不是没有阅读下去的兴趣了?Tomcat知道吧,它就是一个servlet容器,当用户向服务器发送一个HTTP请求时,Servlet容器(如Tomcat)会根据其配置找到对应的Servlet,并调用该Servlet的相关方法来处理请求和生成响应。

HttpServlet 是Java Servlet API中的一个抽象类,位于 javax.servlet.http 包下,用于处理HTTP协议的请求和响应。它是构建Web应用程序时用来处理客户端HTTP请求的核心组件,几乎所有的动态Web应用都会直接或间接地使用到 HttpServlet。它拥有doGet,doPost方法。

我们Java程序员经常到处吹嘘我们做的web程序是MVC架构的,抱着spring mvc的大腿到处讨饭吃,在Spring MVC框架中,Servlet扮演着至关重要的角色,特别是DispatcherServlet作为Spring MVC的核心组件,它是整个Spring Web MVC架构的前端控制器(Front Controller)设计模式的实现。

DispatcherServlet 它就是一个特殊的Servlet,它继承了HttpServlet,并且针对Spring框架进行了专门的设计和优化。在Spring MVC的应用程序中,通常会在web.xml文件中配置DispatcherServlet,以此作为处理所有HTTP请求的入口点。

在Spring MVC的工作流程中,DispatcherServlet的主要职责包括:

  1. 请求分发:接收到HTTP请求后,DispatcherServlet根据请求的URL路径信息,解析并匹配到对应的处理器(Controller),并将请求进一步分发给Controller处理。

  2. 模型视图映射:Controller处理完请求后,会将控制权交还给DispatcherServlet,此时Controller可能已经准备好了模型数据(Model)。DispatcherServlet根据返回的逻辑视图名(View Name),寻找合适的视图解析器(View Resolver)将逻辑视图转换成实际的视图(如JSP页面、Thymeleaf模板或其他视图技术)。

  3. 视图渲染:最终,DispatcherServlet将模型数据传递给选定的视图,由视图负责将数据渲染成最终的HTTP响应内容返回给客户端。

还记得在Spring MVC中,DispatcherServlet的传统配置步骤吗?

我们在web.xml中是这么配置的:

java 复制代码
<!-- web.xml -->
<web-app>
  <!-- ... -->
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 可选配置:设置Spring MVC配置文件的位置 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
    </init-param>
    <!-- 可选配置:设置DispatcherServlet的加载顺序 -->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <!-- 配置DispatcherServlet处理哪些请求 -->
    <url-pattern>/</url-pattern> <!-- 通常配置为'/'来处理所有请求 -->
  </servlet-mapping>
  <!-- ... -->
</web-app>

配置Spring MVC相关的配置文件 : 在上面的<init-param>中指定了contextConfigLocation,在这里你可以指定Spring MVC相关的配置文件。在这个文件中,可以配置Spring MVC的各种组件,如HandlerMappings、Controllers、ViewResolvers等。

java 复制代码
<!-- /WEB-INF/spring/dispatcher-servlet.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 配置注解驱动 -->
    <mvc:annotation-driven/>

    <!-- 配置其他Spring MVC组件,如HandlerMappings、Controllers等 -->
    <!-- ... -->

</beans>

那么web.xml中的servlet为啥这么配置?那就得从一个远古时代说起了,比如我们定义一个关于啤酒的servlet并且拦截的是/Tester.do这个路径,就是如下的配法,是不是大差不差。

而init-param呢?它就是这个servlet自己的参数。DispatcherServlet就可以通过getServletConfig().getInitParameterNames();去获取我们MVC定义的东西了。

2.ServletContext。

在Java Servlet规范中,ServletContext 是一个接口,它表示Web应用的全局上下文信息。每一个部署在Servlet容器(如Tomcat、Jetty等)中的Web应用都有一个与之关联的ServletContext实例,它在Web应用启动时由Servlet容器创建并贯穿整个应用生命周期。在古老的配置中是这么定义的:

ServletContext 主要功能包括:

  1. 属性管理

    • 你可以通过setAttribute方法向ServletContext中存储全局应用范围的对象,这些对象可供同一个Web应用中的所有Servlet、Filter或JSP页面共享和访问。
    • 同样,也可以通过getAttribute方法获取存储在ServletContext中的属性值。
  2. 资源访问

    • 提供了获取Web应用中资源的方法,如getResourceAsStream(String path)可以从类路径或Web应用根目录下获取资源流。
  3. 初始化参数

    • 可以在web.xml文件中为整个Web应用定义初始化参数,然后通过getInitParameter(String name)getInitParameterNames()方法来访问这些参数。
  4. 监听器注册

    • 支持注册ServletContextListener监听器,以便在Web应用启动和停止时执行一些初始化和清理工作。
  5. Servlet注册

    • 虽然不常用,但可以通过ServletRegistration接口注册Servlet。
  6. Session管理

    • 可以创建新的HttpSession实例,但通常在Servlet或Filter中直接通过HTTP请求对象获取。
  7. 获取MIME类型

    • 通过getMimeType(String file)方法可以获得指定文件名的MIME类型。

总之,ServletContext为Web应用提供了一个共享的全局环境,使得应用中的各个组件能够进行有效的通信和资源共享,同时也是实现Web应用初始化、配置和管理的重要手段。在Spring MVC中,WebApplicationContext就是对ServletContext进行了扩展,以更好地与Spring框架集成。

WebApplicationContext是不是听着很熟悉了?

它是Spring提供的一种特殊的应用上下文,它是ApplicationContext接口的扩展,专为Web应用而设计。WebApplicationContext不仅包含了常规Spring IoC容器的所有功能,还与Servlet容器的ServletContext紧密关联,它可以被绑定到Servlet容器的ServletContext上,使得Spring Bean可以访问到ServletContext的资源和服务。

即可以与Servlet容器(如Tomcat、Jetty等)中的 ServletContext 相关联,使得Spring应用能够访问到Web容器的全局上下文信息。

在Servlet容器启动时,通过 ContextLoaderListenerDispatcherServlet 可以自动创建和绑定 WebApplicationContext

那么ApplicationContext又是啥呢?

ApplicationContext接口是Spring框架中的一个重要组成部分,它是Spring IoC容器(Inversion of Control Container)的核心接口之一,扩展自BeanFactory接口,提供了更多用于企业级Java应用的功能。

ApplicationContext的主要作用和特点包括:

  1. Bean管理

    • 负责加载、实例化、配置和管理应用中的Bean(也就是Java对象),包括处理Bean的生命周期、依赖注入等。
  2. 依赖注入(DI)支持

    • 支持自动装配Bean之间的依赖关系,通过XML配置文件、注解等方式声明Bean的依赖关系。
  3. 资源访问

    • 提供对多种资源(如文件、URL、类路径资源等)的访问能力,便于应用加载外部配置或资源文件。
  4. 国际化与消息处理

    • 实现了MessageSource接口,支持国际化的消息处理,可以根据不同的语言环境加载相应的消息资源。
  5. 事件发布与监听

    • 支持应用事件驱动编程模型,Bean可以注册为事件监听器,通过ApplicationEventPublisher接口发布和接收应用范围内的事件。
  6. AOP支持

    • 集成了面向切面编程(AOP)功能,允许开发者定义和织入切面,以实现诸如日志、事务管理等横切关注点的分离。
  7. 环境配置

    • 继承了EnvironmentCapable接口,可以访问应用运行时的环境信息,包括属性配置、profiles等。
  8. 多层级容器支持

    • 支持容器层级结构,可以创建嵌套的上下文,实现更精细的组件划分和配置。

常见的ApplicationContext实现类有:

  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext
  • AnnotationConfigApplicationContext
  • XmlWebApplicationContext(适用于Web应用,与Servlet容器集成)

使用ApplicationContext代替基本的BeanFactory,可以更容易地获得更复杂的企业级功能支持,是构建大型、复杂Spring应用的推荐选择。

那ApplicationContext和WebApplicationContext的关系就能连上了。WebApplicationContext 是针对Web环境的专用应用上下文,它在 ApplicationContext 的基础之上添加了Web应用特性的支持,使得Spring框架能够更好地应用于Web应用程序的开发之中。在实际使用中,当开发Web应用时,通常会使用 WebApplicationContext 作为IoC容器,以充分利用其针对Web环境所提供的增强功能。

那种年龄比较大的程序员可能见过这俩配置文件:applicationContext.xml 和 servlet-context.xml

applicationContext.xml(Spring 核心配置)

  • 通常称为"全局应用上下文"或"根应用上下文",它是整个Web应用的基础配置文件,涵盖了所有非Web相关的Bean配置。
  • 这个文件中定义的Bean通常是那些与Web层无关,或者在任何Web相关的交互之外也能独立运行的组件,例如Service层、DAO层、数据源、事务管理、工具类等。
  • ApplicationContext在整个应用启动时创建,生命周期长于Servlet容器创建的Servlet Context。

例子:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描服务、DAO和其他非MVC层的组件 -->
    <context:component-scan base-package="com.example.service, com.example.dao"/>

    <!-- 其他Spring核心配置,如数据源、事务管理等 -->

</beans>

servlet-context.xml 或 spring-servlet.xml (Spring MVC配置)

  • 通常是指Spring MVC的专用上下文,它是Web应用中专门为Spring MVC组件配置的文件。
  • 这个文件主要用于配置和管理与Web相关的Bean,特别是Controller、视图解析器(ViewResolver)、拦截器(Interceptor)、转换器(Converter)、格式化器(Formatter)等Spring MVC相关的组件。
  • Servlet级别的上下文是在Servlet容器启动相应的Servlet(例如DispatcherServlet)时创建的,它的生命周期与Servlet容器管理的Servlet相关联。

例子:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描Controller组件 -->
    <context:component-scan base-package="com.example.web.controller"/>

    <!-- 开启Spring MVC注解支持 -->
    <mvc:annotation-driven/>

    <!-- 视图解析器配置等其他MVC相关配置 -->

</beans>

两者的联系在于:

  • 它们可以共享同一个父上下文,也就是说,servlet-context.xml可以继承applicationContext.xml中的Bean定义。
  • 在Spring MVC项目中,往往通过web.xml配置使得DispatcherServlet加载servlet-context.xml时,同时使servlet-context.xml成为applicationContext.xml的子上下文,从而使得全局配置的Bean能被所有MVC相关的Bean所访问,同时又能在MVC上下文中定义特定的Web组件。

总结来说applicationContext.xml负责非Web相关的一般性应用组件的配置,而servlet-context.xml负责与Web相关的MVC组件的具体配置,两者共同构建起完整的Spring Web应用上下文层次结构。

那么新程序员见到的配置大概如下:

AppConfig.java (Spring 核心配置)

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;

@Configuration
@ComponentScan(basePackages = {"com.example.service", "com.example.dao"})
public class AppConfig {
    // 可以在这里添加额外的@Bean定义或其他配置
}

WebConfig.java (Spring MVC配置)

java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ComponentScan;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.web.controller")
public class WebConfig implements WebMvcConfigurer {
    // 可以在此重写或添加WebMvcConfigurer接口的方法来自定义MVC配置
}

为啥上面在Spring 注册Bean时之所以常常会特意排除Controller组件的自动扫描,什么原因呢?

  1. 容器角色分工

    • Spring容器分为核心容器和其他基于核心容器构建的功能模块容器,比如Spring MVC容器。Spring MVC容器专注于处理Web请求和响应,而核心容器则关注服务、领域模型、数据访问等更底层的服务和组件。将Controller放在Spring MVC容器中管理,有助于明确职责划分。
  2. 容器初始化和性能优化

    • Controller类的数量往往较多,且仅在处理HTTP请求时才需要用到。若在Spring核心容器中也扫描并初始化Controller,会导致容器初始化时加载过多不必要的类,可能影响启动速度和内存占用。将Controller在Spring MVC容器中按需初始化,可以做到按功能模块延迟加载,提升启动效率。
  3. 请求处理流程的集中管理

    • Spring MVC容器在处理HTTP请求时具有特殊的处理机制,如通过DispatcherServlet转发请求至对应的Controller,使用@RequestMapping等注解映射请求。如果Controller也在核心容器中扫描注册,就需要额外配置请求的路由规则,不如直接在Spring MVC容器中管理来得自然和高效。
  4. 上下文层次关系

    • 如前面提及,Spring容器与Spring MVC容器之间可能存在父子上下文关系。在这种结构下,Controller在Spring MVC子容器中注册有利于实现更好的上下文隔离,例如Controller可以直接访问核心容器中的服务,而核心容器中的组件无需感知Controller的存在。

因此,在Spring应用的配置中,通常会在Spring MVC配置中单独指定扫描Controller所在的包路径,而在核心Spring配置中则扫描除Controller之外的服务、DAO等组件所在的包,以此达到合理的容器组织和性能优化的目的。

就这么使得spring 和 web容器怼一块了,在此基础上封装了很多拿来就用的东西,让sping蓬勃发展起来,造就了现在Java程序员失业的现状。

3.listener。

我们再看一下这个图:

在上面古老的例子里,我们看到web容器的context上下文配置的都是字符串,那只是字符串可不中,我们来看一下这个美女的担忧。

不过,在此之后谁将这个String参数转换成由Web应用各部分共享的一个具体DataSource引用呢?

不能把这个代码放在servlet中,因为你选择谁作为第一个servlet来查找DataSource,并把它存储在一个属性中呢?你真的想保证总是让某个特定的servlet最先运行吗?好好考虑一下。

噢,如果整个Web应用有一个main方法就好了。能放一些在servlet之前运行的代

码.........

这个时候你才发现servlet没有main方法。

那么我们的listener就登场了。看一下怎么定义一个古老的listener:

比如我们初始化一个狗对象,放到上下文中去。

然后在web.xml中定义这个listener就好了。

看起来是不是很熟悉了,对,我们的spring里也有这样的配置,来看ContextLoaderListener怎么定义到web.xml中去的:

java 复制代码
<!-- web.xml -->
<web-app>
    <!-- ... -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 指定Spring配置文件位置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <!-- ... -->
</web-app>

ContextLoaderListener的作用:

  • 当Web应用启动时,Servlet容器会先加载和启动所有在web.xml中配置的监听器。
  • ContextLoaderListener监听器会在Web应用启动时自动创建和初始化Spring应用上下文(ApplicationContext),这个上下文通常包含那些不需要依赖HTTP请求即可初始化的Bean,如服务层、数据访问层组件等。
  • 通过配置context-param标签可以指定Spring配置文件的位置,ContextLoaderListener会读取这些配置文件来初始化Spring应用上下文。

是不是一下子就对应上了?应该就清晰listener的工作职责了吧。

相关推荐
徐*红18 分钟前
java 线程池
java·开发语言
尚学教辅学习资料18 分钟前
基于SSM的养老院管理系统+LW示例参考
java·开发语言·java毕设·养老院
2401_8576363918 分钟前
计算机课程管理平台:Spring Boot与工程认证的结合
java·spring boot·后端
1 9 J20 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
Code apprenticeship21 分钟前
Java面试题(2)
java·开发语言
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
霖雨3 小时前
使用Visual Studio Code 快速新建Net项目
java·ide·windows·vscode·编辑器
SRY122404193 小时前
javaSE面试题
java·开发语言·面试
Fiercezm3 小时前
JUC学习
java
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言