文章目錄
- 一. 存儲Bean對象
- 1. 配置掃描路徑
- 2. 添加注解存儲 Bean 對象
- 2.1 使用五大類注解存儲Bean
- 2.2 為什么要有五大類注解?
- 2.3 有關獲取Bean參數的命名規則
- 3. 使用方法注解儲存 Bean 對象
- 3.1 方法注解儲存對象的用法
- 3.2 @Bean的重命名
- 3.3 同?類型多個 @Bean 報錯
- 二. 獲取 Bean 對象(對象裝配)
- 1. 屬性注入
- 2. Setter注入
- 3. 構造方法注入(Spring官方推薦)
- 4. @Resource:另?種注入關鍵字
一. 存儲Bean對象
1. 配置掃描路徑
配置掃描路徑是使用注解之前的前置工作,是非常重要的,是必須的操作項.只有被配置的包下的所有類,添加了注解才能被正確的識別并保存到 Spring
中.
首先創建一個Spring項目.創建好后,第一步就是配置掃描路徑:在resources
目錄中創建一個spring-config.xml
文件.然后在spring-config.xml
添加如下配置:
<?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:content="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 https://www.springframework.org/schema/context/spring-context.xsd"><content:component-scan base-package=""></content:component-scan>
</beans>
<content:component-scan base-package=""></content:component-scan>
中base-package=""
的值設置為需要掃描對象的根路徑.注意:這個根路徑是從java目錄開始的.
2. 添加注解存儲 Bean 對象
想要將對象存儲在spring中,有兩種注解類型可以實現:
-
使用類注解(五大類注解):
@Controller
:控制器:驗證用戶請求的數據正確性(相當于安保系統).@Service
:服務:編排和調度具體執行方法(相當于客服中心).@Repository
:持久層:和數據庫交互;(相當于執行者) = DAO(Date Access Obiect)數據訪問層@Component
:組件;(工具類)@Configuration
:配置項(項目中的一些配置)
-
方法注解
@Bean
2.1 使用五大類注解存儲Bean
首先,我們來了解如何使用五大類注解來儲存對象
-
@Controller
package com.spring.demo;import org.springframework.stereotype.Controller;@Controller public class UserController {public void sayHi() {System.out.println("UserController sayHi!");} }
在掃描路徑下創建該
UserController
類.并在類上加@Controller
注解,此時就將Bean
存儲到容器中了.接下來就是從 Spring 中讀取出我們的對象,這里還是先使用依賴查找的方式來獲取 Bean,使用五大類注解,默認情況下,Bean 的名字就是原類名首字母小寫(小駝峰).
import com.spring.demo.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class App {public static void main(String[] args) {//1.獲取 srping 容器ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//2.獲取 bean 對象//獲取對象時使用類名的小駝峰形式作為 name 參數UserController userController = context.getBean("userController", UserController.class);//3.使用 beanuserController.sayHi();} }
運行結果:
注意:要將Bean存儲到Spring中需要滿足兩個條件:- 使用五大類注解創建的類
- 必須在配置的掃描路徑下.(包括子包)
掃描路徑也叫做根路徑.兩個條件缺一不可.
為什么要設置根路徑?
設置根路徑其實也是為了提高程序的性能,因為如果不設置根路徑,Spring 就會掃描項目文件中所有的目錄,但并不是所有類都需要儲存到 Spring當中,這樣性能就會比較低,設置了根路徑,Spring 就只掃描該根路徑下所有的目錄就可以了,提高了程序的性能。下來我們演示一下沒有配置掃描路徑下的情況:
還需要知道的是使用注解存儲的 Bean 和使用XML存儲的的 Bean 是可以一同使用的,比如我們將剛剛有問題的Student
重新通過XML的方式進行存儲.
注意:默認情況下,使用原類名首字母小寫就能讀取到Bean對象.特例情況:原類名如果首字母和第二個字母都是大寫的情況下,那么bean名稱就是原類名.
-
@Service
啟動類中代碼://1.獲取 srping 容器ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");//2.獲取 bean 對象//獲取對象時使用類名的小駝峰形式作為 name 參數UserService userService = context.getBean("userService", UserService.class);//3.使用 beanuserService.sayHi();
UserService
類:package com.spring.demo;import org.springframework.stereotype.Service;@Service public class UserService {public void sayHi(){System.out.println("UserService sayHi!");} }
運行結果:
其余三種使用方式相同,此處不再做介紹.
2.2 為什么要有五大類注解?
既然都五大類完成的是同樣的工作,那為什么要有五大類注解呢?
其實五大類注解主要是為了規范 Java 項目的代碼,Java 項目的標準分層如下:
- 控制層(Controller)
- 服務層(Service)
- 數據持久層(Dao)
而五大類注解便是對應著不同的層級別使用的,讓程序猿看到某一個注解就可以明確這個了類是做什么的。
程序的?程分層,調?流程如下:
包括企業中也是按照這樣的結構來將項目分層的,典型的比如阿里,它只是在標準分層在服務層(Service)做了一個擴展,劃分的更加細致詳細了.
五大類注解主要起到的是“見名知意”的作用,代碼層面上來看,作用是類似的.查看五大類源碼可知:
五大類的源碼中除了 @Component
以外,其他四大類注解中都包含了 @Component 注解的功能,這四大類注解都是基于 @Component 實現的,是 @Component 拓展。
2.3 有關獲取Bean參數的命名規則
上文中在使用依賴查找的方式獲取Bean
時,我們講到了getBean
方法的BeanName
是使用類名的小駝峰形式(即類名的首字母小寫)以及第一個字母和第二個字母都大寫情況下的特例.
注意:BeanName的規范命名規則并不是 Spring 獨創的,而依照 Java 標準庫的規則進行的。
BeanName的規范命名規則:
- 如果類名不存在或類名為空字符串,
BeanName
為原類名。 - 如果類名字長度大于1,且第一個與第二個字符為大寫,
BeanName
為原類名。 - 其他情況,
BeanName
為原類名的小駝峰形式.
3. 使用方法注解儲存 Bean 對象
3.1 方法注解儲存對象的用法
類注解是添加到某個類上的,而方法注解是放到某個方法上的.在Spring框架的設計中,方法注解@Bean
要配合類注解才能將對象正常存儲到Spring容器中.
舉個🌰:我們有一個普通書本類
package com.spring.demo;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;@Controller
public class BookInfo {private String bookName;private String author;private String style;public String getBookName() {return bookName;}public void setBookName(String bookName) {this.bookName = bookName;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public String getStyle() {return style;}public void setStyle(String style) {this.style = style;}@Override@Beanpublic String toString() {return "com.spring.demo.BookInfo{" +"bookName='" + bookName + '\'' +", author='" + author + '\'' +", style='" + style + '\'' +'}';}}
下面演示使用@Bean方法注解儲存對象:
package com.spring.demo;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;@Controller
public class Books {@Beanpublic BookInfo getBook(){BookInfo bookInfo = new BookInfo();bookInfo.setBookName("三體");bookInfo.setAuthor("劉慈欣");bookInfo.setStyle("文學科幻");return bookInfo;}public void sayHi(){System.out.println("Books sayHi!");}
}
啟動類:
獲取方法注解儲存的對象時,傳入的BeanName
參數值默認值就是方法名,上面的代碼中方法名為getBook,所以獲取時,就使用getBook作為參數來進行獲取。
import com.spring.demo.BookInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class App2 {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");BookInfo book = context.getBean("getBook", BookInfo.class);System.out.println(book);}
}
運行結果:
3.2 @Bean的重命名
獲取方法注解儲存的對象時,傳入的BeanName參數值默值為方法名,但像上面那樣返回對象的方法名稱往往是getXXX這樣式取名的,雖然在語法與實現上是沒有問題的,但實際開發寫出這樣的代碼,看起來還是比較別扭的。
實際上注解 @Bean 是可以加參數的,給儲存的對象起別名,像下面這個樣子。
package com.spring.demo;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;@Controller
public class Books {@Bean(name = "book")public BookInfo getBook(){BookInfo bookInfo = new BookInfo();bookInfo.setBookName("三體");bookInfo.setAuthor("劉慈欣");bookInfo.setStyle("文學科幻");return bookInfo;}public void sayHi(){System.out.println("Books sayHi!");}
}
也可以給 Bean 設置多個別名,總結起來有如下幾種方式:
//方式一(省略參數名的情況下默認是name)
@Bean("article1")
//方式二
@Bean(name = "article2")
//方式三
@Bean(value = "article3")
//起多個別名
@Bean(name = {"article4", "article5"})
@Bean(value = {"article6", "article7"})
@Bean({"article8", "article9", "article10"})
但是需要注意,當重新命名之后,就不能使用原來的方法名來獲取對象了.
所以使用 @Bean 存儲對象的beanName命名規則是,當沒有設置name/value屬性時,此時 Bean 的默認名字就是方法名,一旦添加了別名name/value屬性后,就只能通過重命名的別名來獲取 Bean 了,默認的使用方法名獲取 Bean 對象就不能使用了。
@Bean 使用時,同一類如果多個 Bean 使用相同的名稱,此時程序執行是不會報錯的,他會根據類加載順序和類中代碼從上至下的的順序,將第一個 Bean 存放到 Spring 中,但第一個之后的對象就不會被存放到容器中了,也就是只有在第一次創建 Bean 的時候會將對象和 Bean 名稱關聯起來,后續再有相同名稱的Bean存儲時候,容器會自動忽略。
還可以通過類注解 @Order 注解控制類加載順序(值越小,優先級越高),進而影響 Bean 的存放的先后順序.
3.3 同?類型多個 @Bean 報錯
當出現以下多個 Bean,返回同?對象類型時程序會報錯,如下代碼所示:
運行結果如下:
報錯的原因是,?唯?的 Bean 對象.
同?類型多個 Bean 報錯處理:
解決同?個類型,多個 bean 的解決?案有以下兩個:
- 使?
@Resource(name="user1")
定義。(@Resource下文有介紹)
- 使?
@Qualifier
注解定義名稱。
二. 獲取 Bean 對象(對象裝配)
獲取bean對象也叫做對象裝配.是把bean對象取出來放到某個類中.有時候也叫對象注?(DI).
對象裝配(對象注?)的實現?法以下 3 種:
- 屬性注?
- 構造?法注?
- Setter 注?
接下來,我們分別來看。
下?我們按照實際開發中的模式,將Service
類注?到Controller
類中。
1. 屬性注入
屬性注?是使? @Autowired
實現的,將 Service
類注?到 Controller
類中。
User
代碼:
package com.spring.demo;public class User {private Integer id;private String name;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}
}
Service
代碼:
package com.spring.demo;import org.springframework.stereotype.Service;@Service
public class UserService {public void sayHi(){System.out.println("UserService sayHi!");}public User getUser(Integer id){User user = new User();user.setId(id);user.setName("xxxflower-"+id);return user;}}
Controller
代碼:
package com.spring.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController {@Autowiredprivate UserService userService;public User getUser(Integer id){return userService.getUser(id);}public void sayHi() {System.out.println("UserController sayHi!");}
}
運行結果:
優點:簡單.
缺點:
- 沒辦法實現final修飾的變量注入.
- 兼容不好:只適用于Ioc容器.
- 風險:因為寫法簡單,所以違背單一設計原則的概率更大.
2. Setter注入
Setter 注?和屬性的 Setter ?法實現類似,只不過在設置 set ?法
的時候需要加上 @Autowired
注解,如下代碼所示:
package com.spring.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController2 {private UserService userService;//2.使用Setter注入@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}public User getUser(Integer id){return userService.getUser(id);}
}
運行結果:
優點:符合單一設計原則(每個方法只傳遞一個對象)
缺點:
- 不能注入不可變對象
- 使用setter注入的對象可能會被修改.
3. 構造方法注入(Spring官方推薦)
構造?法注?是在類的構造?法中實現注?,如下代碼所示:
package com.spring.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;@Controller
public class UserController3 {private UserService userService;//3.構造方法注入@Autowiredpublic UserController3(UserService userService){this.userService = userService;}public User getUser(Integer id){return userService.getUser(id);}
}
運行結果:
特點:如果當前類中只有一個構造方法的話,那么@Autowired 注解可以省略.
優點:
- 可以注入一個不可變對象(使用fianl修飾的對象)
問題:為什么構造方法可以注入一個不可變對象,而屬性注入和Setter注入卻不行?
答:這是Java的規定,在java中,被final對象必須滿足以下兩個條件中的任意一個:
- final修飾的對象,是直接復制的.
- final修飾的對象,必須在構造方法中賦值.
- 注入的對象不會被改變(構造方法只能執行一次)
- 構造方法注入可以保證注入對象完全被初始化
- 通用性更好.
4. @Resource:另?種注入關鍵字
在進行類注?時,除了可以使? @Autowired
關鍵字之外,我們還可以使? @Resource
進?注?,如下代碼所示:
package com.spring.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;import javax.annotation.Resource;@Controller
public class UserController {// @Autowired@Resourceprivate UserService userService;public User getUser(Integer id){return userService.getUser(id);}public void sayHi() {System.out.println("UserController sayHi!");}
}
上述三種注入方式使用@Autowired
行,使用@Resource
也行,但是這兩者也是有區別的:
- 出身不同:@Autowired 來自于
Spring
,而 @Resource 來?于JDK
的注解; - 使?時設置的參數不同:相比于
@Autowired
來說,@Resource
?持更多的參數設置,例如
name
設置,根據名稱獲取 Bean。 @Autowired
可?于Setter
注?、構造函數注?和屬性注?,?@Resource
只能?于Setter
注?和屬性注?,不能?于構造函數注?。@Autowired
先根據類型查找(byType),之后再根據名稱查找(byName)。@Resource
先根據名稱去查,之后再根據類型去查。