作者
:學(xué)Java的冬瓜博客主頁
:?冬瓜的主頁??專欄
:【Framework】主要內(nèi)容
:往spring中存儲(chǔ)Bean對(duì)象的三大方式:XML方式(Bean標(biāo)簽);五大類注解;方法注解。從spring中取對(duì)象的兩種方式:基本方法、注解方法(屬性注入、set注入、構(gòu)造方法注入)。
Spring中Bean的存取方式
在Spring中,Bean的裝配方式有兩種,xml方式和注解方式。自己開發(fā)的類可以使用@Component注解或xml方式裝配,推薦使用注解,因?yàn)楦雍?jiǎn)潔方便。
引入第三方庫(kù)的包可以使用@Bean注解的方式或xml的方式,推薦使用xml方式,將庫(kù)的內(nèi)容和自己的代碼分離。
一、三大方式存儲(chǔ)對(duì)象到spring容器中
1、XML方式把Bean存儲(chǔ)到spring
1.1、創(chuàng)建Bean類
a. 在Java目錄下創(chuàng)建多級(jí)目錄包c(diǎn)om.spring.bean,在該包下創(chuàng)建Student類和Teacher類
b. Bean對(duì)象中,Student類如下(Teacher類的屬性和方法與之相似,有私有屬性,有Set和Get方法,構(gòu)造方法)
package com.spring.bean;
public class Student {
private String name;
private int age;
public Student(){
System.out.println("init Student");
}
public void wmi(){
System.out.println("I am student: my name is "+ name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.2、將Bean注冊(cè)到Spring的xml配置文件
a. 在
resources下創(chuàng)建spring中對(duì)象注冊(cè)的配置文件
,可以命名為spring-config.xml
b.將下列配置信息粘貼到配置文件中,并且使用bean標(biāo)簽對(duì)student和teacher類進(jìn)行注冊(cè),注冊(cè)時(shí)id屬性是對(duì)保存到spring中的
對(duì)象的重命名
,class 屬性是該類在項(xiàng)目中的路徑+包名+類名
,而相應(yīng)的文件會(huì)從spring依賴中自動(dòng)獲取。
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--將類對(duì)象,com.spring.bean.Student存進(jìn)spring容器,名字是student,是一個(gè)spring bean-->
<bean id="student" class="com.spring.bean.Student"/>
<bean id="teacher" class="com.spring.bean.Teacher"/>
</beans>
2、五大類注解方式 @Controller等存儲(chǔ)Bean到spring
2.1、五大類注解和JavaEE標(biāo)準(zhǔn)分層
A:五大類注解
@Controller:控制器,驗(yàn)證用戶請(qǐng)求的數(shù)據(jù)的正確性 。
@Service:服務(wù),調(diào)度具體的執(zhí)行方法。
@Repository:用戶持久層,和數(shù)據(jù)庫(kù)進(jìn)行交互。
@Component:組件(工具類)
@Configuration:配置項(xiàng),項(xiàng)目中的一些配置。為什么有配置項(xiàng)類?有很多原因,有一個(gè)就是在啟動(dòng)程序時(shí),配置文件不一定會(huì)加載,這時(shí)就需要使用配置項(xiàng)來起到在程序運(yùn)行時(shí)告訴程序需要先加載配置文件的作用。
B:JavaEE標(biāo)準(zhǔn)分層
在后端的分層當(dāng)中,至少有控制層、服務(wù)層,數(shù)據(jù)持久層三個(gè)層次。
C:使用五大類注解和標(biāo)準(zhǔn)分層規(guī)范項(xiàng)目
下圖中:實(shí)體類使用entity或model表示,其他部分按照五大類注解的方式分層,分出控制器類,服務(wù)類,數(shù)據(jù)庫(kù)操作類,組件,配置文件類。
2.2、五大類注解方式 往spring中 存儲(chǔ)Bean對(duì)象
A:創(chuàng)建包,Bean對(duì)象,并給類(Bean對(duì)象)添加適當(dāng)?shù)奈宕箢愖⒔?/mark>
注意:滿足需要存儲(chǔ)在spring中的類在配置文件的base-package中,且這個(gè)類使用了五大類注解,就可以使用spring進(jìn)行存取。
B:創(chuàng)建spring配置文件spring-config.xml,并在配置文件中注冊(cè)可能要存入spring所有類的所在包
在下圖中,可能存入spring的類是TestController包下的任意一個(gè)類(或TestController包的字包中的類)。如下圖,springTest包的子包中的類都將保存在spring中,所以注冊(cè)包springTest:base-package="springTest"
注意點(diǎn):
- 在配置文件中注冊(cè)可能涉及的包和存儲(chǔ)的類使用五大類注解二者缺一不可,如果缺少,報(bào)錯(cuò)說沒有找到這個(gè)Bean對(duì)象。
- 需要存儲(chǔ)在spring中的類本身的位置可以在注冊(cè)的包中或者它的子包中。
-
使用五大類注解和注冊(cè)包這個(gè)方法與使用bean標(biāo)簽(XML方式)(使用
路徑+包名+類名
)的方式可以混合使用。即如果存在某個(gè)類需要存儲(chǔ)在spring中,但又不適合放在注冊(cè)的包里的任何位置,就可以將這個(gè)類放在注冊(cè)包之外,但是使用bean注冊(cè)當(dāng)前類對(duì)象(即使用XML的方式存取對(duì)象)。
3、方法注解方式 @Bean存儲(chǔ)對(duì)象到spring中
3.1、實(shí)體類的命名
補(bǔ)充:
實(shí)體類的管理:實(shí)體類不交給spring管理,由我們自己管理
。
實(shí)體類的命名:
DO,基本對(duì)象,和數(shù)據(jù)庫(kù)表結(jié)構(gòu)一一對(duì)應(yīng),如UserEntity或UserDO或者直接和數(shù)據(jù)庫(kù)表名一致 User
。
VO,擴(kuò)展對(duì)象,前端傳給后端的對(duì)象。如UserVO
,使用多個(gè)VO描述各種業(yè)務(wù)。
3.2、存儲(chǔ)Bean對(duì)象
要點(diǎn):
- 1> 方法注解需要配合五大類注解使用。比如UserBeans類下有多個(gè)方法注解,在UserBeans上需要加上五大類注解(一般是@Component)。之所以如此,是為了提高效率。
- 2> 方法注解的對(duì)象名可以改名。方法注解 對(duì)象的名字不同于五大類注解,方法注解是默認(rèn)方法名,但是可以在方法注解@Bean后加括號(hào),使用name或value屬性給Bean對(duì)象改名。
注意:
- 給Bean對(duì)象改名后,就不能再使用方法名獲取到對(duì)象,只能使用修改后的名字。
- 如果存在方法名相同的兩個(gè)@Bean方法在不同類中(都未修改名字),當(dāng)獲取Bean對(duì)象時(shí)可以在方法上添加@Order(數(shù)字),數(shù)字小的方法會(huì)先注入,但是后注入的方法會(huì)發(fā)生覆蓋。
二、獲取Bean對(duì)象(依賴注入)
1、獲取對(duì)象的最基本的方法
1.1、獲取Spring容器方式
a.目的相同:
BeanFactory
和ApplicationContext
的目的相同,都是為了獲取到spring容器
b.實(shí)現(xiàn)不同:
BeanFactory是使用類似于懶漢模式進(jìn)行類加載并進(jìn)行對(duì)象的初始化(獲取到spring后,使用getBean方法獲取對(duì)象時(shí),才進(jìn)行相關(guān)對(duì)象的類加載,并進(jìn)行對(duì)象的初始化)。
ApplicationContext則是使用類似于餓漢模式(一獲取spring容器會(huì)立即加載配置文件,并進(jìn)行對(duì)象的初始化(執(zhí)行構(gòu)造方法,靜態(tài)方法,靜態(tài)代碼塊等))
c.父子關(guān)系:BeanFactory是ApplicationContext的子類
法一:BeanFactory接口和ClassPathResource繼承類
給ClassPathResource傳參spring的配置文件的文件名,給XmlBeanFactory傳參ClassPathResource文件,最后BeanFactory接口接收其子類XmlBeanFactory類。
// 獲取spring容器
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
使用BeanFactory和XmlBeanFactory和ClassPathResource獲取Bean對(duì)象完整代碼:
import com.spring.bean.Student;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class TeacherMain {
public static void main(String[] args) {
// 懶漢加載:調(diào)用時(shí),才加載spring容器中的bean對(duì)象,效率不高,但是內(nèi)存消耗低。
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
Student student = (Student) factory.getBean("student");
student.wmi();
}
}
法二:ApplicationContext接口和ClassPathXmlApplicationContext繼承類
獲取spring容器(使用ApplicationContext接收其子類ClassPathXmlApplicationContext,給ClassPathXmlApplicationContext傳參配置文件的文件名)
// 獲取Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
使用ApplicationContext接口和其子類ClassPathXmlApplicationContext獲取Bean對(duì)象的完整代碼:
import com.spring.bean.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class StudentMain {
public static void main(String[] args) {
// 餓漢加載:一次性加載并初始化spring配置文件中的對(duì)象,后續(xù)操作spring容器中的bean對(duì)象時(shí)會(huì)很快,但是費(fèi)內(nèi)存。
// 1通過resources文件夾中的xml文件名,獲取spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2獲取Bean對(duì)象
Student student2 = context.getBean("student", Student.class);
// 3使用spring bean對(duì)象
student.wmi();
}
}
1.2、getBean獲取指定的Bean對(duì)象
getBean方法的參數(shù),可以使用多種方式進(jìn)行傳參:
一,使用名稱獲取
二、使用類型獲取,(如果存儲(chǔ)兩個(gè)名稱不同,類型相同的對(duì)象,會(huì)找不到唯一的Bean,從而報(bào)錯(cuò))
三、使用名稱+類型獲取(最保險(xiǎn),不容易出錯(cuò))
1.3、獲取Bean對(duì)象的名稱的命名源碼分析
在獲取Bean對(duì)象時(shí),Bean對(duì)象名是什么?
首先,為了突出重點(diǎn),我們先直接亮出結(jié)論:
規(guī)律:
①:如果需要保存在spring中的對(duì)象所屬的類 的首字母和第二個(gè)字母都大寫,如(ACar->ACar),那么Bean對(duì)象的名字為原類名。使用context.getBean("原類名", 類名.class)
方式從spring中獲取對(duì)象。
②:如果不是情況一,那就把首字母小寫(如A->a,RedCar->redCar)。如要從spring中獲取一個(gè)CarController使用context.getBean("carController", CarController.class)
獲取CarController對(duì)象。
現(xiàn)在,我們查看spring源碼,印證我們的結(jié)論:
A:操作:雙擊shift鍵,搜索BeanName,找到好幾個(gè)類,因?yàn)槲覀兪且褺ean的命名規(guī)則,根據(jù)Bean的命名英文推導(dǎo),一個(gè)一個(gè)去找,我這里直接給出結(jié)論:雙擊shift鍵,搜索BeanName,出現(xiàn)下圖,點(diǎn)擊AnnotationBeanNameGenerator(注解BeanName生成類)。
B:點(diǎn)擊左邊的structure,彈出該類的各種方法。點(diǎn)擊generatorBeanName方法。按住ctrl,點(diǎn)擊下圖中調(diào)用的方法。
C:在generatorBeanName方法中 ctrl+點(diǎn)擊方法調(diào)用,跳轉(zhuǎn)到BuildDefaultBeanName(參數(shù)一,參數(shù)二),在該方法中再次ctrl+點(diǎn)擊方法調(diào)用跳轉(zhuǎn)到它的重載函數(shù)BuildDefaultBeanName(參數(shù))。再使用ctrl+點(diǎn)擊方法調(diào)用,跳轉(zhuǎn)到了最終的目的函數(shù)。
D:目的函數(shù)Introspector類的decapitalize方法中
注意: decapitalize方法中,傳入的參數(shù)name是要存入spring的對(duì)象所屬的類名。
如果傳入的參數(shù)是null,或者參數(shù)長(zhǎng)度為0,那么直接返回類名,代表這個(gè)存入spring的對(duì)象的名字是null或空字符串。
如果傳入的參數(shù)長(zhǎng)度大于1,且首字母和次首字母都是大寫,那么直接返回類名,代表這個(gè)存入spring的對(duì)象的名字是原類名。
如果傳入?yún)?shù)不是null,長(zhǎng)度大于0,如果只有一個(gè)字母,將首字母小寫返回。如果有多個(gè)字母,且首字母和第二個(gè)字母不都是大寫,那就將原類名的第一個(gè)字母小寫返回,作為存入spring的對(duì)象的名字。
E:結(jié)論: spring代碼生成Bean對(duì)象的命名是使用jdk的標(biāo)準(zhǔn)起名的
看下圖,在上面的操作C時(shí),點(diǎn)擊標(biāo)出來的藍(lán)色框定位當(dāng)前類AnnotationBeanNamGenerator的位置,可以看到這個(gè)類是存在于spring源碼中的。
操作C完成后,跳轉(zhuǎn)到到Introspector類的decapitalize方法中,再尋找Introspector類的所在位置,如下圖,它出現(xiàn)在了jdk的rt.jar中。
2、使用依賴注入從spring獲取對(duì)象
注意:指在Controller的類中注入Service,在Service中注入Repository。學(xué)spring到目前為止暫時(shí)無法實(shí)現(xiàn) 在main方法所在的類中注入Controller,在main中只能使用getBean獲取對(duì)象,后續(xù)在springboot中則不需要main方法,而是tomcat自動(dòng)執(zhí)行。
2.1、法一:屬性注入
屬性注入:
步驟A:加注解 @Autowired
步驟B:寫要注入的類作為當(dāng)前類的屬性。
如下圖:
屬性注入優(yōu)缺點(diǎn):
優(yōu)點(diǎn):簡(jiǎn)單,易操作。
缺點(diǎn):
1> 無法實(shí)現(xiàn)final修飾的變量的注入,如private final CarService carService
使用屬性注入,無法成功注入;
2> 兼容性不足,只適用于IoC容器;
3> 易違反單一設(shè)計(jì)原則(注入多個(gè)對(duì)象,做多個(gè)功能)。
2.2、法二:setter注入
setter注入:
步驟A:要注入對(duì)象作為當(dāng)前類的屬性。
步驟B:注解@Autowired + set方法給要注入的對(duì)象賦值。(參數(shù)是自己從spring中取出來傳給set方法作為參數(shù))set注入的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):嚴(yán)格遵循單一設(shè)計(jì)原則
缺點(diǎn):
1> 無法實(shí)現(xiàn)final修飾的變量注入;
2> 注入的對(duì)象有被修改的風(fēng)險(xiǎn),因?yàn)閟et方法是public修飾的。
2.3、法三:構(gòu)造方法注入
構(gòu)造方法注入:
步驟A:要注入對(duì)象作為當(dāng)前類的屬性。
步驟B:注解+構(gòu)造方法給注入對(duì)象賦值。(參數(shù)是自己從spring中取出來傳給構(gòu)造方法作為參數(shù))
構(gòu)造方法注入優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
1>可以實(shí)現(xiàn)final修飾變量的注入。(在Java中,final修飾的變量要么直接賦值,要么在構(gòu)造方法中賦值,這是語法規(guī)定,因此構(gòu)造方法可以實(shí)現(xiàn)final修飾變量的注入,而set方法無法實(shí)現(xiàn))
2>注入對(duì)象無法被修改,因?yàn)闃?gòu)造方法只執(zhí)行一次。
3> 構(gòu)造方法注入可以保證注入對(duì)象被完全初始化。因?yàn)闃?gòu)造方法注入是對(duì)象實(shí)例化的過程中進(jìn)行的,而屬性注入和set注入是在對(duì)象實(shí)例化之后進(jìn)行的。
4> 通用性更好,因?yàn)槭荍ava語法規(guī)定,不僅僅適用于IoC容器。文章來源:http://www.zghlxwxcb.cn/news/detail-652754.html
補(bǔ)充:在依賴注入中 @Autowired和 @Resource的區(qū)別
問題1: @Autowired和 @Resource有時(shí)可以替換,有時(shí)不行,接下來分析它們的區(qū)別:
來源不同:@Autowired來自spring,@Resource來自JDK
使用范圍不同:@Autowired可以適用于三種注入,@Resource只支持屬性注入和set注入,不支持構(gòu)造方法注入。
參數(shù)個(gè)數(shù)和類型不同:@Resource支持多個(gè)參數(shù)設(shè)置,適用于同一個(gè)類注入多個(gè)對(duì)象情況,對(duì)修改對(duì)象名字上更加靈活;@Autowired只有一個(gè)boolean類型的參數(shù)設(shè)置。
查找Bean對(duì)象方式不同:@Autowired先根據(jù)類型查找,后根據(jù)名稱查找(注入變量的名稱),ByType;@Resource先根據(jù)名稱查找,后根據(jù)類型查找,ByName。
問題2:當(dāng)存儲(chǔ)了同類型的多個(gè)對(duì)象時(shí),可以使用兩種方式依賴注入對(duì)象。
1> @Resource參數(shù)的設(shè)置
2> @Autowired + @Qualifier(value=“對(duì)象名”)文章來源地址http://www.zghlxwxcb.cn/news/detail-652754.html
到了這里,關(guān)于【Spring】-Spring中Bean對(duì)象的存取的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!