Spring中bean的四种注入方式
一、前言
最近在复习Spring的相关内容,这篇博客就来记录一下Spring为bean的属性注入值的四种方式。这篇博客主要讲解在xml文件中,如何为bean的属性注入值,最后也会简单提一下使用注解的方式。废话不多说,直接开始吧。
二、正文
2.1 注入方式
在Spring中,共有四种方式为bean的属性注入值,分别是:
- set方法注入
- 构造器注入
- 静态工厂注入
- 实例工厂注入
下面我就分别演示一下,如何使用这四种方式进行属性的注入。
2.2 set方法注入
在演示前,我们需要准备几个类,我使用下面两个类来进行注入的演示,这两个类分别是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标签,如下所示:
<!-- 定义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。接下来我们就可以测试效果了:
@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的情况,输入结果如下:
User{name='aaa', age=123, car=Car{speed=100, price=99999.9}}
2.3 构造器注入
下面我们来说第二种方式——构造器注入。听名字就可以知道,这种注入值的方式,就是通过调用bean所属类的带参构造器为bean的属性注入值。这也就意味着,我们如果需要使用构造器注入,就得为类提供包含参数的构造方法。构造器注入,实际上有多种匹配属性值的方式,下面我们就来一一列举。我们这里依然使用2.2中定义的Car和User这两个类,测试方法以及类的定义都不需要变,需要改变的仅仅是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容器使用的永远都是最后一个符合条件的构造器,这和上面通过下标匹配是一致的。需要说明的一点是,这三种使用构造器注入的方式,可以混用。
2.4 静态工厂注入
静态工厂注入就是我们编写一个静态的工厂方法,这个工厂方法会返回一个我们需要的值,然后在配置文件中,我们指定使用这个工厂方法创建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}}
2.5 实例工厂注入
实例工厂与静态工厂类似,不同的是,静态工厂调用工厂方法不需要先创建工厂类的对象,因为静态方法可以直接通过类调用,所以在上面的配置文件中,并没有声明工厂类的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}}
2.6 使用注解注入
假如需要使用注解的方式为bean注入属性值,应该这么操作呢?首先,如果bean依赖于其他bean(比如User依赖Car),那么我们可以使用@Autowired或者@Resource这两个注解进行依赖注入,这个大家应该都知道。但是如果要为基本数据类型或者是Java的封装类型(比如String)赋值呢?这时候可以使用@Value注解。这里我就不演示了,感兴趣的可以自行去研究,应该是比xml的方式简单多了。
三、总结
以上就对Spring基于xml配置文件进行属性注入的方式做了一个还算详细的介绍。其实这一部分的内容还是比较基础,毕竟只是Spring的使用,并不涉及原理,只要自己尝试写一遍就了解了。若以上内容存在错误或不足,欢迎指正,共同进步。也希望以上内容对需要的人有所帮助。
四、参考
Spring中bean的四种注入方式的更多相关文章
- JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解
在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...
- 【温故知新】——原生js中常用的四种循环方式
一.引言 本文主要是利用一个例子,讲一下原生js中常用的四种循环方式的使用与区别: 实现效果: 在网页中弹出框输入0 网页输出“欢迎下次光临” 在网页中弹出框输入1 网页输出“查询中……” 在 ...
- spring中bean的五种作用域?Spring中的bean是线程安全的吗?
spring中bean的五种作用域 当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域.Spring支持如下5种作用域: singleto ...
- 读书笔记——spring cloud 中 HystrixCommand的四种执行方式简述
读了<Spring Cloud 微服务实战>第151-154页, 总结如下: Hystrix存在两种Command,一种是HystrixCommand,另一种是HystrixObserva ...
- Spring中对象和属性的注入方式
一:Spring的bean管理 1.xml方式 bean实例化三种xml方式实现 第一种 使用类的无参数构造创建,首先类中得有无参构造器(重点) 第二种 使用静态工厂创建 (1)创建静态的方法,返回类 ...
- Android中Activity的四种启动方式
谈到Activity的启动方式必须要说的是数据结构中的栈.栈是一种只能从一端进入存储数据的线性表,它以先进后出的原则存储数据,先进入的数据压入栈底,后进入的数据在栈顶.需要读取数据的时候就需要从顶部开 ...
- spring Bean的三种注入方式
1.构造函数注入: 构造函数的注入方式分为很多种 (1)普通构造函数,空参数的构造函数 <bean id="exampleBean" class="examples ...
- Spring三 Bean的三种创建方式
创建Bean的三种方式在大多数情况下,Spring容器直接通过new关键字调用构造器来创建Bean实例,而class属性指定Bean实例的实现类,但这不是实例化Bean的唯一方法.实际上,Spring ...
- spring 中 hibernate 的 2种 配置方式(新旧 2种方式)
Spring对hibernate配置文件hibernate.cfg.xml的集成,来取代hibernate.cfg.xml的配置 Spring对hibernate配置文件hibernate.cfg.x ...
随机推荐
- Python中有许多HTTP客户端,但使用最广泛且最容易的是requests
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:北京尚脑软件测试 PS:如有需要Python学习资料的小伙伴可以加点击 ...
- 你自学半年也搞不懂的go底层,看这篇。这篇讲 go的数组、切片、Maps
目录 数组 1.定义数组 2.使用数组 3.定义并赋值 4.数组的大小是类型的一部分 5.数组是值类型(当参数传递到函数中,修改不会改变原来的值) 6.数组长度 7.循环数组 8.多维数组 切片 1. ...
- SpringBoot系列(九)单,多文件上传的正确姿势
SpringBoot系列(九)分分钟解决文件上传 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配 ...
- 12. 前后端联调 + ( proxy代理 ) + ( axios拦截器 ) + ( css Modules模块化方案 ) + ( css-loader ) + ( 非路由组件如何使用history ) + ( bodyParser,cookieParser中间件 ) + ( utility MD5加密库 ) + ( nodemon自动重启node ) + +
(1) proxy 前端的端口在:localhost:3000后端的端口在:localhost:1234所以要在webpack中配置proxy选项 (proxy是代理的意思) 在package.jso ...
- 转:Cookies 和 Session的区别
转自:http://blog.csdn.net/axin66ok/article/details/6175522 1.cookie 是一种发送到客户浏览器的文本串句柄,并保存在客户机硬盘上,可以用来在 ...
- GoJS 教程新手入门(资源整理,解决方案)
以下几个是我在百度.谷歌 上能找到的比较全的GoJs的一些东西,希望对各位有所帮助! 如有外网网站不能访问请自行FQ GoJS官网 第一个推荐的是GoJS的一个类似于社区的问题讨论区,这里面初学者的一 ...
- Python数据分析入门与实践 学习
pandas是一个Python语言的软件包,在我们使用Python语言进行机器学习编程的时候,这是一个非常常用的基础编程库.本文是对它的一个入门教程.pandas提供了快速,灵活和富有表现力的数据结构 ...
- python实现线性回归之简单回归
代码来源:https://github.com/eriklindernoren/ML-From-Scratch 首先定义一个基本的回归类,作为各种回归方法的基类: class Regression(o ...
- 二, 连接Oracle 二
一,sqlplus操作 文件操作命令 1.start和@ 说明: 运行sql脚本 案例: sql>@ home/a.sql或是sql>start home/a.sql 2.edit 说明: ...
- Windows 切换 working directory
用函数 _chdir() 例如用计划任务启动,pwd 是 system32 使用相对路径的地方会出错. 在 main 函数刚启动的时候转换一下 working directory 可解.