最近在复习Spring
的相关内容,这篇博客就来记录一下Spring
为bean
的属性注入值的四种方式。这篇博客主要讲解在xml
文件中,如何为bean
的属性注入值,最后也会简单提一下使用注解的方式。废话很少说,直接开始吧。java
在Spring
中,共有四种方式为bean
的属性注入值,分别是:app
下面我就分别演示一下,如何使用这四种方式进行属性的注入。ide
在演示前,咱们须要准备几个类,我使用下面两个类来进行注入的演示,这两个类分别是User
和Car
类:测试
public class Car { // 只包含基本数据类型的属性 private int speed; private double price; public Car() { } public Car(int speed, double price) { this.speed = speed; this.price = price; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Car{" + "speed=" + speed + ", price=" + price + '}'; } } public class User { private String name; private int age; // 除了上面两个基本数据类型的属性,User还依赖Car private Car car; public User() { } public User(String name, int age, Car car) { this.name = name; this.age = age; this.car = car; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
有了上面两个类,咱们就能够演示set
注入了。须要注意一点,若是咱们须要使用set
注入,那么必需要为属性提供set
方法,Spring
容器就是经过调用bean
的set
方法为属性注入值的。而在xml
文件中,使用set
注入的方式就是经过property
标签,以下所示:this
<!-- 定义car这个bean,id为myCar --> <bean id="myCar" class="cn.tewuyiang.pojo.Car"> <!-- 为car的属性注入值,由于speed和price都是基本数据类型,因此使用value为属性设置值; 注意,这里的name为speed和price,不是由于属性名就是speed和price, 而是set方法分别为setSpeed和setPrice,名称是经过将set删除,而后将第一个字母变小写得出; --> <property name="speed" value="100"/> <property name="price" value="99999.9"/> </bean> <!-- 定义user这个bean --> <bean id="user" class="cn.tewuyiang.pojo.User"> <property name="name" value="aaa" /> <property name="age" value="123" /> <!-- car是引用类型,因此这里使用ref为其注入值,注入的就是上面定义的myCar 基本数据类型或Java包装类型使用value, 而引用类型使用ref,引用另一个bean的id --> <property name="car" ref="myCar" /> </bean>
经过上面的配置,就能够为Car
和User
这两个类型的bean
注入值了。须要注意的是,property的name属性,填写的不是属性的名称,而是set方法去除set,而后将第一个字符小写后的结果。对于基本数据类型,或者是Java的包装类型(好比String),使用value注入值,而对于引用类型,则使用ref,传入其余bean的id。接下来咱们就能够测试效果了:spa
@Test public void test1() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 获取user这个bean User user = context.getBean(User.class); // 输出产看结果 System.out.println(user); }
因为user
包含car
的引用,因此咱们直接输出user
,也可以看到car
的状况,输入结果以下:.net
User{name='aaa', age=123, car=Car{speed=100, price=99999.9}}
下面咱们来讲第二种方式——构造器注入。听名字就能够知道,这种注入值的方式,就是经过调用bean
所属类的带参构造器为bean
的属性注入值。这也就意味着,咱们若是须要使用构造器注入,就得为类提供包含参数的构造方法。构造器注入,实际上有多种匹配属性值的方式,下面咱们就来一一列举。咱们这里依然使用2.2
中定义的Car
和User
这两个类,测试方法以及类的定义都不须要变,须要改变的仅仅是xml
配置文件。code
(一)匹配构造器的参数名称xml
咱们须要经过constructor-arg
标签为构造器传入参数值,可是每一个constructor-arg
标签对应哪个参数值呢?这就有多种方式指定了。第一种就是直接匹配参数名,配置以下:对象
<bean id="myCar" class="cn.tewuyiang.pojo.Car"> <!-- 经过constructor-arg的name属性,指定构造器参数的名称,为参数赋值 --> <constructor-arg name="speed" value="100" /> <constructor-arg name="price" value="99999.9"/> </bean> <bean id="user" class="cn.tewuyiang.pojo.User"> <constructor-arg name="name" value="aaa" /> <constructor-arg name="age" value="123" /> <!-- 和以前同样,基本数据类型或Java包装类型使用value, 而引用类型使用ref,引用另一个bean的id --> <constructor-arg name="car" ref="myCar" /> </bean>
这样就完成了,测试代码和以前同样,运行结果也同样,我这里就不贴出来了。有人看完以后,可能会以为这里的配置和set
注入时的配置几乎同样,除了一个使用property
,一个使用constructor-arg
。确实,写法上同样,可是表示的含义却彻底不一样。property的name属性,是经过set方法的名称得来;而constructor-arg的name,则是构造器参数的名称。
(二)匹配构造器的参数下标
上面是经过构造器参数的名称,匹配须要传入的值,那种方式最为直观,而Spring
还提供另外两种方式匹配参数,这里就来讲说经过参数在参数列表中的下标进行匹配的方式。下面的配置,请结合2.2
节中User
和Car
的构造方法一块儿阅读,配置方式以下:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 下标编号从0开始,构造器的第一个参数是speed,为它赋值100 --> <constructor-arg index="0" value="100" /> <!-- 构造器的第二个参数是price,为它赋值99999.9 --> <constructor-arg index="1" value="99999.9"/> </bean> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- 与上面car的配置同理 --> <constructor-arg index="0" value="aaa" /> <constructor-arg index="1" value="123" /> <constructor-arg index="2" ref="car" /> </bean>
上面就是经过参数的下标为构造器的参数赋值,须要注意的是,参实的下标从0开始。使用上面的方式配置,若赋值的类型与参数的类型不一致,将会在容器初始化bean
的时候抛出异常。若是bean
存在多个参数数量同样的构造器,Spring
容器会自动找到类型匹配的那个进行调用。好比说,Car
有以下两个构造器,Spring
容器将会调用第二个,由于上面的配置中,index = 1
对应的value
是double
类型,与第二个构造器匹配,而第一个不匹配:
public Car(double price, int speed) { this.speed = speed; this.price = price; } // 将使用匹配这个构造器 public Car(int speed, double price) { this.speed = speed; this.price = price; }
还存在另一种特殊状况,那就是多个构造器都知足bean
的配置,此时选择哪个?假设当前car
的配置是这样的:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 两个下标的value值都是整数 --> <constructor-arg index="0" value="100" /> <constructor-arg index="1" value="999"/> </bean>
假设Car
仍是有上面两个构造器,两个构造器都是一个int
类型一个double
类型的参数,只是位置不一样。而配置中,指定的两个值都是int
类型。可是,int
类型也可使用double
类型存储,因此上面两个构造器都是匹配的,此时调用哪个呢?结论就是调用第二个。本身去尝试就会发现,若存在多个构造器匹配bean的定义,Spring容器老是使用最后一个知足条件的构造器。
(三)匹配构造器的参数类型
下面说最后一种匹配方式——匹配构造器的参数类型。直接看配置文件吧:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 使用type属性匹配类型,car的构造器包含两个参数,一个是int类型,一个是double类型 --> <constructor-arg type="int" value="100" /> <constructor-arg type="double" value="99999.9"/> </bean> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- 对于引用类型,须要使用限定类名 --> <constructor-arg type="java.lang.String" value="aaa" /> <constructor-arg type="int" value="123" /> <constructor-arg type="cn.tewuyiang.pojo.Car" ref="car" /> </bean>
上面应该不难理解,直接经过匹配构造器的参数类型,从而选择一个可以彻底匹配的构造器,调用这个构造器完成bean
的建立和属性注入。须要注意的是,上面的配置中,类型并不须要按构造器中声明的顺序编写,Spring
也能进行匹配。这也就意味着可能出现多个可以匹配的构造器,和上一个例子中同样。好比说,Car
仍是有下面两个构造器:
public Car(double price, int speed) { // 输出一句话,看是否调用这个构造器 System.out.println(111); this.speed = speed; this.price = price; } // 将使用匹配这个构造器 public Car(int speed, double price) { // 输出一句话,看是否调用这个构造器 System.out.println(222); this.speed = speed; this.price = price; }
上面两个构造器都是一个int
,一个double
类型的参数,都符合xml文件中,car
这个bean
的配置。经过测试发现,Spring容器使用的永远都是最后一个符合条件的构造器,这和上面经过下标匹配是一致的。须要说明的一点是,这三种使用构造器注入的方式,能够混用。
静态工厂注入就是咱们编写一个静态的工厂方法,这个工厂方法会返回一个咱们须要的值,而后在配置文件中,咱们指定使用这个工厂方法建立bean
。首先咱们须要一个静态工厂,以下所示:
public class SimpleFactory { /** * 静态工厂,返回一个Car的实例对象 */ public static Car getCar() { return new Car(12345, 5.4321); } }
下面咱们须要在xml
中配置car这个bean,并指定它由工厂方法进行建立。配置以下:
<!-- 注意,这里的配置并非建立一个SimpleFactory对象,取名为myCar, 这一句配置的意思是,调用SimpleFactory的getCar方法,建立一个car实例对象, 将这个car对象取名为myCar。 --> <bean id="car" class="cn.tewuyiang.factory.SimpleFactory" factory-method="getCar"/> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- name和age使用set注入 --> <property name="name" value="aaa"/> <property name="age" value="123"/> <!-- 将上面配置的car,注入到user的car属性中 --> <property name="car" ref="car"/> </bean>
以上就配置成功了,测试方法以及执行效果以下,注意看car
的属性值,就是咱们在静态工厂中配置的那样,这说明,Spring
容器确实是使用咱们定义的静态工厂方法,建立了car
这个bean
:
@Test public void test1() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 获取静态工厂建立的car Car car = (Car) context.getBean("car"); // 获取user User user = context.getBean(User.class); System.out.println(car); System.out.println(user); }
输出以下所示:
Car{speed=12345, price=5.4321} User{name='aaa', age=123, car=Car{speed=12345, price=5.4321}}
实例工厂与静态工厂相似,不一样的是,静态工厂调用工厂方法不须要先建立工厂类的对象,由于静态方法能够直接经过类调用,因此在上面的配置文件中,并无声明工厂类的bean
。可是,实例工厂,须要有一个实例对象,才能调用它的工厂方法。咱们先看看实例工厂的定义:
public class SimpleFactory { /** * 实例工厂方法,返回一个Car的实例对象 */ public Car getCar() { return new Car(12345, 5.4321); } /** * 实例工厂方法,返回一个String */ public String getName() { return "tewuyiang"; } /** * 实例工厂方法,返回一个int,在Spring容器中会被包装成Integer */ public int getAge() { return 128; } }
在上面的工厂类中,共定义了三个工厂方法,分别用来返回user
所需的car
,name
以及age
,而配置文件以下:
<!-- 声明实例工厂bean,Spring容器须要先建立一个SimpleFactory对象,才能调用工厂方法 --> <bean id="factory" class="cn.tewuyiang.factory.SimpleFactory" /> <!-- 经过实例工厂的工厂方法,建立三个bean,经过factory-bean指定工厂对象, 经过factory-method指定须要调用的工厂方法 --> <bean id="name" factory-bean="factory" factory-method="getName" /> <bean id="age" factory-bean="factory" factory-method="getAge" /> <bean id="car" factory-bean="factory" factory-method="getCar" /> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- 将上面经过实例工厂方法建立的bean,注入到user中 --> <property name="name" ref="name"/> <property name="age" ref="age"/> <property name="car" ref="car"/> </bean>
咱们尝试从Spring
容器中取出name
,age
,car
以及user
,看看它们的值,测试代码以下:
@Test public void test1() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 获取静态工厂建立的car,name和age这三个bean Car car = (Car) context.getBean("car"); String name = (String) context.getBean("name"); Integer age = (Integer) context.getBean("age"); // 获取user这个bean User user = context.getBean(User.class); System.out.println(car); System.out.println(name); System.out.println(age); System.out.println(user); }
如下就是输出结果,能够看到,咱们经过工厂建立的bean
,都在Spring
容器中可以获取到:
Car{speed=12345, price=5.4321} tewuyiang 128 User{name='tewuyiang', age=128, car=Car{speed=12345, price=5.4321}}
假如须要使用注解的方式为bean
注入属性值,应该这么操做呢?首先,若是bean
依赖于其余bean
(好比User
依赖Car
),那么咱们可使用@Autowired
或者@Resource
这两个注解进行依赖注入,这个你们应该都知道。可是若是要为基本数据类型或者是Java
的封装类型(好比String
)赋值呢?这时候可使用@Value
注解。这里我就不演示了,感兴趣的能够自行去研究,应该是比xml
的方式简单多了。
以上就对Spring
基于xml
配置文件进行属性注入的方式作了一个还算详细的介绍。其实这一部分的内容仍是比较基础,毕竟只是Spring
的使用,并不涉及原理,只要本身尝试写一遍就了解了。若以上内容存在错误或不足,欢迎指正,共同进步。也但愿以上内容对须要的人有所帮助。