【java框架】Spring(2) -- Spring依赖注入配置
1. spring依赖注入方式
今天这里主要讲解一下spring框架中的依赖注入的多种方式及相关的一系列配置,这也是spring这款轻量级di、ioc的bean容器框架的核心魅力所在。
spring依赖注入方式这里着重说明一下构造器参数注入、多类型(arrays、set、list、map、properties对象)注入、xml自动注入、全注解配置注入这几种注入方式。
下面围绕这几种不同注入方式进行一一讲解:
1.1.构造器参数注入
在spring(1)知识入门中我们了解到在applicationcontext.xml文件中配置了bean之后使用springtest来获取bean对象,必须保证bean所在的类中必须有无参数的构造方法,否则会产生如下错误:
而且配置了无参构造方法之后,获取到的bean中对应的属性也是null,如下图:
那不需要无参构造方法并且我想注入bean中的属性值怎么配置呢?
构造器参数注入就解决了上述问题,构造器参数注入的配置方式有三种,如下所示:
① 根据构造器参数索引注入;
<bean id="mybean" class="cn.yif.demo1_constructor.mybean"> <!--方式一:根据构造器参数的索引注入 index:从0开始,属性位置; value:设置值--> <constructor-arg index="0" value="丽娜"></constructor-arg> <constructor-arg index="1" value="21"></constructor-arg> </bean>
② 根据构造器参数名称注入;
<bean id="mybean" class="cn.yif.demo1_constructor.mybean"> <!--方式二:根据构造器的参数名称注入 name:属性名; value:设置值--> <constructor-arg name="username" value="丽娜"></constructor-arg> <constructor-arg name="age" value="21"></constructor-arg> </bean>
③ 根据构造器的类型注入;
<bean id="mybean" class="cn.yif.demo1_constructor.mybean"> <!--方式三:根据构造器的类型注入 type:类型全限定名; value:设置值--> <constructor-arg type="java.lang.string" value="丽娜"></constructor-arg> <constructor-arg type="java.lang.integer" value="21"></constructor-arg> </bean>
④ 特殊情况:如果有一个属性是另外一个类(bean)的对象,注入有以下两种方式;
方式一:在外部定义,内部构造器使用ref引入;
<bean id="otherbean" class="cn.yif.demo1_constructor.otherbean"> <property name="otherage" value="24"></property> <property name="othername" value="李勇"></property> </bean> <!--方式一:otherbean在外部定义好,内部constructor-arg使用ref引入--> <bean id="mybean" class="cn.yif.demo1_constructor.mybean"> <constructor-arg index="0" value="丽娜"></constructor-arg> <constructor-arg index="1" value="21"></constructor-arg> <constructor-arg ref="otherbean"></constructor-arg> </bean>
方式二:在内部constructor-arg标签使用一个完整的bean来注入
<!--方式二:在constructor-arg内部使用bean标签定义一个bean--> <bean id="mybean" class="cn.yif.demo1_constructor.mybean"> <constructor-arg value="丽娜"></constructor-arg> <constructor-arg value="21"></constructor-arg> <constructor-arg> <bean id="otherbean" class="cn.yif.demo1_constructor.otherbean"> <property name="otherage" value="24"></property> <property name="othername" value="李勇"></property> </bean> </constructor-arg> </bean>
1.2.多类型(arrays、set、list、map、properties对象)注入
① 先准备一个具有多类型属性的multitypebean类,并提供setter与tostring方法;
import cn.yif.demo1_constructor.otherbean; import java.math.bigdecimal; import java.util.*; public class multitypebean { // 简单属性 private long id; private string name; private boolean sex; private bigdecimal salary; // 对象属性 private list<string> list; private list<otherbean> otherbeanlist; private set<string> set; private set<otherbean> otherbeanset; private map<string,object> map; //properties资源属性与string数组属性 private properties props1; private properties props2; private string[] arrays; public void setid(long id) { this.id = id; } public void setname(string name) { this.name = name; } public void setsex(boolean sex) { this.sex = sex; } public void setsalary(bigdecimal salary) { this.salary = salary; } public void setlist(list<string> list) { this.list = list; } public void setotherbeanlist(list<otherbean> otherbeanlist) { this.otherbeanlist = otherbeanlist; } public void setset(set<string> set) { this.set = set; } public void setotherbeanset(set<otherbean> otherbeanset) { this.otherbeanset = otherbeanset; } public void setmap(map<string, object> map) { this.map = map; } public void setprops1(properties props1) { this.props1 = props1; } public void setprops2(properties props2) { this.props2 = props2; } public void setarrays(string[] arrays) { this.arrays = arrays; } @override public string tostring() { return "multitypebean{" + "id=" + id + ", name='" + name + '\'' + ", sex=" + sex + ", salary=" + salary + ", list=" + list + ", otherbeanlist=" + otherbeanlist + ", set=" + set + ", otherbeanset=" + otherbeanset + ", map=" + map + ", props1=" + props1 + ", props2=" + props2 + ", arrays=" + arrays.tostring(arrays) + '}'; } }
1.2.1. arrays数组注入
写法一,简写方式:
<property name="arrays" value="xxx,yyy,zzz"></property>
写法二,正式写法:
<property name="arrays"> <array> <value>xxx</value> <value>yyy</value> <value>zzz</value> </array> </property>
1.2.2. list集合注入
<!—对于list<string>的注入--> <property name="list"> <list> <value>aaa</value> <value>bbb</value> <value>ccc</value> </list> </property> <!—对于list<otherbean>的注入--> <property name="otherbeanlist"> <list> <bean id="otherbean" class="cn.yif.demo1_constructor.otherbean"></bean> </list> </property>
1.2.3. set集合注入
<!—对于set<string>的注入--> <property name="set"> <set> <value>aaa</value> <value>bbb</value> <value>ccc</value> </set> </property> <!—对于set<otherbean>的注入--> <property name="otherbeanset"> <set> <bean id="otherbean" class="cn.yif.demo1_constructor.otherbean"></bean> <bean id="otherbean" class="cn.yif.demo1_constructor.otherbean"></bean> </set> </property>
1.2.4. map字典注入
<property name="map"> <map> <entry key="username" value="李丽"></entry> <entry key="age" value="21"></entry> </map> </property>
1.2.5. properties资源属性注入
<property name="props1"> <value> jpa.dialect=org.jpa.dialect.hsqldialect jpa.driverclassname=com.mysql.jdbc.driver </value> </property> <property name="props2"> <props> <prop key="jpa.dialect">org.jpa.dialect.hsqldialect</prop> <prop key="jpa.driverclassname">com.mysql.jdbc.driver 支持中文</prop> </props> </property>
1.2.6. 普通字段属性注入
<property name="id" value="1"></property> <property name="name" value="李华"></property> <property name="sex" value="true"></property> <property name="salary" value="8045.5"></property>
1.3.xml自动注入
使用xml注入bean有两种方式注入配置:
① bytype方式
这种方式是按照注入对象的类型进行注入,注意:类型要求只能配置一个实例,类型表示接口iuserdao这种对应的实体实现类,配置文件中不能有2个实现类类型,否则会抛出如下异常:
caused by: org.springframework.beans.factory.nouniquebeandefinitionexception: no qualifying bean of type [cn.yif.demo3_xmlautoinject.iuserdao] is defined: expected single matching bean but found 2: jdbcdao,jpadao
② byname方式
这种方式是按照bean的名称,即bean配置中id的值进行注入
③ 配置的使用范围
根节点beans default-autowire="byname" 对当前配置文件的所有bean都生效
子节点bean autowire="bytype"只对当前bean生效
具体配置示例如下:
<?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.xsd" default-autowire="bytype"> <!--default-autowire="bytype":表示是全局的注入方式,所有bean通过bytype方式注入--> <bean id="jdbcdao" class="cn.yif.demo3_xmlautoinject.userjdbcdaoimpl"></bean> <!--<bean id="jpadao" class="cn.yif.demo3_xmlautoinject.userjpadaoimpl" autowire="byname"></bean>--> <!--userservice需要userdao这个类型,我通过bytype这个类型注入给你一个就行--> <!--autowire="byname"表示这个bean的注入方式由我自定义注入,我通过byname方式注入--> <bean id="userservice" class="cn.yif.demo3_xmlautoinject.userserviceimpl" autowire="byname"> </bean> <!--useraction需要userservice这个实现类的属性,我通过bytype这个类型给你注入就行--> <bean id="useraction" class="cn.yif.demo3_xmlautoinject.useraction"> </bean> </beans>
1.4.全注解配置
1.4.1.全注解配置使用
在java中写xml配置bean还是比较麻烦的,而spring为了简化还提供了全注解的方式来配置bean。下面是使用全注解配置bean的步骤:
① 在applicationcontext.xml或者当前包路径的springxxxtest-context.xml文件中去配置context命名空间,告诉spirng去哪个包目录下扫描注解;
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 进行包的扫描,去看类上面是否有相应的标签配置 --> <context:component-scan base-package="cn.yif.demo4_allannotations" /> <!-- 这个不是必须的(spring3.2版本前使用) 配上后兼容性好 --> <context:annotation-config /> </beans>
② spring提供了四个注解类型:@component、@repository、@service、@controller用于注解,表示这个类需要被spring管理为bean。
一般@component表示组件,用于不知道是其是在三层结构中的那一层时使用;
@repository一般用于dao数据持久层;
@service一般用于业务层注解;
@controller一般用于表现层(控制层)。
具体配置如下:
action层:
@controller public class useraction { @autowired private iuserservice userservice; public void insert(){ system.out.println("useraction insert..."); userservice.insert(); } }
service层:
@service public class userserviceimpl implements iuserservice { @autowired private iuserdao userjpadaoimpl; public void insert(){ system.out.println("userservice insert..."); userjpadaoimpl.insert(); } }
dao层:
@repository public class userjdbcdaoimpl implements iuserdao { @override public void insert() { system.out.println("userjdbcdao insert..."); } }
1.4.2.全注解配置细节
这里全注解配置上有一个隐藏的问题,比如我按真实情况来一个dao接口,但是这个接口有两个实现iuserdao → userjdbcdaoimpl/userjpadaoimpl,在注入的时候就会出现如下错误:
caused by: org.springframework.beans.factory.nouniquebeandefinitionexception: no qualifying bean of type [cn.yif.demo4_allannotations.iuserdao] is defined: expected single matching bean but found 2: userjdbcdaoimpl,userjpadaoimpl
这里有两种解决方案:
首先为dao生成的bean加上名称,我们调用的时候根据名称调用即可;
@repository("userjdbcdao") public class userjdbcdaoimpl implements iuserdao { @override public void insert() { system.out.println("userjdbcdao insert..."); } } @repository("userjpadao") public class userjpadaoimpl implements iuserdao { @override public void insert() { system.out.println("userjpadao insert..."); } }
在调用时,我们可以使用spring公司提供的注解或者sun公司提供的注解来调用对应名称下的bean注解,如下:
① 使用spring公司提供的一套注解@qualifier(“配置的bean名称”)
@service public class userserviceimpl implements iuserservice { @autowired @qualifier("userjpadao") private iuserdao userdao; public void insert(){ system.out.println("userservice insert..."); userdao.insert(); } }
② 使用@resource(name=”配置的bean名称”)
@service public class userserviceimpl implements iuserservice { @resource(name = "userjdbcdao") private iuserdao userdao; public void insert(){ system.out.println("userservice insert..."); userdao.insert(); } }
总结:现在一般使用全注解配置,比较简单,也比较灵活。