上篇文章我们讲解了如何通过spring的工厂创建对象。对象有了,但是其实往往我们也不能够直接进行使用,有时候需要对对象进行赋值的操作。而spring中有一个比较重要的概念叫做依赖注入,什么是依赖注入的,其实就是对于成员变量的赋值。那么我们就来了解下spring如何完成依赖注入。
一. 传统方式的注入-对象赋值
我们传统的方式是如何完成赋值的呢,一般有三种方式。我们还是以User类为例
publicclassUser{ privateIntegerid; privateStringname; // get set 这里省略 }
- 直接赋值(不常用)
Useruser=newUser(); user.id=1; user.name="张三";
这种方式不是很常见,因为根据封装的特性,一般情况下,我们的成员变量都会设置为私有,所以这种方式一般都用于本类中使用,局限性较大。所以不是很常用。
- set方法赋值
Useruser=newUser(); user.setId(1); user.setName("张三");
这种方式就很常见了,就不多解释了,应该算是最常见的操作了。
- 构造方法赋值
要想使用构造方法赋值,前提是我们要提供对应的有参构造方法。所以User类需要改造一下:
publicclassUser{ privateIntegerid; privateStringname; // get set 这里省略 // 注意无参构造要显示声明出来publicUser(){} publicUser(Integerid, Stringname){ this.id=id; this.name=name; } }
调用有参构造方法赋值User user = new User(1, "张三");
二. spring赋值-依赖注入
spring本身也为我们提供了给对象赋值的方式,并且自己起了一个很有逼格的名字叫依赖注入。所谓依赖注入就是就是给对象的成员变量进行赋值。 那么通过spring的依赖注入有什么好处呢,其实好处就是解耦合。因为一般情况下,我们如果直接在代码里进行对象的赋值,修改起来是比较麻烦的,也就是常说的硬编码,所以一般我们经常把容易修改的值放入配置文件中,而通过spring的依赖注入方式,就可以很好地和配置文件结合,消除硬编码问题。
那么接下来我们就演示下spring的赋值方式
2.1 Set注入
顾名思义,就是通过set方法进行注入。还是以上面的User 类为例,如果使用spring赋值的方式如下:
<beanid="user"class="com.spring.User"><propertyname="id"><value>1</value></property><propertyname="name"><value>张三</value></property></bean>
通过这段配置可以看出,赋值是通过<property> 标签完成的,其中name值代表的就是成员变量的名称,嵌入<value> 标签进行赋值。 这里一定要注意,<property>标签是通过set方法完成赋值的,所以你的User类中一定更要对应的set方法,否则是无法完成赋值操作的。
我们在来说下<value> 标签,这个标签主要用于给8种基本数据类型(包括包装类) 和 String类型赋值使用的,有与类中的id和name分别是Integer和String类型,所以这里使用<value>标签进行赋值。 下面我们来介绍下如果是数组或集合类型的数据如何赋值。我们统一使用User类来举例说明。
publicclassUser{ privateIntegerid; privateStringname; privateint[] nums; privateList<String>hobbyList; privateSet<String>friendSet; privateMap<String,String>phoneMap; privatePropertiesprops; // get set 这里省略 }
通过spring进行赋值:
<beanid="user"class="com.spring.User"><propertyname="id"><value>1</value></property><propertyname="name"><value>张三</value></property><propertyname="nums"><list><value>1</value><value>2</value><value>3</value></list></property><propertyname="hobbyList"><list><value>跑步</value><value>游泳</value><value>唱歌</value></list></property><propertyname="friendSet"><set><value>王五</value><value>李四</value><value>王五</value></set></property><propertyname="phoneMap"><map><entry><key><value>1</value></key><value>iphone</value></entry><entry><key><value>2</value></key><value>huawei</value></entry></map></property><propertyname="props"><props><propkey="abc">123</prop><propkey="def">456</prop></props></property></bean>
好了,上面就分别演示了如何给数组,集合,set, Map, Properties类型赋值的方式,要注意,上面凡是用<value>标签的地方都是因为里面的类型属于8中基本数据类型或String。 如果不是这样的类型就不能用<value>标签了,那么除了这9种类型外的类型应该如何赋值呢, 其实除了这九种类型之外和上述集合类型,剩下的就是我们自定义类型的赋值,这种赋值,我们要用ref的方式做引用。
publicclassUserService { privateUserDaouserDao; } publicclassUserDao{ }
就比如这个案例,UserService中依赖了一个自定义的UserDao类型,这个时候我们应该如何通过spring完成注入呢,首先这是两个类,那么这两个类在注入之前肯定得先把对象创建出来,那么通过<bean> 标签即可完成。然后他们的注入关系,可以通过ref属性,指向类的id即可。
<beanid="userDao"class="com.xxx.UserDao"/><beanid="userService"class="com.xxx.UserService"><propertyname="userDao"><refbean="userDao"/></property></bean>
所以,如果上述的集合中对应的是自定义类型,就要把对应的<value>改为<ref bean="" />
除此之外其实还有一种不太常用的写法。大家了解一下即可。
<beanid = "userService"class="xx.xx.xx.UserService"><propertyname="userDao"><beanclass="xx.xx.xx.UserDao"/></property></bean>
这种写法的缺点是如果UserDao被多次引用,则会存在冗余,不利于复用。
好了接下来我们在介绍一下set注入的简化形式。
- 简化Value:
简化前:
<propertyname="id"><value>1</value></property>
简化后:
<propertyname="id"value=1/>
注意: 通过value属性只能简化8中基本数据类型和String
- 简化ref
简化前:
<propertyname="userDao"><refbean="userDao"></property>
简化后:
<propertyname="userDao"ref="userDao"></property>
- p:标签的形式,
但是如果想要使用这种方式,需要在xml的头上,引入p标签的命名空间配置: xmlns:p="www.springframework.org/schema/p"
jdk类型简化:
<beanid="person"class="com.xxx.Person"p:name="lisi"p:id="100"></bean>
自定义类型简化:
<beanid="userService"class="com.xxx.UserService"p:userDao-ref="userDao"></bean>
2.2 构造方法注入
讲完了set注入,我们再来说下构造方法注入,构造方法注入,顾名思义,就是spring底层通过调用Java类的构造方法完成成员变量的赋值操作。所以我们必须在类中实现类的有参构造方法才行。
publicclassCustomer { privateStringname; privateintage; publicCustomer(Stringname, intage){ this.name=name; this.age=age; } }
正如上面的类,提供了两个参数的有参构造,我们该如何赋值呢:
<beanid="customer"class="com.xxx.Customer"><constructor-arg><value>James</value></constructor-arg><constructor-arg><value>11</value></constructor-arg></bean>
写个测试类测试下:
ApplicationContextctx=newClassPathXmlApplicationContext("/aplicationContext.xml"); Customerc= (Customer)ctx.getBean("customer"); System.out.println(c);// 看下对应的成员变量已经赋值成功
注意:xml文件中赋值的参数个数、顺序要和构造方法中保持一致
但是这里这个value有点问题。因为value可以用来给8种基本类型和String赋值,那如果有重载的时候改如何区分的,比如:
publicclassCustomer { privateStringname; privateintage; publicCustomer(Stringname, intage){ this.name=name; this.age=age; } publicCustomer(Stringname){ this.name=name; } publicCustomer(intage){ this.age=age; } }
有两个只有一个参数的构造方法,而这两个值都是可以使用<value>赋值的,那我怎么区分到底调用的是哪一个呢?
<beanid="customer"class="com.xxx.Customer"><constructor-arg><value>37</value></constructor-arg></bean>
这种写法,最终37会赋值给name属性,也就是默认第一个符合的方法,那如果想赋值给age该怎么办呢? 我们可以直接指定类型赋值。
<beanid="customer"class="com.xxx.Customer"><constructor-argtype="int"><value>37</value></constructor-arg></bean>
同时在spring4.x以后,我们可以使用c标签给构造方法赋值,类似于上面的p标签,同样需要先引入c的命名空间,使用idea会自动引入。xmlns:c="www.springframework.org/schema/c"
<beanid="customer"class="com.xxx.Customer"c:name="李四"c:age="30"></bean>
三. 总结
关于spring的两种注入,我们就讲解完了,这里边更常用的是set注入,大家一定要掌握,实战中也基本都是使用set注入,因为更加灵活。构造方法局限性比较大。这里大家一定要掌握什么时候使用<value> 标签,什么时候使用<ref>. 好了今天的文章就这么多内容。
参考资料
孙帅spring详解:www.bilibili.com/video/BV185…
spring构造方法注入: blog.csdn.net/tianzebeibe…