Phạm vi đậu @Scope (phiên bản nguyên mẫu) không tạo ra bean mới


133

Tôi muốn sử dụng một bean nguyên mẫu có chú thích trong bộ điều khiển của mình. Nhưng mùa xuân đang tạo ra một hạt singleton thay thế. Đây là mã cho điều đó:

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

  private int counter;

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

Mã điều khiển:

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

Mẫu vận tốc:

 LoginAction counter: ${loginAction.str}

Spring config.xmlcó chức năng quét thành phần được kích hoạt:

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

Tôi đang nhận được một số lượng tăng lên mỗi lần. Không thể tìm ra tôi đang sai ở đâu!

Cập nhật

Theo đề xuất của @gkamal , tôi đã HomeController webApplicationContextbiết và nó đã giải quyết được vấn đề.

mã cập nhật:

@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
Tôi ước tôi có thể nhân đôi bạn để thực hiện câu trả lời chính xác trong mã của bạn cho người khác thấy sự khác biệt thực tế
Ali Nem

Câu trả lời:


156

Nguyên mẫu phạm vi có nghĩa là mỗi khi bạn yêu cầu spring (getBean hoặc tiêm phụ thuộc) cho một thể hiện, nó sẽ tạo một thể hiện mới và đưa ra một tham chiếu đến điều đó.

Trong ví dụ của bạn, một phiên bản mới của LoginAction được tạo và đưa vào HomeContoder của bạn. Nếu bạn có một bộ điều khiển khác mà bạn tiêm LoginAction, bạn sẽ có một phiên bản khác.

Nếu bạn muốn có một phiên bản khác nhau cho mỗi cuộc gọi - thì bạn cần gọi getBean mỗi lần - tiêm vào một hạt đậu đơn sẽ không đạt được điều đó.


7
Tôi đã tạo bộ điều khiển ApplicationContextAware và đã nhận getBean và tôi sẽ nhận được đậu tươi mỗi lần. Cảm ơn các bạn!!!
tintin

Làm thế nào điều này hoạt động nếu bean sẽ có requestphạm vi thay vì prototypephạm vi. Bạn vẫn sẽ cần lấy đậu với context.getBean(..)?
dr jerry

2
Hoặc sử dụng proxy có phạm vi, ví dụ @Scope (value = "nguyên mẫu", proxyMode = ScopedProxyMode.TARGET_CLASS)
svenmeier

24

Kể từ mùa xuân 2.5, có một cách rất dễ dàng (và thanh lịch) để đạt được điều đó.

Bạn chỉ có thể thay đổi params proxyModevaluecác @Scopechú thích.

Với thủ thuật này, bạn có thể tránh viết thêm mã hoặc tiêm ApplicationContext mỗi khi bạn cần một nguyên mẫu bên trong một bean đơn.

Thí dụ:

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

Với cấu hình ở trên LoginAction(bên trong HomeController) luôn là một nguyên mẫu mặc dù bộ điều khiển là một singleton .


2
Vì vậy, chúng tôi không có nó bây giờ vào mùa xuân 5?
Raghuveer

16

Chỉ vì hạt đậu được tiêm vào bộ điều khiển có phạm vi nguyên mẫu không có nghĩa là bộ điều khiển!


11

@controll là một đối tượng singleton và nếu tiêm một bean nguyên mẫu vào một lớp singleton sẽ tạo ra bean nguyên mẫu cũng như singleton trừ khi bạn chỉ định sử dụng thuộc tính phương thức tra cứu thực sự tạo ra một phiên bản mới của bean nguyên mẫu cho mỗi cuộc gọi bạn thực hiện.


5

Như được đề cập bởi nicholas.hauschild tiêm bối cảnh mùa xuân không phải là một ý tưởng tốt. Trong trường hợp của bạn, @Scope ("request") là đủ để sửa nó. Nhưng giả sử bạn cần một vài trường hợp LoginActiontrong phương thức điều khiển. Trong trường hợp này, tôi khuyên bạn nên tạo bean của Nhà cung cấp ( giải pháp Spring 4 ):

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

Sau đó tiêm nó vào bộ điều khiển:

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

1
Tôi sẽ đề nghị bơm lò xo ObjectFactoryphục vụ cùng mục đích với nhà cung cấp, nhưng có thể được định nghĩa là bình thường @Beanmà theo tôi là không cần phải trả lại lambda.
xenoterracide

3

Sử dụng ApplicationContextAwarelà buộc bạn vào Spring (có thể có hoặc không có vấn đề). Tôi khuyên bạn nên chuyển qua một LoginActionFactory, mà bạn có thể yêu cầu một ví dụ mới về LoginActionmỗi lần bạn cần.


1
Mặc dù đã có chú thích dành riêng cho mùa xuân; không có vẻ như đó là một mối quan tâm
Dave Newton

1
@Dave, Điểm tốt. Có những lựa chọn thay thế cho một số nội dung DI (JSR 311), nhưng có thể khó hơn để loại bỏ bản thân mọi thứ mà Spring phụ thuộc trong ví dụ này. Tôi cho rằng tôi thực sự chỉ ủng hộ việc factory-methodở đây ...
nicholas.hauschild

1
+1 để tiêm một singleton LoginActionFactoryvào Bộ điều khiển, nhưng factory-methodcó vẻ như nó sẽ không giải quyết được vấn đề vì nó chỉ tạo ra một hạt xuân khác thông qua nhà máy. Việc tiêm hạt đậu đó vào Bộ điều khiển đơn lẻ sẽ không giải quyết được vấn đề.
Brad Cupit

Brad tốt, tôi sẽ loại bỏ đề nghị đó khỏi câu trả lời của tôi.
nicholas.hauschild

3

sử dụng phạm vi yêu cầu @Scope("request")để nhận bean cho từng yêu cầu hoặc @Scope("session")để nhận bean cho mỗi phiên 'người dùng'


1

Một hạt protoype được tiêm bên trong một hạt singelton sẽ hoạt động giống như singelton cho đến khi được gọi một cách công khai để tạo một thể hiện mới bằng cách lấy bean.

context.getBean("Your Bean")

0

@Component

@Scope (value = "nguyên mẫu")

lớp học công cộng TennisCoach thực hiện Coach {

// một số mã

}


0

Bạn có thể tạo lớp tĩnh bên trong bộ điều khiển của mình như thế này:

    @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

Theo mặc định, đậu mùa xuân là singletons. Vấn đề phát sinh khi chúng ta cố gắng dây đậu ở các phạm vi khác nhau. Ví dụ, một nguyên mẫu đậu thành một singleton. Điều này được gọi là vấn đề tiêm đậu phạm vi.

Một cách khác để giải quyết vấn đề là phương pháp tiêm với chú thích @Lookup .

Đây là một bài viết hay về vấn đề tiêm đậu nguyên mẫu vào một cá thể đơn lẻ với nhiều giải pháp.

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


-11

Bộ điều khiển của bạn cũng cần @Scope("prototype")xác định

như thế này:

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

}

1
Tại sao bạn nghĩ bộ điều khiển cũng cần phải là nguyên mẫu?
Jigar Parekh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.