前言在前面的文章中我们拆解了Spring的IoC、AOP、事务管理和设计模式。这些是Spring的核心机制。但Spring还有一个强大的能力扩展点——它提供了多个钩子让你可以在Bean创建的各个阶段插入自定义逻辑。面试中扩展点是区分会用Spring和能定制Spring的分水岭“BeanPostProcessor和BeanFactoryPostProcessor有什么区别”“InitializingBean和PostConstruct哪个先执行”“Aware接口是干什么用的”“如何优雅地批量注册Bean定义”本文拆解Spring中最重要的四个扩展点从执行时机到适用场景让你掌握随时介入Bean创建流程的能力。本文核心问题BeanPostProcessor和BeanFactoryPostProcessor有什么区别各自的执行时机是什么InitializingBean和PostConstruct有什么区别执行顺序是怎样的Aware接口家族分别可以在什么时机获取容器的哪些核心组件ImportBeanDefinitionRegistrar和ImportSelector各在什么场景下使用这些扩展点是如何协同工作的Bean从创建到初始化的完整扩展链是什么读完本文你将对Spring的扩展点拥有从原理到实战的完整理解。一、BeanFactoryPostProcessor——修改Bean的定义疑问BeanFactoryPostProcessor是什么它和BeanPostProcessor有什么区别回答BeanFactoryPostProcessor用于修改Bean的定义元数据在所有BeanDefinition加载完成之后、Bean实例化之前执行。BeanPostProcessor作用于Bean实例化之后用于修改实例本身。1.1 执行时机Spring容器启动流程 1. 扫描所有配置类Configuration、ComponentScan 2. 解析所有Bean、Component等 → 生成BeanDefinition 3. BeanFactoryPostProcessor执行 → 修改BeanDefinition ← 我们在这里 4. 遍历所有BeanDefinition → 实例化Bean 5. BeanPostProcessor执行 → 修改Bean实例 6. 容器启动完成BeanFactoryPostProcessor在定义已加载但实例还未创建的窗口期执行它可以修改BeanDefinition中的属性值、调整作用域、或者新增/删除Bean定义。1.2 典型应用覆盖属性占位符PropertySourcesPlaceholderConfigurer是BeanFactoryPostProcessor的一个实现它负责解析${}占位符。容器启动时所有BeanDefinition已经生成但Value注解的值还是${server.port}这种未解析的字符串。这个PostProcessor在此时扫描所有BeanDefinition将占位符替换为application.yml中的实际值然后Bean才开始实例化。1.3 典型应用强制指定数据源// 一个自定义的BeanFactoryPostProcessor强制将DataSource的实现类替换为DruidComponentpublicclassDataSourceOverridePostProcessorimplementsBeanFactoryPostProcessor{OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory){// 获取DataSource的BeanDefinitionBeanDefinitiondefinitionbeanFactory.getBeanDefinition(dataSource);// 覆盖为Druid连接池不管原始定义是什么实现类definition.setBeanClassName(com.alibaba.druid.pool.DruidDataSource);definition.getPropertyValues().add(initialSize,5);definition.getPropertyValues().add(maxActive,20);}}这个PostProcessor在Bean实例化之前强制修改了DataSource的BeanClassName和属性值。不管你原始配置里写的是什么数据源到这里都会被统一替换为Druid连接池。二、BeanPostProcessor——修改Bean的实例疑问BeanPostProcessor又是什么和BeanFactoryPostProcessor有什么区别回答BeanFactoryPostProcessor操作的是BeanDefinition元数据在实例化之前执行。BeanPostProcessor操作的是Bean实例在实例化之后、初始化前后执行。2.1 两个核心方法publicinterfaceBeanPostProcessor{// 在初始化方法PostConstruct / afterPropertiesSet之前执行defaultObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{returnbean;// 可以返回原Bean或代理对象}// 在初始化方法之后执行defaultObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{returnbean;// AOP代理对象就是在这里生成的}}2.2 AOP代理的生成时机Transactional、Cacheable、自定义AOP切面的代理对象都是在postProcessAfterInitialization中生成的。Spring遍历所有BeanPostProcessor如果发现某个Bean需要被代理就返回代理对象替代原Bean后续容器中持有的就是这个代理对象。这也是事务失效的底层原因——自调用绕过的就是这个代理。2.3 自定义BeanPostProcessor注入追踪日志// 给所有Controller的方法调用自动加日志ComponentpublicclassLoggingBeanPostProcessorimplementsBeanPostProcessor{OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName){// 只处理有RestController注解的Beanif(bean.getClass().isAnnotationPresent(RestController.class)){// 为这个Bean创建代理插入日志逻辑returnProxy.newProxyInstance(bean.getClass().getClassLoader(),bean.getClass().getInterfaces(),(proxy,method,args)-{log.info(调用 {} 的 {} 方法,beanName,method.getName());Objectresultmethod.invoke(bean,args);log.info({} 的 {} 方法执行完成,beanName,method.getName());returnresult;});}returnbean;}}这个PostProcessor在初始化完成后检查每个Bean是否有RestController注解有就创建动态代理包装它。代理在每个方法调用前后打印追踪日志Controller本身的业务代码一行业务逻辑都没受到影响。三、InitializingBean和PostConstruct——初始化扩展疑问PostConstruct和InitializingBean.afterPropertiesSet()有什么区别执行顺序是怎样的回答两者都是在Bean属性赋值完成后执行的初始化回调但执行顺序不同PostConstruct先执行afterPropertiesSet()后执行最后是自定义的init-method。3.1 执行顺序Bean实例化调用构造器 ↓ 属性赋值Autowired注入依赖 ↓ BeanPostProcessor.postProcessBeforeInitialization ↓ PostConstruct 方法执行 ← 第一个初始化钩子 ↓ InitializingBean.afterPropertiesSet() ← 第二个初始化钩子 ↓ Bean(initMethod customInit) ← 第三个初始化钩子 ↓ BeanPostProcessor.postProcessAfterInitializationAOP在这里增强 ↓ Bean就绪3.2 一个实例多种初始化方式并存ComponentpublicclassOrderServiceimplementsInitializingBean{privateOrderMapperorderMapper;Autowired// 构造后注入privatevoidsetOrderMapper(OrderMapperorderMapper){this.orderMapperorderMapper;}PostConstruct// 最先执行publicvoidinit(){log.info(1. PostConstructorderMapper是否已注入{},orderMapper!null);// 此时依赖已注入可以校验非空Assert.notNull(orderMapper,orderMapper不能为空);}Override// 其次执行publicvoidafterPropertiesSet(){log.info(2. afterPropertiesSet执行预热加载把常用数据放入本地缓存);preloadCache();}}PostConstruct适合做依赖校验——此时Autowired已经完成可以断言关键依赖不为空。afterPropertiesSet()适合做预热加载——比如从数据库加载常用数据到本地缓存此时所有依赖已就绪Bean即将投入使用。四、Aware接口——向容器要东西疑问Aware接口是做什么的有哪些常用子接口回答Aware接口让一个Bean能感知到Spring容器中的核心组件——通过回调容器把自身的核心能力传递给你的Bean。4.1 常用Aware接口// 如果你想让Bean感知到自己在容器中的名字publicclassMyBeanimplementsBeanNameAware{OverridepublicvoidsetBeanName(Stringname){log.info(我在容器中的名字是: {},name);}}// 如果你想让Bean获取到BeanFactory容器publicclassMyBeanimplementsBeanFactoryAware{OverridepublicvoidsetBeanFactory(BeanFactorybeanFactory){BeanFactorybfthis.beanFactory;// 容器本身}}// 如果你想让Bean直接获取到ApplicationContextpublicclassMyBeanimplementsApplicationContextAware{OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext){ApplicationContextctxthis.applicationContext;// 应用上下文ctx.getBean(...);// 可以主动查找其他Beanctx.publishEvent(...);// 可以发布事件}}4.2 Aware接口汇总Aware接口获取的对象应用场景BeanNameAwareBean自身在容器中的名字需要在运行时知道自己的beanNameBeanFactoryAware当前BeanFactory需要动态获取其他BeanApplicationContextAware完整的应用上下文需主动获取Bean、发布事件、访问容器能力EnvironmentAware配置环境对象获取配置项或动态切换ProfileResourceLoaderAware资源加载器需加载classpath下的文件ApplicationEventPublisherAware事件发布器需要在非Spring管理的类中发布事件五、ImportSelector和ImportBeanDefinitionRegistrar——批量注册组件疑问SpringBoot的EnableAutoConfiguration就是通过Import导入选择器的具体是怎么做的回答Import可以导入普通类、ImportSelector或ImportBeanDefinitionRegistrar。ImportSelector根据条件选择要导入的类ImportBeanDefinitionRegistrar直接向容器动态注册新的BeanDefinition。5.1 ImportSelector——根据条件选择配置类// AsyncConfigurationSelector根据EnableAsync的adviceMode选择具体的配置类publicclassAsyncConfigurationSelectorextendsAdviceModeImportSelectorEnableAsync{OverridepublicString[]selectImports(AdviceModeadviceMode){switch(adviceMode){casePROXY:returnnewString[]{ProxyAsyncConfiguration.class.getName()};caseASPECTJ:returnnewString[]{AspectJAsyncConfiguration.class.getName()};default:returnnull;}}}5.2 ImportBeanDefinitionRegistrar——直接注册Bean// 在权限系统中用于扫描所有RequirePermission注解的方法并批量注册拦截器publicclassPermissionRegistrarimplementsImportBeanDefinitionRegistrar{OverridepublicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){// 1. 扫描所有标注了RequirePermission的类SetMethodMetadatamethodsfindAnnotatedMethods(RequirePermission);// 2. 为每个权限码动态注册一个权限校验Beanfor(MethodMetadatamethod:methods){StringpermCodemethod.getAnnotationAttributes().get(value).toString();BeanDefinitionBuilderbuilderBeanDefinitionBuilder.genericBeanDefinition(PermissionValidator.class).addConstructorArgValue(permCode);registry.registerBeanDefinition(permissionValidator_permCode,builder.getBeanDefinition());}}}ImportBeanDefinitionRegistrar可以和自定义注解配合写一个EnableXXX注解用户在配置类上标注后自动注册一组相关的Bean定义。这是SpringBoot自动配置starter常用的扩展手段。六、扩展点执行顺序全景图BeanDefinition加载完成 ↓ BeanFactoryPostProcessor.postProcessBeanFactory() ↓ 修改BeanDefinition的元数据 ↓ --------------------------------------------------------- 实例化阶段 --------------------------------------------------------- ↓ 遍历BeanDefinition → 调用构造器实例化Bean ↓ BeanPostProcessor.postProcessBeforeInstantiation可返回代理替代原Bean ↓ --------------------------------------------------------- 属性赋值阶段 --------------------------------------------------------- ↓ Autowired / Value 注解的字段被注入 ↓ Aware接口回调 BeanNameAware.setBeanName() BeanFactoryAware.setBeanFactory() ApplicationContextAware.setApplicationContext() ↓ --------------------------------------------------------- 初始化阶段 --------------------------------------------------------- ↓ BeanPostProcessor.postProcessBeforeInitialization ↓ PostConstruct 方法 ↓ InitializingBean.afterPropertiesSet() ↓ Bean(initMethod) 或其他自定义初始化方法 ↓ BeanPostProcessor.postProcessAfterInitialization ← AOP在这里生成代理 ↓ --------------------------------------------------------- Bean就绪放入一级缓存 ↓ 容器启动完成 ↓ --------------------------------------------------------- 销毁阶段 --------------------------------------------------------- ↓ PreDestroy 方法 ↓ DisposableBean.destroy() ↓ Bean(destroyMethod)七、面试中这样回答面试官“BeanPostProcessor和BeanFactoryPostProcessor有什么区别”回答框架“两者名字相似但执行时机和操作对象完全不同。BeanFactoryPostProcessor在所有BeanDefinition加载完成之后、Bean实例化之前执行——它操作的是Bean的元数据可以修改BeanDefinition中的属性值、作用域等但触碰不到Bean实例。BeanPostProcessor在Bean实例化之后、初始化前后执行——它操作的是Bean实例本身。postProcessBeforeInitialization在PostConstruct之前执行postProcessAfterInitialization在PostConstruct之后执行。AOP代理就是在postProcessAfterInitialization中生成的——这就是为什么自调用事务会失效因为this调用绕过了这里返回的代理对象。”总结BeanFactoryPostProcessor在实例化前修改BeanDefinition元数据PropertySourcesPlaceholderConfigurer解析${}占位符是最经典的应用BeanPostProcessor在实例化后修改Bean实例postProcessBeforeInitialization在PostConstruct之前postProcessAfterInitialization在之后——AOP在这里生成代理InitializingBean和PostConstruct的执行顺序PostConstruct先于afterPropertiesSet前者适合依赖校验后者适合预热加载Aware接口让Bean向容器要东西ApplicationContextAware获取上下文、BeanNameAware获取自己的容器名ImportSelector和ImportBeanDefinitionRegistrar实现批量注册ImportSelector根据条件选择返回类名列表ImportBeanDefinitionRegistrar直接向容器注册新的BeanDefinition下一篇预告Spring原理八——Spring与微服务从SpringBoot到SpringCloud的演进。拆解微服务架构的核心组件——Nacos注册中心与配置中心、Sentinel熔断限流、Gateway网关——在Spring Cloud生态中各自解决什么问题以及秒杀系统的微服务实际架构。