java依賴注入方式介紹
Spring通過DI(依賴注入)實現IOC(控制反轉),常用的注入方式主要有三種:構造方法注入,setter注入,基於註解的注入。
構造方法注入
先簡單看一下測試專案的結構,用maven構建的,四個包:entity:儲存實體,裡面只有一個User類dao:資料訪問,一個介面,兩個實現類service:服務層,一個介面,一個實現類,實現類依賴於IUserDaotest:測試包 在spring的配置檔案中註冊UserService,將UserDaoJdbc通過constructor-arg標籤注入到UserService的某個有引數的構造方法
- <!-- 註冊userService -->
- <
beanid="userService"class="com.lyu.spring.service.impl.UserService"> - <constructor-argref="userDaoJdbc"></constructor-arg>
- </bean>
- <!-- 註冊jdbc實現的dao -->
- <beanid="userDaoJdbc"class="com.lyu.spring.dao.impl.UserDaoJdbc"></bean>
- 1
- 2
- 3
- 4
- 5
- 6
如果只有一個有引數的構造方法並且引數型別與注入的bean的型別匹配,那就會注入到該構造方法中。
- public classUserServiceimplementsIUserService{
- private IUserDao userDao;
- publicUserService(IUserDao userDao) {
- this.userDao = userDao;
- }
- publicvoidloginUser() {
- userDao.loginUser();
- }
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- @Test
- publicvoidtestDI() {
- ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"
); - // 獲取bean物件
- UserService userService = ac.getBean(UserService.class, "userService");
- // 模擬使用者登入
- userService.loginUser();
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
測試列印結果:jdbc-登入成功注:模擬使用者登入的loginUser方法其實只是列印了一條輸出語句,jdbc實現的類輸出的是:jdbc-登入成功,mybatis實現的類輸出的是:mybatis-登入成功。 問題一:如果有多個有引數的構造方法並且每個構造方法的引數列表裡面都有要注入的屬性,那userDaoJdbc會注入到哪裡呢?
- public classUserServiceimplementsIUserService{
- private IUserDao userDao;
- private User user;
- publicUserService(IUserDao userDao) {
- System.out.println("這是有一個引數的構造方法");
- this.userDao = userDao;
- }
- publicUserService(IUserDao userDao, User user) {
- System.out.println("這是有兩個引數的構造方法");
- this.userDao = userDao;
- this.user = user;
- }
- publicvoidloginUser() {
- userDao.loginUser();
- }
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
結果:會注入到只有一個引數的構造方法中,並且經過測試注入哪一個構造方法與構造方法的順序無關
問題二:如果只有一個構造方法,但是有兩個引數,一個是待注入的引數,另一個是其他型別的引數,那麼這次注入可以成功嗎?
- public classUserServiceimplementsIUserService{
- private IUserDao userDao;
- private User user;
- publicUserService(IUserDao userDao, User user) {
- this.userDao = userDao;
- this.user = user;
- }
- publicvoidloginUser() {
- userDao.loginUser();
- }
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
結果:失敗了,即使在costract-arg標籤裡面通過name屬性指定要注入的引數名userDao也會失敗.
問題三:如果我們想向有多個引數的構造方法中注入值該在配置檔案中怎麼寫呢?
- public classUserServiceimplementsIUserService{
- private IUserDao userDao;
- private User user;
- publicUserService(IUserDao userDao, User user) {
- this.userDao = userDao;
- this.user = user;
- }
- publicvoidloginUser() {
- userDao.loginUser();
- }
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
參考寫法:通過name屬性指定要注入的值,與構造方法引數列表引數的順序無關。
- <!-- 註冊userService -->
- <beanid="userService"class="com.lyu.spring.service.impl.UserService">
- <constructor-argname="userDao"ref="userDaoJdbc"></constructor-arg>
- <constructor-argname="user"ref="user"></constructor-arg>
- </bean>
- <!-- 註冊實體User類,用於測試 -->
- <beanid="user"class="com.lyu.spring.entity.User"></bean>
- <!-- 註冊jdbc實現的dao -->
- <beanid="userDaoJdbc"class="com.lyu.spring.dao.impl.UserDaoJdbc"></bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
問題四:如果有多個構造方法,每個構造方法只有引數的順序不同,那通過構造方法注入多個引數會注入到哪一個呢?
- public classUserServiceimplementsIUserService{
- private IUserDao userDao;
- private User user;
- publicUserService(IUserDao userDao, User user) {
- System.out.println("這是第二個構造方法");
- this.userDao = userDao;
- this.user = user;
- }
- publicUserService(User user, IUserDao userDao) {
- System.out.println("這是第一個構造方法");
- this.userDao = userDao;
- this.user = user;
- }
- publicvoidloginUser() {
- userDao.loginUser();
- }
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
結果:哪個構造方法在前就注入哪一個,這種情況下就與構造方法順序有關。
setter注入
配置檔案如下:
- <!-- 註冊userService -->
- <beanid="userService"class="com.lyu.spring.service.impl.UserService">
- <!-- 寫法一 -->
- <!-- <property name="UserDao" ref="userDaoMyBatis"></property> -->
- <!-- 寫法二 -->
- <propertyname="userDao"ref="userDaoMyBatis"></property>
- </bean>
- <!-- 註冊mybatis實現的dao -->
- <beanid="userDaoMyBatis"class="com.lyu.spring.dao.impl.UserDaoMyBatis"></bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
注:上面這兩種寫法都可以,spring會將name值的每個單詞首字母轉換成大寫,然後再在前面拼接上”set”構成一個方法名,然後去對應的類中查詢該方法,通過反射呼叫,實現注入。切記:name屬性值與類中的成員變數名以及set方法的引數名都無關,只與對應的set方法名有關,下面的這種寫法是可以執行成功的
- public classUserServiceimplementsIUserService{
- private IUserDao userDao1;
- publicvoidsetUserDao(IUserDao userDao1) {
- this.userDao1 = userDao1;
- }
- publicvoidloginUser() {
- userDao1.loginUser();
- }
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
還有一點需要注意:如果通過set方法注入屬性,那麼spring會通過預設的空參構造方法來例項化物件,所以如果在類中寫了一個帶有引數的構造方法,一定要把空引數的構造方法寫上,否則spring沒有辦法例項化物件,導致報錯。
基於註解的注入
在介紹註解注入的方式前,先簡單瞭解bean的一個屬性autowire,autowire主要有三個屬性值:constructor,byName,byType。
- constructor:通過構造方法進行自動注入,spring會匹配與構造方法引數型別一致的bean進行注入,如果有一個多引數的構造方法,一個只有一個引數的構造方法,在容器中查詢到多個匹配多引數構造方法的bean,那麼spring會優先將bean注入到多引數的構造方法中。
- byName:被注入bean的id名必須與set方法後半截匹配,並且id名稱的第一個單詞首字母必須小寫,這一點與手動set注入有點不同。
- byType:查詢所有的set方法,將符合符合引數型別的bean注入。
下面進入正題:註解方式註冊bean,注入依賴 主要有四種註解可以註冊bean,每種註解可以任意使用,只是語義上有所差異:
- @Component:可以用於註冊所有bean
- @Repository:主要用於註冊dao層的bean
- @Controller:主要用於註冊控制層的bean
- @Service:主要用於註冊服務層的bean
描述依賴關係主要有兩種:
- @Resource:java的註解,預設以byName的方式去匹配與屬性名相同的bean的id,如果沒有找到就會以byType的方式查詢,如果byType查詢到多個的話,使用@Qualifier註解(spring註解)指定某個具體名稱的bean。
- @Resource
- @Qualifier("userDaoMyBatis")
- private IUserDao userDao;
- publicUserService(){
- }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- @Autowired:spring註解,預設也是以byName的方式去匹配與屬性名相同的bean的id,如果沒有找到,就通過byType的方式去查詢,如果查詢到多個,用@Qualifier註解限定具體使用哪個。
- @Autowired
- @Qualifier("userDaoJdbc")
- private IUserDao userDao;
- 1
- 2
- 3
- 轉載自:https://blog.csdn.net/jinchaoh/article/details/80246577