Spring Bean的3种装配方式

 

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)上使用。

均可设置Bean的id/name: @Xxx(“id/name”) ,如不设置,默认id/name为类名的camel写法。

 

示例

 1 @Service //默认id/name为student,可@Server("")显式指定  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文件的方式。

 

点赞