Bagaimana cara kerja kabel otomatis di Spring?


510

Saya agak bingung bagaimana cara inversi kontrol ( IoC) bekerja Spring.

Katakanlah saya memiliki kelas layanan yang disebut UserServiceImplyang mengimplementasikan UserServiceantarmuka.

Bagaimana ini terjadi @Autowired?

Dan di dalam saya Controllers, bagaimana saya instantiatemendapatkan instancelayanan ini?

Apakah saya akan melakukan yang berikut?

UserService userService = new UserServiceImpl();

Jawaban:


703

Pertama, dan yang paling penting - semua kacang Spring dikelola - mereka "hidup" di dalam wadah, yang disebut "konteks aplikasi".

Kedua, setiap aplikasi memiliki titik masuk ke konteks itu. Aplikasi web memiliki Servlet, JSF menggunakan el-resolver, dll. Juga, ada tempat di mana konteks aplikasi di-bootstrap dan semua kacang - otomatis. Dalam aplikasi web ini bisa menjadi pendengar startup.

Autowiring terjadi dengan menempatkan sebuah instance dari satu kacang ke dalam bidang yang diinginkan dalam sebuah instance dari kacang lain. Kedua kelas harus kacang, yaitu mereka harus didefinisikan untuk hidup dalam konteks aplikasi.

Apa yang "hidup" dalam konteks aplikasi? Ini berarti bahwa konteksnya instantiates objek, bukan Anda. Yaitu - Anda tidak pernah membuat new UserServiceImpl()- wadah menemukan setiap titik injeksi dan menetapkan contoh di sana.

Di pengontrol Anda, Anda hanya memiliki yang berikut ini:

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

    }
}

Beberapa catatan:

  • Dalam Anda applicationContext.xmlAnda harus mengaktifkan <context:component-scan>sehingga kelas-scan untuk @Controller, @Service, dll penjelasan.
  • Titik masuk untuk aplikasi Spring-MVC adalah DispatcherServlet, tetapi tersembunyi dari Anda, dan karenanya interaksi langsung dan bootstrap konteks aplikasi terjadi di belakang layar.
  • UserServiceImpljuga harus didefinisikan sebagai kacang - baik menggunakan <bean id=".." class="..">atau menggunakan @Serviceanotasi. Karena itu akan menjadi satu-satunya implementor UserService, itu akan disuntikkan.
  • Terlepas dari @Autowiredanotasi, Spring dapat menggunakan autowiring yang dapat dikonfigurasi dengan XML. Dalam hal itu, semua bidang yang memiliki nama atau tipe yang cocok dengan kacang yang ada secara otomatis mendapatkan kacang disuntikkan. Bahkan, itu adalah ide awal autowiring - untuk memiliki bidang yang disuntikkan dengan dependensi tanpa konfigurasi apa pun. Anotasi lain seperti @Inject, @Resourcejuga bisa digunakan.

7
ya, UserServiceImpl dijelaskan dengan Layanan, dan UserService adalah antarmuka
Bozho

16
lingkup default adalah singleton, jadi Anda hanya akan memiliki satu instance dari kacang, yang disuntikkan di beberapa tempat. Jika Anda secara eksplisit mendefinisikan ruang lingkup menjadi "prototipe", maka beberapa contoh akan ada, mungkin malas (tergantung pada konfigurasi)
Bozho

2
Terima kasih banyak untuk posting Anda, itu benar-benar beres untuk saya. Mengenai 'Karena itu akan menjadi satu-satunya implementor atau UserService, itu akan disuntikkan.' - bagaimana jika ada beberapa kelas yang mengimplementasikan Layanan Pengguna? Bagaimana Spring mengetahui implementasi yang harus digunakan?
Shishigami

7
jika ada yang ditunjuk sebagai "primer", ia menggunakannya. Kalau tidak, itu melempar pengecualian
Bozho

3
tidak, userService dibuat hanya sekali, itu dalam ruang lingkup tunggal
Bozho

64

Tergantung pada apakah Anda ingin rute penjelasan atau rute definisi XML kacang.

Katakanlah Anda sudah mendefinisikan kacang di applicationContext.xml :

<beans ...>

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

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

</beans>

Autowiring terjadi ketika aplikasi dijalankan. Jadi, difooController , yang demi argumen ingin menggunakan UserServiceImplkelas, Anda akan memberi anotasi sebagai berikut:

public class FooController {

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

    // rest of class goes here
}

Saat dilihat @Autowired , Spring akan mencari kelas yang cocok dengan properti di applicationContext, dan menyuntikkannya secara otomatis. Jika Anda memiliki lebih dari satu UserServicekacang, maka Anda harus memenuhi syarat kacang mana yang harus digunakan.

Jika Anda melakukan hal berikut:

UserService service = new UserServiceImpl();

Ini tidak akan mengambil @Autowiredkecuali Anda mengaturnya sendiri.


2
Jadi apa gunanya mendefinisikan bean iddi applicationContext.xml. Kita harus mendefinisikan userServicevariabel dengan UserServicetipe. Jadi mengapa membuat entri dalam xmlfile.
viper

20

@Autowired adalah anotasi yang diperkenalkan di Spring 2.5, dan hanya digunakan untuk injeksi.

Sebagai contoh:

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
Ini tidak dapat dikompilasi dan umumnya salah. @Autowiredtidak berarti bahwa "Anda dapat menggunakan semua fungsi (metode) dan variabel dalam Bkelas dari kelas A". Apa yang dilakukannya adalah membawa sebuah contoh dari Ake contoh B, sehingga Anda dapat melakukan a.getId()dari B.
Dmitry Minkovsky

@dimadima Jadi jika ia melakukan System.out.println ("Nilai id membentuk kelas A" + a.getId ()) ;, dan tidak seperti yang telah ia lakukan sebenarnya akan lebih benar. Harap balas, karena yang ini jelas bagi saya secara intuitif dan sesuai dengan tingkat pemahaman saya saat ini sedang menjelaskan Autowiring.
John Doe

anotasi otomatis diperkenalkan pada musim semi 2.5 docs.spring.io/spring-framework/docs/2.5.x/api/org/…
SpringLearner

1
Untuk memahami lebih baik karena saya baru dengan ini, akankah @autowired instantiate Kelas A menggunakan konstruktor default? JIKA tidak, bagaimana nilai-nilai akan dipakai dalam kacang atau layanan jika kita menggunakan autowired. Saya kira jika itu memanggil konstruktor default, mengapa menggunakan autowiring di tempat pertama, lakukan saja A a = new A (). Mohon klarifikasi?
Sameer

@Sameer Dengan dependensi Autowiring Anda dapat menyimpan banyak kode boilerplate dalam Tes Unit Anda dan juga Kelas Kontroler, Layanan, dan Dao, karena instantiasi bidang disertakan secara otomatis. Tidak perlu memanggil konstruktor.
kiltek

9

Bagaimana cara @Autowiredkerjanya secara internal?

Contoh:

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

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

File .xml akan terlihat sama jika tidak menggunakan @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>

Jika Anda menggunakan @Autowireditu:

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

File .xml akan terlihat sama jika tidak menggunakan @Autowired:

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

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

Jika masih ada keraguan, silakan ikuti demo langsung di bawah ini

Bagaimana cara @Autowired bekerja secara internal?


6

Anda hanya perlu membubuhi keterangan kelas layanan Anda UserServiceImpldengan anotasi:

@Service("userService")

Kontainer pegas akan menjaga siklus hidup kelas ini saat mendaftar sebagai layanan.

Kemudian pada pengontrol Anda, Anda dapat secara otomatis mengirim (instantiate) dan menggunakan fungsinya:

@Autowired
UserService userService;

3

Injeksi ketergantungan pegas membantu Anda menghapus kopling dari kelas Anda. Alih-alih membuat objek seperti ini:

UserService userService = new UserServiceImpl();

Anda akan menggunakan ini setelah memperkenalkan DI:

@Autowired
private UserService userService;

Untuk mencapai ini, Anda perlu membuat kacang layanan Anda di ServiceConfigurationfile Anda . Setelah itu Anda perlu mengimpor ServiceConfigurationkelas itu ke WebApplicationConfigurationkelas Anda sehingga Anda dapat autowire kacang itu ke Controller Anda seperti ini:

public class AccController {

    @Autowired
    private UserService userService;
} 

Anda dapat menemukan contoh POC berbasis konfigurasi java di sini .


1

Cara standar:

@RestController
public class Main {
    UserService userService;

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

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

Antarmuka layanan pengguna:

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

Kelas UserServiceImpl:

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

Keluaran: Example test UserServiceImpl

Itu adalah contoh yang bagus dari kelas berpasangan ketat, contoh desain yang buruk dan akan ada masalah dengan pengujian (PowerMockito juga buruk).

Sekarang mari kita lihat injeksi ketergantungan SpringBoot, contoh yang bagus dari kopling longgar:

Antarmuka tetap sama,

Kelas utama:

@RestController
public class Main {
    UserService userService;

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

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

Kelas ServiceUserImpl:

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

Keluaran: Example test UserServiceImpl

dan sekarang mudah untuk menulis tes:

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

Saya menunjukkan @Autowiredanotasi pada konstruktor tetapi juga dapat digunakan pada setter atau bidang.


0

Seluruh konsep inversi kontrol berarti Anda bebas dari tugas untuk membuat instance objek secara manual dan memberikan semua dependensi yang diperlukan. Ketika Anda memberi anotasi pada kelas dengan anotasi yang sesuai (mis. @ServiceSpring akan secara otomatis membuat objek untuk Anda. Jika Anda tidak terbiasa dengan anotasi, Anda juga dapat menggunakan file XML. Namun, itu bukan ide yang buruk untuk membuat instance kelas secara manual (dengan newkata kunci) dalam unit test ketika Anda tidak ingin memuat seluruh konteks pegas.


0

Ingatlah bahwa Anda harus mengaktifkan @Autowiredanotasi dengan menambahkan elemen <context:annotation-config/>ke dalam file konfigurasi pegas. Ini akan mendaftar AutowiredAnnotationBeanPostProcessoryang menangani pemrosesan anotasi.

Dan kemudian Anda dapat autowire layanan Anda dengan menggunakan metode injeksi lapangan.

public class YourController{

 @Autowired
 private UserService userService; 

}

Saya menemukan ini dari pos Spring @autowired annotation


0

Ada 3 cara Anda bisa membuat instance menggunakan @Autowired.

1. @Autowiredpada Properties

Anotasi dapat digunakan langsung pada properti, sehingga menghilangkan kebutuhan getter dan setter:

    @Component("userService")
    public class UserService {

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

    @Component
    public class UserController {

        @Autowired
        UserService userService

    }

Dalam contoh di atas, Spring mencari dan menyuntikkan userServicesaat UserControllerdibuat.

2. @Autowiredtentang Setter

The @Autowiredanotasi dapat digunakan pada metode setter. Dalam contoh di bawah ini, ketika anotasi digunakan pada metode setter, metode setter dipanggil dengan instance userServicekapan UserControllerdibuat:

public class UserController {

    private UserService userService;

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

3. @Autowiredtentang Konstruktor

The @Autowiredpenjelasan juga dapat digunakan pada konstruktor. Dalam contoh di bawah ini, ketika anotasi digunakan pada konstruktor, turunan userServicedisuntikkan sebagai argumen ke konstruktor saat UserControllerdibuat:

public class UserController {

    private UserService userService;

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

0

Dengan kata-kata sederhana Autowiring, tautan kabel secara otomatis, kini muncul pertanyaan siapa yang melakukan ini dan jenis kabel apa. Jawabannya adalah: Kontainer melakukan ini dan jenis kabel sekunder didukung, primitif perlu dilakukan secara manual.

Pertanyaan: Bagaimana wadah tahu jenis kabel apa?

Jawaban: Kami mendefinisikannya sebagai byType, byName, constructor.

Pertanyaan: Apakah ada cara kita tidak mendefinisikan jenis autowiring?

Jawab: Ya, ada di sana dengan melakukan satu anotasi, @Autowired.

Pertanyaan: Tetapi bagaimana sistem tahu, saya harus memilih jenis data sekunder?

Jawaban: Anda akan memberikan data itu dalam file spring.xml Anda atau dengan menggunakan anotasi sterotype ke kelas Anda sehingga wadah itu sendiri dapat membuat objek untuk Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.