@Scope (“prototype”) 빈 범위는 새로운 빈을 생성하지 않습니다


133

컨트롤러에 주석이 달린 프로토 타입 Bean을 사용하고 싶습니다. 그러나 봄은 대신 싱글 톤 콩을 만들고 있습니다. 그 코드는 다음과 같습니다.

@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은 컴포넌트 스캐닝을 가능하게한다 :

    <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

범위 프로토 타입은 인스턴스에 대해 스프링 (getBean 또는 종속성 주입)을 요청할 때마다 새 인스턴스를 작성하고 이에 대한 참조를 제공함을 의미합니다.

귀하의 예에서 LoginAction의 새 인스턴스가 생성되어 HomeController에 삽입됩니다. LoginAction을 주입하는 다른 컨트롤러가 있으면 다른 인스턴스를 얻게됩니다.

각 호출에 대해 다른 인스턴스를 원할 경우 매번 getBean을 호출해야합니다. 싱글 톤 Bean에 주입해도이를 달성 할 수 없습니다.


7
컨트롤러 ApplicationContextAware를 만들고 getBean을 수행했으며 매번 신선한 Bean을 얻습니다. 고마워요 !!!
tintin

Bean이 request범위 대신 범위를 가지면 어떻게 작동합니까 prototype? 여전히 Bean을 검색해야 context.getBean(..)합니까?
jerry jerry

2
또는 범위가 지정된 프록시를 사용하십시오. 예 : @Scope (value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
svenmeier

25

Spring 2.5 부터는 그것을 달성하는 매우 쉽고 우아한 방법이 있습니다.

당신은 방금 PARAMS을 변경할 수 있습니다 proxyModevalue@Scope주석.

이 트릭을 사용하면 싱글 톤 Bean 내부에 프로토 타입이 필요할 때마다 추가 코드를 작성하거나 ApplicationContext를 삽입하지 않아도됩니다.

예:

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

컨트롤러가 싱글 톤 인 경우에도 위의 구성 LoginAction(내부 HomeController)은 항상 프로토 타입 입니다.


2
그래서 우리는 지금 봄 5에 그것을 가지고 있지 않습니까?
Raghuveer

16

컨트롤러에 주입 된 빈이 프로토 타입 범위라는 것이 컨트롤러가 있다는 의미는 아닙니다!


11

@controller는 싱글 톤 객체이며, 프로토 타입 Bean을 싱글 톤 클래스에 삽입하면 u 호출마다 프로토 타입 Bean의 새 인스턴스를 실제로 생성하는 lookup-method 속성을 사용하여 지정하지 않는 한 프로토 타입 Bean을 싱글 톤 클래스로 만듭니다.


5

nicholas.hauschild에서 언급했듯이 Spring 컨텍스트를 주입하는 것은 좋은 생각이 아닙니다. 귀하의 경우 @Scope ( "request")로 충분합니다. 그러나 LoginAction컨트롤러 메소드에 여러 인스턴스가 필요하다고 가정 해보십시오 . 이 경우 공급 업체 Bean ( Spring 4 솔루션) 을 작성하는 것이 좋습니다 .

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

그런 다음 컨트롤러에 주입하십시오.

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

1
나는 ObjectFactory공급 업체와 동일한 목적을 제공 하는 스프링 을 주입 할 것을 제안 하지만 @Bean람다를 반환 할 필요가 없다는 것을 의미 하는 정상으로 정의 할 수 있습니다 .
xenoterracide

3

사용하면 ApplicationContextAwareSpring에 연결되는 것입니다 (문제 일 수도 있고 아닐 수도 있음). 를 전달하는 것이 좋습니다 . 필요할 때마다 LoginActionFactory새 인스턴스를 요청할 수 있습니다 LoginAction.


1
그러나 이미 스프링 고유의 주석이 있습니다. 그다지 걱정하지 않는 것 같습니다.
Dave Newton

1
@ 데이브, 좋은 지적. 일부 DI 자료에 대한 대안이 있지만 (JSR 311)이 예제에서 Spring에 의존하는 모든 것을 제거하는 것이 더 어려울 수 있습니다. 난 정말 factory-method여기를 옹호하고 있다고 가정 합니다 ...
nicholas.hauschild

1
LoginActionFactory컨트롤러에 싱글 톤을 주입하면 +1 이지만 factory-method팩토리를 통해 다른 스프링 빈을 생성하기 때문에 문제가 해결되지 않는 것 같습니다. 해당 bean을 싱글 톤 컨트롤러에 주입해도 문제가 해결되지 않습니다.
브래드 큐핏

좋은 지적 브래드, 나는 그 제안을 내 대답에서 제거 할 것입니다.
nicholas.hauschild

3

요청 범위 @Scope("request")를 사용 하여 각 요청에 @Scope("session")대한 Bean을 얻 거나 각 세션 '사용자'에 대한 Bean을 얻으십시오


1

singelton bean 안에 주입 된 protoype bean은 get bean에 의해 새로운 인스턴스를 만들기 위해 명시 적으로 호출 될 때까지 singelton처럼 동작합니다.

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

기본적으로 스프링 빈은 싱글 톤입니다. 다른 범위의 Bean을 연결하려고 할 때 문제가 발생합니다. 예를 들어, 프로토 타입 Bean을 싱글 톤으로 만듭니다. 이것은 범위가 지정된 빈 주입 문제로 알려져 있습니다.

이 문제를 해결하는 또 다른 방법은 @Lookup 주석을 사용한 메소드 주입입니다 .

다음은 여러 솔루션 을 사용 하여 프로토 타입 Bean을 단일 인스턴스주입 하는이 문제에 대한 좋은 기사입니다 .

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


-11

컨트롤러에는 또한 @Scope("prototype")정의 된 것이 필요합니다

이처럼 :

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

}

1
왜 컨트롤러도 프로토 타입이어야한다고 생각합니까?
Jigar Parekh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.