IOC部分
什么是IOC?
IOC:Inversion of Control,翻译为“控制反转”或“反转控制”,强调的是原来在程序中创建Bean的权利反转给第三方。
例如:原来在程序中手动的去 new UserServiceImpl(),手动的去new UserDaoImpl(),而根据IoC思想的指导,寻求一个第三方去创建UserServiceImpl对象和UserDaoImpl对象。这样程序与具体对象就失去的直接联系。
问:谁去充当第三方角色呢?
工厂设计模式,BeanFactory来充当第三方的角色,来产生Bean实例
问:BeanFactory怎么知道产生哪些Bean实例呢?
可以使用配置文件配置Bean的基本信息,BeanFactory根据配置文件来生产Bean实例
什么是DI?
DI:Dependency Injection,依赖注入,某个Bean的完整创建依赖于其他Bean(或普通参数)的注入。
例如:我们想将UserDao的创建权也反转给BeanFactory,与此同时UserService内部还需要用到UserDao实例对象。DI的做法是,UserService存在于BeanFactory中,UserDao也存在于BeanFactory中,将UserDao在BeanFactory内部设置给UserService的过程叫做“注入”,而UserService需要依赖UserDao的注入才能正常工作,这个过程叫做“依赖注入”。
IOC和DI的关系
- IoC强调的是Bean创建权的反转,而DI强调的是Bean的依赖关系,
- IoC强调的是Bean创建权的反转,而DI强调的是通过注入的方式反转Bean的创建权,认为DI是IoC的其中一种实现方式。
什么是AOP?
AOP:Aspect Oriented Programming,面向切面编程
,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面
,而用这种思维去设计编程的方式叫做面向切面编程。
什么是Spring?
spring是一个开源的轻量级Java开发应用框架
,可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能
。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善
,不管是Spring哪个领域的解决方案都是依附于在Spring Framework基础框架的。
分析一下Spring的BeanFactory的开发步骤?以及怎样实现DI依赖注入?
开发4步走:
1)导入Spring的jar包或Maven坐标;
2)定义UserService接口及其UserServiceImpl实现类;
3)创建beans.xml配置文件,将UserServiceImpl的信息配置到该xml中;
4)编写测试代码,创建BeanFactory,加载配置文件,获取UserService实例对象。
注入3步走:
1)定义UserDao接口及其UserDaoImpl实现类;
2)修改UserServiceImpl代码,添加一个setUserDao(UserDao userDao)用于接收注入的对象;
3)修改beans.xml配置文件,在UserDaoImpl的中嵌入配置注入;
ApplicationContext与BeanFactory的关系?
首先对ApplicationContext进行介绍:
ApplicationContext 称为Spring容器,内部封装了BeanFactory,比BeanFactory功能更丰富更强大,使用ApplicationContext 进行开发时,xml配置文件的名称习惯写成applicationContext.xml。
关系:
- BeanFactory是Spring的早期接口,称为Spring的Bean工厂,ApplicationContext是后期更高级接口,称之为Spring 容器;
- ApplicationContext在BeanFactory基础上对功能进行了扩展,例如:监听功能、国际化功能等。BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装;
- Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而且ApplicationContext内部还维护着BeanFactory的引用,所以,ApplicationContext与BeanFactory既有继承关系,又有融合关系。
- Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建(延迟加载),而ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。
BeanFactory的继承体系
BeanFactory是核心接口,项目运行过程中肯定有具体实现参与,这个具体实现就是DefaultListableBeanFactory,而ApplicationContext内部维护的Beanfactory的实现类也是它。
ApplicationContext的继承体系
Spring基础环境下常用的三个ApplicationContext: (只导入spring-context坐标时)
Spring的web环境下常用的两个ApplicationContext: (只导入spring-web坐标时)
基于xml的Spring Bean的配置
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName.
Bean的配置范围
默认情况下,单纯的Spring环境Bean的作用范围有两个:Singleton和Prototype
- singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,
每次getBean时都是从单例池中获取相同的Bean实例
;- prototype:原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,
每次getBean都会创建一个新的Bean实例
。
留下一个小问题:prototype 与ApplicationContext、懒加载;或者Bean的初始化、实例化与实例创建的关系?
Bean的懒加载
当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例,等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可,本质上该Bean还是单例的。
Bean的初始化和销毁方法配置
可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作,如下:
public class UserDaoImpl implements UserDao, InitializingBean {
public UserDaoImpl() {System.out.println("UserDaoImpl创建了...");}
public void init(){System.out.println("初始化方法...");}
public void destroy(){System.out.println("销毁方法...");}
//执行时机早于init-method配置的方法
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean...");
}
}
Bean的实例化配置
Spring的实例化方式主要如下两种:
-
构造方式实例化:底层通过构造方法对Bean进行实例化
有参构造在实例化Bean时,需要参数的注入,通过constructor-arg
标签,嵌入在bean
标签内部提供构造参数 -
工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化
工厂方式实例化Bean,又分为如下三种:
⚫ 静态工厂方法实例化Bean
静态工厂方法实例化Bean,其实就是定义一个工厂类,提供一个静态方法用于生产Bean实例,在将该工厂类及其静态方法配置给Spring即可<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean" factorymethod="getUserDao"> <constructor-arg name="name" value="haohao"/> </bean>
⚫ 实例工厂方法实例化Bean
实例工厂方法,也就是非静态工厂方法产生Bean实例,与静态工厂方式比较,该方式需要先有工厂对象,在用工厂对象去调用非静态方法,所以在进行配置时,要先配置工厂Bean,在配置目标Bean<!-- 配置实例工厂Bean --> <bean id="userDaoFactoryBean2" class="com.itheima.factory.UserDaoFactoryBean2"/> <!-- 配置实例工厂Bean的哪个方法作为工厂方法 --> <bean id="userDao" factory-bean="userDaoFactoryBean2" factory-method="getUserDao"> <constructor-arg name="name" value="haohao"/> </bean>
⚫ 实现FactoryBean规范延迟实例化Bean
上面不管是静态工厂方式还是非静态工厂方式,都是自定义的工厂方法,Spring提供了FactoryBean的接口规范
定义工厂实现FactoryBeanpublic class UserDaoFactoryBean3 implements FactoryBean<UserDao> { public UserDao getObject() throws Exception { return new UserDaoImpl(); } public Class<?> getObjectType() { return UserDao.class; } }
配置FactoryBean交由Spring管理即可
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean3"/>
Bean的依赖注入配置(两种方式)
Bean的自动装配方式
如果被注入的属性类型是Bean引用的话,那么可以在 标签中使用 autowire 属性去配置自动注入方式,属性值有两个:、
- byName:通过属性名自动装配,即去匹配 setXxx 与 id=“xxx”(name=“xxx”)是否一致;
- byType:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错。
基于xml的Spring应用
Spring的getBean()方法
//根据beanName获取容器中的Bean实例,需要手动强转
UserService userService = (UserService) applicationContext.getBean("userService");
//根据Bean类型去容器中匹配对应的Bean实例,如存在多个匹配Bean则报错
UserService userService2 = applicationContext.getBean(UserService.class);
//根据beanName获取容器中的Bean实例,指定Bean的Type类型
UserService userService3 = applicationContext.getBean("userService",
UserService.class);
Spring配置非自定义的Bean
在实际开发中有些功能类并不是我们自己定义的,而是使用的第三方jar包中的,那么,这些Bean要想让Spring进行管理,也需要对其进行配置。
Bean实例化的基本流程
- Spring容器在进行初始化时,会将xml配置的
bean
信息封装成一个BeanDefinition对象, - 所有的BeanDefinition存储到一个名为beanDefinitionMap的Map集合中去,
- Spring框架再对该Map进行遍历,使用反射或者调用指定的工厂方法创建Bean实例对象,
- 创建好的Bean对象存储在一个名为singletonObjects的Map集合中,
- 当调用getBean方法时最终从该Map集合中取出Bean实例对象返回。
xml
bean
-> BeanDefinition对象->汇总->beanDefinitionMap->ApplicationContext底层遍历反射,创建Bean实例对象->singletonObjects,Map<String,Object>->get Bean实例(当执行applicationContext.getBean(beanName)时,从singletonObjects去匹配Bean实例返回)
DefaultListableBeanFactory对象内部维护着一个Map用于存储封装好的BeanDefinitionMap
Bean实例及单例池singletonObjects, beanDefinitionMap中的BeanDefinition会被转化成对应的Bean实例对象,存储到单例池singletonObjects中去,在DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中,维护着singletonObjects。
Spring的后处理器
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
BeanFactoryPostProcessor
:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
BeanPostProcessor
:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行。
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,其中有一个对外进行扩展的点BeanPostProcessor,我们称为Bean后处理。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。
·
Spring Bean的生命周期
分为三个阶段:
- Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断,当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean,最终将一个普通的singleton的Bean通过反射进行实例化。
- Bean的初始化阶段:Bean的创建之后还仅仅是个“半成品”,还需要对Bean实例的属性进行填充,执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitializingBean接口的初始化方法,执行自定义初始化init方法等。
该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,后面要学习的Spring的注解功能等、 spring高频面试题Bean的循环引用问题都是在这个阶段体现的
- Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池SingletonObjects中去了,即完成了Spring Bean的整个生命周期。
针对生命周期第2个阶段-Spring Bean的初始化过程涉及如下几个过程
:
- Bean实例的属性填充
Spring在进行属性注入时,会分为如下几种情况:
- 注入普通属性,String、int或存储基本类型的集合时,直接通过set方法的反射设置进去;
- 注入单向对象引用属性时,从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,在进行注入操作;
- 注入双向对象引用属性时,就比较复杂了,涉及了循环引用(循环依赖)问题
循环依赖解决方案
Spring 提供了三级缓存存储完整的Bean实例
和半成品Bean实例
,用于解决循环依赖问题。
UserService和UserDao循环依赖的过程结合上述三级缓存描述一下:- UserService实例化对象,但尚未初始化,将UserService存储到三级缓存;
- UserService属性注入,需要UserDao,从缓存中获取,没有UserDao;
- UserDao实例化对象,但尚未初始化,将UserDao存储到三级缓存;
- UserDao属性注入,需要UserService,从三级缓存中获取UserService,UserService从三级缓存中存入二级缓存;
- UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存;
- UserService注入UserDao;
- UserService执行其他生命周期,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存
public class DefaultSingletonBeanRegistry ... {
//1、最终存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为"一级缓存"
Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//2、早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为"二级缓存"
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//3、单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时在通过工厂创建Bean,称之为"三级缓存"
Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
- Aware接口的属性注入
- BeanPostProcessor的before()方法回调
- InitializingBean接口的初始化方法回调
- 自定义初始化方法init回调
- BeanPostProcessor的after()方法回调
Spring IOC整体流程
基于注解的Spring应用
基于Bean基本注解开发
基本Bean注解,主要是使用注解的方式替代原有xml的 bean
标签及其标签属性的配置
-
可以通过@Component注解的value属性指定当前Bean实例的beanName,也可以省略不写,不写的情况下为当前类名首字母小写
//获取方式:applicationContext.getBean("userDao"); @Component("userDao") public class UserDaoImpl implements UserDao { } //获取方式:applicationContext.getBean("userDaoImpl"); @Component public class UserDaoImpl implements UserDao { }
-
使用注解对需要被Spring实例化的Bean进行标注,但是需要告诉Spring去哪找这些Bean,要配置组件扫描路径。
<!-- 告知Spring框架去itheima包及其子包下去扫描使用了注解的类 --> <context:component-scan base-package="com.itheima"/>
-
如何配置其他属性(
PostConstruct
注解的提出)
-
衍生注解
Bean依赖注入注解开发
@Resource注解存在与 javax.annotation 包中,Spring对其进行了解析
非自定义的Bean注解开发
非自定义Bean不能像自定义Bean一样使用@Component进行管理,非自定义Bean要通过工厂的方式进行实例化,使用@Bean标注方法即可,@Bean的属性为beanName,如不指定为当前工厂方法名称。
//将方法返回值Bean实例以@Bean注解指定的名称存储到Spring容器中
@Bean("dataSource")
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
Bean配置类的注解开发
@Component等注解替代了标签,但是像
<import>、<context:componentScan>
等非<bean>
标签怎样去使用注解替代呢?
定义一个配置类替代原有的xml配置文件,标签以外的标签,一般都是在配置类上使用注解完成的.
- @Configuration注解标识的类为配置类,替代原有xml配置文件,该注解第一个作用是标识该类是一个配置类,第二个作用是具备@Component作用.
- @ComponentScan 组件扫描配置,替代原有xml文件中的<context:component-scan base-package=“”/>
@Configuration
@ComponentScan({"com.itheima.service","com.itheima.dao"})
public class ApplicationContextConfig {}
base-package的配置方式:
⚫ 指定一个或多个包名:扫描指定包及其子包下使用注解的类
⚫ 不配置包名:扫描当前@componentScan注解配置类所在包及其子包下的类
- @PropertySource 注解用于加载外部properties资源配置
- @Import 用于加载其他配置类。
- @Primary注解用于标注相同类型的Bean优先被使用权,@Primary 是Spring3.0引入的,与@Component和@Bean一起使用,标注该Bean的优先级更高,则在通过类型获取Bean或通过@Autowired根据类型进行注入时,会选用优先级更高的。
- @Profile 注解的作用同于xml配置时学习profile属性,是进行环境切换使用的,如@Profile(“test”)
AOP部分
AOP的相关概念
- 目标对象:target,被增强方法所在的对象
- 代理对象:Proxy,对目标对象进行增强后的对象,客户端实际调用的对象
- 连接点:JoinPoint,目标对象中可以被增强的方法
- 切入点:PointCut,目标对象中实际被增强的方法
- 通知/增强:Advice,增强部分的代码逻辑
- 切面:Aspect,增强和切入点的组合
- 织入:Weaving,将通知和切入点动态组合的过程
用一张图来加深记忆:
基于xml配置的AOP
配置步骤
- 导入AOP相关坐标;
- 准备目标类、准备增强类,并配置给Spring管理;
- 配置切入点表达式(哪些方法被增强)
- 配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)
切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:
execution([访问修饰符]返回值类型 包名.类名.方法名(参数))
其中,
⚫ 访问修饰符可以省略不写;
⚫ 返回值类型、某一级包名、类名、方法名 可以使用 * 表示任意;
⚫ 包名与类名之间使用单点 . 表示该包下的类,使用双点 … 表示该包及其子包下的类;
⚫ 参数列表可以使用两个点 … 表示任意参数。
举例如下:
//表示访问修饰符为public、无返回值、在com.itheima.aop包下的TargetImpl类的无参方法show
execution(public void com.itheima.aop.TargetImpl.show())
//表述com.itheima.aop包下的TargetImpl类的任意方法
execution(* com.itheima.aop.TargetImpl.*(..))
//表示com.itheima.aop包下的任意类的任意方法
execution(* com.itheima.aop.*.*(..))
//表示com.itheima.aop包及其子包下的任意类的任意方法
execution(* com.itheima.aop..*.*(..))
//表示任意包中的任意类的任意方法
execution(* *..*.*(..))
配置类型
AspectJ的通知有以下五种类型
基于AOP的声明式事务控制
事务是开发中必不可少的东西,使用JDBC开发时,我们使用connnection对事务进行控制,使用MyBatis时,我们使用SqlSession对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化,Spring 就将这些技术基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制 和 声明式事务控制
- 编程式事务控制:Spring提供了事务控制的类和方法,使用编码的方式对业务代码进行事务控制,事务控制代码和业务操作代码耦合到了一起,开发中不使用。
- 声明式事务控制:Spring将事务控制的代码封装,对外提供了Xml和注解配置方式,通过配置的方式完成事务的控制,可以达到事务控制与业务操作代码解耦合,开发中推荐使用。
Spring MVC
概述
SpringMVC是一个基于Spring开发的MVC轻量级框架,Spring3.0后发布的组件,SpringMVC和Spring可以无缝整合,使用DispatcherServlet作为前端控制器,且内部提供了处理器映射器、处理器适配器、视图解析器等组件,可以简化JavaBean封装,Json转化、文件上传等操作。
SpringMVC关键组件
SpringMVC的请求处理
-
接收普通请求数据,当请求参数的名称与方法参数名不一致时,可以使用@RequestParam注解进行标注
@GetMapping("/show") public String show(@RequestParam(name = "username",required = true) String name, int age){ System.out.println(name+"=="+age); return "/index.jsp"; }
-
接收实体JavaBean属性数据,单个JavaBean数据:提交的参数名称只要与Java的属性名一致,就可以进行自动封装
-
接收数组或集合数据,客户端传递多个同名参数时,也可以使用单列集合接收,但是需要使用@RequestParam告知框架传递的参数是要同名设置的,不是对象属性设置的。
-
接收数组或集合数据,客户端传递多个不同命参数时,也可以使用Map<String,Object> 进行接收,同样需要用@RequestParam 进行修饰。
-
接收Json数据格式数据,Json数据都是以请求体的方式提交的,且不是原始的键值对格式的,所以我们要使用@RequestBody注解整体接收该数据。
"username":"haohao", "age":18, "hobbies":["eat","sleep"], "birthday":"1986-01-01", "address":{ "city":"tj", "area":"binhai" } }
@PostMapping("/show6") public String show6(@RequestBody String body){ System.out.println(body); return "/index.jsp"; }
请求数据的接受
什么是Rest风格?
Rest(Representational State Transfer)表象化状态转变(表述性状态转变),在2000年被提出,基于HTTP、URI、xml、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新网络应用程序的设计风格和开发方式
Restful风格常用规则
-
用url表示某个模块名称,资源名称为名词;
-
用请求方式表示模块具体业务动作,例如:GET表示查询、POST表示插入、PUT表示更新、DELETE表示删除。
接收Restful风格数据,Restful请求数据一般会在URL地址上携带,可以使用注解 @PathVariable(占位符参数名称)http://localhost/user/100
@PostMapping("/user/{id}") public String findUserById(@PathVariable("id") Integer id){ System.out.println(id); return "/index.jsp"; }
javaweb常用对象的获取(如HttpServletRequest request)
有时在我们的Controller方法中需要用到Javaweb的原生对象,例如:Request、Response等,我们只需要将需要的对象以形参的形式写在方法上,SpringMVC框架在调用Controller方法时,会自动传递实参:
@GetMapping("/javawebObject")
public String javawebObject(HttpServletRequest request, HttpServletResponse response,
HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
return "/index.jsp";
}
SpringMVC的响应处理
-
Spring给客户端响应数据分为两种方式:
- 传统同步方式:准备好模型数据,在跳转到执行页面进行展示,此方式使用越来越少了,基于历史原因,一些旧项目还在使用;
- 前后端分离异步方式:前端使用Ajax技术+Restful风格与服务端进行Json格式为主的数据交互,目前市场上几乎都是此种方式了。
-
传统同步业务在数据响应时,SpringMVC又涉及如下四种模式:
- 请求资源转发
- 请求资源重定向
- 响应模型数据
- 直接写回数据给客户端(直接通过方法的返回值返回给客户端的字符串,但是SpringMVC默认的方法返回值是视图,可以通过@ResponseBody 注解显示的告知此处的返回值不要进行视图处理,是要以响应体的方式处理的)
-
前后端分离异步业务数据响应
- 同步方式回写数据,是将数据响应给浏览器进行页面展示的,而异步方式回写数据一般是回写给Ajax引擎的,即谁访问服务器端,服务器端就将数据响应给谁
- 同步方式回写的数据,一般就是一些无特定格式的字符串,而异步方式回写的数据大多是Json格式字符串。
在讲解SringMVC接收请求数据时,客户端提交的Json格式的字符串,也是使用Jackson进行的手动转换成JavaBean,可以当我们使用了@RequestBody时,直接用JavaBean就接收了Json格式的数据,原理其实就是SpringMVC底层帮我们做了转换,此处@ResponseBody也可以将JavaBean自动给我们转换成Json格式字符串回响应.
@GetMapping("/response5")
@ResponseBody
public User response5() throws JsonProcessingException {
//创建JavaBean
User user = new User();
user.setUsername("haohao");
user.setAge(18);
//直接返回User对象
return user;
}
@ResponseBody和@RestController
可以使用@RestController替代@Controller和@ResponseBody,@RestController内部具备的这两个注解的功能
SpringMVC的拦截器
SpringMVC的拦截器Interceptor规范,主要是对Controller资源访问时进行拦截操作的技术,当然拦截后可以进行权限控制,功能增强等都是可以的。拦截器有点类似 Javaweb 开发中的Filter,拦截器与Filter的区别如下图:
对比Filter和Interceptor:
HandlerInterceptor接口方法的作用以及参数、返回值:
拦截器执行顺序
当Interceptor1和Interceptor2处于放行,Interceptor3处于不放行时,三个方法的执行顺序如下:
拦截器执行顺序取决于 interceptor 的配置顺序
SpringMVC异常处理机制
SpringMVC 处理异常的思路是,一路向上抛,都抛给前端控制器 DispatcherServlet ,DispatcherServlet 在调用异常处理器ExceptionResolver进行处理,如下图:
SpringMVC的异常处理方式
- 简单异常处理器:使用springmvc内置的异常处理器SimpleMappingExceptionResolver
- 自定义异常处理器:实现HandlerExceptionResolver接口,自定义异常进行处理
- 注解方式:使用@ControllerAdvice + @ExceptionHandler来处理
如果全局异常处理器响应的数据都是Json格式的字符串的话,可以使用@RestControllerAdvice替代@ControllerAdvice 和 @ResponseBody
SpringMVC常用的异常解析器