一. 存儲(chǔ)Bean對(duì)象
1. 配置掃描路徑
配置掃描路徑是使用注解之前的前置工作,是非常重要的,是必須的操作項(xiàng).只有被配置的包下的所有類,添加了注解才能被正確的識(shí)別并保存到 Spring
中.
首先創(chuàng)建一個(gè)Spring項(xiàng)目.創(chuàng)建好后,第一步就是配置掃描路徑:在resources
目錄中創(chuàng)建一個(gè)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=""
的值設(shè)置為需要掃描對(duì)象的根路徑.注意:這個(gè)根路徑是從java目錄開(kāi)始的.
2. 添加注解存儲(chǔ) Bean 對(duì)象
想要將對(duì)象存儲(chǔ)在spring中,有兩種注解類型可以實(shí)現(xiàn):
使用類注解(五大類注解):
-
@Component
:@Component是一個(gè)通用的注解,表示一個(gè)類被標(biāo)記為可被Spring容器掃描和管理的組件。使用@Component注解可以將一個(gè)普通的Java類注冊(cè)為一個(gè)SpringBean。 -
@Controller
:@Controller注解用于標(biāo)識(shí)一個(gè)類是Spring MVC中的控制器??刂破髫?fù)責(zé)處理用戶請(qǐng)求,并返回相應(yīng)的視圖或數(shù)據(jù)。通過(guò)@Controller注解標(biāo)記的類會(huì)被Spring自動(dòng)檢測(cè)并注冊(cè)為一個(gè)控制器Bean。 -
@Service
:@Service注解用于標(biāo)識(shí)一個(gè)類是業(yè)務(wù)邏輯層的組件。通常,我們使用@Service注解將一個(gè)服務(wù)類標(biāo)記為Spring管理的Bean,它負(fù)責(zé)處理業(yè)務(wù)邏輯的實(shí)現(xiàn)。 -
@Repository
:@Repository注解用于標(biāo)識(shí)一個(gè)類是數(shù)據(jù)訪問(wèn)層的組件。通常,我們使用@Repository注解將一個(gè)DAO(數(shù)據(jù)訪問(wèn)對(duì)象)類標(biāo)記為Spring管理的Bean,它負(fù)責(zé)封裝與數(shù)據(jù)庫(kù)的交互操作。 -
@Configuration
:用于標(biāo)識(shí)一個(gè)類為配置類,通常用于定義Bean的創(chuàng)建、裝配和其他配置信息。它用于告訴Spring容器該類包含了Bean的定義和依賴關(guān)系,可通過(guò)@Configuration注解的類創(chuàng)建一個(gè)Java-based配置來(lái)代替XML配置文件。
方法注解
-
@Bean
:@Bean注解用于標(biāo)記一個(gè)方法是一個(gè)產(chǎn)生Bean實(shí)例的工廠方法。通常,我們將@Bean注解放置在@Configuration注解的類中的方法上。被@Bean注解標(biāo)注的方法會(huì)被Spring容器調(diào)用,并將其返回的對(duì)象注冊(cè)為一個(gè)Bean,可以通過(guò)名稱或類型進(jìn)行訪問(wèn)。
拓展注解:
-
@Autowired
:@Autowired是一個(gè)自動(dòng)裝配注解,用于實(shí)現(xiàn)依賴注入。當(dāng)一個(gè)類需要依賴其他類的實(shí)例時(shí),可以使用@Autowired注解自動(dòng)將依賴注入到目標(biāo)類中。Spring會(huì)根據(jù)類型進(jìn)行自動(dòng)查找并裝配對(duì)應(yīng)的Bean。
2.1 使用五大類注解存儲(chǔ)Bean
首先,我們來(lái)了解如何使用五大類注解來(lái)儲(chǔ)存對(duì)象
-
@Controller
package com.spring.demo; import org.springframework.stereotype.Controller; @Controller public class UserController { public void sayHi() { System.out.println("UserController sayHi!"); } }
在掃描路徑下創(chuàng)建該
UserController
類.并在類上加@Controller
注解,此時(shí)就將Bean
存儲(chǔ)到容器中了.接下來(lái)就是從 Spring 中讀取出我們的對(duì)象,這里還是先使用依賴查找的方式來(lái)獲取 Bean,使用五大類注解,默認(rèn)情況下,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 對(duì)象 //獲取對(duì)象時(shí)使用類名的小駝峰形式作為 name 參數(shù) UserController userController = context.getBean("userController", UserController.class); //3.使用 bean userController.sayHi(); } }
運(yùn)行結(jié)果:
注意:要將Bean存儲(chǔ)到Spring中需要滿足兩個(gè)條件:- 使用五大類注解創(chuàng)建的類
- 必須在配置的掃描路徑下.(包括子包)
掃描路徑也叫做根路徑.兩個(gè)條件缺一不可.
為什么要設(shè)置根路徑?
設(shè)置根路徑其實(shí)也是為了提高程序的性能,因?yàn)槿绻辉O(shè)置根路徑,Spring 就會(huì)掃描項(xiàng)目文件中所有的目錄,但并不是所有類都需要儲(chǔ)存到 Spring當(dāng)中,這樣性能就會(huì)比較低,設(shè)置了根路徑,Spring 就只掃描該根路徑下所有的目錄就可以了,提高了程序的性能。下來(lái)我們演示一下沒(méi)有配置掃描路徑下的情況:
還需要知道的是使用注解存儲(chǔ)的 Bean 和使用XML存儲(chǔ)的的 Bean 是可以一同使用的,比如我們將剛剛有問(wèn)題的Student
重新通過(guò)XML的方式進(jìn)行存儲(chǔ).注意:默認(rèn)情況下,使用原類名首字母小寫就能讀取到Bean對(duì)象.特例情況:原類名如果首字母和第二個(gè)字母都是大寫的情況下,那么bean名稱就是原類名.
-
@Service
啟動(dòng)類中代碼://1.獲取 srping 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //2.獲取 bean 對(duì)象 //獲取對(duì)象時(shí)使用類名的小駝峰形式作為 name 參數(shù) UserService userService = context.getBean("userService", UserService.class); //3.使用 bean userService.sayHi();
UserService
類:package com.spring.demo; import org.springframework.stereotype.Service; @Service public class UserService { public void sayHi(){ System.out.println("UserService sayHi!"); } }
運(yùn)行結(jié)果:
其余三種使用方式相同,此處不再做介紹.
2.2 為什么要有五大類注解?
既然都五大類完成的是同樣的工作,那為什么要有五大類注解呢?
其實(shí)五大類注解主要是為了規(guī)范 Java 項(xiàng)目的代碼,Java 項(xiàng)目的標(biāo)準(zhǔn)分層如下:
- 控制層(Controller)
- 服務(wù)層(Service)
- 數(shù)據(jù)持久層(Dao)
而五大類注解便是對(duì)應(yīng)著不同的層級(jí)別使用的,讓程序猿看到某一個(gè)注解就可以明確這個(gè)了類是做什么的。
程序的?程分層,調(diào)?流程如下:
包括企業(yè)中也是按照這樣的結(jié)構(gòu)來(lái)將項(xiàng)目分層的,典型的比如阿里,它只是在標(biāo)準(zhǔn)分層在服務(wù)層(Service)做了一個(gè)擴(kuò)展,劃分的更加細(xì)致詳細(xì)了.
五大類注解主要起到的是“見(jiàn)名知意”的作用,代碼層面上來(lái)看,作用是類似的.查看五大類源碼可知:
五大類的源碼中除了 @Component
以外,其他四大類注解中都包含了 @Component 注解的功能,這四大類注解都是基于 @Component 實(shí)現(xiàn)的,是 @Component 拓展。
2.3 有關(guān)獲取Bean參數(shù)的命名規(guī)則
上文中在使用依賴查找的方式獲取Bean
時(shí),我們講到了getBean
方法的BeanName
是使用類名的小駝峰形式(即類名的首字母小寫)以及第一個(gè)字母和第二個(gè)字母都大寫情況下的特例.
注意:BeanName的規(guī)范命名規(guī)則并不是 Spring 獨(dú)創(chuàng)的,而依照 Java 標(biāo)準(zhǔn)庫(kù)的規(guī)則進(jìn)行的。
BeanName的規(guī)范命名規(guī)則:
- 如果類名不存在或類名為空字符串,
BeanName
為原類名。 - 如果類名字長(zhǎng)度大于1,且第一個(gè)與第二個(gè)字符為大寫,
BeanName
為原類名。 - 其他情況,
BeanName
為原類名的小駝峰形式.
3. 使用方法注解儲(chǔ)存 Bean 對(duì)象
3.1 方法注解儲(chǔ)存對(duì)象的用法
類注解是添加到某個(gè)類上的,而方法注解是放到某個(gè)方法上的.在Spring框架的設(shè)計(jì)中,方法注解@Bean
要配合類注解才能將對(duì)象正常存儲(chǔ)到Spring容器中.
舉個(gè)??:我們有一個(gè)普通書本類
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
@Bean
public String toString() {
return "com.spring.demo.BookInfo{" +
"bookName='" + bookName + '\'' +
", author='" + author + '\'' +
", style='" + style + '\'' +
'}';
}
}
下面演示使用@Bean方法注解儲(chǔ)存對(duì)象:
package com.spring.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class Books {
@Bean
public BookInfo getBook(){
BookInfo bookInfo = new BookInfo();
bookInfo.setBookName("三體");
bookInfo.setAuthor("劉慈欣");
bookInfo.setStyle("文學(xué)科幻");
return bookInfo;
}
public void sayHi(){
System.out.println("Books sayHi!");
}
}
啟動(dòng)類:
獲取方法注解儲(chǔ)存的對(duì)象時(shí),傳入的BeanName
參數(shù)值默認(rèn)值就是方法名,上面的代碼中方法名為getBook,所以獲取時(shí),就使用getBook作為參數(shù)來(lái)進(jìn)行獲取。
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);
}
}
運(yùn)行結(jié)果:
3.2 @Bean的重命名
獲取方法注解儲(chǔ)存的對(duì)象時(shí),傳入的BeanName參數(shù)值默值為方法名,但像上面那樣返回對(duì)象的方法名稱往往是getXXX這樣式取名的,雖然在語(yǔ)法與實(shí)現(xiàn)上是沒(méi)有問(wèn)題的,但實(shí)際開(kāi)發(fā)寫出這樣的代碼,看起來(lái)還是比較別扭的。
實(shí)際上注解 @Bean 是可以加參數(shù)的,給儲(chǔ)存的對(duì)象起別名,像下面這個(gè)樣子。
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("文學(xué)科幻");
return bookInfo;
}
public void sayHi(){
System.out.println("Books sayHi!");
}
}
也可以給 Bean 設(shè)置多個(gè)別名,總結(jié)起來(lái)有如下幾種方式:
//方式一(省略參數(shù)名的情況下默認(rèn)是name)
@Bean("article1")
//方式二
@Bean(name = "article2")
//方式三
@Bean(value = "article3")
//起多個(gè)別名
@Bean(name = {"article4", "article5"})
@Bean(value = {"article6", "article7"})
@Bean({"article8", "article9", "article10"})
但是需要注意,當(dāng)重新命名之后,就不能使用原來(lái)的方法名來(lái)獲取對(duì)象了.
所以使用 @Bean 存儲(chǔ)對(duì)象的beanName命名規(guī)則是,當(dāng)沒(méi)有設(shè)置name/value屬性時(shí),此時(shí) Bean 的默認(rèn)名字就是方法名,一旦添加了別名name/value屬性后,就只能通過(guò)重命名的別名來(lái)獲取 Bean 了,默認(rèn)的使用方法名獲取 Bean 對(duì)象就不能使用了。
@Bean 使用時(shí),同一類如果多個(gè) Bean 使用相同的名稱,此時(shí)程序執(zhí)行是不會(huì)報(bào)錯(cuò)的,他會(huì)根據(jù)類加載順序和類中代碼從上至下的的順序,將第一個(gè) Bean 存放到 Spring 中,但第一個(gè)之后的對(duì)象就不會(huì)被存放到容器中了,也就是只有在第一次創(chuàng)建 Bean 的時(shí)候會(huì)將對(duì)象和 Bean 名稱關(guān)聯(lián)起來(lái),后續(xù)再有相同名稱的Bean存儲(chǔ)時(shí)候,容器會(huì)自動(dòng)忽略。
還可以通過(guò)類注解 @Order 注解控制類加載順序(值越小,優(yōu)先級(jí)越高),進(jìn)而影響 Bean 的存放的先后順序.
3.3 同?類型多個(gè) @Bean 報(bào)錯(cuò)
當(dāng)出現(xiàn)以下多個(gè) Bean,返回同?對(duì)象類型時(shí)程序會(huì)報(bào)錯(cuò),如下代碼所示:
運(yùn)行結(jié)果如下:
報(bào)錯(cuò)的原因是,?唯?的 Bean 對(duì)象.
同?類型多個(gè) Bean 報(bào)錯(cuò)處理:
解決同?個(gè)類型,多個(gè) bean 的解決?案有以下兩個(gè):
- 使?
@Resource(name="user1")
定義。(@Resource下文有介紹) - 使?
@Qualifier
注解定義名稱。
二. 獲取 Bean 對(duì)象(對(duì)象裝配)
獲取bean對(duì)象也叫做對(duì)象裝配.是把bean對(duì)象取出來(lái)放到某個(gè)類中.有時(shí)候也叫對(duì)象注?(DI).
對(duì)象裝配(對(duì)象注?)的實(shí)現(xiàn)?法以下 3 種:
- 屬性注?
- 構(gòu)造?法注?
- Setter 注?
接下來(lái),我們分別來(lái)看。
下?我們按照實(shí)際開(kāi)發(fā)中的模式,將Service
類注?到Controller
類中。
1. 屬性注入
屬性注?是使? @Autowired
實(shí)現(xiàn)的,將 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;
}
@Override
public 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 {
@Autowired
private UserService userService;
public User getUser(Integer id){
return userService.getUser(id);
}
public void sayHi() {
System.out.println("UserController sayHi!");
}
}
運(yùn)行結(jié)果:
優(yōu)點(diǎn):簡(jiǎn)單.
缺點(diǎn):
- 沒(méi)辦法實(shí)現(xiàn)final修飾的變量注入.
- 兼容不好:只適用于Ioc容器.
- 風(fēng)險(xiǎn):因?yàn)閷懛ê?jiǎn)單,所以違背單一設(shè)計(jì)原則的概率更大.
2. Setter注入
Setter 注?和屬性的 Setter ?法實(shí)現(xiàn)類似,只不過(guò)在設(shè)置 set ?法
的時(shí)候需要加上 @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注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id){
return userService.getUser(id);
}
}
運(yùn)行結(jié)果:
優(yōu)點(diǎn):符合單一設(shè)計(jì)原則(每個(gè)方法只傳遞一個(gè)對(duì)象)
缺點(diǎn):
- 不能注入不可變對(duì)象
- 使用setter注入的對(duì)象可能會(huì)被修改.
3. 構(gòu)造方法注入(Spring官方推薦)
構(gòu)造?法注?是在類的構(gòu)造?法中實(shí)現(xiàn)注?,如下代碼所示:
package com.spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController3 {
private UserService userService;
//3.構(gòu)造方法注入
@Autowired
public UserController3(UserService userService){
this.userService = userService;
}
public User getUser(Integer id){
return userService.getUser(id);
}
}
運(yùn)行結(jié)果:
特點(diǎn):如果當(dāng)前類中只有一個(gè)構(gòu)造方法的話,那么@Autowired 注解可以省略.
優(yōu)點(diǎn):
- 可以注入一個(gè)不可變對(duì)象(使用fianl修飾的對(duì)象)
問(wèn)題:為什么構(gòu)造方法可以注入一個(gè)不可變對(duì)象,而屬性注入和Setter注入?yún)s不行?
答:這是Java的規(guī)定,在java中,被final對(duì)象必須滿足以下兩個(gè)條件中的任意一個(gè):
- final修飾的對(duì)象,是直接復(fù)制的.
- final修飾的對(duì)象,必須在構(gòu)造方法中賦值.
- 注入的對(duì)象不會(huì)被改變(構(gòu)造方法只能執(zhí)行一次)
- 構(gòu)造方法注入可以保證注入對(duì)象完全被初始化
- 通用性更好.
4. @Resource:另?種注入關(guān)鍵字
在進(jìn)行類注?時(shí),除了可以使? @Autowired
關(guān)鍵字之外,我們還可以使? @Resource
進(jìn)?注?,如下代碼所示:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-658038.html
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
@Resource
private UserService userService;
public User getUser(Integer id){
return userService.getUser(id);
}
public void sayHi() {
System.out.println("UserController sayHi!");
}
}
上述三種注入方式使用@Autowired
行,使用@Resource
也行,但是這兩者也是有區(qū)別的:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-658038.html
- 出身不同:@Autowired 來(lái)自于
Spring
,而 @Resource 來(lái)?于JDK
的注解; - 使?時(shí)設(shè)置的參數(shù)不同:相比于
@Autowired
來(lái)說(shuō),@Resource
?持更多的參數(shù)設(shè)置,例如name
設(shè)置,根據(jù)名稱獲取 Bean。 -
@Autowired
可?于Setter
注?、構(gòu)造函數(shù)注?和屬性注?,?@Resource
只能?于Setter
注?和屬性注?,不能?于構(gòu)造函數(shù)注?。 -
@Autowired
先根據(jù)類型查找(byType),之后再根據(jù)名稱查找(byName)。@Resource
先根據(jù)名稱去查,之后再根據(jù)類型去查。
到了這里,關(guān)于【JavaEE進(jìn)階】Spring 更簡(jiǎn)單的讀取和存儲(chǔ)對(duì)象的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!