Spring知识点回顾 IOC、AOP、Bean实例化、生命周期、拦截器、事务、Spring MVC流程-《Java EE 企业级应用开发教程》读书笔记

博客迁移至:https://blog.csdn.net/wangshihuidev

前言

本文主要内容包含如下:


image.png

内容来自于《Java EE 企业级应用开发教程》这本书的翻读笔记,内容相对简单,用于基础知识的复习巩固。

Spring的核心容器

Spring框架提供了两种核心容器,分别为BeanFactory和ApplicationContext。

知识点:BeanFactory和ApplicationContext的区别是什么?

BeanFactory

BeanFactoryBeanFactory由org.springframework.beans.facytory.BeanFactory接口定义,是基础类型的IoC容器,它提供了完整的IoC服务支持。简单来说,BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean,并调用它们的生命周期方法。BeanFactory接口提供了几个实现类,其中最常用的是org.springframework.beans. factory.xml.XmlBeanFactory,该类会根据XML配置文件中的定义来装配Bean。创建BeanFactory实例时,需要提供Spring所管理容器的详细配置信息,这些信息通常采用XML文件形式来管理,其加载配置信息的语法如下。


BeanFactory beanFactory =new XmlBeanFactory(new FileSystemResource("F: /applicationContext.xml"));

ApplicationContext

ApplicationContext是BeanFactory的子接口,也被称为应用上下文,是另一种常用的Spring核心容器。它由org.springframework.context. ApplicationContext接口定义,不仅包含了BeanFactory的所有功能,还添加了对国际化、资源访问、事件传播等方面的支持。创建ApplicationContext接口实例,通常采用两种方法,具体如下。
1.通过ClassPathXmlApplicationContext创建
2.通过FileSystemXmlApplicationContext创建

在使用Spring框架时,可以通过实例化其中任何一个类来创建ApplicationContext容器。通常在Java项目中,会采用通过ClassPathXmlApplicationContext类来实例化ApplicationContext容器的方式,而在Web项目中,ApplicationContext容器的实例化工作会交由Web服务器来完成。Web服务器实例化ApplicationContext容器时,通常会使用基于ContextLoaderListener实现的方式,此种方式只需要在web.xml中添加如下代码。

<! --  指定Spring配置文件的位置,多个配置文件时,以逗号分隔-->
<context-param>    
  <param-name>contextConfigLocation</param-name>    
  <! --  Spring将加载spring目录下的applicationContext.xml文件 -->    
  <param-value>classpath:spring/applicationContext.xml    
  </param-value>
</context-param>

BeanFactory和ApplicationContext两种容器都是通过XML配置文件加载Bean的。二者的主要区别在于,如果Bean的某一个属性没有注入,使用BeanFacotry加载后,在第一次调用getBean()方法时会抛出异常,而ApplicationContext则在初始化时自检,这样有利于检查所依赖属性是否注入。因此,在实际开发中,通常都优先选择使用ApplicationContext,而只有在系统资源较少时,才考虑使用BeanFactory。

控制翻转和依赖注入

控制翻转概念

在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是Spring的控制反转。


image.png

依赖注入方式

依赖注入的实现方式依赖注入的作用就是在使用Spring框架创建对象时,动态地将其所依赖的对象注入Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入,具体介绍如下。

  • 属性setter方法注入:指IoC容器使用setter方法注入被依赖的实例。通过调用无参构造器或无参静态工厂方法实例化Bean后,调用该Bean的setter方法,即可实现基于setter方法的依赖注入。
  • 构造方法注入:指IoC容器使用构造方法注入被依赖的实例。基于构造方法的依赖注入通过调用带参数的构造方法来实现,每个参数代表着一个依赖。

Bean的实例化

Bean的实例化在面向对象的程序中,想要使用某个对象,就需要先实例化这个对象。同样,在Spring中,要想使用容器中的Bean,也需要实例化Bean。实例化Bean有三种方式,分别为:

  1. 构造器实例化(最常用)
  2. 静态工厂方式实例化
  3. 实例工厂方式实例化。
构造器实例化
 <bean id="bean1" class="com.itheima.instance.constructor.Bean1" />
静态工厂方式实例化

静态工厂方式实例化使用静态工厂是实例化Bean的另一种方式。该方式要求开发者创建一个静态工厂的方法来创建Bean的实例,其Bean配置中的class属性所指定的不再是Bean实例的实现类,而是静态工厂类,同时还需要使用factory-method属性来指定所创建的静态工厂方法。

<bean id="bean2"            
class="com.itheima.instance.static_factory.MyBean2Factory"           
factory-method="createBean" />
</beans>

在上述配置文件中,首先通过<bean>元素的id属性定义了一个名称为bean2的Bean,然后由于使用的是静态工厂方法,所以需要通过class属性指定其对应的工厂实现类为MyBean2Factory。由于这种方式配置Bean后,Spring容器不知道哪个是所需要的工厂方法,所以增加了factory-method属性来告诉Spring容器,其方法名称为createBean。

实例工厂方式实例化

实例工厂方式实例化还有一种实例化Bean的方式就是采用实例工厂。此种方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时,在配置文件中,需要实例化的Bean也不是通过class属性直接指向的实例化类,而是通过factory-bean属性指向配置的实例工厂,然后使用factory-method属性确定使用工厂中的哪个方法。

<! -- 配置工厂 -->
<bean id="myBean3Factory"8            
class="com.itheima.instance.factory.MyBean3Factory" />
<! -- 使用factory-bean属性指向配置的实例工厂,
使用factory-method属性确定使用工厂中的哪个方法-->
<bean id="bean3" factory-bean="myBean3Factory"
factory-method="createBean" />

在上述配置文件中,首先配置了一个工厂Bean,然后配置了需要实例化的Bean。在id为bean3的Bean中,使用factory-bean属性指向配置的实例工厂,该属性值就是工厂Bean的id。使用factory-method属性来确定使用工厂中的createBean()方法。

Bean的生命周期

Spring容器可以管理singleton作用域的Bean的生命周期,在此作用域下,Spring能够精确地知道该Bean何时被创建,何时初始化完成以及何时被销毁。对于prototype作用域的Bean, Spring只负责创建,当容器创建了Bean实例后,Bean的实例就交给客户端代码来管理,Spring容器将不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring容器都会创建一个新的实例,并且不会管那些被配置成prototype作用域的Bean的生命周期。

了解Bean的生命周期的意义就在于,可以在某个Bean生命周期的某些指定时刻完成一些相关操作。这种时刻可能有很多,但在一般情况下,常会在Bean的postinitiation(初始化后)和predestruction(销毁前)执行一些相关操作。

image.png

Bean的生命周期的整个执行过程描述如下。
(1)根据配置情况调用Bean构造方法或工厂方法实例化Bean。
(2)利用依赖注入完成Bean中所有属性值的配置注入。
(3)如果Bean实现了BeanNameAware接口,则Spring调用Bean的setBeanName()方法传入当前Bean的id值。
(4)如果Bean实现了BeanFactoryAware接口,则Spring调用setBeanFactory()方法传入当前工厂实例的引用。
(5)如果Bean实现了ApplicationContextAware接口,则Spring调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
(6)如果BeanPostProcessor和Bean关联,则Spring将调用该接口的预初始化方法postProcessBeforeInitialzation()对Bean进行加工操作,这个非常重要,Spring的AOP就是用它实现的。
(7)如果Bean实现了InitializingBean接口,则Spring将调用afterPropertiesSet()方法。
(8)如果在配置文件中通过init-method属性指定了初始化方法,则调用该初始化方法。
(9)如果有BeanPostProcessor和Bean关联,则Spring将调用该接口的初始化方法post ProcessAfterInitialization()。此时,Bean已经可以被应用系统使用了。
(10)如果在<bean> 中指定了该Bean的作用范围为scope="singleton",则将该Bean放入Spring IoC的缓存池中,将触发Spring对该Bean的生命周期管理;如果在<bean>中指定了该Bean的作用范围为scope="prototype",则将该Bean交给调用者,调用者管理该Bean的生命周期,Spring不再管理该Bean。
(11)如果Bean实现了DisposableBean接口,则Spring会调用destory()方法将Spring中的Bean销毁;如果在配置文件中通过destory-method属性指定了Bean的销毁方法,则Spring将调用该方法进行销毁。Spring为Bean提供了细致全面的生命周期过程,通过实现特定的接口或通过<bean>的属性设置,都可以对Bean的生命周期过程产生影响。我们可以随意地配置<bean>的属性,但是在这里建议不要过多地使用Bean实现接口,因为这样会使代码和Spring聚合比较紧密。

Bean的装配方式

Bean的装配方式Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如

  1. 基于XML的装配
  2. 基于注解(Annotation)的装配
  3. 自动装配
基于XML的装配

基于XML的装配Spring提供了两种基于XML的装配方式:

  • 设值注入(Setter Injection)
  • 构造注入(Constructor Injection)

在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。因此,设值注入要求一个Bean必须满足以下两点要求。

  • Bean类必须提供一个默认的无参构造方法。
  • Bean类必须为需要注入的属性提供对应的setter方法。
    使用设值注入时,在Spring配置文件中,需要使用<bean>元素的子元素<property>来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。
基于注解(Annotation)的装配

@Autowired:用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配。
@Resource:其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配。
@Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。· @Qualifier:与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。

自动装配

自动装配虽然使用注解的方式装配Bean,在一定程度上减少了配置文件中的代码量,但是也有企业项目中,是没有使用注解方式开发的,那么有没有什么办法既可以减少代码量,又能够实现Bean的装配呢?答案是肯定的,Spring的<bean>元素中包含一个autowire属性,我们可以通过设置autowire的属性值来自动装配Bean。所谓自动装配,就是将一个Bean自动地注入到其他Bean的Property中。

image.png

在默认情况下,配置文件中需要通过ref来装配Bean,但设置了autowire="byName"后,Spring会自动寻找userService Bean中的属性,并将其属性名称与配置文件中定义的Bean做匹配。由于UserServiceImpl中定义了userDao属性及其setter方法,这与配置文件中id为userDao的Bean相匹配,所以Spring会自动地将id为userDao的Bean装配到id为userService的Bean中。

Spring AOP

目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring 2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。

动态代理

Spring中的AOP代理,可以是JDK动态代理,也可以是CGLIB动态代理。

知识点: JDK动态代理和CGLIB动态代理的区别是什么?

JDK动态代理

JDK动态代理JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
基本流程:
JdkProxy类实现了InvocationHandler接口,并实现了接口中的invoke()方法,所有动态代理类所调用的方法都会交由该方法处理。在创建的代理方法createProxy()中,使用了Proxy类的newProxyInstance()方法来创建代理对象。newProxyInstance()方法中包含3个参数,其中第1个参数是当前类的类加载器,第2个参数表示的是被代理对象实现的所有接口,第3个参数this代表的就是代理类JdkProxy本身。在invoke()方法中,目标类方法执行的前后,会分别执行切面类中的check_Permissions()方法和log()方法。

CGLIB代理

CGLIB代理JDK动态代理的使用非常简单,但它还有一定的局限性——使用动态代理的对象必须实现一个或多个接口。如果要对没有实现接口的类进行代理,那么可以使用CGLIB代理。CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。
基本流程:
首先创建了一个动态类对象Enhancer,它是CGLIB的核心类;然后调用了Enhancer类的setSuperclass()方法来确定目标对象;接下来调用了setCallback()方法添加回调函数,其中的this代表的就是代理类CglibProxy本身;最后通过return语句将创建的代理类对象返回。intercept()方法会在程序执行目标方法时被调用,方法运行时将会执行切面类中的增强方法。

基于代理类的AOP实现

在Spring中,使用Proxy FactoryBean是创建AOP代理的最基本方式。
Spring中的通知按照在目标类方法的连接点位置,可以分为以下5种类型。

  • MethodInterceptor(环绕通知)在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
  • MethodBeforeAdvice(前置通知)在目标方法执行前实施增强,可以应用于权限管理等功能。
  • AfterReturningAdvice(后置通知)在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除临时文件等功能。
  • ThrowsAdvice(异常通知)在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
  • IntroductionInterceptor(引介通知)在目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类)。

ProxyFactoryBeanProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。

image.png

首先通过<bean>元素定义了目标类和切面,然后使用ProxyFactoryBean类定义了代理对象。在定义的代理对象中,分别通过<property>子元素指定了代理实现的接口、代理的目标对象、需要织入目标类的通知以及代理方式。

AspectJ开发

AspectJ开发AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,Spring AOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。使用AspectJ实现AOP有两种方式:

  • 基于XML的声明式AspectJ
  • 基于注解的声明式AspectJ
基于XML的声明式AspectJ

基于XML的声明式AspectJ基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在<aop:config>元素内。<aop:config>元素及其子元素如图所示。

image.png

Spring配置文件中的<beans>元素下可以包含多个<aop:config>元素,一个<aop:config>元素中又可以包含属性和子元素,其子元素包括<aop:pointcut>、<aop:advisor>和<aop:aspect>。在配置时,这3个子元素必须按照此顺序来定义。在<aop:aspect>元素下,同样包含了属性和多个子元素,通过使用<aop:aspect>元素及其子元素就可以在XML文件中配置切面、切入点和通知。图中灰色部分标注的元素即为常用的配置元素,这些常用元素的配置代码如下所示。

>> <! -- 定义切面Bean --><bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" />
<aop:config>    
<! --1.配置切面 --> 
  <aop:aspect  id="aspect"  ref="myAspect">  
   <! --  2.配置切入点 -->   
  <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))"                                            id="myPointCut" />  
   <! --3.配置通知 -->  
   <! -- 前置通知 -->    
 <aop:before method="myBefore" pointcut-ref="myPointCut" />    
 <! -- 后置通知 -->   
  <aop:after-returning method="myAfterReturning"         
    pointcut-ref="myPointCut" returning="returnVal" />    
 <! -- 环绕通知 -->   
  <aop:around method="myAround" pointcut-ref="myPointCut" />  
   <! -- 异常通知 -->   
  <aop:after-throwing method="myAfterThrowing"        
     pointcut-ref="myPointCut" throwing="e" />     
  <! -- 最终通知 -->     
  <aop:after method="myAfter" pointcut-ref="myPointCut" />  
 </aop:aspect>
</aop:config>

配置切面在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean(如上述代码中定义的myAspect)。定义完成后,通过<aop:aspect>元素的ref属性即可引用该Bean。

在上述配置代码片段中,execution(* com.itheima.jdk..(..))就是定义的切入点表达式,该切入点表达式的意思是匹配com.itheima.jdk包中任意类的任意方法的执行。其中execution()是表达式的主体,第1个表示的是返回类型,使用代表所有类型;com.itheima.jdk表示的是需要拦截的包名,后面第2个表示的是类名,使用代表所有的类;第3个表示的是方法名,使用表示所有方法;后面(..)表示方法的参数,其中的“..”表示任意参数。需要注意的是,第1个*与包名之间有一个空格。

在AOP的配置信息中,使用<aop:after-returning>配置的后置通知和使用<aop:after>配置的最终通知虽然都是在目标方法执行之后执行,但它们也是有所区别的。后置通知只有在目标方法成功执行后才会被织入,而最终通知不论目标方法如何结束(包括成功执行和异常中止两种情况),它都会被织入。

基于注解的声明式AspectJ

基于注解的声明式AspectJ与基于代理类的AOP实现相比,基于XML的声明式ApectJ要便捷得多,但是它也存在着一些缺点,那就是要在Spring文件中配置大量的代码信息。为了解决这个问题,AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。

image.png

使用<aop:aspectj-autoproxy />来启动Spring对基于注解的声明式AspectJ的支持。

Spring事务管理概述

事务管理的方式Spring中的事务管理分为两种方式:

  • 编程式事务管理:是通过编写代码实现的事务管理,包括定义事务的开始、正常执行后的事务提交和异常时的事务回滚。
  • 声明式事务管理:是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后通过AOP技术将事务管理的“切面”代码织入到业务目标类中。
声明式事务管理

声明式事务管理Spring的声明式事务管理可以通过两种方式来实现:

  • 基于XML的方式
  • 基于Annotation的方式。
基于XML的方式

基于XML方式的声明式事务基于XML方式的声明式事务管理是通过在配置文件中配置事务规则的相关声明来实现的。Spring 2.0以后,提供了tx命名空间来配置事务,tx命名空间下提供了<tx:advice>元素来配置事务的通知(增强处理)。当使用<tx:advice>元素配置了事务的增强处理后,就可以通过编写的AOP配置,让Spring自动对目标生成代理。配置<tx:advice>元素时,通常需要指定id和transaction-manager属性,其中id属性是配置文件中的唯一标识,transaction-manager属性用于指定事务管理器。除此之外,还需要配置一个<tx:attributes>子元素,该子元素可通过配置多个<tx:method>子元素来配置执行事务的细节。<tx:advice>元素及其子元素如图所示。

image.png
基于Annotation的方式

基于Annotation方式的声明式事务Spring的声明式事务管理还可以通过Annotation(注解)的方式来实现。这种方式的使用非常简单,开发者只需做两件事情:
① 在Spring容器中注册事务注解驱动,其代码如下。

<tx:annotation-driven transaction-manager="transactionManager"/>

② 在需要使用事务的Spring Bean类或者Bean类的方法上添加注解@Transactional。如果将注解添加在Bean类上,则表示事务的设置对整个Bean类的所有方法都起作用;如果将注解添加在Bean类中的某个方法上,则表示事务的设置只对该方法有效。使用@Transactional注解时,可以通过其参数配置事务详情。@Transactional注解可配置的参数信息如表。

image.png

Spring MVC的工作流程

image.png

Spring MVC程序的完整执行流程如下。
(1)用户通过浏览器向服务器发送请求,请求会被Spring MVC的前端控制器DispatcherServlet所拦截。
(2)DispatcherServlet拦截到请求后,会调用HandlerMapping处理器映射器。
(3)处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet会通过返回信息选择合适的HandlerAdapter(处理器适配器)。
(5)HandlerAdapter会调用并执行Handler(处理器),这里的处理器指的就是程序中编写的Controller类,也被称之为后端控制器。
(6)Controller执行完成后,会返回一个ModelAndView对象,该对象中会包含视图名或包含模型和视图名。
(7)HandlerAdapter将ModelAndView对象返回给DispatcherServlet。
(8)DispatcherServlet会根据ModelAndView对象选择一个合适的ViewReslover(视图解析器)。
(9)ViewReslover解析后,会向DispatcherServlet中返回具体的View(视图)。
(10)DispatcherServlet对View进行渲染(即将模型数据填充至视图中)。
(11)视图渲染结果会返回给客户端浏览器显示。

Spring数据绑定流程

数据绑定介绍在执行程序时,Spring MVC会根据客户端请求参数的不同,将请求消息中的信息以一定的方式转换并绑定到控制器类的方法参数中。这种将请求消息数据与后台方法参数建立连接的过程就是Spring MVC中的数据绑定。在数据绑定过程中,Spring MVC框架会通过数据绑定组件(DataBinder)将请求参数串的内容进行类型转换,然后将转换后的值赋给控制器类中方法的形参,这样后台方法就可以正确绑定并获取客户端请求携带的参数了。

image.png

(1)Spring MVC将ServletRequest对象传递给DataBinder。
(2)将处理方法的入参对象传递给DataBinder。
(3)DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中。
(4)调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。
(5)校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。

Spring JSON数据交互

JSON数据转换为了实现浏览器与控制器类(Controller)之间的数据交互,Spring提供了一个HttpMessageConverter<T>接口来完成此项工作。该接口主要用于将请求信息中的数据转换为一个类型为T的对象,并将类型为T的对象绑定到请求方法的参数中,或者将对象转换为响应信息传递给浏览器显示。Spring为HttpMessageConverter<T>接口提供了很多实现类,这些实现类可以对不同类型的数据进行信息转换。其中MappingJackson2HttpMessageConverter是Spring MVC默认处理JSON格式请求响应的实现类。该实现类利用Jackson开源包读写JSON数据,将Java对象转换为JSON对象和XML文档,同时也可以将JSON对象和XML文档转换为Java对象。


image.png

在使用注解式开发时,需要用到两个重要的JSON格式转换注解,分别为@RequestBody和@ResponseBody,关于这两个注解的说明如表所示。

Spring RESTful支持

什么是RESTful?RESTful也称之为REST(Representational State Transfer),可以将它理解为一种软件架构风格或设计风格,而不是一个标准。简单来说,RESTful风格就是把请求参数变成请求路径的一种风格。例如,传统的URL请求格式为:http://.../queryItems? id=1而采用RESTful风格后,其URL请求为:http://.../items/1从上述两个请求中可以看出,RESTful风格中的URL将请求参数id=1变成了请求路径的一部分,并且URL中的queryItems也变成了items(RESTful风格中的URL不存在动词形式的路径,如queryItems表示查询订单,是一个动词,而items表示订单,为名词)。

/** *接收RESTful风格的请求,其接收方式为GET */
@RequestMapping(value="/user/{id}", 
method=RequestMethod.GET)
@ResponseBodypublic User selectUser(@PathVariable("id") String id){       
User user=new User();    
...
//返回JSON格式的数据    
return user;
}

在上述代码中,@RequestMapping(value="/user/{id}", method=RequestMethod.GET)注解用于匹配请求路径(包括参数)和方式。其中value="/user/{id}"表示可以匹配以“/user/{id}”结尾的请求,id为请求中的动态参数;method=RequestMethod.GET表示只接收GET方式的请求。方法中的@PathVariable("id")注解则用于接收并绑定请求参数,它可以将请求URL中的变量映射到方法的形参上,如果请求路径为“/user/{id}”,即请求参数中的id和方法形参名称id一样,则@PathVariable后面的“("id")”可以省略。

Spring 拦截器

拦截器概述Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并做相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。
拦截器的定义要使用Spring MVC中的拦截器,就需要对拦截器类进行定义和配置。通常拦截器类可以通过两种方式来定义。一种是通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义;另一种是通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

HandlerInterceptor三个方法的具体描述如下:

  • preHandler()方法:该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。
  • postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。
  • afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。
单个拦截器的执行流程

单个拦截器的执行流程在运行程序时,拦截器的执行是有一定顺序的,该顺序与配置文件中所定义的拦截器的顺序相关。如果在项目中只定义了一个拦截器,那么该拦截器在程序中的执行流程如图所示。

image.png

程序首先会执行拦截器类中的preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行;在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应;在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。

多个拦截器的执行流程

在大型的企业级项目中,通常不会只有一个拦截器,开发人员可能会定义很多拦截器来实现不同的功能。那么多个拦截器的执行顺序又是怎样的呢?下面通过一张图来描述多个拦截器的执行流程(假设有两个拦截器Interceptor1和Interceptor2,并且在配置文件中,Interceptor1拦截器配置在前)。

image.png

当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容