Spring
一、单例Bean
@Scope
singleton:bean在每个Spring IOC容器中只有一个实例
prototype:一个bean的定义可以有多个实例
线程安全
不是线程安全的,如果在bean中定义了可修改的成员变量,需要考虑线程安全问题,可以使用多例或者加锁来解决
Spring bean并没有可变的状态(比如Service和DAO类),所以在某种程度上来说Spring的单例是线程安全的
AOP
概念:AOP称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和罗技,抽取并封成一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块的耦合度,同时提高了系统的可维护性。事务的底层使用了AOP,AOP底层是动态代理。
常见的AOP使用场景
记录操作日志
缓存处理
Spring中的内置事务处理
Spring中的事务
编程式事务控制
使用TransactionTemplate来实现,对业务代码有侵入性,项目中很少使用
声明式事务控制
声明式事务管理建立在AOP之上,本质是通过AOP功能对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法之前加入一个事物,在执行完目标方法后根据执行情况进行提交或者回滚
二、Spring事务失效的场景
异常捕获处理
@Transactional管理的方法中,多次操作中间存在异常并通过try catch进行了捕获,事务通知只有捕捉到目标抛出的异常才能进行后续的事务回滚处理,如果目标自己处理掉了异常,事务通知无法获悉则会导致失效
解决方法:在catch块内将异常抛出
抛出异常检查
@Transactional管理的方法中,多次操作中间存在检查异常(如:FileNotFountException),由于Spring默认回滚非检查异常,所以事务会失效
解决方法:配置rollbackFor属性,@Transactional(rollbackFor=Exception.class)
非public方法
@Transactional管理的方法中,方法的访问权限不是public,由于Spring为方法创建代理、添加事务通知、前提条件都是该方法是public,所以会导致事务失效
解决方案:改为public方法
三、Bean的生命周期
BeanDefinition
概念:Spring容器在进行实例化时,会将xml配置的的信息封装成一个BeanDefinition对象, Spring根据BeanDifinition来创建Bean对象,里面有很多属性来描述Bean
属性方法
getBeanClassName():获取Bean的类名,用于通过反射创建对象
getInitMethodName():获取初始化方法的名称
getPropertyValues():获取Bean的属性值
getScope():获取作用域
isLazyInit():是否延迟初始化
流程
四、循环引用
流程
三级缓存解决循环依赖
概念
一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
二级缓存:缓存早期的bean对象(生命周期还没走完)
三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的
非代理对象(一级缓存和二级缓存)
代理对象(三级缓存)
构造方法循环依赖
无法通过三级缓存的方法来解决循环依赖问题,解决方法是在构造方法上,参数类型前加上注解@Lazy懒加载,需要对象的时候才进行实例化
五、SpringMvc的执行流程
视图阶段
流程
1、用户发出请求到前端控制器DispatcherServlet
2、DispacherServlet收到请求调用HandlerMapping(处理器映射器)
3、HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器,再一起返回给DispatcherServlet
4、DispatcherServlet调用HandlerAdapter(处理器适配器)
5、HandleAdapter经过适配调用具体的处理器(Handler/Controller)
6、Controller执行完成后返回ModelAndVeiw对象
7、HandleAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
9、ViewReslover解析后返回具体Veiw视图
10、DispatcherServlet根据View进行渲染视图
11、DispatcherServlet响应用户
前后端分离阶段(接口开发、异步)
在这里插入图片描述
流程
1、用户发出请求到前端控制器DispatcherServlet
2、DispacherServlet收到请求调用HandlerMapping(处理器映射器)
3、HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器,再一起返回给DispatcherServlet
4、DispatcherServlet调用HandlerAdapter(处理器适配器)
5、HandleAdapter经过适配调用具体的处理器(Handler/Controller)
6、方法上添加了@RequestBody
7、通过HttpMessageConverter返回结果转换为JSON并响应
六、自动配置原理
@SpringBootConfiguration:与@Configuration注解作用相同,用来声明当前类是一个配置类
@ComponentScan:组件扫描注解,默认扫描当前引导类所在包及其子包
@EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解(@Import({AutoConfigurationImportSelector.class})导入自动配置选择器:导入META-INF/spring.factories 内的自动配置类,根据条件注解将类添加到Spring容器中)
常见注解
Spring
@Component、@Controller、@Service、@Repository:使用在类上,用于实例化Bean
@Autowired:使用在字段上,用于根据类型依赖注入
@Qualifier:结合@Autowired一起使用,用于根据名称进行依赖注入
@Scope:标注Bean的作用范围
@Configuration:指定当前类是一个配置类,当创建容器时会从该类上加载注解
@ComponentScan:用于指定Spring在初始化容器时要扫描的包
@Bean:使用在方法上,标注该方法的返回值存储到Spring容器中
@Import:使用@Import导入的类会被Spring加载到IOC容器中
@Aspect、@Befor、@After、@Around、@Pointcut:用于AOP切面编程
SpringMvc
@RequestMapping:用于映射请求路径,可以定义在类上和方法上。用于类上,表示类中的所有方法,都以该地址作为父路径
@RequestBody:注解实现接收http请求的json数据,将json转为java对象
@RequestParam:指定请求参数的名称
@PathViriable:从请求路径中获取请求参数传递给方法的形式参数
@RequestBody:注解实现将controller方法返回对象转为json对象响应给客户端
@RequestHeader:获取指定的请求头数据
@RestController:@Controller+@RespnseBody
SpringBoot
@SpringBootConfiguration:与@Configuration注解作用相同,用来声明当前类是一个配置类
@ComponentScan:组件扫描注解,默认扫描当前引导类所在包及其子包
@EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解(@Import({AutoConfigurationImportSelector.class})导入自动配置选择器:导入META-INF/spring.factories 内的自动配置类,根据条件注解将类添加到Spring容器中)
七、场景模拟
1、Spring框架中的单例bean是线程安全的吗?
不是线程安全的,是这样的。
当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求对应的业务逻辑(成员方法),如果该处理逻辑中有对该单列状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。
比如:我们通常在项目中使用的Spring bean都是不可可变的状态(比如Service类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。
如果你的bean有多种状态的话(比如 View Model对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用由“singleton”变更为“prototype”。
2、什么是AOP
aop是面向切面编程,在spring中用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取公共模块复用,降低耦合,一般比如可以做为公共日志保存,事务处理等
3、你们项目中有没有使用到AOP
我们当时在后台管理系统中,就是使用aop来记录了系统的操作日志
主要思路是这样的,使用aop中的环绕通知+切点表达式,这个表达式就是要找到要记录日志的方法,然后通过环绕通知的参数获取请求方法的参数,比如类信息、方法信息、注解、请求方式等,获取到这些参数以后,保存到数据库
4、Spring中的事务是如何实现的
spring实现的事务本质就是aop完成,对方法前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
5、Spring中事务失效的场景有哪些
第一个,如果方法上异常捕获处理,自己处理了异常,没有抛出,就会导致事务失效,所以一般处理了异常以后,别忘了跑出去就行了
第二个,如果方法抛出检查异常,如果报错也会导致事务失效,最后在spring事务的注解上,就是@Transactional上配置rollbackFor属性为Exception,这样别管是什么异常,都会回滚事务
第三,我之前还遇到过一个,如果方法上不是public修饰的,也会导致事务失效
嗯,就能想起来那么多
6、Spring的bean的生命周期
首先会通过一个非常重要的类,叫做BeanDefinition获取bean的定义信息,这里面就封装了bean的所有信息,比如,类的全路径,是否是延迟加载,是否是单例等等这些信息
在创建bean的时候,第一步是调用构造函数实例化bean
第二步是bean的依赖注入,比如一些set方法注入,像平时开发用的@Autowire都是这一步完成
第三步是处理Aware接口,如果某一个bean实现了Aware接口就会重写方法执行
第四步是bean的后置处理器BeanPostProcessor,这个是前置处理器
第五步是初始化方法,比如实现了接口InitializingBean或者自定义了方法init-method标签或@PostContruct
第六步是执行了bean的后置处理器BeanPostProcessor,主要是对bean进行增强,有可能在这里产生代理对象
最后一步是销毁bean
7、Spring中的循环引用
循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A
循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部分的循环依赖
①一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
②二级缓存:缓存早期的bean对象(生命周期还没走完)
③三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的
那具体解决流程清楚吗?
第一,先实例A对象,同时会创建ObjectFactory对象存入三级缓存singletonFactories
第二,A在初始化的时候需要B对象,这个走B的创建的逻辑
第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存singletonFactories
第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象同时存入二级缓存,这个是有两种情况,一个是可能是A的普通对象,另外一个是A的代理对象,都可以让ObjectFactory来生产对应的对象,这也是三级缓存的关键
第五,B通过从通过二级缓存earlySingletonObjects 获得到A的对象后可以正常注入,B创建成功,存入一级缓存singletonObjects
第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A创建成功存入一次缓存singletonObjects
第七,二级缓存中的临时对象A清除
8、构造方法出现了循环依赖怎么解决?
由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的的依赖注入,可以使用@Lazy懒加载,什么时候需要对象再进行bean对象的创建
9、SpringMVC的执行流程知道嘛
1、用户发送出请求到前端控制器DispatcherServlet,这是一个调度中心
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
当然现在的开发,基本都是前后端分离的开发的,并没有视图这些,一般都是handler中使用Response直接结果返回
10、Springboot自动配置原理
在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
-
@SpringBootConfiguration
-
@EnableAutoConfiguration
-
@ComponentScan
其中@EnableAutoConfiguration
是实现自动化配置的核心注解。
该注解通过@Import
注解导入对应的配置选择器。关键的是内部就是读取了该项目和该项目引用的Jar包的的classpath路径下 ”META-INF/spring.factories” 文件中的所配置的类的全类名。
在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
一般条件判断会有像@ConditionalOnClass
这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
11、Spring 的常见注解有哪些?
第一类是:声明bean,有@Component、@Service、@Repository、@Controller
第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resourse
第三类是:设置作用域 @Scope
第四类是:spring配置相关的,比如@Configuration,@ComponentScan 和 @Bean
第五类是:跟aop相关做增强的注解 @Aspect,@Before,@After,@Around,@Pointcut
12、SpringMVC常见的注解有哪些?
有@RequestMapping:用于映射请求路径;
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象;
@RequestParam:指定请求参数的名称;
@PathViriable:从请求路径下中获取请求参数(/user/{id}),传递给方法的形式参数;@ResponseBody:注解实现将controller方法返回对象转化为json对象响应给客户端。@RequestHeader:获取指定的请求头数据,还有像@PostMapping、@GetMapping这些。
13、Springboot常见注解有哪些?
Spring Boot的核心注解是@SpringBootApplication , 他由几个注解组成 :
- @SpringBootConfiguration: 组合了- @Configuration注解,实现配置文件的功能;
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项
- @ComponentScan:Spring组件扫描