@Scope(“ prototype”)Beanスコープが新しいBeanを作成しない


133

注釈付きのプロトタイプBeanをコントローラーで使用したい。しかし、春は代わりにシングルトン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}

Spring config.xmlではコンポーネントスキャンが有効になっています。

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

毎回カウントが増えています。どこがいけないのかわからない!

更新

以下のよう@gkamalによって提案され、私が作っHomeController webApplicationContext-aware、それが問題を解決しました。

更新されたコード:

@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
コードに正しい答えを実装して他の人が実際の違いを確認できるように、二重に賛成票を投じたいと思います
Ali Nem

回答:


156

スコーププロトタイプとは、Spring(getBeanまたは依存性注入)にインスタンスを要求するたびに、新しいインスタンスを作成し、そのインスタンスへの参照を提供することを意味します。

この例では、LoginActionの新しいインスタンスが作成され、HomeControllerに注入されます。LoginActionを注入する別のコントローラーがある場合は、別のインスタンスを取得します。

呼び出しごとに異なるインスタンスが必要な場合-毎回getBeanを呼び出す必要があります-シングルトンBeanに注入してもそれは実現しません。


7
コントローラーApplicationContextAwareを作成してgetBeanを実行し、毎回新鮮なBeanを取得しています。みんなありがとう!!!
タンタン2011年

Beanがrequestスコープではなくスコープを持つ場合、これはどのように機能しますかprototype。それでもBeanを取得する必要がありますcontext.getBean(..)か?
ジェリー博士2015

2
または、スコープ付きプロキシを使用します。つまり、@ Scope(value = "prototype"、proxyMode = ScopedProxyMode.TARGET_CLASS)
svenmeier

25

Spring 2.5以降、それを実現する非常に簡単な(そしてエレガントな)方法があります。

あなただけのparamsを変更することができますproxyModeし、value@Scope注釈。

このトリックを使用すると、シングルトンBean内にプロトタイプが必要になるたびに、余分なコードを記述したり、ApplicationContextを注入したりする必要がなくなります。

例:

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

コントローラがシングルトンであっても、上記の構成LoginAction(内部HomeController)は常にプロトタイプです。


2
それで、私たちは今春5にそれを持っていませんか?
Raghuveer

16

コントローラーに注入されたBeanがプロトタイプスコープであるからといって、コントローラーが意味するわけではありません!


11

@controllerはシングルトンオブジェクトであり、プロトタイプBeanをシングルトンクラスに注入すると、ルックアップメソッドのプロパティを使用して指定した場合を除き、プロトタイプ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

を使用するApplicationContextAwareと、Springに縛られます(問題になる場合とそうでない場合があります)。を渡すことをお勧めしLoginActionFactoryます。これは、LoginAction必要になるたびにの新しいインスタンスを要求できます。


1
ただし、Spring固有のアノテーションはすでにあります。それはそれほど心配ではないようです。
Dave Newton

1
@デイブ、グッドポイント。DIの一部(JSR 311)には代替手段がありますが、この例では、Springに依存するすべてのものを取り除くのが難しい場合があります。私は本当にfactory-methodここを主張しているだけだと思います...
nicholas.hauschild

1
LoginActionFactoryコントローラーにシングルトンを注入するための+1 。ただしfactory-method、ファクトリーを介して別のSpring Beanを作成するだけなので、問題を解決するようには見えません。そのBeanをシングルトンコントローラーに挿入しても問題は解決されません。
ブラッドキューピ

良い点ブラッド、私は私の提案からその提案を削除します。
nicholas.hauschild 2013

3

リクエストスコープ@Scope("request")を使用して、各リクエストの@Scope("session")Beanを取得するか、各セッション「ユーザー」のBeanを取得します


1

singelton Bean内に注入されたプロトタイプ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

デフォルトでは、Spring Beanはシングルトンです。この問題は、異なるスコープのBeanをワイヤリングしようとすると発生します。たとえば、プロトタイプ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、2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.