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

@Service搭配@Scope("prototype"):控制器实现原型Bean行为需求

解决单例Controller中注入原型Bean的实例复用问题

你应该是遇到了这个情况:你的EmailService标记了@Service和@Scope("prototype"),但在单例的LoginController里通过@Autowired注入后,每次请求拿到的都是同一个实例,完全没体现出原型Bean“每次获取都新建”的特性对吧?

这是因为Spring的单例Bean(比如默认的@Controller)在初始化时会一次性注入所有依赖的Bean——哪怕依赖的是原型Bean,之后也不会再重新创建新的实例。下面给你几种实用的解决办法:

方法1:使用@Lookup注解(最推荐)

这是Spring专门为单例Bean获取原型Bean设计的注解,写法简洁且符合Spring风格。你只需要在Controller里定义一个抽象方法来获取EmailService,加上@Lookup即可:

@Controller
@RequestMapping(value="/")
public class LoginController {

    // 定义抽象方法,Spring会动态生成代理实现
    @Lookup
    protected EmailService getEmailService() {
        return null;
    }

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String register(){
        EmailService emailService = getEmailService();
        System.out.println(emailService); // 每次请求都会打印不同的实例地址
        emailService.sendConfirmationKey();
        return "register-success";
    }

    @RequestMapping(value = "/resetKey", method = RequestMethod.POST)
    public String resetKey(){
        EmailService emailService = getEmailService();
        System.out.println(emailService);
        emailService.sendResetKey();
        return "reset-success";
    }
}

方法2:通过ApplicationContext手动获取

让Controller实现ApplicationContextAware接口,每次需要的时候从容器中手动拉取新的原型Bean实例:

@Controller
@RequestMapping(value="/")
public class LoginController implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String register(){
        EmailService emailService = applicationContext.getBean(EmailService.class);
        System.out.println(emailService);
        emailService.sendConfirmationKey();
        return "register-success";
    }

    // resetKey方法同理处理
}

方法3:使用JSR-330的Provider接口

如果你的项目里引入了JSR-330依赖(Spring默认已经包含),可以注入Provider<EmailService>,每次调用get()方法就能拿到新的原型实例:

import javax.inject.Provider;

@Controller
@RequestMapping(value="/")
public class LoginController {

    @Autowired
    private Provider<EmailService> emailServiceProvider;

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String register(){
        EmailService emailService = emailServiceProvider.get();
        System.out.println(emailService);
        emailService.sendConfirmationKey();
        return "register-success";
    }

    // resetKey方法同理处理
}

这三种方法里,@Lookup是最省心的方案,代码也最干净,推荐优先使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:12:41