1. Spring依賴註入方式 今天這裡主要講解一下Spring框架中的依賴註入的多種方式及相關的一系列配置,這也是Spring這款輕量級DI、IOC的Bean容器框架的核心魅力所在。 Spring依賴註入方式這裡著重說明一下構造器參數註入、多類型(Arrays、Set、List、Map、Prope ...
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(); } }
總結:現在一般使用全註解配置,比較簡單,也比較靈活。