@Scope («прототип») сфера використання бобів не створює нового


133

Я хочу використовувати анотований прототип бота у своєму контролері. Але весна натомість створює однотонну квасолю. Ось код для цього:

@Component
@Scope("prototype")
public class LoginAction {

  private int counter;

  public LoginAction(){
    System.out.println(" counter is:" + counter);
  }
  public String getStr() {
    return " counter is:"+(++counter);
  }
}

Код контролера:

@Controller
public class HomeController {
    @Autowired
    private LoginAction loginAction;

    @RequestMapping(value="/view", method=RequestMethod.GET)
    public ModelAndView display(HttpServletRequest req){
        ModelAndView mav = new ModelAndView("home");
        mav.addObject("loginAction", loginAction);
        return mav;
    }

    public void setLoginAction(LoginAction loginAction) {
        this.loginAction = loginAction;
    }

    public LoginAction getLoginAction() {
        return loginAction;
    }
    }

Шаблон швидкості:

 LoginAction counter: ${loginAction.str}

config.xmlУвімкнено функцію сканування компонентів Spring :

    <context:annotation-config />
    <context:component-scan base-package="com.springheat" />
    <mvc:annotation-driven />

Я щоразу отримую посилений підрахунок. Не можу зрозуміти, де я помиляюся!

Оновлення

Як запропонував @gkamal , я зробив HomeController webApplicationContext-знавство, і це вирішило проблему.

оновлений код:

@Controller
public class HomeController {

    @Autowired
    private WebApplicationContext context;

    @RequestMapping(value="/view", method=RequestMethod.GET)
    public ModelAndView display(HttpServletRequest req){
        ModelAndView mav = new ModelAndView("home");
        mav.addObject("loginAction", getLoginAction());
        return mav;
    }

    public LoginAction getLoginAction() {
        return (LoginAction) context.getBean("loginAction");
    }
}

12
Я хочу, щоб я міг подвоїти вас за впровадження правильної відповіді у свій код, щоб інші бачили фактичну різницю
Алі Нем

Відповіді:


156

Прототип сфери застосування означає, що кожного разу, коли ви запитаєте spring (getBean або ін'єкцію залежності) для екземпляра, він створить новий екземпляр і дасть посилання на це.

У вашому прикладі новий екземпляр LoginAction створюється та вводиться у ваш HomeController. Якщо у вас є інший контролер, в який ви вводите LoginAction, ви отримаєте інший екземпляр.

Якщо ви хочете отримати інший екземпляр для кожного виклику - тоді вам потрібно викликати getBean кожен раз - введення в одиночну частину цього не досягне.


7
Я зробив контролер ApplicationContextAware і зробив getBean, і я отримую свіжу квасолю кожен раз. Спасибі, хлопці!!!
tintin

Як це працює, якщо боб мав би requestсферу замість prototypeсфери. Чи все-таки вам знадобиться отримати квасоля context.getBean(..)?
д-р Джері

2
Або скористайтеся проксі-сервером, тобто @Scope (значення = "прототип", proxyMode = ScopedProxyMode.TARGET_CLASS)
svenmeier

25

З весни 2.5 існує дуже простий (і елегантний) спосіб досягти цього.

Ви можете просто зміни параметрів proxyModeі valueв @Scopeанотації.

За допомогою цього трюку ви можете уникнути написання додаткового коду або впорскувати ApplicationContext кожного разу, коли вам потрібен прототип всередині одинарних бобів.

Приклад:

@Service 
@Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)  
public class LoginAction {}

З конфігурацією вище LoginAction(всередині HomeController) завжди є прототипом, навіть якщо контролер є однотонним .


2
Отже, у нас її немає навесні 5?
Raghuveer

16

Тільки тому, що боб, що вводиться в контролер, є прототипом, це не означає, що це контролер!


11

@controller є одиночним об'єктом, і якщо ввести прототип bean до класу синглтон, зробить прототип прототипу також однотонним, якщо ви не вкажете, використовуючи властивість методу пошуку, яка фактично створює новий екземпляр прототипу bean для кожного виклику, який ви здійснюєте.


5

Як згадує nicholas.hauschild вводити весняний контекст - це не дуже гарна ідея. У вашому випадку, @Scope ("запит") достатньо, щоб виправити це. Але скажімо, що вам потрібно кілька примірників LoginActionметоду контролера. У цьому випадку я рекомендую створити боб Постачальника ( Весна 4 ):

    @Bean
    public Supplier<LoginAction> loginActionSupplier(LoginAction loginAction){
        return () -> loginAction;
    }

Потім введіть його в контролер:

@Controller
public class HomeController {
    @Autowired
    private  Supplier<LoginAction> loginActionSupplier;  

1
Я б запропонував інжекторні пружини, ObjectFactoryякі служать тій же цілі, що і постачальник, але їх можна визначити як звичайну, @Beanпід якою я маю на увазі не потрібно повертати лямбда.
ксенотеррацид

3

Використання ApplicationContextAwareприв'язує вас до Весни (що може бути, а може і не бути проблемою). Я рекомендую передати а LoginActionFactory, який ви можете запитувати новий екземпляр LoginActionкожного разу, коли він вам потрібен.


1
Однак уже є специфічні для весни примітки; не здається, це викликає особливе занепокоєння.
Дейв Ньютон

1
@Dave, хороший пункт. Існують альтернативи для деяких матеріалів DI (JSR 311), але може бути важче позбутися від усього весняного, залежного в цьому прикладі. Я припускаю, що я справді просто виступаю factory-methodтут ...
nicholas.hauschild

1
+1 за введення сингтона LoginActionFactoryв контролер, але factory-method, схоже, це не вирішило б проблему, оскільки воно просто створює черговий пружинистий завод через завод. Введення цього квасолі в однотонний контролер не вирішить проблему.
Бред Купіт

Добре, Бред, я вилучу цю пропозицію зі своєї відповіді.
nicholas.hauschild

3

використовувати область запиту, @Scope("request")щоб отримати bean для кожного запиту або @Scope("session")отримати bean для кожного користувача сеансу


1

Прототипний квасоля, що вводиться всередину сингелтонових бобів, буде поводитись так, як сингелтон, доки експіліктично закликається створити новий екземпляр get bean.

context.getBean("Your Bean")


0

Ви можете створити статичний клас всередині свого контролера так:

    @Controller
    public class HomeController {
        @Autowired
        private LoginServiceConfiguration loginServiceConfiguration;

        @RequestMapping(value = "/view", method = RequestMethod.GET)
        public ModelAndView display(HttpServletRequest req) {
            ModelAndView mav = new ModelAndView("home");
            mav.addObject("loginAction", loginServiceConfiguration.loginAction());
            return mav;
        }


        @Configuration
        public static class LoginServiceConfiguration {

            @Bean(name = "loginActionBean")
            @Scope("prototype")
            public LoginAction loginAction() {
                return new LoginAction();
            }
        }
}

0

За замовчуванням весняні боби є однотонними. Проблема виникає, коли ми намагаємося накрутити квасоля різної сфери. Наприклад, прототип боб в синглтон. Це відома як проблема з ін'єкцією квасолі.

Інший спосіб вирішити проблему - це ін'єкція методу з анотацією @Lookup .

Ось приємна стаття з цього питання про введення прототипу в однотонний екземпляр з декількома рішеннями.

https://www.baeldung.com/spring-inject-prototype-bean-into-singleton


-11

Ваш контролер також потребує @Scope("prototype")визначеного

подобається це:

@Controller
@Scope("prototype")
public class HomeController { 
 .....
 .....
 .....

}

1
чому ви думаєте, що контролер також повинен бути прототипом?
Джигар Парех
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.