文章目录
Spring的生命周期
spring会基于你当前的类生成BeanDefintion
,然后实例化。 BeanDefinition描述了一个bean的实例
,包括属性值
,构造方法参数值和继承自它的类的更多信息,在Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition
结构存储
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
四步之间穿插的各种扩展点。如下
第一大类:影响多个Bean的接口
实现了这些接口的Bean会切入到多个Bean的生命周期
中。这些接口的功能非常强大,Spring内部扩展也经常使用这些接口,例如自动注入
以及AOP的实现
都和他们有关。
- BeanPostProcessor :
实例化阶段的前后
实例化前: 可以控制返回的对象。
实例化: 推断构造方法
实例化后: 可以控制是否调用set方法,true调用,false不调用。
@Component
public class MainConfig implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (beanName.equals("user")) {
System.out.println("实例化前");
}
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if ("user".equals(beanName)) {
System.out.println("实例化后");
}
return false;
}
- InstantiationAwareBeanPostProcessor
初始化阶段的前后
初始化前: 可以在初始化之前判断当前方法有无指定的注解,无指定可以选择终止之类的。
初始化后: 可以进行aop。自动注入等。
第二大类:只调用一次的接口
- Aware类型的接口 : 所有的Aware方法都是在初始化阶段之前调用的!比如
BeanNameAware
可以感知到bean在容器的位置并获取值。 - 生命周期接口:
InitializingBean
可以自定义初始化方法比如验证注入的值是否是你要的数据。通常很多框架底层跟spring整合都有使用。
当容器关闭时调用DisposableBean
实现是 通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy() 方法。对应生命周期的销毁阶段。
简单来说每个类实例的过程都进行切面增强功能,创建完交给容器管理,提高整体的扩展力。是spring的核心底层。
Bean的作用域
通过@Scope
可以设置
单例池、单例Bean、单例模式的区别:
- 单例Bean不表示Spring容器中只有一个Employee类型的Bean,可以创建多个不同名的Employee类型的Bean。
- 单例模式是指每次创建的对象都是同一个。
- 单例池底层是一个
ConcurrentHashMap
,第一次创建单例对象时,会把它存入ConcurrentHashMap,下次再创建该对象时,会直接从单例池中拿。
BenFactory与ApplicationContext的区别
- 如果使用
ApplicationContext
,如果配置的bean是单例,它都会被实例化。好处是可以预先加载,坏处是浪费内存。 - 当使用
BeanFactory
实例化对象时,配置的bean不会马上被实例化
,而是等到你使用该bean的时候(getBean)才会被实例化。好处是节约内存,坏处是速度比较慢。 - ApplicationContext继承了BenFactory能够实现更多功能,比如国际化,事件发布。
Bean线程安全问题
- 单例bean
不是线程安全的
,因为多个线程共用一个对象,但是通常我们开发使用bean都是无状态
的(不会保存数据)某种程度上是线程安全的。但是如果bean有了状态就要去修改bean的作用域保证线程安全了!
循环依赖问题
为什么会出现循环依赖问题?
- 在Spring中,一个对象不是简单
new
出来的,而是经历一系列的生命周期。因为这种特殊的创建方式才会产生循环依赖问题
。当然,在spring出现循环依赖是很常见,有的场景spring帮我们解决了,有的场景是需要我们自己解决。 - 两个对象在
填充属性时
互相依赖对方的bean,而每个bean的需要走完生命周期创建导致无限循环。
常见依赖场景
单例的循环依赖
@Component
public class AService {
@Autowired
private BService b;
}
@Component
public class BService {
@Autowired
private AService a;
}
spring采用缓存
来解决循环依赖问题。
- singletonObjects 一级缓存: 保存完整的bean实例。
- earlySingletonObjects 二级缓存: 半成品的bean。
- singletonFactories三级缓存: 函数式接口判断时什么类型的bean,暴露对象提前代理。
spring是如何解决循环依赖的:
- A,B循环依赖,在原有的基础上先暴露一个半成品A
也就是加入到三级缓存
(三级缓存中的函数式接口判断是否被代理,返回对应类型的对象)供其他对象使用,再去初始化B,初始化B如果发现依赖于A也就是循环依赖就会注入
三级缓存中的半成品
的A,然后加入二级缓存
,填充其他属性,从二级缓存取出数据,放入单例池,完成初始化,移除缓存数据(保持单例
)。
为什么要存在三级缓存?
- 如果只有二级缓存会导致进行
AOP
拿到的都是普通对象,不能完成AOP
的功能。 - 主要是可以解决循环依赖对象需要提前被
aop
代理,动态判断拿取对应的对象。
其他场景手动解决方案
@DependsOn
注解,指定加载先后关系。- 使用
@Lazy
注解,延迟加载。
SpringMVC
执行流程
- 用户发送出请求到前端控制器
DispatcherServlet
。 - DispatcherServlet收到请求调用
HandlerMapping
(处理器映射器)。 HandlerMapping
找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。(HandlerMappeing帮你找到HelloController类,并创建对象)- DispatcherServlet调用
HandlerAdapter
(处理器适配器)。 - HandlerAdapter经过适配调用具体的处理器(Handler/Controller)(调用具体的方法)。
- Controller执行完成返回ModelAndView对象。
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
- ViewReslover解析后返回具体View(视图)。
- DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
- DispatcherServlet响应用户。
事务
事务管理方式
- 程序化事务管理: 在此过程中,在编程的帮助下管理事务。它为您提供极大的灵活性,但维护起来非常困难。
- 声明式事务管理: 在此,事务管理与业务代码分离。仅使用注解
@Transactional
或基于 XML的配置来管理事务。
可以指定方法需要的隔离级别,传播行为,超时。
传播行为
- PROPAGATION_REQUIRED(默认): 若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。
- PROPAGATION_REQUIRES_NEW: 总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
- PROPAGATION_SUPPORTS: 指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。