spring的Bean的生命周期
阶段1: 处理名称,检查缓存
- 先把别名解析为实际名称,再进行后续处理
- 若要FactoryBean本身,需要使用&名称获取
- singletonObjects是一级缓存,放单例成品对象
- singletonFactories是三级缓存,放单例工厂
- earlySingletonObjects是二级缓存,放单例工厂的产品,可称为提前单例对象
阶段2:处理父子容器
- 父子容器的bean名称可以重复
- 优先找子容器的bean,找到了直接返回,找不到继续到父容器找
阶段3:dependsOn
1.dependsOn用在非显式依赖的bean的创建顺序控制
阶段4: 按Scope创建bean
- scope理解为从xxx范围内找这个bean更加贴切
- singleton scope表示从单例池范围内获取bean,如果没有,则创建并放入单例池
- prototype scope表示从不缓存bean,每次都创建新的
- request scope 表示从request对象范围内获取bean,如果没有,则创建并放入request
阶段5-1:创建bean实例
- AutowiredAnnotationBeanPostProcessor选择构造
优先选择带@Autowired注解的构造
若有唯一的带参构造,也会入选
2 . 采用默认构造
如果上面的后处理器和BeanDefiniation都没找到构造,采用默认构造,即使是私有的
阶段5-2:依赖注入
- AutowiredAnnotationBeanPostProcessor(注解匹配)
识别@Autowired及@Value标注的成员,封装为InjectionMetadata进行依赖注入
2.CommonAnnotationBeanPostProcessor(注解匹配)
识别@Resource标注的成员,封装为InjectionMetadata进行依赖注入
3.AUTOWIRE_BY_NAME(根据名字匹配)
根据成员名字找bean对象,修改mbd的propertyValues,不会考虑简单类型的成员
4.AUTOWIRE_BY_TYPE(根据类型匹配)
根据成员类型执行resolveDependency找到依赖注入的值,修改mod的propertyValues
5.applyPropertyValues(即xml中<property name ref|value/>)(精确指定)
根据mbd的propertyValues进行依赖注入
小插曲,问多种配置bean,那种优先级高?
- 优先级最高的:精确指定注入bean的名称
<property name ="bean3" ref ="bean2"/>
- 优先级次之的:通过AUTOWIRE_BY_NAME匹配
- 优先级最低的:@Autowired匹配
阶段5-3: 初始化
1.内置Aware接口的装配
包括BeanNameAware,BeanFactoryAware等
2.内置Aware接口的装配
由ApplicationContextAwareProcessor解析,执行时机在postProcessBeforeInitialzation
3.@PostConstruct
由CommonAnnotationBeanPostProcessor解析,执行时机在postProcessBeforeInitialization
4.InitializingBean
通过接口回调执行初始化
5.initMethod(即
根据BeanDefinition得到的初始化方法执行初始化
6.创建aop代理
由AnnotationAwareAspectJAutoProxyCreator创建,执行时机在postProcessAfterInitialization
它们的执行顺序
1.Aware接口->@PostConstruct->InitializingBean->initMethod
阶段5-4:注册可销毁bean
判断依据
- 如果实现了DisposableBean或AutoCloseable接口,则为可销毁bean
- 如果自定义了destroyMethod,则为可销毁bean
- 如果采用@Bean没有指定destroyMethod,则采用自动推断方式获取销毁方法名(close,shutdown)
- 如果有@PreDestroy标注的方法
存储位置
- singleton scope的可销毁bean会存储beanFactory的成员当中
- 自定义scope的可销毁bean会存储于对应的域对象当中
- prototype scope不会存储,需要自己找到此对象销毁
存储时都会封装为DisposableBeanAdapter类型对销毁方法的调用进行适配
阶段6:类型转换
- 如果getBean的requiredType参数与实际得到的对象类型不同,会尝试进行类型转换
阶段7:销毁bean
- singleton bean的销毁在ApplicationContext.close时,此时会找到所有DisposableBean的名字,逐一销毁
- 自定义scope bean的销毁在作用域对象生命周期结束时
- prototype bean 的销毁可以通过自己手动调用AutowireCapableBeanFactory.destroyBean方法执行销毁
- 同一bean中不同形式销毁方法的调用次序
优先后处理器销毁,即@PreDestroy
其次DisposableBean接口销毁
最后destroyMethod销毁(包括自定义名称,推断名称,AutoCloseable接口多选一)
Spring事务失效的几种场景及原因
抛出检查异常导致事务不能正确回滚
原因:Spring默认只会回滚非检查异常
解法:配置rollbackFor属性
业务方法内自己try-catch异常导致事务不能正确回滚
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理异常,事务通知无法知悉
解法1:异常原样抛出
解法2:手动设置TransactionStatus.setRollbackOnly()
aop切面顺序导致事务不能正确回滚
原因:事务顺序优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常…
解法:同情况2
非public方法导致的事务失效
原因: Spring为方法创建代理,添加事务通知,前提条件都是该方法是public的
解法:改为public方法
父子容器导致的事务失效
原因:子容器扫描范围过大,把未加事务配置的service扫描进来
解法1:各扫描各的,不要图简便
解法2:不要用父子容器,所有bean放在同一容器
调用本类方法调用不经过代理,因此无法增强
原因:本类方法调用不经过代理,因此无法增强
解法1:依赖注入自己(代理)来调用
解法2:通过AopContext拿到代理对象,来调用
解法3:通过CTW,LTW实现功能增强
@Transactional没有保证原子行为
原因:事务的原子性仅涵盖insert,update,delete,select…for update,select方法并不阻塞
@Transactional方法导致的synchronized失效
原因:synchronized保证的仅是目标方法的原子性,环绕目标方法的还有commit等操作,它们并未处于sync块内
SpringMVC执行流程
初始化阶段:
- 在Web容器第一次用到DispatcherServlet的时候,会创建其对象并执行init方法
- init方法内会创建Spring Web容器,并调用容器refresh方法
- refresh过程中会创建并初始化SpringMVC中的重要组件,例如MultipartResolver,HandlderMapping,HandlerAdapter,HandlerExceptionResolver,ViewResolver等
- 容器初始化后,会将上一步初始化好的重要组件,赋值给DispatcherServlet的成员变量,留待后用
匹配阶段:
用户发送的请求统一到达前端控制器DispatcherServlet
DispatcherServlet遍历所有HandlerMapping,找到与路径匹配的处理器
HandlerMapping有多个,每个HandlerMapping会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别@RequestMapping的优先级最高
对应@RequestMapping的处理器是HandlerMethod,它包含了控制器对象和控制器方法信息
其中路径与处理器的映射关系在HandlerMapping初始化时就会建立好
将HandlerMethod连同匹配到的拦截器,生成调用链对象HandlerExecutionChain返回
遍历HandlerAdapter处理器适配器,找到能处理HandlerMethod的适配器对象,开始调用
执行阶段:
执行拦截器preHandle
由HandlerAdapter调用HandlerMethod
调用前处理不同类型的参数
调用后处理不同类型的返回值
第2步没有异常
返回ModelAndView
执行拦截器postHandle方法
解析视图,得到View对象,进行视图渲染
第2步有异常,进入HandlerExceptionResolver异常处理流程
最后都会执行拦截器的afterCompletion方法
如果控制器方法标注了@ResponseBody注解,则在第2步,就会生成json结果,并标记ModelAndView已处理,这样就不会执行第3步的视图渲染
SpringBootApplication
@SpringBootConfiguration
@ComponentScan
excludeFilters-用来在组件扫描时进行排除,也会排除自动配置类
@EnableAutoConfiguration
@AutoConfigurationPackage -用来记住扫描的起始包
@Import(AutoConfigurationImportSelector.class)用来加载META-INf/spring.factories中的自动配置类
SpringBoo的启动
- springboot是通过main方法下的SpringApplication.run方法启动的
- 启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式祖册bean
- 而它是通过启动类的SpringBootApplication注解进行开始解析的
- 他会根据EnableAutoConfiguration开启自动化配置
- 里面有个核心方法ImportSelect选择性的导入
- 根据loadFanctoryNames根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置
- 他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器
创建代理
最基本的切面是Advisor,一个Aspect切面对应一到多个Advisor
最基本的Advice是MethodInterceptor,其他Advice最终都将适配为MethodInterceptor
创建代理的方式
实现了用户自定义接口,采用jdk动态代理
没有实现用户自定义接口,采用cglib代理
设置了setProxyTargetClass(true),统一采用cglib代理
切面,切点,通知等不会被代理
AnnotationAwareAspectJAutoProxyCreator调用时机:创建阶段,依赖注入阶段,初始化阶段
循环依赖
单例set方法(包括成员变量)循环依赖,Spring会利用三级缓存解决,无需额外配置
- 一级缓存存放成品对象
- 二级缓存存放发生了循环依赖时的产品对象(可能是原始bean,也可能是代理bean)
- 三级缓存存放工厂对象,发生循环依赖时,会调用工厂获取产品
- Spring期望在初始化时创建代理,但如果发生了循环依赖,会由工厂提前创建代理,后续初始化就不必重复创建代理
- 二级缓存的意义在于,如果提前创建了代理对象,在最后的阶段需要从二级缓存中获取此代理对象,作为最终结果
构造方法及多例循环依赖解决办法
- @Lazy
- @Scope
- ObjectFactroy & ObjectProvider
- Provider