@Scope ("prototype") fasulye kapsamı yeni fasulye oluşturmuyor


133

Denetleyicimde açıklamalı bir prototip çekirdeği kullanmak istiyorum. Ancak bahar bunun yerine tekli fasulye yaratıyor. İşte bunun kodu:

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

  private int counter;

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

Denetleyici kodu:

@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;
    }
    }

Hız şablonu:

 LoginAction counter: ${loginAction.str}

Spring config.xml, bileşen taramayı etkinleştirmiştir:

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

Her seferinde artan bir sayı alıyorum. Nerede yanlış yaptığımı anlayamıyorum!

Güncelleme

@Gkamal'ın önerdiği gibi -aware yaptım HomeController webApplicationContextve sorunu çözdüm.

güncellenmiş kod:

@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
Keşke başkalarının gerçek farkı görmesi için kodunuzda doğru cevabı uyguladığınız için size iki kat daha fazla oy verebilseydim
Ali Nem

Yanıtlar:


156

Kapsam prototipi, spring (getBean veya bağımlılık enjeksiyonu) bir örnek için her sorduğunuzda yeni bir örnek oluşturacağı ve buna bir referans vereceği anlamına gelir.

Örneğinizde yeni bir LoginAction örneği yaratılır ve HomeController cihazınıza enjekte edilir. LoginAction'ı enjekte ettiğiniz başka bir denetleyiciniz varsa, farklı bir örnek alırsınız.

Her arama için farklı bir örnek istiyorsanız - o zaman her seferinde getBean'i çağırmanız gerekir - tek bir çekirdeğe enjekte etmek bunu başaramayacaktır.


7
ApplicationContextAware denetleyicisini yaptım ve getBean yaptım ve her seferinde taze fasulyeyi alıyorum. Teşekkürler beyler!!!
tintin

Fasulye requestkapsam yerine kapsama sahip olsaydı, bu nasıl çalışır prototype? Yine de fasulyeyi almanız gerekir mi context.getBean(..)?
dr jerry

2
Veya kapsamlı bir proxy kullanın, yani @Scope (value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
svenmeier

26

Bahar 2.5'ten bu yana bunu başarmanın çok kolay (ve zarif) bir yolu var.

Sadece params değiştirebilir proxyModeve valuebir @Scopeaçıklama.

Bu numara ile, tekli bir çekirdek içinde bir prototipe her ihtiyaç duyduğunuzda fazladan kod yazmaktan veya ApplicationContext'i enjekte etmekten kaçınabilirsiniz.

Misal:

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

Yukarıdaki yapılandırma ile LoginAction(içeride HomeController), denetleyici bir singleton olsa bile her zaman bir prototiptir .


2
Yani şimdi 5 baharında yok mu?
Raghuveer

16

Denetleyiciye enjekte edilen çekirdeğin prototip kapsamlı olması, denetleyicinin olduğu anlamına gelmez!


11

@controller bir singleton nesnesidir ve bir singleton sınıfa bir prototip çekirdeği enjekte ederseniz, yaptığınız her çağrı için aslında yeni bir prototip çekirdeği örneği oluşturan arama yöntemi özelliğini kullanmayı belirtmediğiniz sürece, prototip çekirdeğini de singleton yapacaktır.


5

Nicholas.hauschild'in bahsettiği gibi Bahar bağlamını enjekte etmek iyi bir fikir değildir. Sizin durumunuzda @Scope ("istek") bunu düzeltmek için yeterlidir. Ancak LoginAction, denetleyici yönteminin birkaç örneğine ihtiyacınız olduğunu varsayalım . Bu durumda, Tedarikçinin çekirdeğini oluşturmanızı tavsiye ederim ( Bahar 4 çözümü):

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

Ardından kontrolöre enjekte edin:

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

1
ObjectFactoryTedarikçiyle aynı amaca hizmet eden yayların enjekte edilmesini öneririm , ancak normal olarak tanımlanabilir, @Beanyani lambda iade etmeye gerek yoktur.
xenoterracide

3

Kullanmak ApplicationContextAwaresizi Bahar'a bağlıyor (bu bir sorun olabilir veya olmayabilir). Her ihtiyaç duyduğunuzda LoginActionFactoryyeni bir örneğini isteyebileceğiniz a'yı geçmenizi tavsiye ederim LoginAction.


1
Halihazırda Bahar'a özgü ek açıklamalar var; bu bir endişe gibi görünmüyor.
Dave Newton

1
@Dave, İyi nokta. Bazı DI şeyler için alternatifler vardır (JSR 311), ancak bu örnekte Spring'e bağlı her şeyden kendinizi kurtarmak daha zor olabilir. Sanırım factory-methodburayı gerçekten savunuyorum ...
nicholas.hauschild

1
LoginActionFactoryDenetleyiciye bir singleton enjekte etmek için +1 , ancak factory-methodfabrika aracılığıyla başka bir taze fasulye oluşturduğu için sorunu çözecek gibi görünmüyor. Bu fasulyeyi singleton Denetleyiciye enjekte etmek sorunu çözmez.
Brad Cupit

İyi nokta Brad, bu öneriyi cevabımdan çıkaracağım.
nicholas.hauschild

3

@Scope("request")her istek @Scope("session")için bean almak veya her oturum 'kullanıcı' için bean almak için istek kapsamını kullanın


1

Bir singelton fasulyesinin içine enjekte edilen bir prototip fasulyesi, get bean ile yeni bir örnek oluşturmak için açıkça çağrılana kadar singelton gibi davranacaktır.

context.getBean("Your Bean")


0

Denetleyicinizin içinde şu şekilde statik sınıf oluşturabilirsiniz:

    @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

Varsayılan olarak, Bahar fasulyeleri tek tondur. Sorun, farklı kapsamlardaki fasulyeleri kablolamaya çalıştığımızda ortaya çıkıyor. Örneğin, bir prototip fasulyeyi bir singleton haline getirin. Bu, kapsamlı fasulye enjeksiyon problemi olarak bilinir.

Sorunu çözmenin başka bir yolu da @Lookup ek açıklamasıyla yöntem enjeksiyonudur .

İşte bu konuda, prototip çekirdeklerin çoklu çözümlerle tekli bir örneğe enjekte edilmesiyle ilgili güzel bir makale .

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


-11

Denetleyicinizin de @Scope("prototype")tanımlanan

bunun gibi:

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

}

1
neden denetleyicinin de prototip olması gerektiğini düşünüyorsunuz?
Jigar Parekh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.