Spring Bean的3種裝配方式

chenhongyong發表於2019-07-02

 

Bean常用的裝配方式有3種:

  • 基於xml的裝配
  • 基於Annotation(註解)的裝配
  • 基於Java配置類的裝配

 

 

基於xml的裝配

在xml檔案中配置Bean。

如果依賴很多,xml配置檔案會很臃腫,後期維護、升級不方便。自動裝配可解決這一問題。

 

基於xml的自動裝配

在<bean>中使用autowired屬性完成依賴的自動裝配,不再使用<property>手動注入setter方法中的依賴,簡化了配置,減少了xml中的程式碼量。

autowire屬性的4個屬性值:

  • no   不使用自動裝配。預設autowire屬性時預設值就是no。
  • byName   根據setter方法的名稱來自動裝配
  • byType  根據所依來的型別(Bean)來自動裝配
  • constructor  根據建構函式的形參表的資料型別進行byType方式的自動裝配
  • default   全域性自動裝配

 

1、byName

示例:

 1 class Student{
 2 
 3     private String name;
 4 
 5     public Student(String name){
 6         this.name=name;
 7     }
 8 
 9     public String getName(){
10         return name;
11     }
12 }
13 
14 
15 class Teacher{
16     private Student student;
17 
18     public void setStudent(Student student) {
19         this.student = student;
20     }
21 
22     public void say(){
23         System.out.println(student.getName()+",叫家長來一下。");
24     }
25 }

Teacher依賴於Student,依賴的物件要寫成成員變數的形式。如果要使用自動裝配,依賴物件的注入只能使用setter方式。

byName,name指的是setXxx()注入依賴的那個xxx,比如setStudent(Student student),name指的是student,將set後面的部分提出來,變成Camel寫法。name不是指形參名的student。

 

xml中的配置:

  <bean id="student" class="my_package.Student">
        <constructor-arg value="張三" />
    </bean>

    <bean id="teacher" class="my_package.Teacher" autowire="byName" />

第一個Bean是基於xml的普通裝配,第二個Bean的配置是byName形式的自動裝配。

byName自動裝配的執行過程:在這個Bean的定義中,找到setter方法,這裡是setStudent(),其name是student(set後面部分提出來,變成Camel寫法),根據這個name(student)找打到id/name是student的Bean例項,將這個例項自動注入。

所以對<bean>的id/name、setter方法的命名有嚴格要求。

 

原本是要用<property name ref="" />子元素注入依賴的,如果依賴較多,會寫一大堆<property>子元素。自動裝配,不管這個一Bean有多少個依賴,一句程式碼搞定,減少了程式碼量,由Spring容器自動注入依賴。但Spring容器要做更多的工作,裝配速度會變慢。

 

說明:自動裝配只能完成setter形式的依賴注入,不能完成構造器方式的依賴注入,且只能注入其它Bean,不能注入String、陣列、集合等Java自帶的型別。

 

測試程式碼:

1 public class Test {
2     public static void main(String[] args) {
3         ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
4         Teacher teacher=applicationContext.getBean("teacher",Teacher.class);
5         teacher.say();
6     }
7 }

執行,控制檯列印出"張三,叫家長來一下。"。

 

 

2、byType

根據要注入的Bean的型別來自動裝配。

在上面的例子中,setStudent(Student student),要注入的依賴是Student型別的例項。

byType自動裝配的執行過程:在這個Bean的定義中,找到setter方法,找到setter方法要注入的Bean的型別(Student),在Spring容器中找到Student型別的例項,注入。

 

如果Spring容器中該依賴有多個配置,比如:

  <bean id="student" class="my_package.Student">
        <constructor-arg value="張三" />
    </bean>

    <bean id="student1" class="my_package.Student">
        <constructor-arg value="李四" />
    </bean>

它們都是Student這個Bean的配置,Spring容器不知道要注入的依賴是哪一個,會報錯,所以依賴的bean只能有一個配置

 

這種是可以的:

  <bean id="student" class="my_package.Student" scope="prototype">
        <constructor-arg value="張三" />
    </bean>

雖然Spring容器中可能有這個Bean的多個例項,但這些例項是一樣的。

 

示例:

將byName示例中的xml中的配置修改如下即可

  <bean id="student" class="my_package.Student">
        <constructor-arg value="張三" />
    </bean>
    
    <bean id="teacher" class="my_package.Teacher" autowire="byType" />

 

 

 

3、constructor

 1 class Student{
 2     public String getName(){
 3         return "張三";
 4     }
 5 }
 6 
 7 class Teacher{
 8     private Student student;
 9 
10     public Teacher(Student student){
11         this.student=student;
12     }
13     
14     public void say(){
15         System.out.println(student.getName()+",叫家長來一下。");
16     }
17 }

需要用構造器注入依賴,Spring容器會自動根據構造器中引數型別,用byType方式注入對應型別的依賴。

只能有一個構造器,否則Spring容器不知道使用哪個構造器。

 

xml中的配置:

    <bean id="student" class="my_package.Student" />

    <bean id="teacher" class="my_package.Teacher" autowire="constructor" />

 

 

 

4、default

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">

    <bean id="student" class="my_package.Student">
        <constructor-arg value="張三" />
    </bean>

    <bean id="teacher" class="my_package.Teacher" autowire="default" />

在<beans>中設定預設的自動裝配方式,在需要使用自動裝配的<bean>指定autowire="default",這樣該<bean>使用的自動裝配方式就是<beans>中設定的預設方式。

統一了應用的自動裝配方式。

 

 

 

 

 

基於註解的裝配

基於xml裝配的方式,如果<bean>很多,xml檔案依然很臃腫。基於註解的裝配解決了這一問題。

基於註解的裝配是最常用的。

 

Spring常用的註解:

  • @Autowired    按型別自動裝配(byType)。
  • @Qualifier   按名稱自動裝配,不能單用,需要和@Autowired配合使用(byName)。
  • @Resource    是byName、byType方式的結合。

記法:Autowired——Type,Qualifier——Name,ATQN。

 

示例  @Autowired

 1 class Student{
 2     private String name;
 3 
 4     public Student(String name){
 5         this.name=name;
 6     }
 7 
 8     public String getName(){
 9         return name;
10     }
11 }
12 
13 
14 class Teacher{
15     @Autowired
16     private Student student;
17 
18     public void say(){
19         System.out.println(student.getName()+",叫家長來一下。");
20     }
21 }

 

不必寫setter方法,也不必寫構造器。在依賴的物件上新增@Autowired註解(當然也可以在setter方法上寫),即按照型別自動裝配依賴。

上面在Student型別的依賴上新增了@Autowired註解,會自動在Spring容器中,找到Student型別的Bean,注入。相當於byType

 

xml中的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config />

    <bean id="student" class="my_package.Student">
        <constructor-arg value="張三" />
    </bean>

    <bean id="teacher" class="my_package.Teacher"/>

</beans>

 

基於註解的裝配都需要用<context:annotation-config />開啟註解裝配,這句程式碼是告訴Spring容器,下面的這些bean使用的是註解裝配。

<bean>中不使用autowire屬性。

 

測試:

1 public class Test {
2     public static void main(String[] args) {
3         ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
4         Teacher teacher=applicationContext.getBean("teacher",Teacher.class);
5         teacher.say();
6     }
7 }

可以看到控制檯列印出"張三,叫家長來一下。"。

 

 

示例  @Qualifier

修改Teacher類程式碼如下,其餘不變。

1 class Teacher{
2     @Autowired
3     @Qualifier(value="student")
4     private Student student;
5 
6     public void say(){
7         System.out.println(student.getName()+",叫家長來一下。");
8     }
9 }

在依賴的Bean例項上新增@Qualifier,@Qualifier不能單獨用,還需要新增@Autowired。

@Qualifier是byName方式的自動裝配,需要用value指定依賴Bean的id/name,Spring容器根據這個value找到id/name為vBean,注入。

可簡寫為@Qualifier("student")。

 

 

示例  @Resource

1 class Teacher{
2     @Resource(name = "student")
3     private Student student;
4 
5     public void say(){
6         System.out.println(student.getName()+",叫家長來一下。");
7     }
8 }

先根據name找到Spring容器中name/id為student的Bean,注入。即優先以getName方式。

如果找不到name/id為指定值的Bean,或預設name直接寫@Resource,則以預設的getName方式:寫在欄位上預設name為成員變數名(student),寫在setter方法上預設為set後面部分得Camel寫法,比如setStudent()預設name為student。

如果還是找不到依賴的Bean,則以byType方式注入。

 

 

說明

  • 以上註解寫在欄位上、setter方法上均可,都是注入一個依賴。
  • Spring提供了@Resource註解,但此註解需要第三方包javax.annotation-api.jar的支援。如果用Maven,會自動新增Spring依賴的第三方包,比如commons-logging.jar、javax.annotation.jar,如果是自己新增Spring的jar庫,則還需要手動新增Spring依賴的第三方jar包。
  • 需要在xml中用<context:annotation-config />開啟註解。

 

 

 

Spring常用的其它註解

  • @Service       將業務層(Service層)的類標識為Spring容器中的Bean
  • @Controller   將控制層的類標識為Spring容器中的Bean
  • @Repository    將資料訪問層(Dao層)的類標識為Spring容器中的Bean
  • @Component   將一個類標識為Spring容器中的Bean,相當於具有以上3個註解的功能。但一般都使用專門的,就是說通常使用上面3個註解,很少使用@Component。

 前3個是專用的,第四個是通用的。這些註解都只能在類(Bean)上使用。

 

示例

 1 @Service
 2 class Student{
 3     public String getName(){
 4         return "張三";
 5     }
 6 }
 7 
 8 @Service
 9 class Teacher{
10     @Resource(name = "student")
11     private Student student;
12 
13     public void say(){
14         System.out.println(student.getName()+",叫家長來一下。");
15     }
16 }

 

 

xml中的配置:

    <context:component-scan base-package="my_package" />

    <bean id="student" class="my_package.Student" /> 

需要使用 <context:component-scan  base-package="" />指定要掃描的包,這樣會Spring容器會自動掃描指定的包,如果包中有上面4個註解,就將之裝配為Bean。

<context:component-scan  base-package="" />會自動開啟註解,所以不必再寫<context:annotataion-config />。

 

 

其實上面4個註解的作用相當於<bean  class=""   />。

標註了這4個註解的類,Spring會自動在xml中把這個類配置為Bean,就是說在xml中不必寫<bean  class=""   />。

但只能是<bean class=""   />這樣基礎的配置,如果要<constructor-arg>、<property>傳遞Java自帶型別的引數,或其他Bean必須使用這個Bean的id/name(這個Bean要配置id/name),就不能省略該Bean的配置。

 

上面的例子中,Teacher類預設了<bean class="my_package.Teacher" />。

@Resource(name = "student")
    private Student student;

Teacher類要用到Student類的id/name,所以Student類寫了配置。

其實不寫Student類的配置,則會使用byType方式向Teacher注入依賴,也可以。

<context:component-scan base-package="my_package" />這句程式碼不能預設。

 

 

自動裝配簡化了配置,減少了程式碼量,但需要Spring容器做更多的工作,所以建立Bean的速度要慢一些。

 

 

 

 

基於Java配置類的裝配

不使用xml檔案配置Bean,而是單獨寫一個類來配置Bean。

 1 class Student{
 2     private String name;
 3 
 4     public Student(String name){
 5         this.name=name;
 6     }
 7     
 8 
 9     public String getName(){
10         return name;
11     }
12 }
13 
14 class Teacher{
15     private Student student;
16 
17     public Teacher(Student student){
18         this.student=student;
19     }
20     
21     public void say(){
22         System.out.println(student.getName()+",叫家長來一下。");
23     }
24 }
25 
26 @Configuration   //表示這個類是用來配置Bean的
27 class Config{
28     @Value("張三") String name;   //建立一個成員變數,相當於String name="張三";
29 
30     @Bean(name = "student")   //配置一個Bean,相當於xml中的一個<bean>
31     public Student student(){
32         Student student=new Student(name);   //建立並返回Bean的例項。
33         return student;
34     }
35 
36     @Bean(name = "teacher")
37     public Teacher teacher(){
38         return new Teacher(student());  //建立並返回Bean的例項,因為寫了構造器,所以可以直接構造器注入依賴。可直接呼叫本類中的其它方法建立依賴的例項,注入。
39     }     
40 }
41 
42 public class Test {
43     public static void main(String[] args) {
44         ApplicationContext applicationContext=new AnnotationConfigApplicationContext(Config.class);  //注意,和xml配置不同。引數是配置類。
45         Teacher teacher=applicationContext.getBean("teacher",Teacher.class);
46         teacher.say();
47     }
48 }

 

上面的例子是通過構造器初始化Bean,也可以寫setter方法,通過setter方法初始化Bean:

 1 class Student{
 2     private String name;
 3 
 4     public void setName(String name){
 5         this.name=name;
 6     }
 7 
 8     public String getName(){
 9         return name;
10     }
11 }
12 
13 class Teacher{
14     private Student student;
15 
16     public void setStudent(Student student){
17         this.student=student;
18     }
19 
20     public void say(){
21         System.out.println(student.getName()+",叫家長來一下。");
22     }
23 }
24 
25 @Configuration
26 class Config{
27     @Value("張三") String name;
28 
29     @Bean(name = "student")
30     public Student student(){
31         Student student=new Student();
32         student.setName(name);
33         return student;
34     }
35 
36     @Bean(name = "teacher")
37     public Teacher teacher(){
38         Teacher teacher=new Teacher();
39         teacher.setStudent(student());
40         return teacher;
41     }
42 }

 

基於Java配置類的裝配,會將Bean的配置耦合到應用程式碼中,不推薦使用。基於Java配置類的註解還有其它的,此處不再介紹。

 

 

使用xml檔案配置Bean,是為了解耦,但隨著Bean的增多,xml檔案越來越臃腫,所以一般是折中使用註解+xml檔案的方式。

 

相關文章