You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用Spring实现@Bean等效功能及Bean声明方法问询

我来给你几个不依赖Spring实现类似Bean声明和依赖注入的方案,都是实际项目里常用且足够优雅的思路:

方案1:手动依赖注入(最轻量无框架)

这是最直接的方式,完全不用任何框架,自己控制所有实例的创建和依赖传递,核心是用构造函数注入依赖(比字段注入更清晰,还方便单元测试)。

首先把原来的Spring配置转换成普通的配置类,负责创建Bean实例:

// 类似Spring的@Configuration,负责实例化所有需要的组件
public class AppConfig {
    private final String apiBaseUrl;

    // 把配置参数通过构造函数传入,避免硬编码
    public AppConfig(String apiBaseUrl) {
        this.apiBaseUrl = apiBaseUrl;
    }

    // 对应原来的@Bean方法,创建MyApi实例
    public MyApi createMyApi() {
        return Feign.builder().target(MyApi.class, apiBaseUrl);
    }

    // 创建AnotherClass,同时把MyApi依赖传进去
    public AnotherClass createAnotherClass(MyApi myApi) {
        return new AnotherClass(myApi);
    }
}

然后在应用启动的入口类里完成所有初始化:

public class AppBootstrap {
    public static void main(String[] args) {
        // 初始化配置
        AppConfig config = new AppConfig("https://your-target-api.com");
        
        // 创建Bean实例,按依赖顺序来
        MyApi myApi = config.createMyApi();
        AnotherClass anotherClass = config.createAnotherClass(myApi);
        
        // 之后就可以正常使用anotherClass了
        anotherClass.executeApiCall();
    }
}

最后修改AnotherClass,用构造函数接收依赖,去掉框架依赖:

public class AnotherClass {
    private final MyApi myApi;

    // 构造函数注入,依赖关系一目了然
    public AnotherClass(MyApi myApi) {
        this.myApi = myApi;
    }

    public void executeApiCall() {
        myApi.someEndpoint();
    }
}

这个方案的好处是完全无依赖,代码逻辑透明,没有Spring那种“魔法”,而且单元测试时可以很轻松地传入Mock的MyApi实例。

方案2:实现极简DI容器(适合多Bean场景)

如果你的项目里需要管理很多Bean,手动传递依赖会很繁琐,那可以自己写一个超轻量的DI容器,模拟Spring的核心功能——管理Bean的生命周期和依赖注入。

比如写一个简单的容器类:

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class SimpleContainer {
    // 存储Bean的提供者(用于创建实例)
    private final Map<Class<?>, Supplier<?>> beanSuppliers = new HashMap<>();
    // 存储单例Bean的实例
    private final Map<Class<?>, Object> singletonBeans = new HashMap<>();

    // 注册单例Bean的创建逻辑
    public <T> void registerSingleton(Class<T> beanType, Supplier<T> supplier) {
        beanSuppliers.put(beanType, supplier);
    }

    // 获取Bean实例,单例会缓存起来
    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<T> beanType) {
        // 先看有没有缓存的单例
        if (singletonBeans.containsKey(beanType)) {
            return (T) singletonBeans.get(beanType);
        }
        // 没有的话用提供者创建实例
        Supplier<?> supplier = beanSuppliers.get(beanType);
        if (supplier == null) {
            throw new IllegalArgumentException("No bean registered for type: " + beanType.getName());
        }
        T bean = (T) supplier.get();
        singletonBeans.put(beanType, bean);
        return bean;
    }
}

使用的时候就像这样:

public class AppBootstrap {
    public static void main(String[] args) {
        SimpleContainer container = new SimpleContainer();

        // 注册MyApi的单例创建逻辑
        container.registerSingleton(MyApi.class, 
            () -> Feign.builder().target(MyApi.class, "https://your-target-api.com"));

        // 注册AnotherClass,依赖MyApi直接从容器获取
        container.registerSingleton(AnotherClass.class, 
            () -> new AnotherClass(container.getBean(MyApi.class)));

        // 获取实例使用
        AnotherClass anotherClass = container.getBean(AnotherClass.class);
        anotherClass.executeApiCall();
    }
}

这个容器虽然简单,但已经能处理大部分场景,还可以扩展支持原型Bean(每次获取都创建新实例)、依赖自动解析等功能,完全可以根据自己的需求定制。

方案3:优化单例实现(用静态工厂解耦)

如果你觉得单例是最适合你的场景,但原来的实现不够优雅,可以把单例的创建逻辑封装到静态工厂类里,而不是直接在MyApi里写单例代码,这样更解耦,也方便测试和配置。

比如写一个工厂类:

public class MyApiFactory {
    private static MyApi instance;
    private static String apiUrl;

    // 初始化方法,在应用启动时调用一次
    public static void init(String targetUrl) {
        if (instance != null) {
            throw new IllegalStateException("MyApiFactory has already been initialized");
        }
        apiUrl = targetUrl;
        instance = Feign.builder().target(MyApi.class, apiUrl);
    }

    // 获取实例的方法
    public static MyApi getInstance() {
        if (instance == null) {
            throw new IllegalStateException("MyApiFactory not initialized yet");
        }
        return instance;
    }

    // 用于单元测试的重置方法
    public static void reset() {
        instance = null;
        apiUrl = null;
    }
}

使用的时候:

public class AppBootstrap {
    public static void main(String[] args) {
        // 先初始化工厂
        MyApiFactory.init("https://your-target-api.com");
        
        // 创建AnotherClass时传入工厂提供的实例
        AnotherClass anotherClass = new AnotherClass(MyApiFactory.getInstance());
        anotherClass.executeApiCall();
    }
}

AnotherClass还是用构造函数注入,保持依赖清晰。这个方案比传统的饿汉/懒汉单例更优雅,因为把创建逻辑集中在工厂里,还能控制初始化时机,测试时可以调用reset()方法重新初始化。


总结一下:如果项目规模小,推荐用手动依赖注入;如果Bean较多,用极简DI容器;如果就是想用单例,用静态工厂优化实现。这三个方案都不需要Spring,而且都遵循了依赖注入的核心思想——依赖倒置,代码清晰可维护。

内容的提问来源于stack exchange,提问作者Fundhor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:44:02