Spring 攻略第002讲

5 篇文章 0 订阅
4 篇文章 0 订阅

调用构造程序创建 Bean

首先,假定你打算开发一个在线销售产品的购物应用程序。先创建一个 Product类,这个类有多个属性,例如产品名称和价格。因为商店中有许多类型的产品,所以你定义 Product 类为抽象类,用于不同产品子类的扩展。

这次我们新建一个名为“sesametech.springrecipes.s006”包,在该包下创建“Product”类,主要代码如下:

public abstract class Product {

 

       private String name;

       private double price;

 

       public Product() {

       }

 

       public Product(String name, double price) {

              this.name = name;

              this.price = price;

       }

 

 

       @Override

       public String toString() {

              return name + " " + price;

       }

 

       getter & setter …

 

}

然后创建两个产品子类 Battery(电池)和 Disk(光盘),每个类都有自己的属性,主要代码分别如下:

public class Battery extends Product {

      

       private boolean rechargeable; // 是否为可充电电池

      

       public Battery() {

              super();

       }

      

       public Battery(String name, double price) {

              super(name, price);

       }

 

       getter & setter …

 

}

 

public class Disk extends Product {

      

       private int capacity; // 容量

      

       public Disk() {

              super();

       }

      

       public Disk(String name, double price) {

              super(name, price);

       }

 

       getter & setter …

 

}

 

为了在 Spring IOC 容器中定义这些产品,我们在“sesametech.springrecipes.s006”包下创建“beans.xml”配置文件,内容如下:

       <bean

              id="aaa"

              class="sesametech.springrecipes.s006.Battery">

              <!-- 通过设值方法配置 Bean 属性 -->

              <property name="name"value="AAA" />

              <property name="price"value="2.5" />

              <property name="rechargeable"value="true" />

       </bean>

      

       <bean

              id="cdrw"

              class="sesametech.springrecipes.s006.Disk">

              <!-- 通过设值方法配置 Bean 属性 -->

              <property name="name"value="CD-RW" />

              <property name="price"value="1.5" />

              <property name="capacity"value="1024" />

       </bean>

最后,我们在“sesametech.springrecipes.s006”包下,创建“ProductTest”测试类并测试它,主要代码如下:

       @SuppressWarnings("resource")

       public static void main(String[] args) {

              ApplicationContextcontext = newClassPathXmlApplicationContext("sesametech/springrecipes/s006/beans.xml");

              Productproduct = (Product) context.getBean("aaa");

              System.out.println(product.toString());

             

              product = (Product) context.getBean("cdrw");

              System.out.println(product.toString());

   }

 

现在,我们来大概分析一下这个应用程序。首先,它没有指定任何<constructor-arg> 标签来配置 Bean 属性,这样Spring 将会默认调用不带参数的构造方法来实例化相关的类,并通过设值方法注入属性值。前面的 Bean 配置等价于如下代码片段:

              Batteryaaa = new Battery();

              aaa.setName("AAA");

              aaa.setPrice(2.5);

              aaa.setRechargeable(true);

             

              Diskcdrw = new Disk();

              cdrw.setName("CD-RW");

              cdrw.setPrice(1.5);

              cdrw.setCapacity(1024);

   但是,如果有一个或者多个 <constructor-arg> 标签,Spring  将匹配参数最合适的构造方法。作为例子,我们将前面的“beans.xml”配置文件拷贝一份,并更名为“beans-constructor.xml”,并按如下所示修改配置文件:

       <bean

              id="aaa"

              class="sesametech.springrecipes.s006.Battery">

              <constructor-arg value="AAA"/>

              <constructor-arg value="2.5"/>

              <property name="rechargeable"value="true" />

       </bean>

      

       <bean

              id="cdrw"

              class="sesametech.springrecipes.s006.Disk">

              <constructor-arg value="CD-RW"/>

              <constructor-arg value="1.5"/>

              <property name="capacity"value="1024" />

       </bean>

同时,我们修改一下“ProductTest”测试类中 main() 方法里的应用程序上下文加载配置文件的路径,如下所示:

       //ApplicationContext context = new ClassPathXmlApplicationContext("sesametech/springrecipes/s006/beans.xml");

              ApplicationContextcontext = newClassPathXmlApplicationContext("sesametech/springrecipes/s006/beans-constructor.xml");

 

此时,看起来运行一切正常。因为 Product 和子类在构造方法上没有任何歧义,前述的 Bean 配置等价于下面的代码片段:

              Batteryaaa = new Battery("AAA", 2.5);

              aaa.setRechargeable(true);

             

              Diskcdrw = new Disk("CD-RW", 1.5);

              cdrw.setCapacity(1024);

 

解决构造方法歧义

当你为 Bean 指定一个或者多个构造方法时参数时,Spring将试图在 Bean 类中寻找对应的构造方法并传递用于实例化Bean 的参数。但是,如果你的参数可以同时适用多个构造方法,就可能会造成 Spring 在匹配构造方法时出现歧义。这样的话,Spring 可能无法调用你所预期的构造方法。为了解决这个问题,Spring给出一种解决方案,允许你为 <constructor-arg> 标签指定type 和 index 属性,以帮助 Spring 查找预期的构造方法。

作为示例,我们在上面的“Disk”子类中增加一个“type”属性,同时增加一个构造方法,并覆写“Product”父类的toString() 方法,代码片段如下:

       private int capacity; // 容量

       private String type; // 类型

      

       public Disk() {

              super();

       }

      

       public Disk(String name, String type) {

              this.setName(name);

              this.setType(type);

       }

      

       public Disk(String name, double price) {

              super(name, price);

       }

      

       @Override

       public String toString() {

              return super.toString() + " " + type + " " + capacity;

       }

       ……

然后,在“beans-constructor.xml”配置文件中增加一个 ID 为“dvdrw”的 bean 定义,如下所示:

       <bean

              id="dvdrw"

              class="sesametech.springrecipes.s006.Disk">

              <constructor-arg value="DVD-RW"/>

              <constructor-arg value="3.5"/>

              <property name="type" value="DVD"/>

              <property name="capacity"value="4096" />

       </bean>

最后,我们在“ProductTest”测试类中,增加以下代码:          

              Diskdvdrw = (Disk) context.getBean("dvdrw");

              System.out.println(dvdrw.toString());

我们的意图是,配置一个 name 为“DVD-RW”、price为“3.5”、type 为“DVD”、capacity 是“4096”的产品,但此时你若运行此程序将会得到如下结果:

AAA 2.5

CD-RW 0.0 1.5 1024

DVD-RW 0.0 DVD 4096

price 变成了“0.0”并且 CD-RW 的 type 变成了“1.5”。这个意外结果的起因是调用了第一个带有 name 和 type 参数的构造方法,而不是第二个带有 name 和 price 参数的构造方法。这是因为 Spring 默认将两个参数都解析为String 类型,而第一个构造方法不需要类型转换,因此被认定为最合适的。为了达到我们的预期,你必须设置<constructor-arg> 中的 type 属性。因此,我们把配置文件修改成如下所示:

       <bean

              id="cdrw"

              class="sesametech.springrecipes.s006.Disk">

              <constructor-arg type="java.lang.String"value="CD-RW" />

              <constructor-arg type="double" value="1.5" />

              <property name="type"value="CD" />

              <property name="capacity"value="1024" />

       </bean>

      

       <bean

              id="dvdrw"

              class="sesametech.springrecipes.s006.Disk">

              <constructor-arg type="java.lang.String"value="DVD-RW" />

              <constructor-arg type="double"value="3.5" />

              <property name="type"value="DVD" />

              <property name="capacity"value="4096" />

       </bean>

再次运行程序,如果不出意外,应该得到正常的结果,如下所示:

AAA 2.5

CD-RW 1.5 CD 1024

DVD-RW 3.5 DVD 4096

现在,我们再为“Disk”子类增加一个“discount”折扣属性,然后再增加一个新的构造方法,如下所示:

public class Disk extends Product {

      

       private int capacity; // 容量

       private String type; // 类型

       private double discount; // 折扣

      

       public Disk() {

              super();

       }

      

       public Disk(String name, String type) {

              this.setName(name);

              this.setType(type);

       }

      

       public Disk(String name, double price) {

              super(name, price);

       }

      

       public Disk(double discount, String name) {

              this.setDiscount(discount);

              this.setName(name);

       }

      

       @Override

       public String toString() {

              return super.toString() + " " + discount + " " + type + " " + capacity;

       }

 

getter & setter …

}

此时,再次运行程序,可能得到正确的这结果,或者得到如下意外的结果:

AAA 2.5

CD-RW 0.0 1.5 CD 1024

DVD-RW 0.0 3.5 DVD 4096

price 为“0.0”,而本应该是 price 的值却变成了 discount 了。这种不确定性的起因是 Spring 在内部对每个构造方法与参数的兼容性评分造成的。但Spring 在评分过程中并没有考虑参数出现在 XML 配置文件中的顺序。这意味着,Spring对第一个构造方法和第三个构造方法的评分是相同的,而具体选择哪一个构造方法又取决于匹配的顺序。根据 JavaReflection API 中的 Class.getDeclaredConstructors() 方法,返回的构造方法顺序将是任意的、无序的,可能与声明的顺序不同或者恰好相同。所有这些因素导致了构造方法匹配中的歧义。

为了避免这个问题,你必须通过<constructor-arg> 中的 index 属性,明确指出参数的索引位置。设置了type 和 index 属性后,Spring 就可以精确地为 Bean 找到预期的构造方法。但是,如果相当确定构造方法不会导致歧义,就可以忽略type 和 index 属性设置。

我们再次将上面示例中的配置文件改成如下所示:

       <bean

              id="cdrw"

              class="sesametech.springrecipes.s006.Disk">

              <constructor-arg type="java.lang.String"index="0" value="CD-RW"/>

              <constructor-arg type="double"index="1" value="1.5"/>

              <property name="discount"value="0.7" />

              <property name="type"value="CD" />

              <property name="capacity"value="1024" />

       </bean>

      

       <bean

              id="dvdrw"

              class="sesametech.springrecipes.s006.Disk">

              <constructor-arg type="java.lang.String"index="0" value="DVD-RW"/>

              <constructor-arg type="double"index="1" value="3.5"/>

              <property name="discount"value="0.9" />

              <property name="type"value="DVD" />

              <property name="capacity"value="4096" />

       </bean>


好了,今天就写到这里了,继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值