背景介绍:
服务端期望使用 面向对象编程, 和 spring 结合的话只能是通过 prototype 的 bean 定义,并通过 getBean 获取.
优雅停机探究:
代码说明:
1. 类关系 SimpleSpringTest PrototypeScopeBean
2. 执行流程: SimpleSpringTest内的方法通过getBean 获取prototype的 bean. 内有有线程池执行相关逻辑.
3. 两个类对实现了 spring 的关闭事件通知
4. 先上层SimpleSpringTest关闭连接,然后内部PrototypeScopeBean关闭线程shutdown.保证所有线程都执行完才退出. [ 否则由于内存无持久化,导致相关数据和流程丢失.分布式问题. 最好通过 mq 发送. ]
操作流程:
1. 启动代码
2.关闭代码
3. 正常结果: 按预期顺序关闭. 暂停3秒后,输出对应的关闭日志
异常:
1. 都打印出了对应的关闭日志.但是没有暂停3秒.
深入探究:
1. 对应的线程池里并没有执行 worker. 还以为我自己对线程池的 shutdown和 await 的理解错误了. 细细又看了一遍源码.还研究了下interruptIdleWorkers里的线程 interrupt方法,把对应线程都中断了. 但是runnable 的 sleep又没有打印出被interrupt的错误日志. 以为自己对interrupt 理解错了.又学习了一遍interrupt.
2. 最终发现原来的PrototypeScopeBean和 close 时的PrototypeScopeBean不是同一个 bean
3. 回过头看 spring 的关闭流程代码.
发现发起 event 时会回调
1. sington 实例,
2. get 新的 prototype bean
而我的线程池是非 static 的,导致出错. 无法完成优雅关闭.
xml 配置的都会生成新的 bean 定义.
发现了通知的 list,通过
defaultRetriever.applicationListeners 笔者注释: 实现了 listener 的sington 的实例.
NOTE: 如果 bean 有@scope 的 prototype 注解,但是 xml 里又配置了 <bean,id 不一致> 但是没有配置 scope. 会生成两个 beanDefinition,且一个是 sington,一个是 prototype. 注解不影响 xml 的属性. 单例的实例会主动生成.
defaultRetriever.applicationListenerBeans 笔者注释: 字符串 ,所有实现了listener的 beanDefinition Name
NOTE:每个在 annotation 注解 和 xml 配置解析后都会生成一个 beanDefinition. 只有sington才会生成直接生成实例. 可在 getBean(class)的代码里证明.
总结:
1. beanDefinition 何时生成?
id 是@service 的 value, xml 配置里的 id .一个sington 配置两遍,会有两个 beanDefinition. 注解解析也有. 同时生成. 如果 id 一致,xml 会覆盖注解.
2. 实例何时生成?
singoton: xml 定义解析时,注解解析时
prototype: getBean 时. 一旦有多个 beanDefinition,通过getBean(class)会报错 bean 数量不对.
3. spring 事件通知通知哪些对象?
1. 所有继承了 listener 的 sington 实例
2. 新建一个 prototype 的实例,并且调用. 所以 prototype 里被事件处理的东西最好是 static 的. 不然没有意义.这点面向对象写法容易出错,且容易内存泄露.新建大量的线程池.线程池何时被回收.?
4. prototype的特点是什么?
1. getBean("name")时 或者被注入autowired injected时会自动生成一个 bean
2. getBean(class) 在只有一个 beanDefinition 时不会抛错. 不要 xml 写了一个,注解又有一个.
NOTE: 这个接口会有锁,并发下性能比较差. 3.1.2
附录:
事件通知堆栈:
- "Thread-0"@2,180 in group "main": RUNNING
- onApplicationEvent():48, PrototypeScopeBean {com...test.unmock.test}
- onApplicationEvent():15, PrototypeScopeBean {com..test.unmock.test}
- multicastEvent():97, SimpleApplicationEventMulticaster {org.springframework.context.event}
- publishEvent():324, AbstractApplicationContext {org.springframework.context.support}
- doClose():1025, AbstractApplicationContext {org.springframework.context.support}
- run():958, AbstractApplicationContext$1 {org.springframework.context.support}
从线上问题谈spring生命周期类lifeCycle类和bean的生命周期