Spring源碼日常筆記(一)

Spring源碼日常筆記(一)

學習Spring源碼太枯燥,看着看着就想睡覺,爲了讓自己不睡覺,一邊學習一邊記錄下學習的過程,有錯誤的地方望大佬親噴.(看的是享學課堂james老師講的,若有侵權,必刪)

初識Spring

Spring是一種開源輕量級框架,是爲了解決企業應用程序開發複雜性而創建的,Spring致力於解決JavaEE的各層解決方案,而不僅僅於某一層的方案。
2003年2月Spring框架正式稱爲一道開源項目,Spring致力於J2EE應用的各種解決方案,而不僅僅專注於某一層解決方案。可以說Spring是企業應用開發的“一站式”選擇, Spring貫穿於表現層、業務層、持久層,然而Spring並不想取代那些已經有的框架,而是以高度的開放性,與這些已有的框架進行整合。

Spring的優點

1、讓現有的技術更容易使用,
2、促進良好的編程習慣。
Spring是一個全面的解決方案,它堅持一個原則:不從新造輪子。已經有較好解決方案的領域,Spring絕不重複性實現,比如:對象持久化和OR映射,Spring只對現有的JDBC,Hibernate等技術提供支持,使之更容易使用,而不做重複的實現。Spring框架有很多特性,這些特性由7個定義良好的模塊構成。
1、 Spring Core:即,Spring核心,它是框架最基礎的部分,提供IOC和依賴注入特性
2、 Spring Context:即,Spring上下文容器,它是BeanFactory功能加強的一個子接口
3、 Spring Web:它提供Web應用開發的支持
4、 Spring MVC:它針對Web應用中MVC思想的實現
5、 Spring DAO:提供對JDBC抽象層,簡化了JDBC編碼,同時,編碼更具有健壯性。
6、 Spring ORM:它支持用於流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等等。
7、 Spring AOP:AOP即,面向切面編程,它提供了與AOP聯盟兼容的編程實現

Spring的常用組件

Spring的初體驗

1 創建maven工程
2 導入spring-context依賴

<dependency>
  		<groupId>org.springframework</groupId>
  		<artifactId>spring-context</artifactId>
  		<version>5.0.6.RELEASE</version>
</dependency>

3.1(xml方式)
(1) 創建bean.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"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="person" class="com.enjoy.cap1.Person">
  	<property name="name" value="james"></property>
  	<property name="age" value="19"></property>
  </bean>
</beans>

(2) 新建person.java

public class Person {
   private String name;
   private Integer age;
   
   public Person(){
   	super();
   }
   public String getName() {
   	return name;
   }
   public void setName(String name) {
   	this.name = name;
   }
   public Person(String name, Integer age) {
   	super();
   	this.name = name;
   	this.age = age;
   }
   public Integer getAge() {
   	return age;
   }
   @Override
   public String toString() {
   	return "Person [name=" + name + ", age=" + age + "]";
   }
   public void setAge(Integer age) {
   	this.age = age;
   }
}

(3) 測試類

public class MainTest1 { 
   public static void main(String args[]){
   	//把beans.xml的類加載到容器
   	ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
   	//從容器中獲取bean
   	Person person = (Person) app.getBean("person");
   	
   	System.out.println(person);
   }
}

控制檯輸出:
Person [name=james, age=19]

3.2(註解方式)
如果我們用註解開發, 很明顯是不需要XML的,3.1方法中bean.xml可刪除.

(1) 使用@Configuration 和 @Bean註解,@Bean註解可以定義bean的name,如下:

@Configuration
public class MainConfig {
  //給容器中註冊一個bean, 類型爲返回值的類型, 
  @Bean("abcPerson")
  public Person person01(){
  	return new Person("james",20);
  }
}

(2) 測試一下

public class MainTest2 { 
  public static void main(String args[]){
  	//把beans.xml的類加載到容器
  	
  	ApplicationContext app = new AnnotationConfigApplicationContext(MainConfig.class);
  	String[] namesForBean = app.getBeanNamesForType(Person.class);
  	for(String name:namesForBean){
  		System.out.println(name);
  	}
  }
}

控制檯輸出:
abcPerson

Spring @ComponentScan註解掃描規則

(1) 簡單使用:

@Configuration
@ComponentScan(value="com.enjoy.cap2")
public class Cap2MainConfig {
	//給容器中註冊一個bean, 類型爲返回值的類型, 
	@Bean
	public Person person01(){
		return new Person("james",20);
	}
}

@ComponentScan(value=“com.enjoy.cap2”)表示掃描com.enjoy.cap2下的包
com.enjoy.cap2包下結構如下:
controller,service,dao下都加了@Controller,@service,@Respostry註解,讓ComponentScan能掃描到.
在這裏插入圖片描述
測試類:

public class Cap2Test {
    public static void main(String[] args) {
        ApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
        String[] beanDefinitionNames = app.getBeanDefinitionNames();
        for(String s : beanDefinitionNames){
            System.out.println(s);
        }
    }
}

控制檯輸出:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
cap2MainConfig
orderController
orderDao
orderService
person01

(2) 定製包掃描時的過濾規則
新建dao, service,controller,如下圖
Controller
Dao
Service
在Cap2MainConfig2 @ComponentScan註解中加入配置: @Filter: 掃描規則

@Configuration
//@Controller  @Service  @Respostry  @Component
@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,使用自定義掃描範圍
public class Cap2MainConfig {
	//給容器中註冊一個bean, 類型爲返回值的類型, 
	@Bean
	public Person person01(){
		return new Person("james",20);
	}
}

ComponentScan 下屬性的意思
@ComponentScan value:指定要掃描的包
excludeFilters = Filter[] 指定掃描的時候按照什麼規則排除那些組件
includeFilters = Filter[] 指定掃描的時候只需要包含哪些組件
useDefaultFilters = false 默認是true,掃描所有組件,要改成false
----掃描規則如下
FilterType.ANNOTATION:按照註解
FilterType.ASSIGNABLE_TYPE:按照給定的類型;比如按BookService類型
FilterType.ASPECTJ:使用ASPECTJ表達式
FilterType.REGEX:使用正則指定
FilterType.CUSTOM:使用自定義規則,自已寫類,實現TypeFilter接口

這裏使用FilterType.ANNOTATION的例子遇到一個問題,如下:
配置類:

@Configuration
//@Controller  @Service  @Respostry  @Component
@ComponentScan(value="com.enjoy.cap2", excludeFilters={
		@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
}, useDefaultFilters=false)
public class Cap2MainConfig {
	//給容器中註冊一個bean, 類型爲返回值的類型, 
	@Bean
	public Person person01(){
		return new Person("james",20);
	}
}

com.enjoy.cap2結構如下:
在這裏插入圖片描述
我們使用測試類測試:

public class Cap2Test {

    public static void main(String[] args) {
        ApplicationContext app = new AnnotationConfigApplicationContext(Cap2MainConfig.class);
        String[] beanDefinitionNames = app.getBeanDefinitionNames();
        for(String s : beanDefinitionNames){
            System.out.println(s);
        }
    }
}

我們理想輸出的結果應該是:
cap2MainConfig
orderDao
orderService
person01
將Controller排除掉

但是現實輸出的結果是:
cap2MainConfig
person01
將Controller,Service,Dao都排除掉了

這是爲什麼呢,好像跟我們想的有點誤差:
(1)@componentScan會交給Scanorg.springframework.context.config.ContextNamespaceHandler這個類處理.

registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());

(2)ComponentScanBeanDefinitionParser會讀取配置文件信息並組裝成org.springframework.context.annotation.ClassPathBeanDefinitionScanner進行處理。
(3)如果沒有配置@ComponentScan的use-default-filters屬性,則默認爲true,在創建ClassPathBeanDefinitionScanner時會根據use-default-filters.
如果爲true,會調用registerDefaultFilters方法.我們這邊設置use-default-filters爲true,
會調用registerDefaultFilters方法.
具體實現如下:

protected void registerDefaultFilters() {
	//Component註解會默認把Controller,Service,Repository註解都掃描進來.
  this.includeFilters.add(new AnnotationTypeFilter(Component.class));
  ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
  try {
    this.includeFilters.add(new AnnotationTypeFilter(
      ((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));
    logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
  }
  catch (ClassNotFoundException ex) {
    // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
  }
  try {
    this.includeFilters.add(new AnnotationTypeFilter(
      ((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false));
    logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
  }
  catch (ClassNotFoundException ex) {
    // JSR-330 API not available - simply skip.
  }
}

(4)在進行掃描時會通過include-filter/exclude-filter來判斷你的Bean類是否是合法的:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
 for (TypeFilter tf : this.excludeFilters) {
   if (tf.match(metadataReader, this.metadataReaderFactory)) {
      return false;
   }
 }
 for (TypeFilter tf : this.includeFilters) {
   if (tf.match(metadataReader, this.metadataReaderFactory)) {
      AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
      if (!metadata.isAnnotated(Profile.class.getName())) {
         return true;
      }
      AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
      return this.environment.acceptsProfiles(profile.getStringArray("value"));
    }
  }
 return false;
}

掃描時首先通過exclude-filter 進行黑名單過濾,然後通過include-filter 進行白名單過濾,否則默認排除。

下面舉一個FilterType.CUSTOM的例子(常用)
先新增自定義過濾規則類:(name中包含"order"掃描進來)

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")){//當類包含order字符, 則匹配成功,返回true
			return true;
		}
		return false;
	}
}

在Cap2MainConfig申明:

@ComponentScan(value="com.enjoy.cap2",includeFilters={
		@Filter(type=FilterType.CUSTOM,classes={JamesTypeFilters.class})
},useDefaultFilters=false) 
public class Cap2MainConfig2 {
	@Bean
	public Person person01(){
		return new Person("james",20);
	}
}

打印結果:
----->com.enjoy.cap2.Cap2Test
----->com.enjoy.cap2.config.JamesTypeFilter
----->com.enjoy.cap2.controller.OrderController
----->com.enjoy.cap2.dao.OrderDao
----->com.enjoy.cap2.service.OrderService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
cap2MainConfig
orderController
orderDao
orderService
person01

scope掃描規則

  1. 新建Cap3MainConfig.java
@Configuration
public class Cap3MainConfig {
	//給容器中註冊一個bean, 類型爲返回值的類型, 默認是單實例
	/*
	 * prototype:多實例: IOC容器啓動的時候,IOC容器啓動並不會去調用方法創建對象, 而是每次獲取的時候纔會調用方法創建對象
	 * singleton:單實例(默認):IOC容器啓動的時候會調用方法創建對象並放到IOC容器中,以後每次獲取的就是直接從容器中拿(大Map.get)的同一個bean
	 * request: 主要針對web應用, 遞交一次請求創建一個實例
	 * session:同一個session創建一個實例
	 */
	@Bean
	public Person person(){
		//默認爲單例
		return new Person("james",20);
	}
}

//單例只會被實例化一次

多例的情況下添加@Scope註解:

@Configuration
public class Cap3MainConfig {
	@Scope("prototype")
	@Bean
	public Person person(){
		return new Person("james",20);
	}
}

注:
prototype: 多實例:IOC容器啓動並不會去調用方法創建對象放在容器中,而是 每次獲取的時候纔會調用方法創建對象,見test02
singleton: 單實例(默認):IOC容器啓動會調用方法創建對象放到IOC容器中
以後每交獲取就是直接從容器(理解成從map.get對象)中拿
request: 主要針對WEB應用,同一次請求創建一個實例
session: 同一個session創建一個實例(後面兩個用得不多,瞭解即可)

lazy懶加載

新建Cap4MainConfig.java:

@Configuration
public class Cap4MainConfig {
	//給容器中註冊一個bean, 類型爲返回值的類型, 默認是單實例
	/*
	 * 懶加載: 主要針對單實例bean:默認在容器啓動的時候創建對象
	 * 懶加載:容器啓動時候不創建對象, 僅當第一次使用(獲取)bean的時候才創建被初始化
	
	 */
	@Lazy
	@Bean
	public Person person(){
		System.out.println("給容器中添加person.......");
		return new Person("james",20);
	}
}
  • 加上@Lazy說明此bean是懶加載的, 只有獲取anno.getBean時纔會加載到IOC容器中.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章