Spring的单例bean不是线程安全的。
成员变量和成员方法需要考虑线程安全的问题。
局部变量一般是没有线程问题的。
Spring的bean中例如Service类和DAO类是无状态的类。
不能被修改的类就是无状态的类,所以说无状态的类是线程安全的。
总结:
不一定线程安全。
Spring框架中有一个@Scope注解,默认值就是singleton,单例的。
一般在spring的bean中都是注入无状态的类,这样是没有线程安全问题的,如果在bean中定义了可修改的成员变量,就要考虑线程安全了,可以使用多例或者加锁来解决。
面向切面编程,将那些对多个对象产生影响的公共行为和逻辑,抽取出来封装成一个可重用的模块,这个模块就叫做“切面”。
底层是由动态代理实现的。
优点:减少重复代码,降低模块间的耦合度,提高了可维护性。
常用场景:记录操作日志,缓存处理,spring内置的事务管理。
spring中事务是如何实现的?
spring支持编程式事务和声明式事务。
编程式事务对业务代码有侵入性,所以项目很少使用。
大多都使用声明式事务,本质就是使用aop对方法进行拦截,在方法前后以及异常进行事务管理的功能增强。
总结:
什么是AOP?
面向切面编程,将一些跟业务无关,但是却对多个对象产生影响的公共行为和逻辑,抽取出来生成公共模块进行复用,降低耦合度。
项目中的场景应用?
记录操作日志,缓存处理,spring的事务管理。
一般都是用环绕通知Around和切点表达式pointCut(用来寻找方法)实现。
spring的事务如何实现的?
就是通过aop,对方法进行功能增强,在方法执行前开启事务,在方法结束后或异常时回滚提交事务。
异常捕获处理
在方法中已经将异常捕获处理掉并没有抛出。
事务只有捕捉到了抛出的异常才可以进行处理,如果有异常业务中直接捕获处理掉没有抛出,事务是无法感知到的。
解决:在catch块throw抛出异常。
抛出检查异常
spring默认只会回滚非检查异常。
解决:
非public方法导致事务失效
spring为方法创建代理,添加事务通知,前提条件都需要方法是public的。
解决:把方法改为public。
总结:
异常捕获处理,自己处理了异常,没有抛出,解决:手动抛出。
抛出检查异常,配置rollbackFor属性为Exception.class。
非public方法导致的事务失效,改为public。
Spring容器会在进行实例化时,将xml配置的
beanClassName:bean的类名
initMethodName:初始化方法名称
properryValues:bean的属性值
scope:作用域
lazyInit:延迟初始化
最后销毁bean
bean的创建和赋值是分开的
总结
通过BeanDefinition获取bean的定义信息
调用构造函数实例化bean
bean的依赖注入
实现Aware接口(BeanNameAware,BeanFactoryAware,ApplicationContextAware)
Bean的后置处理器BeanPostProcessor-前置
初始化方法(InitializingBean,init-method)
Bean的后置处理器BeanPostProcessor-后置
销毁bean
Spring的三级缓存
一级缓存 singletonObjects 单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象。
二级缓存 earlySingletonObjects 缓存早期的bean对象(生命周期还没走完)
三级缓存 singletonFactories 缓存ObjectFactory,表示对象工厂,用来创建某个对象。
总结
循环依赖就是两个或者两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A。
循环依赖在spring中是允许存在的,spring框架依据三级缓存已经解决了大部分的循环依赖。
一级缓存 singletonObjects 单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象。
二级缓存 earlySingletonObjects 缓存早期的bean对象(生命周期还没走完)
三级缓存 singletonFactories 缓存ObjectFactory,表示对象工厂,用来创建某个对象。
首先实例化A—》原始对象A生成一个ObjectFactory对象,存入三级缓存—》需要注入B但是B不存在,就去实例化B—》原始对象B生成一个ObjectFactory对象,存入三级缓存—》需要注入A,就去三级缓存中获取—》通过A的ObjectFactory对象创建代理对象,存入二级缓存—》将A的代理对象注入给B—》B创建成功,存入一级缓存—》将B注入给A—》A创建成功,存入一级缓存。
构造方法出现循环依赖怎么办?
由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的依赖注入。
解决:使用@Lazy进行懒加载,什么时候需要对象什么时候再创建bean对象。
JSP版
用户发送出请求到前端控制器DispatcherServlet。
前端控制器收到请求后调用处理器映射器HandlerMapping。
处理器映射器根据路径找到具体的处理器的类名和方法名,生成处理器对象(如果有拦截器就会一起封装到其中),返回给DispatcherServlet。
前端控制器调用处理器适配器HandlerAdapter。
处理器适配器结果适配调用具体的处理器。
执行完后将ModelAndView对象返回给处理器适配器,处理器适配器将它返回给前端控制器。
前端控制器将模型和视图对象传给视图解析器ViewReslover。
视图解析器解析后将具体的View返回给前端控制器。
前端控制器根据View进行渲染。
最后将数据和视图响应给用户。
前后端分离版
用户发送请求到前端控制器。
前端控制器收到请求后将调用处理器映射器
处理器映射器根据路径找到具体的处理器类名和方法名,然后生成处理器对象,如果有拦截器也会将拦截器一起封装,最后返回给前端控制器。
前端控制器调用处理器适配器。
处理器适配器经过适配调用具体的处理器。
会将方法上添加了@ResponseBody的返回值通过HttpMessageConverter来将返回值传换成JSON返回给前端控制器,然后前端控制器进行响应用户。
在springboot项目中的引导类上有一个注解@SpringBootApplication,这个注解对三个注解进行了封装,分别是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其中@EnableAutoConfiguration是实现自动化配置的核心注解。这个注解通过@Import注解将对应的配置导入。
内部就是读取了该项目和该项目引用的jar包的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。在这些配置类中所定义的Bean会根据条件注解所指定的条件来决0是否将其导入到Spring容器中。
条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。