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的主要职责包括:
-
请求分发:接收到HTTP请求后,DispatcherServlet根据请求的URL路径信息,解析并匹配到对应的处理器(Controller),并将请求进一步分发给Controller处理。
-
模型视图映射:Controller处理完请求后,会将控制权交还给DispatcherServlet,此时Controller可能已经准备好了模型数据(Model)。DispatcherServlet根据返回的逻辑视图名(View Name),寻找合适的视图解析器(View Resolver)将逻辑视图转换成实际的视图(如JSP页面、Thymeleaf模板或其他视图技术)。
-
视图渲染:最终,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
主要功能包括:
-
属性管理:
- 你可以通过
setAttribute
方法向ServletContext
中存储全局应用范围的对象,这些对象可供同一个Web应用中的所有Servlet、Filter或JSP页面共享和访问。 - 同样,也可以通过
getAttribute
方法获取存储在ServletContext
中的属性值。
- 你可以通过
-
资源访问:
- 提供了获取Web应用中资源的方法,如
getResourceAsStream(String path)
可以从类路径或Web应用根目录下获取资源流。
- 提供了获取Web应用中资源的方法,如
-
初始化参数:
- 可以在
web.xml
文件中为整个Web应用定义初始化参数,然后通过getInitParameter(String name)
和getInitParameterNames()
方法来访问这些参数。
- 可以在
-
监听器注册:
- 支持注册
ServletContextListener
监听器,以便在Web应用启动和停止时执行一些初始化和清理工作。
- 支持注册
-
Servlet注册:
- 虽然不常用,但可以通过
ServletRegistration
接口注册Servlet。
- 虽然不常用,但可以通过
-
Session管理:
- 可以创建新的
HttpSession
实例,但通常在Servlet或Filter中直接通过HTTP请求对象获取。
- 可以创建新的
-
获取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容器启动时,通过 ContextLoaderListener
或 DispatcherServlet
可以自动创建和绑定 WebApplicationContext
。
那么ApplicationContext又是啥呢?
ApplicationContext
接口是Spring框架中的一个重要组成部分,它是Spring IoC容器(Inversion of Control Container)的核心接口之一,扩展自BeanFactory
接口,提供了更多用于企业级Java应用的功能。
ApplicationContext
的主要作用和特点包括:
-
Bean管理:
- 负责加载、实例化、配置和管理应用中的Bean(也就是Java对象),包括处理Bean的生命周期、依赖注入等。
-
依赖注入(DI)支持:
- 支持自动装配Bean之间的依赖关系,通过XML配置文件、注解等方式声明Bean的依赖关系。
-
资源访问:
- 提供对多种资源(如文件、URL、类路径资源等)的访问能力,便于应用加载外部配置或资源文件。
-
国际化与消息处理:
- 实现了
MessageSource
接口,支持国际化的消息处理,可以根据不同的语言环境加载相应的消息资源。
- 实现了
-
事件发布与监听:
- 支持应用事件驱动编程模型,Bean可以注册为事件监听器,通过
ApplicationEventPublisher
接口发布和接收应用范围内的事件。
- 支持应用事件驱动编程模型,Bean可以注册为事件监听器,通过
-
AOP支持:
- 集成了面向切面编程(AOP)功能,允许开发者定义和织入切面,以实现诸如日志、事务管理等横切关注点的分离。
-
环境配置:
- 继承了
EnvironmentCapable
接口,可以访问应用运行时的环境信息,包括属性配置、profiles等。
- 继承了
-
多层级容器支持:
- 支持容器层级结构,可以创建嵌套的上下文,实现更精细的组件划分和配置。
常见的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组件的自动扫描,什么原因呢?
-
容器角色分工:
- Spring容器分为核心容器和其他基于核心容器构建的功能模块容器,比如Spring MVC容器。Spring MVC容器专注于处理Web请求和响应,而核心容器则关注服务、领域模型、数据访问等更底层的服务和组件。将Controller放在Spring MVC容器中管理,有助于明确职责划分。
-
容器初始化和性能优化:
- Controller类的数量往往较多,且仅在处理HTTP请求时才需要用到。若在Spring核心容器中也扫描并初始化Controller,会导致容器初始化时加载过多不必要的类,可能影响启动速度和内存占用。将Controller在Spring MVC容器中按需初始化,可以做到按功能模块延迟加载,提升启动效率。
-
请求处理流程的集中管理:
- Spring MVC容器在处理HTTP请求时具有特殊的处理机制,如通过
DispatcherServlet
转发请求至对应的Controller,使用@RequestMapping
等注解映射请求。如果Controller也在核心容器中扫描注册,就需要额外配置请求的路由规则,不如直接在Spring MVC容器中管理来得自然和高效。
- Spring MVC容器在处理HTTP请求时具有特殊的处理机制,如通过
-
上下文层次关系:
- 如前面提及,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的工作职责了吧。