spring如何管理bean:核心流程与配置方式
spring如何管理bean,核心是通过IOC容器完成bean的定义解析、实例创建、属性填充、初始化、销毁全生命周期管控,支持xml、注解、javaConfig三种定义方式,你可以根据项目阶段选择,xml适合老项目维护,注解适合快速开发,javaConfig适合需要强类型编译校验的场景,该整套管理机制仅作用于交由spring容器托管的对象,手动new出来的对象不会进入这套生命周期,不受spring容器管控。
spring如何管理bean:bean的定义读取
容器启动时会先扫描你提供的配置源,把bean的元数据读取并封装成BeanDefinition对象,这个对象保存bean的类全限定名、作用域、依赖信息、初始化方法、销毁方法等全部配置,容器后续创建实例都以BeanDefinition作为依据。使用注解模式时,@Component、@Service、@Repository、@Controller都会标记类为bean,配合@ComponentScan指定扫描包路径,容器自动发现并生成BeanDefinition;xml模式依靠
作用域是这一步里很关键的配置项,不同作用域决定容器怎么复用bean实例。
| 作用域 | 实例创建时机 | 实例复用规则 |
|---|---|---|
| singleton | 容器启动(懒加载除外) | 整个容器仅一个实例 |
| prototype | 每次获取bean时 | 每次获取生成新实例 |
| request | 单次http请求 | 同一个请求内复用 |
| session | 单次用户会话 | 同一个session内复用 |
singleton是默认作用域,很多开发人员会误以为singleton对象是线程安全的,实际spring只保证实例唯一,不做线程同步处理,bean内部有可变成员变量时,并发访问容易出现数据错乱。
spring如何管理bean:实例化与依赖注入
拿到BeanDefinition之后,容器会选择构造方法、工厂方法两种途径完成实例化,构造注入优先匹配带@Autowired的构造,没有标记注解时优先使用无参构造。实例刚创建完成时,对象只是一块内存对象,属性还没有赋值,下一步进入依赖注入阶段,容器解析这个bean需要的其他bean,从容器中查找匹配实例,通过setter方法或者字段反射完成赋值。@Autowired默认按类型匹配,类型匹配到多个bean时,会结合bean名称二次匹配,你可以搭配@Qualifier指定bean名称消除歧义,@Primary用来设置优先选择的bean。
循环依赖是这个阶段高频遇到的问题,singleton模式下spring依靠三级缓存来解决setter注入产生的循环依赖,构造注入形式的循环依赖无法被自动处理,容器启动会直接抛出异常。
spring如何管理bean:初始化回调与后置处理
依赖注入全部完成后,进入初始化阶段,执行顺序固定,先执行@PostConstruct标记的方法,再执行InitializingBean接口的afterPropertiesSet方法,最后执行xml或者@Bean注解中指定的init-method。BeanPostProcessor会在初始化前后对bean做拦截处理,AOP代理对象就是在这个环节生成的,原生bean对象被替换成代理对象,后续从容器拿到的其实是代理实例。
这里有个容易混淆的点,BeanPostProcessor作用于所有bean,优先级高于普通bean,容器会优先完成所有BeanPostProcessor的实例化和初始化,再处理业务bean。
spring如何管理bean:销毁与容器关闭
singleton类型的bean会在容器关闭时触发销毁流程,prototype类型bean容器不会主动管理销毁,你需要自己编写代码回收资源。销毁顺序为先执行@PreDestroy标记的方法,再执行DisposableBean接口的destroy方法,最后执行配置里指定的destroy-method。容器关闭必须调用close()或者registerShutdownHook,只停止主线程不会触发销毁回调,数据库连接、线程池这类资源就无法正常释放。
bean的管理生命周期不能脱离容器,容器销毁之后,所有singletonbean都会失效,继续调用bean方法会抛出异常。
懒加载可以改变singletonbean的创建时机,@Lazy标记之后,bean不会在容器启动时实例化,等到第一次从容器获取这个bean的时候,才走完实例化、注入、初始化整套流程。
