《Spring In Action(第4版)》閱讀總結(一)Spring上下文、Bean的生命週期、裝配Bean

本文基於Spring5.05
官網地址:https://projects.spring.io/sp...java

spring 加載應用上下文

AnnotationConfigApplicationContext 一個或多個Java配置類加載Spring應用上下文
AnnotationConfigWebApplicationContext 一個或多個Java配置類加載Spring Web應用上下文
ClassPathXmlApplicationContext 從類路徑下的一個或多個XML配置文件加載上下文,把應用上下文的定義文件做爲類資源
FileSystemXmlApplicationContext 從文件系統下的一個或多個XML配置文件加載上下文
XmlWebApplicationContext 從Web應用下的一個或多個XML配置文件加載上下文
GenericApplicationContext 以可刷新的方式讀取不一樣bean定義格式加載上下文

Bean的生命週期

實例化 Spfing對Bean進行實例化
填充屬性 Spring將值和bean的引用注入到bean對應的屬性中
BeanNameAware 的 SetBeanName() 若是Bean實現了BeanNameAware接口,Spring將bean的id傳遞給SetBeanNamer()方法
BeanFactoryAware 的 setBeanFactory() 若是Bean實現了BeanFactoryAware,Spring將調用setBeanFactory()方法,將BeanFactory容器實例傳入
ApplicationContextAware 的 setApplicationContext() 若是Bean實現了ApplicationContextAware,Spring將調用setBeanFactory()方法,將BeanFactory容器實例傳入
BeanPostProcess 的 postProcessBeforeInitialization() -
InitializingBean 的 afterPropertiesSet() 和自定義初始方法 @PostConstruct -> InitializingBean -> init-method
BeanPostProcess 的 postProcessAfterInitialization() -
已完成Bean的初始化,使用Bean直至容器關閉 -
DisposableBean 的 destory()方法 和自定義銷燬方法 @PreDestory -> DisposableBean -> destory-method

裝配Bean

  1. 聲明bean
  2. 構造器注入和Setter方法注入
  3. 裝配bean
  4. 控制bean的建立和銷燬

Spring配置的可選方案

  1. 在XML中進行顯式配置
  2. 在Java中進行顯式配置(推薦,一般用於第三方Bean定義)
  3. 隱式的bean發現機制和自動裝配(推薦)

實現自動化配置

1)組件掃描(compoent sacnning):Spring會自動發現應用上下文所建立的bean
2)自動裝配(autowiring):Spring自動知足bean之間的依賴
實現自動化配置一般是在Java類中聲明註解,並經過組件掃描使指定範圍內的註解生效的一種方式正則表達式

I 在XML中進行顯式配置
元素 父元素 屬性 說明
<bean>
聲明Bean定義
<beans> id 惟一標示
name 名稱,多個用','隔開,不能與已有id重複
class 指定實例類型
scope 單例/多例
lazy-init 懶加載
autowire 自動裝配策略
autowire-candidate 是否參與自動注入
primary 優先注入
depends-on 依賴指定bean
init-method 自定義初始化方法
destroy-method 自定義銷燬方法
abstract 抽象類,是則不建立對象
parent 指定父類Bean,繼承父類屬性值
factory-bean 指定工廠Bean
factory-method 指定工廠方法
<alias>
聲明Bean的別名
<beans> name 指定Bean
alias 別名
<property>
經過Setter方法初始化Bean
(可以使用P-命名空間代替)
<bean> name 指定屬性名,以setName()形式
ref 給引用類型指定bean
value 給基本類型賦值
<constructor-arg>
經過構造器方法初始化Bean
(spring3.0後可以使用c-命名空間代替)
<bean> index 構造方法參數索引,從0開始
type 構造方法參數類型,會有歧義
name 構造方法參數名稱
ref 引用類型指定bean
value 基本類型賦值
<set>/<List>
聲明集合
<constructor-arg>/<property> 注入參數爲集合時使用(c/p-命名空間沒法裝配集合)
<ref>
聲明集合元素
<set>/<List> bean 指定Bean定義
<value>
聲明集合元素
<set>/<List> <value>val</value> 指定集合字面量元素值
util-命名空間 <beans> <util:constant> 應用某個類型的public static域,並將其暴露爲bean
<util:list> 建立一個java.util.List類型的bean,其中包含值與引用
<util:set> 建立一個java.util.Set類型的bean,其中包含值與引用
<util:map> 建立一個java.util.Map類型的bean,其中包含值與引用
<util:properties> 建立一個java.util.properties類型的bean
<util:property-path> 應用某個類型的public static域,並將其暴露爲bean
<import> 引入新的xml配置

Ps:關於p/c-命名空間,添加xml配置後,在bean中做爲屬性使用
語法:p:屬性名[-ref]="字面量或BeanId"
屬性名可設置爲 name(參數名)、_0(參數下標,從0開始)、_(只有一個參數時可以使用)spring

II 在Java中進行顯式配置
註解 說明 屬性 屬性說明
@Bean 聲明Bean定義,定義在方法上,Bean的Id默認爲方法名 value/name 指定Bean的Id
autowire 指定自動注入策略,默認爲NO
initMethod 指定自定義初始化方法
destroyMethod 指定自定義銷燬方法
@Scope 指定做用域 value/scopeName 設置單例、多例等做用域
proxyMode 設置動態代理模式,JDK、cglib等
@Import 導入新的Java配置類 value 指定要導入的Java配置類,可設置單個(a.class)或數組({a.class,b.class})
@ImportResource 導入新的XML配置 value 指定導入XML配置路徑,可爲單個("a.xml")或數組({"a.xml","b.xml"})
@PropertySource 導入新的properties文件 value 指定導入properties文件路徑,可爲單個("a.properties")或數組({"a.properties","b.properties"})
III 實現自動化裝配
註解 說明 屬性 屬性說明
@Configuration 聲明配置類,Spring會從配置類中加載上下文 value
@Component 聲明組件,同@Controller,@Service... value 指定Bean的Id,也可經過@Named聲明Id(jsr330)
@Autowired 可修飾類變量、set方法,以ByType方式自動注入組件,同@Resource(jsr250),@Inject(jsr330) required 默認true,未找到注入的Bean會報錯,false關閉,關閉時注意NullPointerException
@Qualifier 與Autowired配置使用,指定Bean的Id注入 value 指定Bean的Id
@ComponentScan 啓用組件掃描,也可在XML中配置<context:component-scan> basePackages 指定掃描包,可接受單個包名("com.*")和數組{"service","controller"},不安全,重構代碼修改包結構會出現問題
basePackageClasses 指定類所在的包做爲組件掃描的基礎包。可經過在須要導入的包中建立並指定Marker interface空接口

高級裝配

  1. Spring profile
  2. 條件化的bean聲明
  3. 自動裝配與歧義性
  4. bean的做用域
  5. Spring表達式語言

環境與profile

在Spring3.1中,Spring引入了bean profile的功能,要使用profile,將全部不一樣的bean定義整理到一個或多個profile之中,在將應用部署到每一個環境時,要確保對應的profile處於激活(active)的狀態。沒有指定profile的bean始終會被建立數組

在Java中聲明profile

@Profile()
在Spring3.1中,只能在類級別上使用@Profile註釋,與@Configuration配合使用
在Spring3.2開始,能夠再方法級別上使用,與@Bean註解一同使用安全

在XML中聲明profile

在<beans>中聲明profile屬性
可在<beans>中嵌套聲明<beans> 實現多個profile共存一個xml文檔session

激活profile,依賴於兩個獨立的屬性

spring.profiles.active 設置激活的profile
spring.profiles.default active未設置則默認爲default值dom

設置激活的方式

  1. 做爲DispatcherServlet的初始化參數 <init-param>
  2. 做爲Web應用的上下文參數 <context-param>
  3. 做爲JNDI條目
  4. 做爲環境變量 ctx.getEnvironment().setActiveProfiles("dev");ctx.refresh();
  5. 做爲JVM的系統屬性 -Dspring.profiles.active="dev"
  6. 在集成測試類上,使用@ActiveProfiles註解設置

條件化的bean

Spring4引入@Conditional註解
可使用到帶有@Bean註解的方法上。若是給定的條件爲true,就會建立這個Bean,不然忽略。post

@Conditional能夠指定任意實現Condition接口的類型,
並實現boolean matches(ConditionContext context,AnnotatedTypeMetadata metadata)返回true則建立Bean測試

ConditionContext的做用:

  1. getRegistry() 返回的BeanDefinitionRegistry檢查bean定義
  2. getBeanFactory() 返回的ConfigurableListableBeanFactory檢查bean是否存在,甚至探查bean的屬性
  3. getEnvironment() 返回的Environment檢查環境變量是否存在以及它的值是什麼
  4. getResourceLoader() 返回的ResourceLoader 檢查加載的資源
  5. getClassLoader() 返回的ClassLoader 加載並檢查類是否存在

AnnotatedTypeMetadata的做用:

檢查帶有@Bean註解的方法上還有什麼其餘註解
isAnnotatrd() 指定註解是否存在
getAnnotationAttributes 等獲取註解集合ui

spring4開始,Profile註解進行重構,使其基於@conditional和Condition實現。

處理自動裝配的歧義性

僅有一個Bean匹配所需的結果時,自動裝配纔是有效的。
若是不只有一個bean可以匹配結果的話,這種歧義性會阻礙Spring自動裝配屬性、構造器參數或方法參數。

消除歧義性

  1. 選Bean中的某一個設爲首選primary,@Primary(Java配置)、<bean primary="true">(XML配置)
  2. 使用限定符(qualifier)幫助Spring將可選Bean的範圍縮小到一個bean。@Qualifier

Bean的做用域

單例(singleton): 在整個應用中,只會建立bean的一個實例(默認)
原型(prototype):每次注入或經過Spring應用上下文獲取的時候,都會建立一個新的bean實例。
會話(session) :在Web應用中,爲每一個會話建立一個bean實例
請求(request) :在Web應用中,爲每一個請求建立一個bean實例
Java配置:使用 @Scope 註解聲明做用域,能夠與@Component 或 @Bean一塊兒使用
XML配置:<bean scope="singleton">
可經過ConfigurableBeanFactory.SCOPE_PROTOTYPE 或 SCOPE_SINGLETON 指定
也可用WebApplicationContext.SCOPE_SESSION等(須要在Web應用中使用)
@Scope 還有一個proxyMode 能夠設置動態代理模式(經過ScopedProxyMode 枚舉類來設置JDK、cglib等)
在XML中聲明做用域代理:<bean><aop:scoped-proxy ></bean>默認是cglib
可經過設置proxy-target-class=false 更改成JDK代理

Spring表達式語言(Spring Expression Language)

運行時值注入

1)屬性佔位符(Property placeholder)
2) Spring表達式語言(SpEL)

注入外部的值

使用@PropertySource和Environment

最簡單方式:聲明屬性源(@PropertySource導入資源)並經過Spring的Environment來檢索屬性
@Autowired
Environment env;

Environment 經常使用API

String getProperty(String key)
String getProperty(String key, String defaultValue)
T getProperty(String key, Class<T> type)
T getProperty(String key, Class<T> type, String defaultValue)

String getRequiredProperty(String key)
T getRequiredProperty(String key, Class<T> type)

boolean containsProperty(String var1)

Class<T> getPropertyAsClass(String key, Class<T> type)

String[] getActiveProfiles(); 獲取激活的profiles
String[] getDefaultProfiles(); 獲取默認的profiles
boolean acceptsProfiles(String... var1); 若是environment支持給定profile的話,就返回true

解析屬性佔位符

在Spring裝配中,佔位符的形式爲使用"${...}"包裝的屬性名稱

在XML中使用屬性佔位符(前提是經過<context:property-placeholder location=" "/>引入資源)

<context:property-placeholder location="classpath:demo.properties"/>
<bean id="demo" class="" c:_name="${demo.name}">

在Java中使用屬性佔位符(需配置一個PropertyPlaceholderConfigurer 或 PropertySourcesPlaceholderConfigurer Bean,Spring 3.1 之後推薦後者,由於其可以基於Spring Environment及其屬性源來解析佔位符)

public class Demo {
    @Value("${demo.name}")
    private String name;
}

使用Spring表達式語言進行裝配

Spring3引入Spring表達式語言(Spring Expression Language,SpEL)。經過表達式,在運行時計算獲得值,實現裝配。
SpEL的形式爲"#{...}"

SpEL的特性包括:

1) 使用Bean的Id來引用Bean
2)調用方法和訪問對象的屬性
3)對值進行算術、關係和邏輯運算
4)正則表達式匹配
5)集合操做
除依賴注入,Spring Security支持SpEL定義安全限制規則;Thymeleaf模板支持SpEL引用模型數據

列子:

//獲取當前時間的毫秒值,T()表達式會將java.lang.System視爲Java中對應的類,並調用其靜態方法currentTimeMills()
#{T(System).currentTimeMills()} 

//獲取Id爲demo的Bean,並引用其name屬性(應該是經過get方法獲取的吧!未驗證)
#{demo.name}

//經過systemProperties對象引用系統屬性
#{systemProperties['disc.title']}

SpEL基礎表達式

//1)表示字面值
#{3.14159}
#{9.87E4}
#{'demo'}
#{false}    //true和false的計算結果就是它們對應的Boolean類型的值

//2)引用bean、屬性和方法
#{demo}    //獲取Id爲demo的Bean的引用
#{demo.name}    //獲取Id爲demo的Bean的name屬性值
#{demo.getName()}
#{demo.getName().toUpperCase()}
#{demo.getName()?.toUpperCase()}    //避免getName()返回null,出現NullPointException,使用'?.'類型安全的運算符,若是getName()返回null,則不會調用toUpperCase(),表達式返回null

//3)在表達式中使用類型(依賴T()這個關鍵的運算符,其真正價值在於訪問目標類型的靜態方法和常量)
#{T(java.lang.Math)}    //表示Math的class對象引用
#{T(java.lang.Math).PI}
#{T(java.lang.Math).random()}

//4)SpEL運算符
//算數運算:+ - * / % ^
//比較運算:< > == <= >= lt gt eq le ge
//邏輯運算:and or not
//條件運算:?:(ternary) ?:(Elvis)
//正則匹配:matches
#{2 * T(java.lang.Math).PI * cricle.radius}
#{T(java.lang.Math).PI * cricle.radius ^ 2}    //^ 是用於乘方計算的運算符
#{demo.name + 'and' + demo.realname}    //使用String類型的值,+ 爲鏈接符
#{demo.age == 20}
#{demo.age eq 20}    //比較運算符有兩種形式:符號形式和文本形式,二者等同,計算結果爲Boolean值
#{demo.name != null ? demo.name : "roylion"}    //三元運算符的一個常見場景:檢查null值,並用一個默認值替代null
#{demo.name ?: "roylion"}    //此三元運算符一般稱爲Elvis運算符,用來簡化上述場景。
#{demo.emial matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}    //匹配正則表達式

//5)計算集合
#{demo.books[4].title}
#{demo.books[T(java.lang.Math).random() * demo.books.size()].title}
//[] 運算符能夠從集合或數組中按照索引獲取元素,甚至String(基於0開始)
#{'Im a big handsome '[3]}
//.?[...] 對集合進行過濾,獲得集合的一個子集,
//[]中接受另外一個表達式,當SpEL迭代書本列表時,會對每一本書計算這個表達式,若是爲true,則會存放到新的集合中
#{demo.books.?[title eq 'springInAction']}
#{demo.books.^[title eq 'springInAction']}    //.^[] 查詢第一個匹配項
#{demo.books.$[title eq 'springInAction']}    //.$[] 查詢最後一個匹配項

#{demo.books.![title]}    //.![] 從集合每一個成員中選擇特定的屬性放到另一個集合中

保持SpEL表達式的簡潔,儘可能不要寫複雜的SpEL表達式。由於SpEl表達式是String類型,測試困難

相關文章
相關標籤/搜索