刚接触Spring框架时,我们通常用@Component或@Bean注解来声明Bean,这就像用微波炉加热预制菜——简单快捷但缺乏定制空间。当遇到需要动态构造、条件化初始化的复杂对象时,就需要请出今天的主角:BeanDefinition.setInstanceSupplier()和FactoryBean。这就像从家常菜进阶到分子料理,虽然操作复杂,但能精准控制每个细节。
我在重构一个支付网关时深有体会。有的支付渠道需要运行时动态加载证书,有的需要代理对象实现AOP拦截。这时候传统的注解方式就力不从心了,而这两种方案就像瑞士军刀的不同组件:setInstanceSupplier()像精确的镊子,适合细粒度控制单个对象的构建过程;FactoryBean则像多功能钳子,适合封装整套对象生产流水线。下面我们通过真实项目场景,拆解它们的适用边界。
setInstanceSupplier()是Spring 5.0引入的API,它允许我们直接干预Bean的实例化阶段。想象你正在组装乐高,默认情况Spring会按照说明书(BeanDefinition)自动拼装,而这个API相当于让你亲手捏住每一块积木。
它的核心价值在于:
去年优化一个配置中心时,我需要根据不同的环境变量创建数据源。传统做法要写一堆@Conditional注解,而用Supplier只需要三行代码:
java复制definition.setInstanceSupplier(() -> {
return "prod".equals(env) ? new ProdDataSource() : new TestDataSource();
});
在微服务架构中,这种模式特别适合:
看个更复杂的例子——实现一个线程安全的随机数生成器:
java复制@Configuration
public class RandomConfig {
@Bean
public BeanDefinitionBuilder randomBuilder() {
GenericBeanDefinition definition = new GenericBeanDefinition();
definition.setInstanceSupplier(ThreadLocalRandom::current);
return definition;
}
}
这里直接把Java原生API包装成Spring Bean,避免了不必要的包装层。
如果说setInstanceSupplier是手术刀,那么FactoryBean就是整套手术设备。它不仅控制实例化,还能管理生命周期。我常把它比作汽车工厂:getObject()是生产线,getObjectType()是产品说明书,isSingleton()决定是量产还是定制。
一个经典的误区和真相:
在实现分布式锁时,我用FactoryBean完美封装了Redis和ZooKeeper的差异:
java复制public class DistributedLockFactory implements FactoryBean<Lock> {
@Override
public Lock getObject() {
return lockType == LockType.REDIS ?
new RedisLock(connection) :
new ZkLock(client);
}
@Override
public Class<?> getObjectType() {
return Lock.class;
}
}
真正体现FactoryBean威力的场景是与其他Spring特性联动:
比如这个可观测的RPC客户端工厂:
java复制public class RpcClientFactory implements FactoryBean<RpcClient>, InitializingBean {
private RpcClient client;
@Override
public void afterPropertiesSet() {
this.client = new RpcClient();
Metrics.register(client); // 注册监控
}
@Override
public RpcClient getObject() {
return client;
}
}
经过多个项目的实践,我总结出这样的决策路径:
关键指标对比表:
| 维度 | setInstanceSupplier | FactoryBean |
|---|---|---|
| 代码侵入性 | 低(函数式接口) | 中(需实现接口) |
| 生命周期控制 | 仅实例化阶段 | 完整生命周期 |
| AOP支持 | 需额外配置 | 原生支持 |
| 条件化构造 | 更直观 | 需结合其他机制 |
| 性能开销 | 更低(直接引用) | 稍高(接口调用) |
最难忘的是在一次性能优化中,误将高耗时的资源加载放在Supplier里,导致每次getBean都重新初始化。后来改用FactoryBean配合@Lazy才解决:
java复制public class HeavyResourceFactory implements FactoryBean<Resource> {
private Resource resource;
@Override
public Resource getObject() {
if(resource == null) {
synchronized(this) {
resource = loadFromRemote(); // 双重检查锁定
}
}
return resource;
}
}
真正的高手会混用两者。比如先用FactoryBean搭建框架,再用Supplier实现细节:
java复制public class HybridFactory implements FactoryBean<Service>, BeanClassLoaderAware {
private Supplier<Service> supplier;
public void setBeanClassLoader(ClassLoader classLoader) {
this.supplier = () -> {
return Proxy.newProxyInstance(classLoader,
new Class[]{Service.class}, new DebugInvoker());
};
}
@Override
public Service getObject() {
return supplier.get();
}
}
结合Spring Boot自动配置,可以创建智能工厂:
java复制@AutoConfiguration
public class SmartFactoryAutoConfig {
@Bean
@ConditionalOnMissingBean
public FactoryBean<Cache> cacheFactory(Environment env) {
return new FactoryBean<>() {
@Override
public Cache getObject() {
return env.acceptsProfiles("cloud") ?
new RedisCache() : new LocalCache();
}
// 省略其他方法
};
}
}
这种模式在Starter开发中特别有用,能根据运行环境自动选择实现。
对于数据库连接等昂贵资源,对象池是常见需求。先用Supplier实现基础版:
java复制definition.setInstanceSupplier(() -> {
return pool.borrowObject(); // 从Apache Commons Pool获取
});
再用FactoryBean实现增强版:
java复制public class PooledDataSourceFactory implements FactoryBean<DataSource>, DisposableBean {
private final ObjectPool<Connection> pool;
@Override
public DataSource getObject() {
return new PooledDataSource(pool);
}
@Override
public void destroy() {
pool.close(); // 优雅关闭
}
}
通过注解处理器,可以在编译期检查FactoryBean的合法性:
java复制@AutoService(Processor.class)
public class FactoryBeanProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
// 检查getObjectType()返回值是否匹配实际类型
}
}
这套机制在我司中间件团队已经防止了数十次类型不匹配的线上事故。