spring mvc在controller中如何获取servletcontext

spring mvc在controller中如何获取servletcontext,第1张

既然使用了Spring,那就可以通过注解形式,在controller中获取到servletContext;

@Autowired

private ServletContext servletContext;

需要导入包:import javaxservletServletContext;

本篇,我打算从springMVC项目的webxml的配置文件入手,通过部分源码逐步去理解解释三个容器的关系以及调用顺序,因为是基于我个人的理解,可能有所不足。

一般webxml文件里会有如下两段配置信息:

我们先了解下webxml,以下引用自 《webxml文件是什么?有什么用?--详解》 :

然后结合我们上面的webxml中关于spring和spring mvc的配置信息来进入话题:

    首先,启动web容器的时,会先生成对应项目的ServelContent对象,这个是每个项目的上下文,这个ServelContent可以管理所有的servlet,并将我们webxml中设置的<context-param>内容作为键值对交给这个对象。

    然后加载<listener>标签内容,这个时候就会产生orgspringframeworkwebcontextContextLoaderListener。

spring的这个 ContextLoaderListener 在接下来的过程中很重要,我们来看一下源码

首先,可以看出它继承了ContextLoader类,并实现了ServletContextListener接口。

这里再直接引用他人的结论   《Spring中ContextLoaderListener作用》

好了,人家说法中回到我们的起点了,我们基本都被人告知“ContextLoaderListener的作用是创建并初始化spring容器”

那我们就可以深入进去看看,到底哪里做了这一步:

首先,我们知道了ServletContextListene是ServletContext的监听者,监听器的响应动作就是在服务器启动时contextInitialized会被调用,关闭的时候contextDestroyed被调用,这个好理解,那我们就来看一下ContextLoaderListener重写的contextInitialized方法到底做了什么。

我们再进入观察initWebApplicationContext方法细看

我因为自己消化过一遍,直接给出关键位置的方法说明——

1、首先是278行:创建了WebApplicationContext,我们可以理解为spring容器的壳子有了

2、其次是288和289行:对ApplicationContext加载了配置文件,并设置servletContext为WebApplicationContext的parent,到这一步,可以理解为我们的spring容器也就差不多成型了

3、接下来是294行:把ApplicationContext对象以键值对的形式存到servletContext中,这一步很关键,就是因为servletContext中存在这个键值对,所以其他内部成员可以通过servletContext访问到ApplicationContext,当然也能使用其管理的bean,而spring mvc则没有这样存在servletContext,所以我觉得正是这一步决定了子容器springmvc可以取用父容器内的bean,反着则不然。

接下来直到轮到我们的springmvc容器<servlet>标签内容

会生成控制orgspringframeworkwebservletDispatcherServlet,这是一个前端控制器,主要的内容我之前也有一篇文章做过自我记录

《Spring MVC的工作机制简单理解》

我们可以看到设置的

<load-on-startup>1</load-on-startup>

这个标签大概意思就是:

1、load-on-startup 元素标记容器是否应该在web应用程序启动的时候就加载这个servlet,(实例化并调用其init()方法)。

2、它的值必须是一个整数,表示servlet被加载的先后顺序。

3、如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。

4、如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。

在DispatcherServlet的时候就根据springMVC容器容器的配置文件生成。

比如我这边就是

那顺序确定了,我们再看一下spring和spring mvc的父子关系哪里确定:

我们可以从下面3个截图看到dispatcherServlet的继承关系,同时,init方法用的是dispatcherServlet父类的父类的方法。

重点在于initServletBean()方法,经过追踪,我们找到该方法的最终实现又是在dispatcherServlet的父类FrameworkServlet中

其中涉及父子关系的实际是在219行的initWebApplicationContext()方法

initWebApplicationContext()方法主要用于创建或刷新WebApplicationContext实例,并对Servlet功能所使用的变量进行初始化。

从238行源码就可以看到,它获得ContextLoaderListener中初始化的rootContext,

在246行设置了父子关系的引用,也就是从这一点我们看到了spring和springMVC的父子关系!

并且,可以看到这只是一条单向的引用,spring中没有引用直接指向springMVC,也就是子类能找到父类,然而父类都不知道这个子类,父子容器之间内部对象调用关系更明了。

再通过构造函数和Servlet的contextAttribute属性查找ServletContext来进行webApplicationContext实例的初始化,最终。

这个方法内263行源码onRefresh(wac)方法是FrameworkServlet提供的模板方法,在子类,也就是我们的DispatcherServlet的onRefresh()方法中进行了重写。而在onRefresh()方法中调用了initStrategies()方法来完成初始化工作,初始化Spring MVC的9个组件。

1、Tomcat在启动时给每个Web应用创建一个全局的上下文环境,这个上下文就是ServletContext,其为后面的Spring容器提供环境。

2、Tomcat在启动过程中触发容器初始化事件,Spring的ContextLoaderListener会监听到这个事件,它的contextInitialized方法会被调用,在这个方法中,Spring会初始化全局的Spring根容器,这个就是Spring的IoC容器,IoC容器初始化完毕后,Spring将其存储到ServletContext中,便于以后来获取。

3、Tomcat在启动过程中还会扫描Servlet,一个Web应用中的Servlet可以有多个,以SpringMVC中的DispatcherServlet为例,这个Servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个Servlet请求。

4、Servlet会在容器启动时加载或延迟加载(根据启动级别设置数字)。延迟加载时,当第一个请求达到时,serlet容器发现对应Servlet还没有被实例化,就调用Servlet的init方法。

在spring MVC里

        DispatcherServlet在初始化的时候会建立自己的容器,叫做SpringMVC 容器,用来持有Spring MVC相关的Bean。同时,Spring MVC还会通过ServletContext拿到Spring根容器,并将Spring根容器设为SpringMVC容器的父容器,请注意,Spring MVC容器可以访问父容器中的Bean,但是父容器不能访问子容器的Bean, 也就是说Spring根容器不能访问SpringMVC容器里的Bean。

        说的通俗点就是,在Controller里可以访问Service对象,但是在Service里不可以访问Controller对象。

简而言之,Spring是企业级Java的开源开发框架。Spring框架的核心功能可用于开发任何java应用程序。Spring框架的核心模块如下:

任何由 Spring IoC 容器初始化的普通 Java 类都称为 Spring Bean。我们使用 spring 应用程序上下文来获取 Spring Bean 实例。 Spring IoC Container 管理 Spring Bean 范围/作用域的生命周期并在 bean 中注入任何所需的依赖项。

Spring bean的不同作用域:

对于任何 Java 应用程序,都有两个不同的作用域,称为单例(Singleton)和原型(Prototype)

主要有三种不同的作用域(或范围),即 请求(request)、会话(session)和全局会话(global-session) ,专门针对基于 Spring 的 Java Web 应用程序。

Singleton 是任何 bean 的默认作用域。这意味着每个 IoC 容器将创建单个 bean 实例。因此,单例 bean 不是线程安全的。

要设置 spring bean 的范围,我们可以在 标签中使用scope属性。 @scope 用于基于注释的 DI。

Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理它们从创建到销毁的完整生命周期。 Spring 容器使用依赖注入 (DI) 来管理组成应用程序的组件。

有两种不同类型的容器:

BeanFactory 容器 :这是 Spring 容器的核心。 orgspringframeworkbeansfactoryBeanFactory 是一个接口,充当 IoC 容器,它实例化、配置和管理许多 bean。应用示例如下:

ApplicationContext 容器 :orgspringframeworkcontextApplicationContext 接口也充当 IoC 容器,但 ApplicationContext 接口建立在 BeanFactory 接口之上,以提供一些BeanFactory 额外的功能,例如与 Spring 的 AOP 的简单集成、消息资源处理(对于 I18N )、事件传播、Web 应用程序的应用层特定上下文(例如 WebApplicationContext)。所以使用 ApplicationContext 比使用 BeanFactory更好些。示例代码如下:

对于基于注解的依赖注入,使用@Autowired 注解。标有@Component/@Service/@Repository 等的类可以注入到标有@Autowired 的属性中

@Autowired 应用于:

1)基于构造器和setter的区别

2)context:annotation-config 和 context:component-scan 的区别

3)@Component、@Controller、@Repository & @Service 注解的区别

如果一个类用@Component/@Controller/@Service/@Repository 注解标记,那么Spring DI 容器可以在组件扫描机制期间识别该类。但是,对于服务层类使用@Service 是个好主意,并且@Controller 应该在spring mvc web 控制器中使用。 @Repository 用于将 DAO 导入 DI 容器。此外,任何未经检查的异常都将被转换为 Spring DataAccessException。

4)ViewResolver 与 MultipartResolver

ViewResolver 用于按名称解析视图。该接口由 InternalResourceViewResolver 实现 ;

MultipartResolver 用于处理 web 应用程序中的文件上传。

5)Spring MVC 中的验证

orgspringframeworkvalidationValidator 接口支持 spring MVC 验证。验证表单的一些实用方法是 ValidationUtils 类中的 rejectIfEmptyOrWhitespace() 和 rejectIfEmpty()。示例如下:

Spring MVC 中验证表单的另一种方法是:

HandlerInterceptor 接口充当 spring MVC 拦截器。它在服务请求之前和之后拦截。如果您实现了 HandlerInterceptor 接口,则 preHandle()、postHandle() 和 afterCompletion() 是要覆盖的方法。但是,为了避免覆盖,您可以使用 HandlerInterceptorAdapter 类。

实现 ServletContextAware 和 ServletConfigAware 接口并覆盖以下方法:

数据库事务是一组被视为关联工作单元的 *** 作。事务的主要原则是提交所有 *** 作或在失败的情况下回滚所有 *** 作。在交易中提交数据时,我们需要确保交易协议/称为 ACID(原子性-一致性-隔离-持久性)的属性:

全局事务 vs 本地事务:

脏读、幻读和不可重复读:

隔离与传播:

在旧版本的 spring 和 hibernate 集成中,需要 HibernateDAOSupport 和 HibernateTemplate。但是,较新版本的 Spring 不建议使用这两个类(这里仅做了解)。

通常我们从 HibernateDAOSupport 扩展我们的 DAO 类,并且 getHibernateTemplate() 方法将可用于Hibernate会话中的 CRUD *** 作。由于这不是推荐的方法,所以我们在 DAO 中注入会话工厂(SessionFactory)。下面的代码片段会给你一些关于 HibernateDAOSupport 和 HibernateTemplate 的想法:

DAO 是一种设计模式,以最大限度地减少应用程序和后端之间的耦合;

ORM 处理如何将对象映射到对象关系数据库中,从而减少数据库和应用程序之间的耦合。

如果您在没有 DAO 的情况下使用 ORM,那么您的应用程序将变得依赖于 ORM,因此很难从一个 ORM(例如Hibernate)移动到另一个 ORM(例如 NoSQL)。

Spring DAO 是使用@Repository 注解实现的。 Spring 存储库扩展 JPARepository 并传递 JPA 实体及其主键。

最后,关于Spring框架相关的概念就简要介绍到这里,希望这能给你进入并深入Spring技术栈一个简单入口,而不会被Spring技术生态所惊吓(Spring现在都成软件开发技术的全家桶了,啥都有)——日进一步,锲而不舍,终将大成!

Spring是 分层 的JavaSE/EE ( 一站式 ) 轻量级 开源的 容器框架 ,以 IOC (Inverse of Control 控制反转:对象之间的关系由容器创建,降低了程序之间的依赖性)和 AOP (Aspect Oriented Programming 面向切面编程)为内核的容器框架

一站式、分层:

JavaEE的有三层结构:web层、业务层、数据访问层(持久层,集成层),而Spring框架有对三层的每层解决方案:

web层:Spring MVC

持久层:JDBC Template (applicationContextxml)

业务层:Spring的Bean管理(Bean:是spring根据用户提供相应的需求创建的对象,由IOC管理)(service)

spring官网:>

通过AbstractApplicationContext#refresh()提供了在加载完配置文件后的启动过程。以SpringBoot的Web环境来看启动过程(文末有spring容器的启动过程及区别)

1prepareRefresh():由于在此时还没有加载servlet容器,所以servletContext=null,servletConfig=null,还没有进行初始化propertySource,添加了几个ApplicationListeners。

2obtainFreshBeanFactory():创建BeanFactory,即DefaultListableBeanFactory

3prepareBeanFactory():对DefaultListableBeanFactory进行属性填充。如:添加类加载器,添加BeanPostProcessor等等。

4postProcessBeanFactory():在上面prepareBeanFactory()公共的之后,提供子类的特殊处理,注册特殊的后处理器,此处为ServletWebServerApplicationContext的具体实现。

5invokeBeanFactoryPostProcessors():主要是扫描包,注册成BeanDefinition,对类配置的信息解析成BeanDefinition的属性。然后执行实现了BeanFactoryPostProcessor接口的bean的postProcessBeanFactory()方法。

对于BeanDefinitionRegistryPostProcessor类型的BeanFactoryPostProcessor,执行postProcessBeanDefinitionRegistry

ConfigurationWarningsApplicationContextInitializer类型的processor,检查包名是否异常

获取类型为BeanDefinitionRegistryPostProcessor并且是PriorityOrdered优先排序类型的beanDefinition

上面获取postProcessorNames的具体实现如下

调用刚获取到的currentRegistryProcessor列表中的bean的postProcessBeanDefinitionRegistry()方法

获取到启动类

对启动类开始进行解析,包装成SourceClass,开始解析

获取到需要扫描的包信息,开始进行解析

解析是否有includeFilters,excludeFilters,lazyInit等属性,解析basePackage,basePackageClasses配置,如果没有,则根据启动类的包名获取,最终获得需要扫描的包。

解析包下所有beanDefinition,遍历并逐个解析。  如果实现了AnnotatedBeanDefinition接口,解析是否有lazy,Primary,DependsOn,Role,Description等注解并对beanDefinition进行相应属性设置。   检查是否在beanDefinitionMap中,如果不存在则将beanDefinition注册到beanDefinitionMap中。

对获取到的包下的beanDefinition进行遍历处理。处理是否import,ImportResource,Bean注解,处理实现的接口。

处理完BeanDefinitionRegistryPostProcessor类型的bean之后,处理之前注册的处理器,前两个处理器没做任何处理,第三个处理器ConfigurationClassPostProcessor,对目前加载的所有的beanDefinition进行处理。如果是启动类,生成代理类并替换原来的beanClass

获取实现了BeanFactoryPostProcessor接口的类,去除了上面已经解析过的internalConfigurationAnnotationProcessor,根据是否排序进行分类,然后按照PriorityOrdered,Ordered,nonOrdered以此执行。

6registerBeanPostProcessors():获取实现了BeanPostProcessor的bean,并根据是否实现order接口进行排序,并按顺序注册到beanPostProcessors中。

7initMessageSource():

8initApplicationEventMulticaster():注册事件广播器

9onRefresh():获取ServletWebServerFactory(tomcatServletWebServerFactory),创建TomcatWebServer,然后initServletPropertySources

创建TomcatWebServer之后获取需要初始化的servlet,filter

将servlet添加到servletContext中

将filter添加到servletContext中

10registerListeners():在事件广播器中注册监听器,注册监听器name,如果earlyApplicationEvents存在,则广播事件。

11finishBeanFactoryInitialization():实例化bean

先进行实例化之前的 *** 作,再实例化bean。

如果有实现InstantiationAwareBeanPostProcessor, 则执行postProcessBeforeInstantiation

根据实例化策略进行实例化(这里是cglib)包装成BeanWrapper

实例化之后执行后处理

执行postProcessProperties

属性设置,在AutowiredAnnotationBeanPostProcessor中进行依赖注入

是否实现BeanNameAware,BeanClassLoaderAware,BeanFactoryAware等接口

init之前

调用自定义的init方法,在InitDestroyAnnotationBeanPostProcessor中处理

如果实现了InitializingBean,执行afterPropertiesSet()方法

然后执行init之后的方法

12finishRefresh():主要做的事情是启动tomcat,并发布事件

逐个listener去匹配事件

以下为spring环境启动与web环境启动的相同和不同:

1prepareRefresh():区别在于initPropertySources()的实现不同,spring上下文为AnnotationConfigApplicationContext,web上下文AnnotationConfigServletWebServerApplicationContext

2obtainFreshBeanFactory():相同

3prepareBeanFactory():相同

4postProcessBeanFactory():spring使用的AbstractApplicationContext的默认处理,没有提供实现,web提供了实现注册了特殊的处理器

5invokeBeanFactoryPostProcessors():相同

6registerBeanPostProcessors():相同

7initMessageSource():相同

8initApplicationEventMulticaster():相同

9onRefresh():spring什么都没做,使用的AbstractApplicationContext的默认实现,web提供了创建web容器的过程

10registerListeners():相同

11finishBeanFactoryInitialization():相同

12finishRefresh():spring主要做的事情是发布事件,web容器多了一步启动web容器

综上:web环境和spring环境启动容器流程基本相同,spring默认使用AbstractApplicationContext的实现,web多了properties解析,注册特殊处理器,创建webServer和启动webServer的过程。

classpath:是从类路径里查找配置文件,也就是/WEB-INF/classes目录下找SpringMVC-servletxml。

你写了classpath了,不会从web-info下找,而是去web-inf/classes下面找,所以找不到。

服务器(Tomcat)读取资源文件

方式一:采用流+Properties

当工程部署到Tomcat中时,按照上边方式,则会出现找不到该文件路径的异常。经搜索资料知道,Java工程打包部署到Tomcat中时,properties的路径变到顶层(classes下),这是由Maven工程结构决定的。由Maven构建的web工程,主代码放在src/main/java路径下,资源放在src/main/resources路径下,当构建为war包的时候,会将主代码和资源文件放置classes文件夹下,并且,此时读取文件需要采用流(stream)的方式读取,并通过JDK中Properties类加载,可以方便的获取到配置文件中的信息,如下。

采用Spring注解

如果工程中使用Spring,可以通过注解的方式获取配置信息,但需要将配置文件放到Spring配置文件中扫描后,才能将配置信息放入上下文,然后在程序中可以使用 @Value进行获取properties文件中的属性值。

采用Spring配置

也可以在Spring配置文件中读取属性值,赋予类成员变量。

以上就是关于spring mvc在controller中如何获取servletcontext全部的内容,包括:spring mvc在controller中如何获取servletcontext、从源码理解总结web容器、spring容器、spring mvc容器三者关系、快速切入:Spring框架核心概念总览等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9799326.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-02
下一篇 2023-05-02

发表评论

登录后才能评论

评论列表(0条)

保存