說明:
參考資料:b戰尚矽谷楊博超老師的ssm整合視訊
2.3基于注解管理bean
2.3.1、實驗一:标記與掃描
①注解
和 XML 配置檔案一樣,注解本身并不能執行,注解本身僅僅隻是做一個标記**,具體的功能是架構檢測 到注解标記的位置,然後針對這個位置按照注解标記的功能來執行具體操作。**
**本質上:**所有一切的操作都是Java代碼來完成的,XML和注解隻是告訴架構中的Java代碼如何執行。
舉例:元旦聯歡會要布置教室,藍色的地方貼上元旦快樂四個字,紅色的地方貼上拉花,黃色的地方貼 上氣球。
②掃描
Spring 為了知道程式員在哪些地方标記了什麼注解,就需要通過掃描的方式,來進行檢測。然後根據注 解進行後續操作。
③建立Maven Module
配置pom.xml。 注意:建立Maven Module後做的第一件事就是配置pom.xml
<dependencies>
<!-- 基于Maven依賴傳遞性,導入spring-context依賴即可導入目前所需所有jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.1</version>
</dependency>
<!-- junit測試 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
④建立spring配置檔案
取名applicationContext.xml,放到resources下.
暫時不寫内容
⑤辨別元件的常用注解
每一個bean都是ioc容器中的元件,使用下面這些 辨別元件的注解,可以将類的對象交給ioc容器管理。
這4個注解的功能是一樣的。
@Component:将類辨別為普通元件
@Controller:将類辨別為控制層元件
@Service:将類标 識為業務層元件
@Repository:将類辨別為持久層元件
問:以上四個注解有什麼關系和差別?
通過檢視源碼我們得知,@Controller、@Service、@Repository這三個注解隻是在@Component注解 的基礎上起了三個新的名字。
對于Spring使用IOC容器管理這些元件來說沒有差別。是以@Controller、@Service、@Repository這 三個注解隻是給程式員看的,讓我們能夠便于分辨元件的作用、功能。
4個注解的功能一樣。
注意:雖然它們本質上一樣,但是為了代碼的可讀性,為了程式結構嚴謹我們程式員肯定不能随便胡亂标記。
⑥建立元件
先建立好三層,即controller層、service層、dao層,然後使用注解建立元件. 注意:這4個注解不能放到接口上,因為接口不能建立對象。
建立控制層元件UserController
@Controller
public class UserController {
}
建立接口UserService
public interface UserService {
}
建立業務層元件UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
}
建立接口UserDao
public interface UserDao {
}
建立持久層元件UserDaoImpl
@Repository
//我是這樣記得。因為dao層是與資料庫互動的,而Repository是倉庫的意思,都有 庫 的含義,是以,用@Repository辨別
//建立dao層元件
public class UserDaoImpl implements UserDao {
}
⑦掃描元件
需要在spring的配置檔案中配置。我這的spring配置檔案叫 applicationContext.xml
掃描就是讓spring架構知道,我在哪加了注解,加了什麼注解。然後,spring架構就會根據注解執行相應的操作
使用context:component-scan 标簽
情況一:最基本的掃描方式
通過包來掃描,會掃描包下的所有類。
<context:component-scan base-package="com.atguigu.spring">
</context:component-scan>
情況二:指定要排除的元件
<context:component-scan base-package="com.atguigu.spring">
<!-- context:exclude-filter标簽:指定排除規則。就是不掃描哪些元件 -->
<!--
type:設定排除的依據
type="annotation",根據注解排除,expression中設定要排除的注解的全類名 (常用)
type="assignable",根據類型排除,expression中設定要排除的類型的全類名
-->
<!--不掃描com.atguigu.spring包下,使用Controller注解的類-->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<!--<context:exclude-filter type="assignable"
expression="com.atguigu.controller.UserController"/>-->
</context:component-scan>
注解的全類名:找到寫的注解,然後右鍵,copy Reference(快捷鍵:Ctrl+Alt+Shift+c)。當然,也可以自己敲
情況三:僅掃描指定元件(很少用)
<context:component-scan base-package="com.atguigu.spring" use-default-filters="false">
<!-- context:include-filter标簽:包含掃描,指定在原有掃描規則的基礎上追加的規則 -->
<!-- use-default-filters屬性:取值false表示關閉預設掃描規則 -->
<!-- 此時必須設定use-default-filters="false",因為預設規則就是掃描指定包下所有類 -->
<!--
type:設定包含的依據
type="annotation",根據注解包含,expression中設定要包含的注解的全類名
type="assignable",根據類型包含,expression中設定要包含的類型的全類名
-->
<!--隻掃描com.atguigu.spring包下,使用Controller注解的類-->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<!--<context:include-filter type="assignable"
expression="com.atguigu.controller.UserController"/>-->
</context:component-scan>
⑧測試
@Test
public void testIocByAnnotation(){
//擷取ioc容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//擷取對象. 因為咱已經通過注解,将些類的對象交給了ioc容器管理,是以,ioc容器中有這些對象,可以直接擷取。
UserController userController = ioc.getBean(UserController.class);
UserService userService = ioc.getBean(UserService.class);//通過類型擷取時,可以使用類本身 |父類| 實作的接口類型都可
UserDao userDao = ioc.getBean(UserDao.class);
System.out.println(userController);
System.out.println(userService);
System.out.println(userDao);
}
能有輸出結果(輸出的都是位址),說明我們已經實作了 通過注解的方式将對象交給ioc容器管理了。
⑨bean的id
在我們使用XML方式管理bean的時候,每個bean都有一個唯一辨別(使用bean标簽的id屬性設定),便于在其他地方引用。現在使用 注解後,每個元件仍然應該有一個唯一辨別。
a>預設情況 :
類名首字母小寫就是bean的id。
例如:UserController類對應的bean的id就是userController。
b> 自定義bean的id :
可通過辨別元件的注解的value屬性設定自定義的bean的id
@Service(“userService”)//預設為userServiceImpl public class UserServiceImpl implements UserService {}
隻設定注解的value屬性時,value=“”可以省略。
bean的預設id
@Test
public void testIocByAnnotation(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//通過注解+掃描所配置的bean的id,預設為類的小駝峰,即類名的首字母小寫
UserController userController = (UserController) ioc.getBean("userController");
System.out.println(userController);
//注意,通過預設的id擷取時,不能用接口,得用類(or實作類)
UserService userService = (UserService) ioc.getBean("userServiceImpl");
System.out.println(userService);
}
2.3.2、實驗二:基于注解的自動裝配
回顧:
自動裝配:根據指定的政策,在IOC容器中比對某一個bean,自動為指定的bean中所依賴的類類型或接口類 型屬性指派
①場景模拟
參考基于xml的自動裝配
在UserController中聲明UserService對象
在UserServiceImpl中聲明UserDao對象
②@Autowired注解
在成員變量上直接标記@Autowired注解即可完成自動裝配,不需要提供setXxx()方法。以後我們在項
目中的正式用法就是這樣。
@Autowired: 實作自動裝配功能的注解
- @Autowired注解能夠辨別(放)的位置。 辨別–在這是 動詞
- 放在成員變量上,此時不需要設定成員變量的set方法
- 放在set方法上
- 放在 為目前成員變量指派的有參構造上
@Controller
public class UserController {
//在UserController中需要用到UserService
@Autowired
private UserService userService;
public void saveUser(){
userService.saveUser();
}
}
public interface UserService {
void saveUser();
}
@Service
public class UserServiceImpl implements UserService {
//在UserServiceImpl中需要用到UserDao
@Autowired
private UserDao userDao;
@Override
public void saveUser() {
userDao.insertUser();
}
}
public interface UserDao {
void insertUser();
}
@Repository
public class UserDaoImpl implements UserDao {
@Override
public void insertUser() {
System.out.println("儲存使用者---儲存成功");
}
}
spring的配置檔案applicationContext.xml中,隻有注解掃描的配置
③測試
@Test
public void testIocByAnnotation(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
UserController userController = ioc.getBean(UserController.class);
userController.saveUser(); //成功執行,說明使用注解自動裝配 成功
}
④@Autowired注解其他細節
@Autowired注解可以标記在構造器和set方法上
@Controller
public class UserController {
//在UserController中需要用到UserService
private UserService userService;
//放在 為目前成員變量指派的有參構造上
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
@Controller
public class UserController {
//在UserController中需要用到UserService
private UserService userService;
//放在成員變量的set方法上
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
⑤@Autowired工作流程
@Autowired的工作原理。
private UserService userService;//userService就是變量名
private UserService a;//a就是變量名
我的對@Autowire工作原理的了解:
a》預設通過byType的方式,在ioc容器中通過類型比對某個bean為屬性指派
b》若ioc中有多個類型比對的bean,此時會自動轉換為byName的方式實作自動裝配的效果
即,将要指派的屬性的屬性名作為bean的id 去比對某個bean為屬性指派
c》若byType和byName的方式都無法實作自動裝配,即ioc容器中有多個類型比對的bean且這些bean的
id和要指派的屬性的屬性名都不一緻,此時抛出異常:NoUniqueDefinitionException
d》此時,可以在要指派的屬性上,添加一個注解@Qualifier(“xxxx”),通過該注解的value屬性值,指定
某個bean的id,将這個bean為屬性指派。
@Controller
public class UserController {
//在UserController中需要用到UserService
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
@Autowired中有屬性required,預設值為true,是以在自動裝配無法找到相應的bean時,會裝 配失敗
可以将屬性required的值設定為false,則表示能裝就裝,裝不上則使用bean屬性的 預設值 。
但是實際開發時,基本上所有需要裝配元件的地方都是必須自動裝配的(Autowired),用不上這個required屬性。
開發中,一個類型的bean在ioc中不會設定多個