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,如下圖
在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掃描規則
- 新建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容器中.