การลงทะเบียนอัตโนมัติทำงานอย่างไรใน Spring


510

ฉันสับสนเล็กน้อยว่าการกลับตัวของการควบคุม ( IoC) ทำงานSpringอย่างไร

สมมติว่าฉันมีคลาสบริการที่เรียกUserServiceImplว่าใช้UserServiceอินเทอร์เฟซ

สิ่งนี้จะเป็น@Autowiredอย่างไร

และในฉันฉันControllersจะได้instantiateรับinstanceบริการนี้อย่างไร?

ฉันจะทำสิ่งต่อไปนี้หรือไม่?

UserService userService = new UserServiceImpl();

คำตอบ:


703

ก่อนอื่นและสำคัญที่สุด - Spring beans ทั้งหมดได้รับการจัดการ - พวกเขา "สด" ภายในคอนเทนเนอร์ที่เรียกว่า

ประการที่สองแต่ละแอปพลิเคชันมีจุดเริ่มต้นไปยังบริบทนั้น แอปพลิเคชั่นบนเว็บมี Servlet, JSFใช้ตัวแก้ไขปัญหา el, ฯลฯ นอกจากนี้ยังมีสถานที่ที่บริบทของแอปพลิเคชันถูก bootstrapped และ bean ทั้งหมด - autowired ในเว็บแอปพลิเคชันสิ่งนี้สามารถเป็นผู้ฟังเริ่มต้นได้

การป้อนอัตโนมัติเกิดขึ้นโดยการวางอินสแตนซ์ของหนึ่งถั่วลงในฟิลด์ที่ต้องการในอินสแตนซ์ของอีกถั่วหนึ่ง ทั้งสองคลาสควรเป็น beans เช่นควรกำหนดให้อยู่ในบริบทของแอ็พพลิเคชัน

"การใช้ชีวิต" ในบริบทของแอปพลิเคชันคืออะไร ซึ่งหมายความว่าบริบทยกตัวอย่างวัตถุไม่ใช่คุณ new UserServiceImpl()นั่นคือ - คุณไม่เคยทำ- ภาชนะค้นหาจุดฉีดแต่ละจุดและตั้งค่าอินสแตนซ์ที่นั่น

ในตัวควบคุมของคุณคุณมีดังต่อไปนี้:

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

    }
}

หมายเหตุเล็กน้อย:

  • ในของapplicationContext.xmlคุณคุณควรเปิดใช้งาน<context:component-scan>เพื่อให้มีการสแกนคลาสสำหรับคำอธิบายประกอบ@Controller, @Serviceและอื่น ๆ
  • จุดเริ่มต้นสำหรับแอปพลิเคชัน Spring-MVC คือ DispatcherServlet แต่มันถูกซ่อนไว้จากคุณและด้วยเหตุนี้การโต้ตอบโดยตรงและการบูตของบริบทแอปพลิเคชันจึงเกิดขึ้นเบื้องหลัง
  • UserServiceImplควรถูกกำหนดเป็น bean ไม่ว่าจะใช้<bean id=".." class="..">หรือใช้@Serviceคำอธิบายประกอบ เนื่องจากมันจะเป็นผู้ดำเนินการเพียงอย่างเดียวของUserServiceมันจึงจะถูกฉีด
  • นอกเหนือจากการเพิ่มความคิดเห็น@Autowiredแล้ว Spring ยังสามารถใช้การเรียกอัตโนมัติที่กำหนดค่าได้ XML ในกรณีดังกล่าวทุกฟิลด์ที่มีชื่อหรือประเภทที่ตรงกับถั่วที่มีอยู่จะได้รับการฉีดถั่วโดยอัตโนมัติ ในความเป็นจริงนั่นเป็นแนวคิดเริ่มต้นของการเรียกใช้อัตโนมัติ - เพื่อให้ฟิลด์ถูกแทรกด้วยการพึ่งพาโดยไม่มีการกำหนดค่าใด ๆ คำอธิบายประกอบอื่น ๆ เช่น@Inject, @Resourceนอกจากนี้ยังสามารถนำมาใช้

7
ใช่ UserServiceImpl ได้รับการเพิ่มความคิดเห็นด้วย Service และ UserService เป็นส่วนต่อประสาน
Bozho

16
ขอบเขตเริ่มต้นคือซิงเกิลดังนั้นคุณจะมีอินสแตนซ์เดียวของ bean ซึ่งถูกแทรกในหลาย ๆ ที่ หากคุณอย่างชัดเจนกำหนดขอบเขตจะเป็น "ต้นแบบ" จากนั้นหลายกรณีจะมีอยู่อาจจะขี้เกียจ (ขึ้นอยู่กับการตั้งค่า)
Bozho

2
ขอบคุณมากสำหรับการโพสต์ของคุณมันล้างสิ่งต่าง ๆ ให้ฉัน เกี่ยวกับ 'เนื่องจากจะเป็นผู้ดำเนินการหรือผู้ใช้บริการเดียวเท่านั้นจึงจะถูกส่งไป' - จะเป็นอย่างไรถ้ามีหลายคลาสที่ใช้บริการของผู้ใช้ ฤดูใบไม้ผลิรู้ได้อย่างไรว่าการนำไปใช้งานแบบใดควรใช้อย่างไร?
Shishigami

7
หากมีสิ่งใดสิ่งหนึ่งที่ถูกกำหนดให้เป็น "หลัก" มันจะใช้มัน มิฉะนั้นจะส่งข้อยกเว้น
Bozho

3
ไม่ได้สร้าง userService เพียงครั้งเดียวมันอยู่ในขอบเขต
Bozho

64

ขึ้นอยู่กับว่าคุณต้องการเส้นทางหมายเหตุประกอบหรือเส้นทางนิยามถั่ว XML

สมมติว่าคุณได้กำหนดถั่วไว้ในapplicationContext.xml:

<beans ...>

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

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

</beans>

การบันทึกอัตโนมัติเกิดขึ้นเมื่อแอปพลิเคชันเริ่มต้นขึ้น ดังนั้นในfooControllerข้อโต้แย้งที่ต้องการใช้UserServiceImplคลาสคุณต้องใส่คำอธิบายประกอบดังต่อไปนี้:

public class FooController {

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

    // rest of class goes here
}

เมื่อเห็น@Autowiredแล้วสปริงจะมองหาคลาสที่ตรงกับคุณสมบัติในapplicationContextและฉีดให้โดยอัตโนมัติ หากคุณมีUserServiceถั่วมากกว่าหนึ่งตัวคุณจะต้องมีคุณสมบัติที่ควรใช้

หากคุณทำสิ่งต่อไปนี้:

UserService service = new UserServiceImpl();

มันจะไม่รับ@Autowiredเว้นแต่คุณจะตั้งค่าด้วยตัวคุณเอง


2
ดังนั้นสิ่งที่ใช้ในการกำหนดในbean id applicationContext.xmlเราจะต้องกำหนดuserServiceตัวแปรด้วยUserServiceชนิด เหตุใดจึงต้องป้อนข้อมูลในxmlไฟล์
viper

20

@Autowired เป็นคำอธิบายประกอบที่แนะนำใน Spring 2.5 และใช้สำหรับการฉีดเท่านั้น

ตัวอย่างเช่น:

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
สิ่งนี้จะไม่รวบรวมและไม่ถูกต้องโดยทั่วไป @Autowiredไม่ได้หมายความว่า "คุณสามารถใช้ทุกฟังก์ชั่น (เมธอด) และตัวแปรในBคลาสจากคลาสA" สิ่งที่ไม่สามารถนำตัวอย่างของAเข้ามาในกรณีของBเพื่อให้คุณสามารถทำจากa.getId() B
Dmitry Minkovsky

@dimadima ดังนั้นถ้าเขาทำ System.out.println ("คุณค่าของรูปแบบ id คลาส A" + a.getId ()) ;, และถ้าไม่ใช่เขาทำจริง ๆ แล้วมันจะถูกต้องมากขึ้น โปรดตอบกลับเนื่องจากเป็นสิ่งที่ชัดเจนสำหรับฉันและตามระดับความเข้าใจปัจจุบันของฉันกำลังอธิบายการตอบรับอัตโนมัติ
John Doe

คำอธิบายประกอบแบบ autowired ถูกนำเสนอในฤดูใบไม้ผลิ 2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/ …
SpringLearner

1
เพื่อความเข้าใจที่ดีขึ้นในขณะที่ฉันยังใหม่กับสิ่งนี้ @autowired จะช่วยยกระดับ Class A โดยใช้ constructor เริ่มต้นหรือไม่ ถ้าไม่วิธีรับค่าอินสแตนซ์ใน bean หรือบริการถ้าเราใช้ autowired ฉันเดาว่ามันเรียกว่าคอนสตรัคเตอร์เริ่มต้นทำไมต้องใช้การลงทะเบียนอัตโนมัติในตอนแรกให้ทำ A = new A () กรุณาชี้แจง
Sameer

@Sameer โดยการพึ่งพา Autowiring คุณสามารถบันทึกรหัส boilerplate จำนวนมากในการทดสอบหน่วยของคุณและยังมี Controller, Service และ Dao Classes เนื่องจากการเริ่มต้นของฟิลด์จะมาพร้อมกับมันโดยอัตโนมัติ ไม่จำเป็นต้องเรียกนวกรรมิก
kiltek

9

วิธีการที่ไม่@Autowiredทำงานภายใน?

ตัวอย่าง:

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

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

ไฟล์. xml จะมีลักษณะเหมือนกันหากไม่ได้ใช้@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>

หากคุณกำลังใช้งานอยู่@Autowired:

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

ไฟล์. xml จะมีลักษณะเหมือนกันหากไม่ได้ใช้@Autowired:

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

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

หากยังมีข้อสงสัยลองดูตัวอย่างสดด้านล่าง

@Autowired ทำงานอย่างไรภายใน


6

คุณเพียงแค่ใส่คำอธิบายประกอบคลาสบริการของคุณUserServiceImplด้วยหมายเหตุ

@Service("userService")

สปริงคอนเทนเนอร์จะดูแลวงจรชีวิตของคลาสนี้เมื่อลงทะเบียนเป็นบริการ

จากนั้นในคอนโทรลเลอร์คุณสามารถต่อสายอัตโนมัติ (ยกตัวอย่าง) และใช้ฟังก์ชั่นการทำงาน:

@Autowired
UserService userService;

3

สปริงพึ่งพาการฉีดช่วยให้คุณลบข้อต่อออกจากคลาส แทนที่จะสร้างวัตถุเช่นนี้

UserService userService = new UserServiceImpl();

คุณจะใช้มันหลังจากแนะนำ DI:

@Autowired
private UserService userService;

เพื่อให้บรรลุเป้าหมายนี้คุณต้องสร้าง bean ของบริการในServiceConfigurationไฟล์ของคุณ หลังจากนั้นคุณต้องนำเข้าServiceConfigurationคลาสนั้นไปยังคลาสของคุณWebApplicationConfigurationเพื่อให้คุณสามารถรับถั่วนั้นลงในคอนโทรลเลอร์ของคุณดังนี้:

public class AccController {

    @Autowired
    private UserService userService;
} 

คุณสามารถค้นหา POC กำหนดค่า Java ตามที่นี่ ตัวอย่างเช่น


1

วิธีมาตรฐาน:

@RestController
public class Main {
    UserService userService;

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

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

ส่วนต่อประสานบริการผู้ใช้:

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

คลาส UserServiceImpl:

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

เอาท์พุท: Example test UserServiceImpl

นั่นเป็นตัวอย่างที่ดีของคลาสที่มีการเชื่อมต่อที่แน่นหนาตัวอย่างการออกแบบที่ไม่ดีและจะมีปัญหากับการทดสอบ (PowerMockito ก็ไม่ดีเช่นกัน)

ตอนนี้เรามาดูการฉีดพึ่งพา SpringBoot ตัวอย่างที่ดีของการมีเพศสัมพันธ์แบบหลวม:

อินเตอร์เฟสยังคงเหมือนเดิม

ชั้นหลัก:

@RestController
public class Main {
    UserService userService;

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

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

ServiceUserImpl คลาส:

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

เอาท์พุท: Example test UserServiceImpl

และตอนนี้มันง่ายที่จะเขียนทดสอบ:

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

ฉันแสดง@Autowiredคำอธิบายประกอบบน constructor แต่สามารถใช้กับ setter หรือ field ได้


0

แนวคิดทั้งหมดของการผกผันของการควบคุมหมายความว่าคุณเป็นอิสระจากงานบ้านเพื่อยกตัวอย่างวัตถุด้วยตนเองและให้การอ้างอิงที่จำเป็นทั้งหมด เมื่อคุณใส่คำอธิบายประกอบคลาสด้วยคำอธิบายประกอบที่เหมาะสม (เช่น@Service) Spring จะยกตัวอย่างวัตถุให้คุณโดยอัตโนมัติ หากคุณไม่คุ้นเคยกับคำอธิบายประกอบคุณสามารถใช้ไฟล์ XML แทนได้ อย่างไรก็ตามไม่ใช่ความคิดที่ดีที่จะสร้างอินสแตนซ์ของคลาสด้วยตนเอง (ด้วยnewคำหลัก) ในการทดสอบหน่วยเมื่อคุณไม่ต้องการโหลดบริบทสปริงทั้งหมด


0

โปรดทราบว่าคุณต้องเปิดใช้งาน@Autowiredคำอธิบายประกอบโดยการเพิ่มองค์ประกอบ<context:annotation-config/>ลงในไฟล์กำหนดค่าสปริง สิ่งนี้จะลงทะเบียนสิ่งAutowiredAnnotationBeanPostProcessorที่ดูแลการประมวลผลคำอธิบายประกอบ

จากนั้นคุณสามารถ autowire บริการของคุณโดยใช้วิธีการฉีดภาคสนาม

public class YourController{

 @Autowired
 private UserService userService; 

}

ฉันพบสิ่งนี้จากคำอธิบายประกอบ Spring @autowiredของโพสต์


0

มี 3 @Autowiredวิธีที่คุณสามารถสร้างตัวอย่างในการใช้เป็น

1. @Autowiredในคุณสมบัติ

การเพิ่มความคิดเห็นสามารถใช้กับคุณสมบัติโดยตรงจึงไม่จำเป็นต้องใช้ตัวรับและตัวตั้งค่า:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

ในตัวอย่างด้านบนสปริงจะค้นหาและแทรกuserServiceเมื่อUserControllerสร้าง

2. @Autowiredใน Setters

@Autowiredคำอธิบายประกอบที่สามารถใช้กับวิธีการตั้งค่า ในตัวอย่างด้านล่างเมื่อมีการใช้คำอธิบายประกอบในเมธอด setter เมธอด setter จะถูกเรียกพร้อมกับอินสแตนซ์ของuserServiceเวลาที่UserControllerสร้าง:

public class UserController {

    private UserService userService;

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

3. @Autowiredในตัวสร้าง

@Autowiredคำอธิบายประกอบนอกจากนี้ยังสามารถนำมาใช้ในการก่อสร้าง ในตัวอย่างด้านล่างเมื่อมีการใช้คำอธิบายประกอบบน Constructor อินสแตนซ์ของuserServiceจะถูกฉีดเป็นอาร์กิวเมนต์ให้กับ Constructor เมื่อUserControllerสร้าง:

public class UserController {

    private UserService userService;

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

0

ในคำง่าย ๆ Autowiring การเดินสายเชื่อมโยงโดยอัตโนมัติตอนนี้มาคำถามที่ทำสิ่งนี้และชนิดของสายไฟ คำตอบคือ: คอนเทนเนอร์นี้รองรับการเดินสายแบบทุติยภูมิและต้องมีการดำเนินการเบื้องต้นด้วยตนเอง

คำถาม: คอนเทนเนอร์รู้ได้อย่างไรว่าสายไฟประเภทใด

คำตอบ: เรากำหนดเป็น byType, byName, constructor

คำถาม: มีวิธีที่เราไม่กำหนดประเภทของการรับสายอัตโนมัติหรือไม่?

คำตอบ: ใช่มันอยู่ที่นั่นโดยทำหนึ่งคำอธิบายประกอบ @Autowired

คำถาม: แต่ระบบรู้ได้อย่างไรว่าฉันต้องเลือกข้อมูลทุติยภูมิประเภทนี้

คำตอบ: คุณจะให้ข้อมูลนั้นในไฟล์ spring.xml ของคุณหรือโดยใช้คำอธิบายประกอบ sterotype ในคลาสของคุณเพื่อให้คอนเทนเนอร์สามารถสร้างวัตถุให้คุณได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.