ทำความเข้าใจเกี่ยวกับ Spring @Autowired การใช้งาน


309

ฉันกำลังอ่านเอกสารอ้างอิง spring 3.0.x เพื่อทำความเข้าใจคำอธิบายประกอบของ Spring Autowired:

3.9.2 @Autowired และ @Inject

ฉันไม่สามารถเข้าใจตัวอย่างด้านล่าง เราต้องทำอะไรบางอย่างใน XML เพื่อให้มันใช้งานได้

ตัวอย่าง 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

ตัวอย่าง 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

ทั้งสองคลาสสามารถนำ autowired ไปใช้งานในส่วนต่อประสานที่เหมือนกันและใช้คลาสเดียวกันได้อย่างไร?

ตัวอย่าง:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

วิธีการออกแบบแบบใดจะถูกเรียก? ฉันจะแน่ใจได้อย่างไรว่าวิธีการออกแบบของคลาสสีแดงจะถูกเรียกใช้และไม่ใช่บลู

คำตอบ:


542

TL; DR

คำอธิบายประกอบ @Autowired สำรองอะไหล่คุณจำเป็นต้องทำการเดินสายด้วยตัวเองในไฟล์ XML (หรือวิธีอื่น ๆ ) และเพียงแค่หาสิ่งที่คุณจำเป็นต้องฉีดที่ไหนและทำเพื่อคุณ

คำอธิบายแบบเต็ม

@Autowiredบันทึกย่อช่วยให้คุณสามารถข้ามการกำหนดค่าอื่น ๆ ของสิ่งที่จะฉีดและเพียงแค่ไม่ได้สำหรับคุณ สมมติว่าแพ็คเกจของคุณคือcom.mycompany.moviesคุณต้องใส่แท็กนี้ลงใน XML (ไฟล์บริบทของแอปพลิเคชัน):

<context:component-scan base-package="com.mycompany.movies" />

แท็กนี้จะทำการสแกนอัตโนมัติ สมมติว่าแต่ละคลาสที่ต้องกลายเป็นถั่วนั้นมีหมายเหตุประกอบที่ถูกต้องเช่น@Component(สำหรับถั่วอย่างง่าย) หรือ@Controller(สำหรับการควบคุม servlet) หรือ@Repository(สำหรับDAOคลาส) และคลาสเหล่านี้อยู่ที่ไหนสักแห่งภายใต้แพ็คเกจcom.mycompany.moviesสปริงจะค้นหาสิ่งเหล่านี้ทั้งหมดและสร้าง ถั่วสำหรับแต่ละคน สิ่งนี้ทำในการสแกน 2 ครั้งของคลาส - ครั้งแรกที่มันแค่ค้นหาคลาสที่ต้องกลายเป็น bean และแมปการฉีดที่ต้องทำและในการสแกนครั้งที่สองมันจะอัดถั่ว แน่นอนคุณสามารถกำหนด beans ของคุณในไฟล์ XML แบบดั้งเดิมหรือด้วยคลาส@Configuration (หรือการรวมกันของทั้งสาม)

@Autowiredคำอธิบายประกอบบอกว่าฤดูใบไม้ผลิที่ฉีดความต้องการที่จะเกิดขึ้น หากคุณใส่วิธีการsetMovieFinderนั้นจะเข้าใจ (โดยคำนำหน้าset+ @Autowiredคำอธิบายประกอบ) ที่ถั่วจะต้องถูกฉีด ในการสแกนครั้งที่สอง Spring จะค้นหาถั่วชนิดหนึ่งMovieFinderและหากพบถั่วชนิดนั้นมันจะฉีดลงในวิธีนี้ Exceptionหากพบสองถั่วเช่นคุณจะได้รับ เพื่อหลีกเลี่ยงการExceptionคุณสามารถใช้@Qualifierคำอธิบายประกอบและบอกว่าถั่วใดในสองเม็ดที่จะฉีดด้วยวิธีต่อไปนี้:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

หรือถ้าคุณต้องการประกาศ beans ใน XML ของคุณก็จะมีลักษณะดังนี้:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

ในการ@Autowiredประกาศคุณจะต้องเพิ่มการ@Qualifierบอกว่าถั่วสองสีใดที่จะฉีด:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

หากคุณไม่ต้องการใช้คำอธิบายประกอบสองรายการ ( @Autowiredและ@Qualifier) คุณสามารถใช้@Resourceเพื่อรวมสองสิ่งนี้:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

@Resource(คุณสามารถอ่านข้อมูลพิเศษบางอย่างเกี่ยวกับมันในความคิดเห็นแรกในคำตอบนี้) อะไหล่คุณใช้สองคำอธิบายประกอบและแทนที่จะคุณจะใช้อย่างใดอย่างหนึ่ง

ฉันจะเพิ่มความคิดเห็นอีกสองรายการ:

  1. การปฏิบัติที่ดีที่จะใช้@Injectแทน@Autowiredเพราะมันไม่ได้เป็นฤดูใบไม้ผลิที่เฉพาะเจาะจงและเป็นส่วนหนึ่งของJSR-330มาตรฐาน
  2. การฝึกฝนที่ดีอีกวิธีหนึ่งคือการใส่@Inject/ @Autowiredบนตัวสร้างแทนวิธีการ หากคุณวางไว้บนนวกรรมิกคุณสามารถตรวจสอบว่าถั่วที่ถูกฉีดนั้นไม่เป็นโมฆะและล้มเหลวอย่างรวดเร็วเมื่อคุณพยายามเริ่มต้นแอปพลิเคชันและหลีกเลี่ยงการNullPointerExceptionเมื่อคุณจำเป็นต้องใช้ถั่วจริง

อัปเดต : เพื่อให้ภาพสมบูรณ์ฉันสร้างคำถามใหม่เกี่ยวกับ@Configurationชั้นเรียน


6
เพียงเพื่อให้คำตอบที่ยอดเยี่ยมของคุณเสร็จสมบูรณ์: '@Resource' เป็นส่วนหนึ่งของมาตรฐาน JSR-250 และมีความหมายพิเศษเหนือกว่าการฉีดง่าย ๆ (ดังที่คุณได้กล่าวว่า '@Autowired' มาจาก Spring และ '@Inject' เป็นส่วนหนึ่งของ JSR-330) :)
อิกนาชิโอรูบิโอ

หากMovieFinderเป็นส่วนต่อMovieFinderImplประสานและเรามี bean สำหรับ(bean id = movieFinder) สปริงจะฉีดอัตโนมัติตามประเภทหรือตามชื่อ?
Jaskey

@jaskey - @Qualifierมันขึ้นอยู่กับว่าคุณใช้ ถ้าคุณทำตามชื่อถ้าไม่ใช่ - ตามประเภท By-type จะทำงานได้ก็ต่อเมื่อคุณมีถั่วชนิดเดียวMovieFinderในบริบทของคุณ มากกว่า 1 จะนำไปสู่ข้อยกเว้น
Avi

@Avi คำตอบที่ดีเลิศ แต่ผมไม่เข้าใจว่า@Autowiredคำอธิบายประกอบทำงานในprepareวิธีการในตัวอย่างที่ 2 มันกำลังเริ่มต้นMovieRecommenderแต่ในทางเทคนิคแล้วมันไม่ใช่ตัวตั้งค่า
Karan Chadha

@ KaranChadha - @Autowiredยังใช้งานได้กับตัวสร้าง พบการอ้างอิงที่ต้องการและส่งไปยังตัวสร้าง
Avi

21

ไม่มีสิ่งใดในตัวอย่างที่บอกว่า "คลาสที่ใช้อินเทอร์เฟซเดียวกัน" MovieCatalogเป็นประเภทและCustomerPreferenceDaoเป็นประเภทอื่น ฤดูใบไม้ผลิสามารถแยกพวกเขาออกจากกันได้อย่างง่ายดาย

ใน Spring 2.x การเดินสายถั่วส่วนใหญ่เกิดขึ้นผ่าน bean ID หรือชื่อ สิ่งนี้ยังคงได้รับการสนับสนุนจาก Spring 3.x แต่บ่อยครั้งคุณจะมีอินสแตนซ์หนึ่งของถั่วที่มีประเภทบางประเภท - บริการส่วนใหญ่เป็นแบบซิงเกิล การสร้างชื่อสำหรับชื่อนั้นน่าเบื่อ ดังนั้น Spring จึงเริ่มให้การสนับสนุน "autowire ตามประเภท"

ตัวอย่างที่แสดงคือวิธีการต่าง ๆ ที่คุณสามารถใช้ในการอัดถั่วลงในฟิลด์วิธีการและตัวสร้าง

XML มีข้อมูลทั้งหมดที่ Spring ต้องการอยู่แล้วเนื่องจากคุณต้องระบุชื่อคลาสแบบเต็มในแต่ละ bean คุณจะต้องระมัดระวังในการใช้อินเตอร์เฟสเป็นอย่างดี:

การลงทะเบียนอัตโนมัตินี้จะล้มเหลว:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

เนื่องจาก Java ไม่ได้เก็บชื่อพารามิเตอร์ไว้ในรหัสไบต์ดังนั้น Spring จึงไม่สามารถแยกความแตกต่างระหว่างสองถั่วได้อีกต่อไป การแก้ไขคือการใช้@Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }

@AaronDigulla นั่นเป็นเรื่องดี อย่างไรก็ตามฉันต้องการทราบว่าคุณเรียกใช้ฟังก์ชันprepareอย่างไรพารามิเตอร์ใดบ้างที่จะใช้เพื่อเรียกใช้ฟังก์ชันนี้
Nguyen Quang Anh

@ NguyenQuangAnh ฉันไม่ได้เรียกวิธีการที่ Spring จะทำเมื่อสร้างถั่ว สิ่งนี้จะเกิดขึ้นอย่างแน่นอนเมื่อ@Autowiredมีการฉีดเขตข้อมูล สปริงจะเห็นว่าจำเป็นต้องใช้พารามิเตอร์และจะใช้กฎเดียวกันกับที่ใช้ในการฉีดภาคสนามเพื่อค้นหาพารามิเตอร์
Aaron Digulla

5

ใช่คุณสามารถกำหนดค่าไฟล์สปริงบริบท XML ของ servlet เพื่อกำหนด beans ของคุณ (เช่นคลาส) เพื่อให้สามารถทำการฉีดอัตโนมัติให้คุณได้ อย่างไรก็ตามโปรดทราบว่าคุณต้องทำการกำหนดค่าอื่น ๆ เพื่อให้ Spring up และ run และวิธีที่ดีที่สุดในการทำเช่นนั้นคือการทำตามขั้นตอนการสอน

เมื่อคุณกำหนดค่า Spring เรียบร้อยแล้วคุณสามารถทำสิ่งต่อไปนี้ในไฟล์ xml บริบท servlet Spring ของคุณสำหรับตัวอย่างที่ 1 ด้านบนเพื่อทำงาน (โปรดเปลี่ยนชื่อแพ็กเกจของcom.moviesเป็นชื่อแพ็กเกจที่แท้จริงคืออะไร class จากนั้นตรวจสอบให้แน่ใจว่าไฟล์ jar ที่เหมาะสมอยู่บน classpath):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

หรือถ้าคลาส MovieFinder มีคอนสตรัคเตอร์ที่มีค่าดั้งเดิมคุณก็สามารถทำสิ่งนี้ได้

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

หรือถ้าคลาส MovieFinder มีตัวสร้างที่คาดว่าจะมีคลาสอื่นคุณก็สามารถทำสิ่งนี้ได้

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... โดยที่ ' otherBeanRef ' เป็นถั่วอื่นที่มีการอ้างอิงถึงคลาสที่คาดหวัง


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