@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
相关产品推荐
相关产品推荐

