Spring Boot常用註解(二)
1.概述
在 Spring Boot常用註解(一) - 宣告Bean的註解 中學習了Spring Boot中宣告Bean的註解
那Spring容器中的Bean是怎麼實現自動裝配(依賴)的呢?
這就是接下來學習的注入註解咯
注入Bean的註解:
- @Autowired
- @Inject
- @Resource
[email protected]註解
@Autowired註解原始碼:
package org.springframework.beans.factory.annotation;
/**
1. @since 2.5
2. @see AutowiredAnnotationBeanPostProcessor
3. @see Qualifier
4. @see Value
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
- @Autowired註解作用在建構函式、方法、方法引數、類欄位以及註解上
- @Autowired註解可以實現Bean的自動注入
2.1 @Autowired註解原理
在Spring Boot應用啟動時,Spring容器會自動裝載一個org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor處理器,當容器掃掃描到@Autowired註解時,就會在IoC容器就會找相應型別的Bean,並且實現注入。
2.2 @Autowired註解使用
在Web MVC中控制層(Controller)訪問的是業務層(Service),而業務層(Service)訪問的是資料層(Dao),使用@Autowired註解實現注入
資料層:
package com.example.demo.chapter1.useannotation.autowired.dao;
/**
* 資料層介面,用於訪問資料庫,返回資料給業務層
* */
public interface IDao {
public String get();
}
/**
* 資料層介面實現類
*
* 1.資料層Bean用@Repository標註
* 2.當前Bean的名稱是"autowiredUserDaoImpl"
* 3.設定當前Bean的為原型模式,即每次獲取Bean時都建立一個新例項
* */
@Repository("autowiredUserDaoImpl")
@Scope("prototype")
public class UserDaoImpl implements IDao {
public String get() {
return "@Autowired註解實現自動裝配";
}
}
業務層:
package com.example.demo.chapter1.useannotation.autowired.service;
/**
* 業務層介面
* 從資料層獲取資料,處理結果返回給控制層
* */
public interface IService {
public String get();
}
/**
* 業務層介面實現
*
* 1.資料層Bean用@Service標註
* 2.當前Bean的名稱是"autowiredUserServiceImpl"
* 3.設定當前Bean的為原型模式,即每次獲取Bean時都建立一個新例項
* 4.業務層有一個數據層介面IDao,使用@Autowired註解標註
* */
@Service("autowiredUserServiceImpl")
@Scope("prototype")
public class UserServiceImpl implements IService {
/**
* @Autowired實現自動裝配
* Spring IoC容器掃描到@Autowired註解會去查詢IDao的實現類,並自動注入
* */
@Autowired
private IDao dao;
@Override
public String get() {
return dao.get();
}
}
- Bean通過註解@Service宣告為一個Spring容器管理的Bean,Spring容器會掃描classpath路徑下的所有類,找到帶有@Service註解的UserServiceImpl類,並根據Spring註解對其進行初始化和增強,命名為autowiredUserServiceImpl
- Spring容器如果發現此類屬性dao也有註解@Autowired,則會從Spring容器中查詢一個已經初始化好的IDao的實現類,如果沒有找到,則先初始化一個IDao實現類
控制層:
package com.example.demo.chapter1.useannotation.autowired.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.chapter1.useannotation.autowired.service.IService;
/**
1. 控制層
2.
3. 1.控制層使用@RestController註解標註,返回json格式的字串
4. 2.獲取業務層返回的資料,輸出到客戶端
5. 3.請求:http:localhost:8080/autowiredController
6. */
@RestController
public class AutowiredController {
@Autowired
private IService service;
@RequestMapping("/autowiredController")
public String get () {
return service.get();
}
}
測試結果:
1. 啟動Spring Boot應用
2. 瀏覽器輸入:http:localhost:8080/autowiredController
3. 返回結果:
2.3 @Qualifier註解
@Qualifier註解原始碼:
package org.springframework.beans.factory.annotation;
/**
* @since 2.5
* @see Autowired
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
- 如果Spring 容器中有兩個資料層介面IDao的實現類
- 而業務層通過@Autowired註解標註的是IDao介面
- 這時就需要@Qualifier註解來指定需要自動裝配的Bean的名稱
- 否則會報如下的錯:
Description:
Field dao in com.example.demo.chapter1.useannotation.autowired.service.UserServiceImpl required a single bean, but 2 were found:
- autowiredAdminDaoImpl: defined in file [D:\eclipse\workspace\boot\demo\target\classes\com\example\demo\chapter1\useannotation\autowired\dao\AdminDaoImpl.class]
- autowiredUserDaoImpl: defined in file [D:\eclipse\workspace\boot\demo\target\classes\com\example\demo\chapter1\useannotation\autowired\dao\UserDaoImpl.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
2.4 @Qualifier註解使用
資料層介面IDao:
public interface IDao {
public String get();
}
IDao實現類 - UserDaoImpl
@Repository("autowiredUserDaoImpl")
@Scope("prototype")
public class UserDaoImpl implements IDao {
public String get() {
return "@Autowired註解實現自動裝配 - UserDaoImpl";
}
}
IDao實現類 - AdminDaoImpl
@Repository("autowiredAdminDaoImpl")
@Scope("prorotype")
public class AdminDaoImpl implements IDao {
@Override
public String get() {
return "@Autowired註解實現自動裝配 - AdminDaoImpl";
}
}
@Qualifier註解使用:
@Service("autowiredUserServiceImpl")
@Scope("prototype")
public class UserServiceImpl implements IService {
/**
* @Autowired實現自動裝配
* Spring IoC容器掃描到@Autowired註解會去查詢IDao的實現類,並自動注入
* */
@Autowired
@Qualifier("autowiredUserDaoImpl")
private IDao dao;
@Override
public String get() {
return dao.get();
}
}
測試結果:
2.4 @Autowired註解的requird屬性
在使用@Autowired註解時,首先在容器中查詢對應型別的bean
- 如果查詢結果Bean剛好為一個,自動注入
- 如果查詢結果Bean不止一個,通過@Qualifier註解指定自動裝配Bean的名稱
- 如果沒有查詢到對應型別的Bean,由於預設@Autowired(required=true),會丟擲異常,解決方法是使用@Autoiwired(quired=false)
- @Autowired(quired=true)意味著依賴是必須的
- @Autowired(quired=false)等於告訴 Spring:在找不到匹配 Bean 時也不報錯
資料層介面:
package com.example.demo.chapter1.useannotation.autowired.dao;
/**
* 資料層介面
* */
public interface ITask {
public String get();
}
業務層介面:
package com.example.demo.chapter1.useannotation.autowired.service;
@Service("taskServiceImpl")
@Scope("prototype")
public class TaskServiceImpl implements IService {
@Autowired
private ITask task;
@Override
public String get() {
return task.get();
}
}
如果沒有介面ITask的實現類,啟動Spring Boot應用會報如下錯:
沒有找到ITask的實現類
Description:
Field task in com.example.demo.chapter1.useannotation.autowired.service.TaskServiceImpl required a bean of type 'com.example.demo.chapter1.useannotation.autowired.dao.ITask' that could not be found.
Action:
Consider defining a bean of type 'com.example.demo.chapter1.useannotation.autowired.dao.ITask' in your configuration.
解決方法 - 新增required=false
@Service("autowiredTaskServiceImpl")
@Scope("prototype")
public class TaskServiceImpl implements IService {
/**
* 介面ITask沒有實現類
* 新增required=false不報錯
* */
@Autowired(required=false)
private ITask task;
@Override
public String get() {
return task.get();
}
}
啟動成功(埠號是8090)
因為ITask沒有實現類,瀏覽器訪問http:localhost:8090/requirdController時會報空指標:
java.lang.NullPointerException: null
at com.example.demo.chapter1.useannotation.autowired.service.TaskServiceImpl.get(TaskServiceImpl.java:23) ~[classes/:na]
at com.example.demo.chapter1.useannotation.autowired.controller.RequirdController.get(RequirdController.java:18) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_60]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_60]