Làm thế nào để tự động làm việc trong mùa xuân?


510

Tôi hơi bối rối khi làm thế nào để đảo ngược điều khiển ( IoC) hoạt động Spring.

Nói rằng tôi có một lớp dịch vụ được gọi là giao diện UserServiceImplthực hiện UserService.

Làm thế nào điều này sẽ được @Autowired?

Và trong tôi Controllers, làm thế nào tôi instantiatecó thể instancecủa dịch vụ này?

Tôi chỉ cần làm như sau?

UserService userService = new UserServiceImpl();

Câu trả lời:


703

Đầu tiên và quan trọng nhất - tất cả các loại đậu mùa xuân đều được quản lý - chúng "sống" bên trong một thùng chứa, được gọi là "bối cảnh ứng dụng".

Thứ hai, mỗi ứng dụng có một điểm vào bối cảnh đó. Các ứng dụng web có Servlet, JSF sử dụng bộ giải quyết el, v.v. Ngoài ra, có một nơi mà bối cảnh ứng dụng được bootstrapping và tất cả các bean - tự động. Trong các ứng dụng web, đây có thể là một trình nghe khởi động.

Tự động xảy ra bằng cách đặt một thể hiện của một bean vào trường mong muốn trong một thể hiện của bean khác. Cả hai lớp nên là bean, tức là chúng phải được định nghĩa để sống trong ngữ cảnh ứng dụng.

"Sống" trong bối cảnh ứng dụng là gì? Điều này có nghĩa là bối cảnh khởi tạo các đối tượng, không phải bạn. Tức là - bạn không bao giờ thực hiện new UserServiceImpl()- container tìm thấy từng điểm tiêm và đặt một thể hiện ở đó.

Trong bộ điều khiển của bạn, bạn chỉ cần có những điều sau đây:

@Controller // Defines that this class is a spring bean
@RequestMapping("/users")
public class SomeController {

    // Tells the application context to inject an instance of UserService here
    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    public void login(@RequestParam("username") String username,
           @RequestParam("password") String password) {

        // The UserServiceImpl is already injected and you can use it
        userService.login(username, password);

    }
}

Một vài lưu ý:

  • Trong bạn applicationContext.xmlbạn nên kích hoạt <context:component-scan>để lớp học được quét cho @Controller, @Servicevv chú thích.
  • Điểm khởi đầu cho một ứng dụng Spring-MVC là DispatcherServlet, nhưng nó bị ẩn khỏi bạn, và do đó sự tương tác trực tiếp và bootstrapping của bối cảnh ứng dụng xảy ra đằng sau bối cảnh.
  • UserServiceImplcũng nên được định nghĩa là bean - sử dụng <bean id=".." class="..">hoặc sử dụng @Servicechú thích. Vì nó sẽ là người thực hiện duy nhất UserService, nó sẽ được tiêm.
  • Ngoài @Autowiredchú thích, Spring có thể sử dụng tính năng tự động cấu hình XML. Trong trường hợp đó, tất cả các trường có tên hoặc loại khớp với một bean hiện có sẽ tự động được chèn một bean. Trên thực tế, đó là ý tưởng ban đầu về tự động - có các trường được chèn phụ thuộc mà không có bất kỳ cấu hình nào. Các chú thích khác như @Inject, @Resourcecũng có thể được sử dụng.

7
có, UserServiceImpl được chú thích với Dịch vụ và UserService là giao diện
Bozho

16
phạm vi mặc định là singleton, vì vậy bạn sẽ chỉ có một thể hiện của bean, được chèn ở nhiều nơi. Nếu bạn xác định rõ ràng phạm vi là "nguyên mẫu", thì nhiều trường hợp sẽ tồn tại, có thể là lười biếng (tùy thuộc vào cấu hình)
Bozho

2
Cảm ơn rất nhiều cho bài viết của bạn, nó thực sự làm sáng tỏ mọi thứ cho tôi. Về 'Vì nó sẽ là người triển khai hoặc Dịch vụ người dùng duy nhất, nên nó sẽ được tiêm.' - nếu có nhiều lớp triển khai Userservice thì sao? Làm thế nào để Spring biết nên thực hiện nó nên sử dụng?
Shishigami

7
nếu có một cái được chỉ định là "chính", nó sẽ sử dụng nó. Nếu không, nó sẽ ném một ngoại lệ
Bozho

3
không, dịch vụ người dùng chỉ được tạo một lần, trong phạm vi đơn lẻ
Bozho

64

Phụ thuộc vào việc bạn muốn tuyến chú thích hay tuyến định nghĩa XML bean.

Giả sử bạn đã có các loại đậu được định nghĩa trong applicationContext.xml:

<beans ...>

    <bean id="userService" class="com.foo.UserServiceImpl"/>

    <bean id="fooController" class="com.foo.FooController"/>

</beans>

Việc tự động xảy ra khi ứng dụng khởi động. Vì vậy, trong fooController, đối với các đối số vì muốn sử dụng UserServiceImpllớp, bạn sẽ chú thích nó như sau:

public class FooController {

    // You could also annotate the setUserService method instead of this
    @Autowired
    private UserService userService;

    // rest of class goes here
}

Khi thấy @Autowired, Spring sẽ tìm một lớp khớp với thuộc tính trong applicationContextvà tự động tiêm nó. Nếu bạn có nhiều hơn một UserServicehạt, thì bạn sẽ phải đủ điều kiện nên sử dụng loại đậu nào.

Nếu bạn làm như sau:

UserService service = new UserServiceImpl();

Nó sẽ không nhận @Autowiredtrừ khi bạn tự đặt nó.


2
Vì vậy, việc sử dụng định nghĩa bean idtrong là gì applicationContext.xml. Chúng ta sẽ phải xác định userServicebiến với UserServiceloại. Vậy tại sao thực hiện nhập vào xmltập tin.
viper

20

@Autowired là một chú thích được giới thiệu vào Mùa xuân 2.5 và nó chỉ được sử dụng để tiêm.

Ví dụ:

class A {

    private int id;

    // With setter and getter method
}

class B {

    private String name;

    @Autowired // Here we are injecting instance of Class A into class B so that you can use 'a' for accessing A's instance variables and methods.
    A a;

    // With setter and getter method

    public void showDetail() {
        System.out.println("Value of id form A class" + a.getId(););
    }
}

10
Điều này sẽ không được biên dịch và thường không chính xác. @Autowiredkhông có nghĩa là "bạn có thể sử dụng tất cả hàm (phương thức) và biến trong Blớp từ lớp A". Những gì nó làm là đưa một thể hiện Avào các thể hiện của B, vì vậy bạn có thể làm a.getId()từ đó B.
Dmitry Minkovsky

@dimadima Vì vậy, nếu anh ấy thực hiện System.out.println ("Giá trị của mẫu id A class" + a.getId ());, và không phải như anh ấy đã thực sự làm điều đó sẽ đúng hơn. Xin vui lòng trả lời, vì điều này là rõ ràng trực giác với tôi và theo mức độ hiểu biết hiện tại của tôi đang giải thích Autowires.
John Doe

chú thích tự động được giới thiệu vào mùa xuân 2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/ Kẻ
SpringLearner

1
Để hiểu rõ hơn vì tôi chưa quen với điều này, liệu @autowired có thể khởi tạo Class A bằng cách sử dụng hàm tạo mặc định không? NẾU không, làm thế nào để các giá trị được khởi tạo trong một bean hoặc dịch vụ nếu chúng ta sử dụng autowired. Tôi đoán nếu nó gọi hàm tạo mặc định, tại sao lại sử dụng tự động ở vị trí đầu tiên, chỉ cần thực hiện A a = new A (). Vui lòng làm rõ?
Sameer

@Sameer Bằng cách phụ thuộc Autowires, bạn có thể lưu rất nhiều mã soạn sẵn trong Bài kiểm tra đơn vị và cả Lớp điều khiển, Dịch vụ và Dao, bởi vì việc khởi tạo các trường đi kèm với nó một cách tự động. Không cần phải gọi các nhà xây dựng.
kiltek

9

Làm thế nào để @Autowiredlàm việc nội bộ?

Thí dụ:

class EnglishGreeting {
   private Greeting greeting;
   //setter and getter
}

class Greeting {
   private String message;
   //setter and getter
}

Tập tin .xml sẽ trông giống nhau nếu không sử dụng @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting">
   <property name="greeting" ref="greeting"/>
</bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Nếu bạn đang sử dụng @Autowiredthì:

class EnglishGreeting {
   @Autowired //so automatically based on the name it will identify the bean and inject.
   private Greeting greeting;
   //setter and getter
}

Tập tin .xml sẽ trông giống nhau nếu không sử dụng @Autowired:

<bean id="englishGreeting" class="com.bean.EnglishGreeting"></bean>

<bean id="greeting" class="com.bean.Greeting">
   <property name="message" value="Hello World"/>
</bean>

Nếu vẫn còn một số nghi ngờ thì hãy xem qua bản demo trực tiếp bên dưới

Làm thế nào để @Autowired làm việc nội bộ?


6

Bạn chỉ cần chú thích lớp dịch vụ của bạn UserServiceImplvới chú thích:

@Service("userService")

Spring container sẽ chăm sóc vòng đời của lớp này khi nó đăng ký làm dịch vụ.

Sau đó, trong bộ điều khiển của bạn, bạn có thể tự động nối dây (khởi tạo) nó và sử dụng chức năng của nó:

@Autowired
UserService userService;

3

Spring phụ thuộc mùa xuân giúp bạn loại bỏ khớp nối từ các lớp học của bạn. Thay vì tạo đối tượng như thế này:

UserService userService = new UserServiceImpl();

Bạn sẽ sử dụng nó sau khi giới thiệu DI:

@Autowired
private UserService userService;

Để đạt được điều này, bạn cần tạo một bean dịch vụ trong ServiceConfigurationtệp của bạn . Sau đó, bạn cần nhập ServiceConfigurationlớp đó vào lớp của mình WebApplicationConfigurationđể bạn có thể tự động chuyển bean đó vào Trình điều khiển của mình như thế này:

public class AccController {

    @Autowired
    private UserService userService;
} 

Bạn có thể tìm thấy một cấu hình java dựa trên POC ở đây ví dụ .


1

Cách tiêu chuẩn:

@RestController
public class Main {
    UserService userService;

    public Main(){
        userService = new UserServiceImpl();
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Giao diện dịch vụ người dùng:

public interface UserService {
    String print(String text);
}

Lớp UserServiceImpl:

public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Đầu ra: Example test UserServiceImpl

Đó là một ví dụ tuyệt vời về các lớp kết hợp chặt chẽ, ví dụ thiết kế xấu và sẽ có vấn đề với việc thử nghiệm (PowerMockito cũng rất tệ).

Bây giờ chúng ta hãy xem xét tiêm phụ thuộc SpringBoot, ví dụ hay về khớp nối lỏng lẻo:

Giao diện vẫn giữ nguyên,

Lớp chính:

@RestController
public class Main {
    UserService userService;

    @Autowired
    public Main(UserService userService){
        this.userService = userService;
    }

    @GetMapping("/")
    public String index(){
        return userService.print("Example test");
    }
}

Lớp ServiceUserImpl:

@Component
public class UserServiceImpl implements UserService {
    @Override
    public String print(String text) {
        return text + " UserServiceImpl";
    }
}

Đầu ra: Example test UserServiceImpl

và bây giờ thật dễ dàng để viết bài kiểm tra:

@RunWith(MockitoJUnitRunner.class)
public class MainTest {
    @Mock
    UserService userService;

    @Test
    public void indexTest() {
        when(userService.print("Example test")).thenReturn("Example test UserServiceImpl");

        String result = new Main(userService).index();

        assertEquals(result, "Example test UserServiceImpl");
    }
}

Tôi đã hiển thị @Autowiredchú thích trên hàm tạo nhưng nó cũng có thể được sử dụng trên setter hoặc trường.


0

Toàn bộ khái niệm đảo ngược điều khiển có nghĩa là bạn không phải làm việc vặt để khởi tạo các đối tượng bằng tay và cung cấp tất cả các phụ thuộc cần thiết. Khi bạn chú thích lớp với chú thích thích hợp (ví dụ @Service) Spring sẽ tự động khởi tạo đối tượng cho bạn. Nếu bạn không quen thuộc với các chú thích, bạn cũng có thể sử dụng tệp XML thay thế. Tuy nhiên, không phải là ý tưởng tồi để khởi tạo các lớp theo cách thủ công (với newtừ khóa) trong các bài kiểm tra đơn vị khi bạn không muốn tải toàn bộ bối cảnh mùa xuân.


0

Hãy nhớ rằng bạn phải kích hoạt @Autowiredchú thích bằng cách thêm phần tử<context:annotation-config/> vào tệp cấu hình mùa xuân. Điều này sẽ đăng ký AutowiredAnnotationBeanPostProcessormà chăm sóc việc xử lý chú thích.

Và sau đó bạn có thể tự động cung cấp dịch vụ của mình bằng cách sử dụng phương pháp tiêm trường.

public class YourController{

 @Autowired
 private UserService userService; 

}

Tôi tìm thấy điều này từ bài viết Chú thích mùa xuân @autowired


0

Có 3 cách bạn có thể tạo một cá thể bằng cách sử dụng @Autowired .

1. @Autowired trên Thuộc tính

Chú thích có thể được sử dụng trực tiếp trên các thuộc tính, do đó loại bỏ sự cần thiết của getters và setters:

    @Component("userService")
    public class UserService {

        public String getName() {
            return "service name";
        }
    }

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

Trong ví dụ trên, Spring tìm và tiêm userServicekhiUserController được tạo.

2. @Autowiredtrên Setters

Các @Autowiredchú thích có thể được sử dụng trên các phương pháp setter. Trong ví dụ dưới đây, khi chú thích được sử dụng trên phương thức setter, phương thức setter được gọi với thể hiện userServicekhi UserControllerđược tạo:

public class UserController {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
            this.userService = userService;
    }
}

3. @Autowiredvề xây dựng

Các @Autowiredchú thích cũng có thể được sử dụng trên nhà xây dựng. Trong ví dụ dưới đây, khi chú thích được sử dụng trên hàm tạo, một thể hiện của userServiceđược thêm vào làm đối số cho hàm tạo khi UserControllerđược tạo:

public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService= userService;
    }
}

0

Nói một cách đơn giản, Autowires, nối dây tự động, bây giờ đến câu hỏi ai làm điều này và loại dây nào. Trả lời là: Container thực hiện điều này và loại dây thứ cấp được hỗ trợ, nguyên thủy cần phải được thực hiện thủ công.

Câu hỏi: Làm thế nào container biết loại dây nào?

Trả lời: Chúng tôi định nghĩa nó là byType, byName, constructor.

Câu hỏi: Có cách nào chúng ta không xác định loại tự động không?

Trả lời: Có, nó ở đó bằng cách thực hiện một chú thích, @Autowired.

Câu hỏi: Nhưng làm thế nào để biết hệ thống, tôi cần chọn loại dữ liệu thứ cấp này?

Trả lời: Bạn sẽ cung cấp dữ liệu đó trong tệp spring.xml của bạn hoặc bằng cách sử dụng các chú thích sterotype cho lớp của bạn để bộ chứa có thể tự tạo các đối tượng cho bạn.

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.