Spring源碼深度解析,Spring源碼以及組件,@ComponentScan   掃描組件,@Scope Bean 的生命週期管理,@Lazy 懶加載(三)(附代碼示例:)

三)初始Spring源碼以及常用組件

目錄

三)初始Spring源碼以及常用組件

一,@ComponentScan   掃描

示例源碼3.1.1:一般我們在掃描的都是使用 xml 方式 去掃描 整個項目,

示例源碼3.1.1:使用@ComponentScan 註解  定義範圍掃描

示例源碼3.1.2:一般我們在掃描的都是使用 xml 方式 去自動掃描,

示例源碼3.1.3:一般我們在掃描的都是使用 xml 方式 去自定義規則掃描, 

二,@Scope :Bean 的生命週期管理

3.2.1 ,@Scope("prototype")示例:   結果最後 執行的 內存地址不一樣,返回==>false

3.2.2,@Scope("singleton")示例:   結果最後 執行的 內存地址不一樣,返回  ====>true,  

3.2.3 ,@Scope("prototype")  XML示例結果最後 執行的 內存地址不一樣,返回 ==>false,  

三,@Lazy   即:懶加載,延時加載 主要針對單例bean:默認在容器啓動的時候不創建對象,僅當第一次使用(獲取)bean的時候才創建被初始

3.3.1 示例源碼:默認在容器啓動的時候不創建對象

四,項目 Demo


 一,@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 scopebean會存活更長的時間,其他的方面沒區別。對於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源碼以及組件(四)(附代碼示例:)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章