目录
3、Spring、SpringMVC、SpringBoot、SpringCloud
3、ApplicationContext和BeanFactory的区别
7、spring-boot-starter-parent作用
13、Spring Boot 是否可以兼容老 Spring 项目吗
15、@ConfigurationProperties和@value的区别
一、Spring基础
1、Spring的核心模块
容器模块( Spring Core):是Spring框架最基础的部分,它提供了依赖注入(DI)特征来实现容器对Bean的管理。核心容器的主要组件是 BeanFactory,BeanFactory是工厂模式的一个实现,是任何Spring应用的核心
AOP模块(Spring AOP):提供了对面向切面编程的丰富支持,Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中,可以自定义拦截器、切点、日志等操作
Web模块(Spring Web):Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文,提供了Spring和其它Web框架的集成,比如Struts、WebWork。还提供了一些面向服务支持,例如:实现文件上传的multipart请求
Test模块(spring Test):提供了对JUnit和TestNG测试的支持。
数据访问(DataAccess):提供了数据访问功能,例如jdbc、orm框架、事务
Messageing:为 Spring 框架集成一些基础的报文传送应用。
2、Spring中用到的设计模式
1、单例模式:Spring中的Bean默认都是单例的
2、原型模式:Bean的作用域可以指定为prototype
3、工厂模式:在BeanFactory和ApplicationContext创建中用到了
4、代理模式:AOP动态代理
5、观察者模式:Spring中ApplicationListener、ApplicationEvent
6、策略模式:加载资源文件的方式,使用了不同的方法,比如: ClassPathResoureceFileSystemResource, ServletContextResource, UrlResource但他们都有共同的借口Resource; 在Aop的实现中,采用了两种不同的方式JDK动态代理和CGLIB代理
7、适配器模式:Adapter
8、模板方法模式:在BeanFactory和ApplicationContext实现中用到了
9、建造者模式:builder
10、责任链模式:使用AOP的时候会生成一个拦截器链
3、Spring、SpringMVC、SpringBoot、SpringCloud
Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring的IOC和AOP。IOC提供了依赖注入的容器, AOP解决了面向横切面编程,然后在此两者的基础上实现了其他延伸产品的高级功能;
SpringMVC是Spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于spring框架中WEB层开发的一部分;
springBoot主要是为了简化开发,约定大于配置,具有自动配置的功能,并且内置了Tomcat等服务器。
SpringCloud它和springBoot一样,注重的是微服务的开发,但是springCloud更关注的是全局微服务的整合和管理,相当于管理多个springBoot框架的单体微服务;
二、SpringIOC
1、IOC的理解
IOC:控制反转,原来我们使用的时候对象是由使用者控制的,有了spring之后,可以将整个象交给容器来帮我们进行管理。
DI:依赖注入,将对应的属性设置到具体的对象中,在工作中我们一般使用@Autowired、@Resource、PopulateBean方决来完成注入
容器:用来储存对象,使用Map的结构进行储存,在Spring储存对象的时候一般会有三级缓存,
SinaletonObiects:一级缓存,存放完整的对象
earlySinaletonObiects:二级缓存,存放半成品的对象
SinaletonFactory:三级缓存,存放lambda表达式和对象名称的映射。
整个Bean的生命周期从创建到使用到销毁,各个环节都是由容器来帮我们控制的。
当前,在整个IOC体系中还又很多细节的点,但是我是之前看的,太久了,记不清楚了。当然,Spring中所有的Bean都是通过反射生成的(主要是Construction和newInstance方法),在整个流程中还会包含很多扩展的点,比如有2个非常重要的接口(BeanFactoryPOSTProcess和BeanPOSTProcess),用来实现扩展功能。
2、Spring中的循环依赖
A对象中有B的属性b,而对象B有A的属性a,在A进行实例化后,就要进行赋值操作,但是A的赋值依赖于B,所以它就要去找内存中有没有B,发现没有B,所以就要创建一个B的实例,在B进行赋值时,发现需要A才能完成填充,而A又处于等待B的状态(是不是很像死锁),所以就会循环的互相依赖,这就叫循环依赖。
spring中bean对象的创建都要经历实例化和初始化(属性填充)的过程,根据对象的状态分类,存在半成品和成品对象的方式,来分别进行初始化和实例化,成品和半成品在存储的时候需要分不同的缓来进行存储;
1、只有一级缓存行不行?
不行,会把成品状态的bean对象和办成品状态的bean对象放到一起,而半成品对象是无法暴露给外部使用的,所以要将成品和半成品分来,一级缓存(singletonObjects)中放成品对象,二级缓存(earlySingletonObjects)放半成品对象。
2、只有二级缓存行不行?
如果整个应用程序中不涉及aop的存在,那么二级缓存中足以解决循环依赖的问题,如果aop中存在了循环依赖,那么就必须要使用三级缓存才能解决
3、为什么需要三级缓存?
三级缓存的value类型是ObiectFactory,是一个函数式接口,不是直接进行调用的,只有在调用getObiect方法的时候才会去调用里面存储的lambda表达式,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个
缓存的存放时间和删除时间:
- 三级缓存:crateBeanInstance后存放三级缓存(调用addSingletonFactory)
- 二级缓存:第一次从三级缓存中确定对象是代理对象还是普通的对象后存放二级缓存,同时删除三级缓存(整个过程在getSingleton方法中执行)
- 一级缓存:生成完整对象之后存放到一级缓存,同时删除二级缓存和三级缓存(整个过程在addSingleton方法中执行)
名称 | 作用 |
---|---|
singletonObjects | 一级缓存:存放完整的Bean |
earlySingletonObjects | 二级缓存:存放提前暴露的Bean,Bean不是完整的,为完成属性注入和执行初始化方法 |
singletonFactories | 三级缓存:存放的是Bean工厂,主要生产Bean,存放到二级缓存中 |
3、ApplicationContext和BeanFactory的区别
BeanFactory是访问Spring容器的顶级接口,采用延迟加载的形式来创建Bean,里面知识提供了一些基本方法的约束和规范。
而为了满足更多的需求,ApplicationContext实现BeanFactory接口,使用立即加载的形式创建Bean,并在此接口的基础上进行了某些功能的扩展(获取环境变量、事件发布、支持国际化),提供更多API的调用,一般我们在工作中更常使用ApplicationContext接口
4、BeanFactory和FactoryBean的区别
BeanFactory和FactoryBean都可以用来创建对象,只不过创建的流程和方式有些不同。
当使用BeanFactory的时候,我们必须要严格的遵守Bean的生命周期,需要经过一系列繁杂的步骤之后才可以创建出单例的对象,是流水线式的创建过程。
而FactoryBean是用户可以自定义Bean对象的创建流程,不需要按照Bean的生命周期来创建,在此接口中包含了3个方法:
isSingleton():判断改对象是否为单例的
getObjectType():获取对象的类型
getObject():在此方法中可以自己创建对象(使用new方式或代理对象的方式),用户可以按照自己的需求随意创建对象,在很多框架集成的时候都会实现该接口,比如Feign
5、@Component和@Bean的区别
首先@Component和 @Bean 是两种使用注解来定义bean的方式。
- 作用对象:@Component注解作用于类,而@Bean注解作用于方法。
- @Bean与配置类(使用@Configuration)一起使用,方法的返回值就是要注入到容器的Bean,方法的名称就是Bean的name;
- @Component注解作用的类需要使用@ComponentScan注解一起使用,@Component注解作用的类在@ComponentScan注解扫描路径下才能将此类注入到容器中;
- @Bean注解比@Component注解更加灵活,例如:引入第三方库、根据条件注入Bean
6、将一个类声明为Spring的bean的注解
- @Component:通用的注解,可标注任意类为Spring组件。如果一个Bean不知道属于哪一个层,可以使用@Component注解标注。
- @Repository:对应持久层,即Dao层,主要用于数据库相关操作。
- @Service:对应服务层,即Service层,主要涉及一些复杂的业务逻辑
- @Controller:对应Spring MVC的控制层,主要用于接受用户请求并调用Service层的方法返回数据给前端页面。
7、Spring中的bean的作用域
-
singleton:唯一bean实例,Spring中的bean默认都是单例的。
-
prototype:每次请求都会创建一个新的bean实例。
-
request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
-
session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
-
global-session:全局session作用域,Spring5中已经没有了。
如何定义Bean的作用域:
- 在Bean标签中,可以通过bean定义中的scope属性进行设置;
- 在注解创建Bean时,可以使用@Scope注解进行设置;
8、Spring框架中的单例Bean是线程安全的么?
Spring中的Bean对象默认是单例的,框架并没有对bean进行多线程的封装处理。
如果Bean是有状态的,那么就需要开发人员自己来保证线程安全的保证,最简单的办法就是改变bean的作用域把singleton改成prototype,这样每次请求bean对象就相当于是创建新的对象来保证线程的安全。
有状态就是由数据存储的功能。
无状态就是不会存储数据,你想一下,我们的controller,service和dao本身并不是线程安全的,只是调用里面的方法,而且多线程调用一个实例的方法,会在内存中复制遍历,这是自己线程的工作内存,是最安全的。
因此在进行使用的时候,不要在bean中声明任何有状态的实例变量或者类变量,如果必须如此,也推荐大家使用ThreadLocal把变量变成线程私有,如果bean的实例变量或者类变量需要在多个线程之间共享,那么就只能使用synchrnized,lock,cas等这些实现线程同步的方法了。
9、Spring中的bean生命周期
Bean的生命周期主要包括实例化、属性填充和初始化3个关键的环节,当然在整个过程中会有一些扩展点的存在。
实例化:
1、实例化bean对象(源码中createBeanInstance方法):通过反射的方式进行对象的创建,此时的创建只是在堆空间中申请空间,属性都是默认值。
属性填充:
2、设置对象属性(源码中populateBean方法):给对象中的属性进行值的设置工作,可能出现循环依赖。
初始化:
3、检查Aware相关接口并设置相关依赖(BeanNameAware、BeanFactoryAware):如果对象中需要引用容器内部的对象,那么需要调用aware接口的子类方法来进行统一的设置
4、BeanPostProcessor的前置处理:对生成的bean对象进行前置的处理工作
5、判断当前bean对象是否设置了InitializingBean接口:进行属性的设置等基本工作
6、检查是否配置有自定义的init-method方法:如果当前bean对象定义了初始化方法,那么在此处调用初始化方法
7、BeanPostProcessor后置处理:对生成的bean对象进行后置的处理工作。(AOP的动态代理在此实现)
8、销毁Bean:首先会判断是否实现了DispoableBean接口,然后去调用destroyMethod方法
10、依赖注入的方式
- 使用属性的set方法注入:通过setXxx()方法注入Bean的属性值或依赖对象,该方式要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。
- 原理:Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值
<!-- 属性注入 -->
<bean id="car" class="com.spring.model.Car">
<property name="maxSpeed" value="200"></property>
<property name="brand" value="红旗CA72"></property>
<property name="price" value="200000.00"></property>
</bean>
- 使用构造器注入:在类中,不用为属性设置setter方法,但是需要生成该类带参的构造方法,在配置文件中配置该类的bean,并配置构造器;
- index:索引,指定注入的属性,从0开始;
- type:指该属性所对应的类型;
- ref :指引用的依赖对象;
- value :注入的不是依赖对象,而是基本数据类型时,就用value
<!-- 构造函数注入(通过入参类型和位置索引确定对应关系) -->
<bean id="car3" class="com.spring.model.Car">
<constructor-arg index="0" type="java.lang.String" value="奔驰"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="中国一汽"></constructor-arg>
<constructor-arg index="2" type="int" value="200"></constructor-arg>
</bean>
- 使用注解注入(推荐):可以使用@Autowired或@Resource注解方式进行Spring的依赖注入;两者的区别是:@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean时,才会按类型装配。
11、如何实现一个IOC容器?
1、先准备一个基本的容器对象,包含一些map结构的集合,用来方便后续过程中存储具体的对象
2、进行配置文件的读取工作或者注解的解析工作,将需要创建的bean都封装成BeanDefinition对象存储在容器中
3、容器将封装好的BeanDefinition对象通过反射的方式进行实例化,完成对象的实例化工作
4、进行对象的初始化操作,也就是给类中的对应属性值就行设置,也就是进行依赖注入,完成整个对象的创建,变成一个完整的bean对象,存储在容器的某个map结构中
5、通过容器对象来获取对象,进行对象的获取和逻辑处理工作
6、提供销毁操作,当对象不用或者容器关闭的时候,将无用的对象进行销毁
12、@Autowired和@Resource区别
1、来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
2、依赖查找的顺序不同:@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;
3、支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
4、依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
5、编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。
三、SpringAOP
1、AOP的理解
AOP是IOC的一个扩展功能,先有的IOC,再有的AOP,只是在IOC的整个流程中新增的一个扩展点而已,在BeanPostProcessor的后置处理方法
AOP的意思是面向切面编程,主要应用于事务处理、日志管理、权限控制等,减少重复代码,降低耦合度,并有利于程序扩展和维护;
Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么就会使用JDK动态代理;而对于没有实现接口的对象,就使用CGlib动态代理。
2、SpringAOP与AspectJAOP区别
Spring AOP 属于运行时增强,基于代理(Proxying);而 AspectJ 是编译时增强,基于字节码操作。Spring AOP已经集成了AspectJ。AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。
3、AOP的常见概念
- 切面(Aspect):可以将@Aspect注解标注在类上,表示这个类是切面,切面中存放的是共公的代码。切面由通知和连接点组成
- 通知(Advice):在切面的某个特定的连接点上执行的动作,可以理解为切面类中的方法,是切面的具体实现。
- 连接点(Join Point):指的是在什么方法上添加公共代码
- 目标对象(Target Object):是指所有被通知的对象,也被称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。
- 代理(Proxy):将通知应用到目标对象之后,被动态创建的对象。
- 织入(Weaving):将切面代码插入到目标对象上,从而生成代理对象的过程。
总结:
在目标对象的某些方法(连接点)添加不同种类的操作(通知),最后通过织入操作实现一个新的代理目标对象
4、通知类型
通知的分类:
执行顺序:
-
无异常情况下:
环绕前通知——》前置通知——》执行业务代码——》环绕后通知——》后置通知——》返回通知
-
有异常情况下:
环绕前通知——》前置通知——》执行业务代码——》环绕后通知——》后置通知——》异常发生——》异常通知
5、AOP的实现方式
- 静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;(AspectJAOP)
- 编译时编织(特殊编译器实现)
- 类加载时编织(特殊的类加载器实现)。
- 动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
- JDK 动态代理是 Java 语言自带的功能,无需通过加载第三方类实现;
- JDK Proxy 是通过拦截器加反射的方式实现的;
- JDK Proxy 只能代理实现接口的类;
- CGLIB
- CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;
- CGLib 无需通过接口来实现,它是针对类实现代理,主要是对指定的类生成一个子类,它是通过实现子类的方式来完成调用的。因为采用的是继承,所以不能对 final 修饰的类进行代理。
- JDK 动态代理是 Java 语言自带的功能,无需通过加载第三方类实现;
四、Spring事务
1、Spring中事务的管理方式
- 编程式事务 : 在代码中硬编码(不推荐使用) : 通过
TransactionTemplate
或者TransactionManager
手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。 - 声明式事务 : 在 XML 配置文件中配置或者直接基于注解(推荐使用) : 实际是通过 AOP 实现(基于
@Transactional
的全注解方式使用最多)
2、@Transactional
属性:
作用范围:
1、方法 :推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,否则不生效。
2、类 :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
3、接口 :不推荐在接口上使用。
实现原理:
@Transactional
的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。
如果一个类或者一个类中的 public 方法上被标注@Transactional
注解的话,Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional
注解的 public 方法的时候,实际调用的是,TransactionInterceptor
类中的 invoke()
方法。这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。
自调用问题:
若同一类中的其他没有 @Transactional
注解的方法内部调用有 @Transactional
注解的方法,有@Transactional
注解的方法的事务会失效。
这是由于Spring AOP
代理的原因造成的,因为只有当 @Transactional
注解的方法在类以外被调用的时候,Spring 事务管理才生效。
失效场景:
3、Spring事务中的隔离级别
- 默认:使用后端数据库默认的隔离级别,Mysql默认采用可重复读;Oracle默认采用读已提交
- 读未提交:事务尚未提交,其他事务即可以看到该事务的修改结果。隔离级别最差,脏读、不可重复读、幻读都不能避免。
- 读已提交:事务只能看到其他事务提交之后的数据。可避免脏读,不可重复读、幻读无法避免。
- 可重复度:一个事务多次查询,无论其他事务对数据如何修改,看到的数据都是一致的。可避免脏读、不可重复读,幻读无法避免。
- 序列化:事务顺序执行,可避免脏读、不可重复读、幻读,但效率最差。因为A事务执行时,其他事务必须等待。
4、Spring中事务传播行为
支持当前事务的情况:
- required(默认):如果当前没有事务,则创建一个新的事务。如果当前存在事务,则加入该事务
- supports: 当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- mandatory: 当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
不支持当前事务的情况:
- required_new: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- not_supported: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- never: 以非事务方式运行,如果当前存在事务,则抛出异常。
其他情况:
- nested: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则等价于required
required_new与nested区别:required_new是新建一个事务并且新开始的这个事务与原有事务无关,而nested则是当前存在事务时会开启一个嵌套事务,在nested情况下,父事务回滚时,子事务也会回滚,而required_new情况下,原有事务回滚,不会影响新开启的事务。
nested和required的区别:required情况下,调用方存在事务时,则被调用方和调用方使用同一个事务,那么被调用方出现异常时,由于共用一个事务,所以无论是否catch异常,事务都会回滚,而在nested情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不会回滚。
五、SpringMVC
1、对Spring MVC的了解
MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。
MVC是一种设计模式,而Spring MVC是MVC模式所实现的框架。
Spring MVC可以帮助我们进行更简洁的Web层的开发,并且它天生与Spring框架集成。
Spring MVC下我们一般把后端项目分为Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。
2、SpringMVC的核心组件
1、DispatcherServlet(
核心的中央处理器)
:负责接收请求、分发,并给予客户端响应。
2、HandlerMapping(
处理器映射器)
:根据 uri 去匹配查找能处理的 Handler
,并会将请求涉及到的拦截器和 Handler
一起封装。
3、HandlerAdapter(
处理器适配器)
:根据 HandlerMapping
找到的 Handler
,适配执行对应的 Handler
;
4、Handler(
请求处理器)
:处理实际请求的处理器,也就是我们平常说的 Controller
控制器。
5、ViewResolver(
视图解析器):
根据 Handler
返回的逻辑视图,解析并渲染真正的视图,并传递给 DispatcherServlet
响应客户端
3、Spring MVC的工作原理
流程说明(重要):
1、客户端(浏览器)发送请求, DispatcherServlet
拦截请求。
2、DispatcherServlet
根据请求信息调用 HandlerMapping(处理器映射器)他
根据 uri 去匹配查找能处理的 Handler并返回给DispatcherServlet
(也就是我们平常说的 Controller
控制器)。
3、DispatcherServlet
调用 HandlerAdapter
适配执行 Handler
;Handler
完成对用户请求的处理后,会返回一个 ModelAndView
对象给DispatcherServlet
。
ModelAndView
包含了数据模型以及相应的视图的信息。
4、DispaterServlet
会调用ViewResolver
根据逻辑 View
查找实际的 View
。
5、DispaterServlet
把返回的 Model
传给 View
进行渲染,并返回给DispaterServlet
。
6、DispaterServlet
把 View
返回给请求者(浏览器)
4、SpringMVC中参数传递方式
实体 Bean 接收请求参数:适用于 get 和 post 方式,Bean 的属性名称必须与请求参数名称相同。
@RequestMapping("/login")
public String login(@RequestBody User user) {
}
处理方法的形参接收请求参数:适用于 get 和 post 方式,直接把表单参数写在控制器类相应方法的形参中,即形参名称与请求参数名称完全相同。
@RequestMapping("/login")
public String login(String name, String pwd) {
}
HttpServletRequest 接收请求参数:适用于 get 和 post 方式,通过该类的getParameter等方法获取参数
@RequestMapping("/login")
public String login(HttpServletRequest request) {
String name = request.getParameter("name");
String pwd = request.getParameter("pwd");
}
@PathVariable 接收 URL 中的请求参数:
@RequestMapping("/login/{name}/{pwd}")
public String login(@PathVariable String name, @PathVariable String pwd) {
}
@RequestParam 接收请求参数:适用于 get 和 post 方式,在方法入参处使用此 注解指定其对应的请求参数
@RequestMapping("/login")
public String login(@RequestParam String name, @RequestParam String pwd) {
}
@ModelAttribute 接收请求参数:适用于 get 和 post 方式,将多个请求参数封装到一个实体对象中,从而简化数据绑定流程,而且自动暴露为模型数据,在视图页面展示时使用
@RequestMapping("/login")
public String login(@ModelAttribute("user") User user) {
}
5、统一异常的处理
推荐使用注解的方式统一异常处理,具体会使用到 @ControllerAdvice
+ @ExceptionHandler
这两个注解
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
//......
}
@ExceptionHandler(value = ResourceNotFoundException.class)
public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
//......
}
}
这种异常处理方式下,会给所有或者指定的 Controller
织入异常处理的逻辑(AOP),当 Controller
中的方法抛出异常的时候,由被@ExceptionHandler
注解修饰的方法进行处理。
六、SpringBoot
1、什么是SpringBoot
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,开发者能快速上手。
- 用来简化Spring应用的初始搭建以及开发过程,使用特定的方式来进行配置
- 创建独立的Spring引用程序main方法运行
- 嵌入的tomcat无需部署war文件
- 简化maven配置
- 自动配置Spring添加对应的功能starter自动化配置
- SpringBoot来简化Spring应用开发,约定大于配置,去繁化简
2、为什么使用SpringBoot
- 独立运行:内嵌了Tomcat、Jetty等服务器
- 简化配置:避免大量的Maven导入和各种版本冲突。
- 自动配置:Spring Boot 能根据当前类路径下的类、jar 包来自动配置 bean。
- 无代码生成和XML配置:
- 应用监控:提供一系列端点可以监控服务及应用,做健康检测。
3、SpringBoot的自动配置原理
启动类上面的注解是@SpringBootApplication,他也是SpringBoot的核心注解,主要组合包含了以下3个注解:
1、@SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能;
2、@EnableAutoConfiguration:打开自动配置的功能;内部实际上就去加载META-INF/spring.factories
文件的信息,然后筛选出以EnableAutoConfiguration为key的数据,结合@Condition条件注解去生成Bean并加入到IOC容器中
3、@ComponentScan:Spring组件扫描。
4、SpringBoot启动自动执行代码的方式
Java自身启动加载方式:
-
static代码块:在类加载的时候自动执行。
-
构造方法:在对象初始化的时候执行(在static后)
Spring启动加载方式:
- @PostConstruct注解:标注在方法上,这个方法在对象依赖注入初始化后执行。
- ApplicationRunner接口:实现ApplicationRunner接口,重写run方法。SpringBoot在启动时会自动执行run方法。
- CommandLineRunner接口:CommandLineRunner接口,重写run方法。SpringBoot在启动时会自动执行run方法。
默认加载顺序为:
static > 构造方法 > @PostConstruct > ApplicationRunner接口 > CommandLineRunner接口
5、SpringBoot的核心配置文件
SpringBoot的核心配置文件是application和bootstrap配置文件。
bootstrap的在application之前加载
application配置文件这个容易理解,主要用于Spring Boot项目的自动化配置。
bootstrap配置文件有以下几个应用场景:
- 使用Spring Cloud Config配置中心时,这时需要在bootstrap配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
- 一些固定的不能被覆盖的属性;
- 一些加密/解密的场景
6、Spring Boot Starter的理解
和自动配置一样,Spring Boot Starter的目的也是简化配置,而Spring Boot Starter解决的是依赖管理配置复杂的问题,有了它,当我需要构建一个Web应用程序时,不必再遍历所有的依赖包,一个一个地添加到项目的依赖管理中,而是只需要一个配置spring-boot-starter-web, 同理,如果想引入持久化功能,可以配置spring-boot-starter-data-jpa:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 也提供了其它的启动器项目,包括用于开发特定类型应用程序的典型依赖项。
spring-boot-starter-web-services - SOAP Web Services
spring-boot-starter-web - Web 和 RESTful 应用程序
spring-boot-starter-test - 单元测试和集成测试
spring-boot-starter-jdbc - 传统的 JDBC
spring-boot-starter-hateoas - 为服务添加 HATEOAS 功能
spring-boot-starter-security - 使用 SpringSecurity 进行身份验证和授权
spring-boot-starter-data-jpa - 带有 Hibernate 的 Spring Data JPA
spring-boot-starter-data-rest - 使用 Spring Data REST 公布简单的 REST 服务
7、spring-boot-starter-parent作用
我们知道,新建一个SpringBoot项目,默认都是有parent的,这个parent就是spring-boot-starter-parent,spring-boot-starter-parent主要有如下作用:
- 定义了Java编译版本
- 使用UTF-8格式编码
- 继承自spring-boor-dependencies,这里面定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号
- 执行打包操作的配置
- 自动化的资源过滤
- 自动化的插件配置
8、如何自定义Spring Boot Starter
- 实现功能
- 添加Properties
@Data
@ConfigurationProperties(prefix = "com.pdai")
public class DemoProperties {
private String version;
private String name;
}
- 添加AutoConfiguration
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {
@Bean
public com.pdai.demo.module.DemoModule demoModule(DemoProperties properties){
com.pdai.demo.module.DemoModule demoModule = new com.pdai.demo.module.DemoModule();
demoModule.setName(properties.getName());
demoModule.setVersion(properties.getVersion());
return demoModule;
}
}
- 添加spring.factory
在META-INF下创建spring.factory文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pdai.demospringbootstarter.DemoAutoConfiguration
- install
9、SpringBoot 打成jar和普通的jar区别
Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过java -jar xxx.jar命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。
Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 \BOOT-INF\classes目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
10、使用Spring Boot实现异常处理
Spring提供了一种使用ControllerAdvice处理异常的非常有用的方法。通过实现一个ControlerAdvice类,来处理控制类抛出的所有异常。
11、SpringBoot 实现热部署方式
主要有两种方式:
- Spring Loaded
- Spring-boot-devtools
12、Spring Boot中的监视器
Spring boot actuator是spring启动框架中的重要功能之一。Spring boot监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。
有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态。
13、Spring Boot 是否可以兼容老 Spring 项目吗
可以兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。
14、SringBoot导入配置文件数据方式
1、通过@ConfigurationProperties注解:先将一个类声明为配置类,然后在该类上增加此注解,通过此注解可以导入yml配置文件中的多个属性。
2、@Value:通过此注解可以导入yml配置文件中的单个属性。
3、@ImportResource:通过此注解可以导入多个xml的配置文件。
4、@Import:通过此注解可以导入多个配置类。
15、@ConfigurationProperties和@value的区别
1、@ConfigurationProperties作用在类上,@value作用在属性上;
2、@ConfigurationProperties主要用于多个字段的批量绑定,@value主要用单个字段的绑定;
3、@ConfigurationProperties支持松散绑定,@value不支持松散绑定;
4、@ConfigurationProperties支持所有类型的绑定(集合、对象、基本数据类型),@value只支持基本数据类型的绑定;