三)初始Spring源碼以及常用組件
目錄
示例源碼3.1.1:一般我們在掃描的都是使用 xml 方式 去掃描 整個項目,
示例源碼3.1.1:使用@ComponentScan 註解 定義範圍掃描
示例源碼3.1.2:一般我們在掃描的都是使用 xml 方式 去自動掃描,
示例源碼3.1.3:一般我們在掃描的都是使用 xml 方式 去自定義規則掃描,
3.2.1 ,@Scope("prototype")示例: 結果最後 執行的 內存地址不一樣,返回==>false
3.2.2,@Scope("singleton")示例: 結果最後 執行的 內存地址不一樣,返回 ====>true,
3.2.3 ,@Scope("prototype") XML示例結果最後 執行的 內存地址不一樣,返回 ==>false,
三,@Lazy 即:懶加載,延時加載 主要針對單例bean:默認在容器啓動的時候不創建對象,僅當第一次使用(獲取)bean的時候才創建被初始
一,@ComponentScan 掃描
- 指定掃描範圍
- 掃描過濾器
- 自定義掃描規則
示例源碼3.1.1:一般我們在掃描的都是使用 xml 方式 去掃描 整個項目,
//base-pacjage 是指掃描範圍
<context:component-scan base-package="org.java"></context:component-scan>
示例源碼3.1.1:使用@ComponentScan 註解 定義範圍掃描
@Configuration
@ComponentScan(value="com.enjoy.cap2")
public class Cap2MainConfig {
//給容器中註冊一個bean, 類型爲返回值的類型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
@Controller
public class OrderController {
}
@Repository
public class OrderDao {
}
@Service
public class OrderService {
}
public class Cap2Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
//通過註解拿到bean 的id
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
}
爲了測試,我在同一個項目下面 分別寫了 @Controller @Repository @Service ,結果意料中的全部被掃描到
示例源碼3.1.2:一般我們在掃描的都是使用 xml 方式 去自動掃描,
// includeFilters Filter 後面的意思是隻掃描出 Controller
// useDefaultFilters:設置爲false 可以掃描全部
@Configuration
@ComponentScan(value="com.enjoy.cap2", includeFilters={
@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
}, useDefaultFilters=false)
public class Cap2MainConfig {
//給容器中註冊一個bean, 類型爲返回值的類型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
然而沒什麼用,我們在 xml 可以直接指定
//base-pacjage 是指掃描範圍,自定義
<context:component-scan base-package="org.java,Controller"></context:component-scan>
示例源碼3.1.3:一般我們在掃描的都是使用 xml 方式 去自定義規則掃描,
@Configuration
@ComponentScan(value="com.enjoy.cap2", includeFilters={
@Filter(type=FilterType.CUSTOM, classes={JamesTypeFilter.class})
}, useDefaultFilters=false)
//@Filter: 掃描規則
//@ComponentScan(value="com.enjoy.cap2",includeFilters={ //@Filter(type=FilterType.ANNOTATION,classes={Controller.class}), //@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class})
//},useDefaultFilters=false) //默認是true,掃描所有組件,要改成false,使用自定義掃描範圍
//@ComponentScan value:指定要掃描的包
//excludeFilters = Filter[] 指定掃描的時候按照什麼規則排除那些組件
//includeFilters = Filter[] 指定掃描的時候只需要包含哪些組件
//useDefaultFilters = false 默認是true,掃描所有組件,要改成false
//----掃描規則如下
//FilterType.ANNOTATION:按照註解
//FilterType.ASSIGNABLE_TYPE:按照給定的類型;比如按BookService類型
//FilterType.ASPECTJ:使用ASPECTJ表達式
//FilterType.REGEX:使用正則指定
//FilterType.CUSTOM:使用自定義規則,自已寫類,實現TypeFilter接口
public class Cap2MainConfig {
//給容器中註冊一個bean, 類型爲返回值的類型,
@Bean
public Person person01(){
return new Person("james",20);
}
}
新增自定義過濾規則類: 如果 類名包含有“Order” 返回 ==》true
public class JamesTypeFilter implements TypeFilter{
private ClassMetadata classMetadata;
/*
* MetadataReader:讀取到當前正在掃描類的信息
* MetadataReaderFactory:可以獲取到其他任何類信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
//獲取當前類註解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//獲取當前正在掃描的類信息
classMetadata = metadataReader.getClassMetadata();
//獲取當前類資源(類的路徑)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("----->"+className);
if(className.contains("Order")){//當類包含er字符, 則匹配成功,返回true
return true;
}
return false;
}
}
public class Cap2Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
}
}
掃描,三個類, 三個類名都包含有 Order
二,@Scope :Bean 的生命週期管理
簡單來說: 對象在spring容器(IOC容器)中的生命週期,也可以理解爲對象在spring容器中的創建方式。
- prototype 即多實例:IOC容器啓動的時候,IOC容器啓動並不會去調用方法創建對象,而是每次獲取的時候纔會調用方法創建對象
- singleton 即單例模式:IOC容器啓動的時候會調用方法創建對象並放到IOC容器中,以後每次獲取的就是直接從容器中拿的同一bean
- request 即主要針對web應用,遞交一次請求創建一個實例。Spring 容器 即XmlWebApplicationContext 會爲每個HTTP請求創建一個全新的RequestPrecessor對象,當請求結束後,該對象的生命週期即告結束,如同java web中request的生命週期。當同時有10個HTTP請求進來的時候,容器會分別針對這10個請求創建10個全新的RequestPrecessor實例,且他們相互之間互不干擾,簡單來講,request可以看做prototype的一種特例,除了場景更加具體之外,語意上差不多。
<bean id ="requestPrecessor" class="...RequestPrecessor" scope="request" />
- session 同一個session創建一個實例,Spring容器會爲每個獨立的session創建屬於自己的全新的UserPreferences實例,比request scope的bean會存活更長的時間,其他的方面沒區別。對於web應用來說,放到session中最普遍的就是用戶的登錄信息,對於這種放到session中的信息,我們可以使用如下形式的制定scope爲session:
<bean id ="userPreferences" class="...UserPreferences" scope="session" />
3.2.1 ,@Scope("prototype")示例: 結果最後 執行的 內存地址不一樣,返回==>false
@Configuration
public class Cap3MainConfig {
//給容器中註冊一個bean, 類型爲返回值的類型, 默認是單實例
/*
* prototype:多實例: IOC容器啓動的時候,IOC容器啓動並不會去調用方法創建對象, 而是每次獲取的時候纔會調用方法創建對象
* singleton:單實例(默認):IOC容器啓動的時候會調用方法創建對象並放到IOC容器中,以後每次獲取的就是直接從容器中拿(大Map.get)的同一個bean
* request: 主要針對web應用, 遞交一次請求創建一個實例
* session:同一個session創建一個實例
*/
@Scope("prototype")
@Bean
public Person person(){
return new Person("james",20);
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap3MainConfig.class);
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
//從容器中分別取兩次person實例, 看是否爲同一個bean
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
System.out.println(bean1 == bean2);
//結論:bean1就是bean2,同一個對象
}
3.2.2,@Scope("singleton")示例: 結果最後 執行的 內存地址不一樣,返回 ====>true,
@Configuration
public class Cap3MainConfig {
/*
* 這裏也可以直接不使用 註解@Scope ,給容器中註冊一個bean, 類型爲返回值的類型, 默認是單實例
* singleton:單實例(默認):IOC容器啓動的時候會調用方法創建對象並放到IOC容器中,以後每次獲取的就是直接從容器中拿(大Map.get)的同一個bean
*/
//@Scope("singleton")
@Scope("singleton")
@Bean
public Person person(){
return new Person("james",20);
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap3MainConfig.class);
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
//從容器中分別取兩次person實例, 看是否爲同一個bean
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
System.out.println(bean1 == bean2);
//結論:bean1就是bean2,同一個對象
}
3.2.3 ,@Scope("prototype") XML示例結果最後 執行的 內存地址不一樣,返回 ==>false,
將 scope 屬性改成 singleton 就是單例
<bean id="prototypeCap3" class="com.enjoy.cap3.config.Cap3MainConfig" scope="prototype"></bean>
public class Cap3MainConfig {
//給容器中註冊一個bean, 類型爲返回值的類型, 默認是單實例
/*
* prototype:多實例: IOC容器啓動的時候,IOC容器啓動並不會去調用方法創建對象, 而是每次獲取的時候纔會調用方法創建對象
* singleton:單實例(默認):IOC容器啓動的時候會調用方法創建對象並放到IOC容器中,以後每次獲取的就是直接從容器中拿(大Map.get)的同一個bean
* request: 主要針對web應用, 遞交一次請求創建一個實例
* session:同一個session創建一個實例
*/
//@Scope("singleton")
//@Bean
public Person person(){
return new Person("james",20);
}
}
public class Cap3Test {
@Test
public void test01(){
//AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap3MainConfig.class);
//把beans.xml的類加載到容器
ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
String[] names = app.getBeanDefinitionNames();
for(String name:names){
System.out.println(name);
}
//從容器中分別取兩次person實例, 看是否爲同一個bean
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
System.out.println(bean1 == bean2);
//結論:bean1就是bean2,同一個對象
}
}
三,@Lazy 即:懶加載,延時加載 主要針對單例bean:默認在容器啓動的時候不創建對象,僅當第一次使用(獲取)bean的時候才創建被初始
3.3.1 示例源碼:默認在容器啓動的時候不創建對象
public class Cap4MainConfig {
//給容器中註冊一個bean, 類型爲返回值的類型, 默認是單實例
/*
* 懶加載: 主要針對單實例bean:默認在容器啓動的時候創建對象
* 懶加載:容器啓動時候不創建對象, 僅當第一次使用(獲取)bean的時候才創建被初始化
*/
@Lazy
@Bean
public Person person(){
System.out.println("給容器中添加person.......");
return new Person("james",20);
}
}
public class Cap4Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap4MainConfig.class);
System.out.println("IOC容器創建完成........");
app.getBean("person");//執行獲取的時候才創建並初始化bean
}
}
執行結果: 在容器創建之後, 並沒有去創建bean ,而是在獲取bean的時候纔去創建並初始化
四,項目 Demo
Spring源碼深度解析,(附代碼示例 碼雲地址: https://gitee.com/Crazycw/SpringCode.git)
參考資料: https://docs.spring.io/spring/docs/4.3.18.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/
請看下篇:Spring源碼深度解析,Spring源碼以及組件(四)(附代碼示例:)