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檔案的方式。