• Spring学习笔记--自动装配Bean属性


    Spring提供了四种类型的自动装配策略:

    • byName – 把与Bean的属性具有相同名字(或者ID)的其他Bean自动装配到Bean的对应属性中。
    • byType – 把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中。
    • constructor – 把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean的对应属性中。
    • autodetect – 首先使用costructor进行自动装配。如果失败,再尝试使用byType进行自动装配。

    我这里以关羽和青龙偃月刀为例: 首先定义一个武器接口Weapon:

    package com.moonlit.myspring;
    
    public interface Weapon {
        public void attack();
    }

    然后定义一个Weapon接口的实现Falchion类:

    package com.moonlit.myspring;
    
    public class Falchion implements Weapon {
        public void attack() {
            System.out.println("falcon is attacking!");
        }
    }

    定义一个英雄接口Hero:

    package com.moonlit.myspring;
    
    public interface Hero {
        public void perform();
    }

    然后定义一个Hero接口的实现Guanyu类(代表关羽):

    package com.moonlit.myspring;
    
    public class GuanYu implements Hero {
        private Weapon weapon;
        public void perform() {
            System.out.println("GuanYu pick up his weapon.");
            weapon.attack();
        }
        public Weapon getWeapon() {
            return weapon;
        }
        public void setWeapon(Weapon weapon) {
            this.weapon = weapon;
        }
    }

    在不涉及自动装配的情况下,我们想要通过Spring的DI将Fachion类对象注入到Guanyu类的weapon属性中,可以新建一个xml文件(我这里取名为spring-idol.xml)并在里面填写:

    spring-idol.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"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
        
      <bean id="falchion" class="com.moonlit.myspring.Falchion" />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu">
        <property name="weapon" ref="falchion" />
      </bean>
        
    </beans>

    其中最主要的内容就是两个bean的声明部分:

      <bean id="falchion" class="com.moonlit.myspring.Falchion" />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu">
        <property name="weapon" ref="falchion" />
      </bean>

    第一个bean标签定义了一个Falchion类型的bean,第二个bean标签中将第一个bean作为weapon的值装配到了weapon属性中。 然后我们可以写一个测试程序来查看效果:

    package com.moonlit.practice;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.moonlit.myspring.Hero;
    
    
    public class AutowirePractice {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("spring-idol.xml");
            Hero guanyu = (Hero) context.getBean("guanyu");
            guanyu.perform();
        }
    }

    输出结果如下:

    GuanYu pick up his weapon.
    falcon is attacking!

    到目前为止还没有涉及到自动装配的内容,接下来开始讲述自动装配的内容。

    byName自动装配

    改变spring-idol.xml中bean声明内容的形式如下:

      <bean id="weapon" class="com.moonlit.myspring.Falchion" />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byName" />

    得到一样的结果。

    我们将Falchion类的id去了一个和Guanyu类的属性weapon一样的名字,并且在guanyu bean中添加了autowire="byName"用于指明装配类型是byName自动装配。这个时候guanyu bean就是在上下文中找名为weapon的bean装配到他自己的weapon属性中。

    byType自动装配

    改变spring-idol.xml中bean声明内容的形式如下:

      <bean id="falchion" class="com.moonlit.myspring.Falchion" />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />

    得到一样的结果。

    这里我们已经不用关注Falchion类对应的bean的id是什么了,因为我们已经定义guanyu bean的autowire属性为"byType"。这个时候guanyu bean会在上下文中寻找和weapon具有相同类型的类对应的bean。
    因为Guanyu类的weapon实现Weapon借口,整个上下文中目前只有一个Weapon接口的实现Falchion类,所以以"byType"类型就检测到了falchion bean并将其注入到了guanyu bean的weapon属性中。
    但是也会出现一种情况就是检测的时候可能会出现多个相同type的bean,这个时候就不知道要装配那个了。比如,我在新建一个实现Weapon接口的方天画戟类HalBerd:

    package com.moonlit.myspring;
    
    public class Halberd implements Weapon {
        public void attack() {
            System.out.println("halberd is attacking!!!");
        }
    }

    并且在xml文件中声明一个新的halberd bean:

    <bean id="halberd" class="com.moonlit.myspring.Halberd" />

    在这种情况下就会出错,因为有两个bean满足byType的结果。

    这个时候有两种解决办法:
    第一种方法是将其中一个bean的primary属性设为false,比如:我们将方天画戟falchion bean的primary属性设为true,以防冠以使用方天画戟(很好奇吕布死了之后,赤兔马归关羽了,方天画戟去哪里了):

      <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
      <bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />

    输出结果如下:

    GuanYu pick up his weapon.
    halberd is attacking!!!

    从输出结果中可以看到,关羽没有使用青龙偃月刀,而是使用方天画戟进行攻击了。

    注:我看的Spring实战(第3版)上面说bean的默认primary属性默认是true,但是我这里用的是spring 4,根据效果来看primary的默认属性应该是false。
    第二种方法是设置其中一个bean的autowire-candidate属性为false,比如:我们将方天画戟的autowire-candidate属性设为false:

      <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
      <bean id="halberd" class="com.moonlit.myspring.Halberd" primary="true" autowire-candidate="false" />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType" />

    这个时候测试程序的输出如下:

    GuanYu pick up his weapon.
    falcon is attacking!

    可以看到这个时候关羽又重拾了青龙偃月刀。可以看到,当halberd bean的autowire-candidate属性设为false时,他将不会作为自动装配的竞选bean之一,这个时候虽然halberd的primary属性为true,但是halberd bean没有参与自动装配的竞选,所以自动装配到了falchion。

    这种感觉就好像:“隔壁村李小花觊觎我已久,但我是一个要成为海贼王的男人,于是拒绝了她……最终她嫁给了隔壁老王,过上了幸福的生活”。

    constructor自动装配

    演示constructor之前我需要在在GuanYu类中添加一个构造函数:

        public GuanYu(Weapon weapon) {
            this.weapon = weapon;
        }

    在xml文件中设置guanyu bean的autowire属性为constructor:

      <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="constructor" />

    输出结果如下:

    GuanYu pick up his weapon.
    falcon is attacking!

    实现了自动装配。

    最佳自动装配

    书上说是将autowire属性设为autodetect,但是我发现在我使用的spring 4(我看的书是spring 3)版本中没有autodetect,autowire属性只有byName,byType,constructor,default,no这些属性。

    默认自动装配

    可以在beans标签中添加default-autowire属性来设置默认自动装配策略,如:在beans标签中添加default-autowire="byType"设置默认自动装配策略为byType自动装配。

    混合使用自动装配和显示装配

    为了演示这个效果,我为Guanyu类添加了一个String成员变量name,虽然关羽就叫关羽,但是人家称呼他的时候还是会称呼他云长之类的。新定义的GuanYu类如下:

    package com.moonlit.myspring;
    
    public class GuanYu implements Hero {
        private String name;
        private Weapon weapon;
    //  public GuanYu(String name, Weapon weapon) {
    //      this.name = name;
    //      this.weapon = weapon;
    //  }
        public void perform() {
            System.out.println(name + " pick up his weapon.");
            weapon.attack();
        }
        public Weapon getWeapon() {
            return weapon;
        }
        public void setWeapon(Weapon weapon) {
            this.weapon = weapon;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }

    (这里我注释掉了GuanYu类的构造函数。)

    一个完全显示装配的例子如下:

      <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu">
        <property name="name"  value="Guan Yun Chang" />
        <property name="weapon" ref="falchion" />
      </bean>

    当然,我们也可以使用显示装配和自动装配结合,显示装配name,自动装配weapon,如下:

      <bean id="falchion" class="com.moonlit.myspring.Falchion"  />
      <bean id="guanyu" class="com.moonlit.myspring.GuanYu" autowire="byType">
        <property name="name"  value="Guan Yun Chang" />
      </bean>

    具有一样效果的输出。

    使用混合装配的最后一个注意事项:当使用constructor自动装配策略时,我们必须让Spring自动装配构造器的所有入参——我们不能混合使用constructor自动装配策略和<constructor-arg>元素。

  • 相关阅读:
    java虚拟机之垃圾回收机制
    java虚拟机之JVM体系结构
    java虚拟机之JVM生命周期
    删除链表中重复的结点
    (二十一)java多线程之Executors
    (十八)java多线程之Callable Future
    (十六)java多线程之优先队列PriorityBlockingQueue
    (十九)java多线程之ForkJoinPool
    (二十)java多线程之ScheduledThreadPoolExecutor
    (六)java多线程之ReadWriteLock
  • 原文地址:https://www.cnblogs.com/moonlightpoet/p/5569239.html
Copyright © 2020-2023  润新知