依賴注入(Inverse of Control)
Spring 實現(xiàn)IoC(Inverse of Control)唯袄,就是通過 Spring容器 來創(chuàng)建萌狂,管理所有的Java對象(Java Bean)。
并且Spring容器使用配置文件來組織各個Java Bean之間的依賴關(guān)系褪测,而不是以硬編碼的方式讓它們耦合在一起滞磺!
沒有各種new真爽,配置文件管理各個Bean 之間的協(xié)作關(guān)系真的炒雞解耦意述,誰用誰知道。
Bean 的作用域 singleton (默認(rèn)) ,prototype 吮蛹,request荤崇,session,global session 后面三個只在web應(yīng)用中有效
創(chuàng)建Bean的三種形式
構(gòu)造器潮针,最常見的形式术荤,如果不采用構(gòu)造注入,Spring底層會調(diào)用Bean類的無參數(shù)構(gòu)造器來創(chuàng)建實例每篷,因此要求該Bean類提供無參數(shù)的構(gòu)造器瓣戚。Spring容器會默認(rèn)初始化所有屬性,基礎(chǔ)類型0或false焦读,引用類型null
-
靜態(tài)工廠 子库,要創(chuàng)建的實例的Bean,class屬性要是靜態(tài)工廠類(因為Spring需要的是哪個工廠類來創(chuàng)建Bean實例)矗晃,用factory-method指定工廠方法仑嗅。
``` package com.example.factory; public class ExampleFactory{ //靜態(tài)工廠方法 public static Example newInstance(String message,int index){ return new Example(message,index); } ---- xml配置文件 <beans> <bean id = "byIndex" class = "com.example.ExampleFactory" factory-method="newInstance"> <constructor-arg index="0" value="Hello World!"/> <constructor-arg index="1" value="1"/> </bean> </beans> ```
-
實例工廠,先配置實例工廠類张症,配置Bean無需class屬性, 用factory-bean指定工廠類id仓技,factory-method指定工廠方法
---- xml配置文件 <beans> <bean id = "exampleFactory" class = "com.example.ExampleFactory"/> <bean id = "byName" factory-bean = "exampleFactory" factory-method="newInstance"> <constructor-arg name="message" value="Hello World!"/> <constructor-arg name="index" value="1"/> </bean> </beans>
循環(huán)依賴問題
- 構(gòu)造器注入的循環(huán)依賴問題是無法解決的
- setter循環(huán)依賴是可以提前暴露剛完成的構(gòu)造器來解決(需要是singleton)
怎么檢測循環(huán)依賴
檢測循環(huán)依賴相對比較容易,Bean在創(chuàng)建的時候可以給該Bean打標(biāo)俗他,如果遞歸調(diào)用回來發(fā)現(xiàn)正在創(chuàng)建中的話脖捻, 即說明了循環(huán)依賴了。
Spring容器將每一個正在創(chuàng)建的Bean 標(biāo)識符放在一個“當(dāng)前創(chuàng)建Bean池”中拯辙,Bean標(biāo)識符在創(chuàng)建過程中將一直保持在這個池中郭变,因此如果在發(fā)現(xiàn)已經(jīng)在“當(dāng)前創(chuàng)建Bean池”里,將拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴;而對于創(chuàng)建完畢的Bean將從“當(dāng)前創(chuàng)建Bean池”中清除掉涯保。
//DefaultSingletonBeanRegistry
protected void beforeSingletonCreation(String beanName) {
if (!this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
怎么檢測循環(huán)依賴
提前暴露。
假如A周伦,B循環(huán)依賴
- 實例A夕春,將未注入屬性的A,暴露給容器(Wrap)
- 給A注入屬性专挪,發(fā)現(xiàn)要用B
- 實例B及志,注入屬性片排,發(fā)現(xiàn)要用A,在單例緩存中沒有找到A速侈,又去Warp中找到了率寡,注入完成
- 遞歸回來,A成功注入B
//初始化Bean之前提前把Factory暴露出去
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
//通過暴露Factory的方式暴露倚搬,是因為有些Bean是需要被代理的
protected Object getEarlyBeanReference(String beanName,
RootBeanDefinition mbd,
Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext(); ) {
BeanPostProcessor bp = (BeanPostProcessor) it.next();
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp
= (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
// 在Bean 的單例緩存中獲取Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
synchronized (this.singletonObjects) {
// 單例緩存中沒有冶共,找提前暴露的
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory singletonFactory = (ObjectFactory) this
.singletonFactories.get(beanName);
// 如果只是提前暴露了工廠(沒有實例),執(zhí)行工廠方法
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
Spring 的幾個緩存池 :
- alreadyCreated:已經(jīng)創(chuàng)建好的Bean 每界,檢測創(chuàng)建好的Bean是否依賴正在創(chuàng)建的Bean捅僵,如果是,說明原創(chuàng)建好多不可用了
- singletonObjects:單例Bean
- singletonFactories : 單例Bean 提前暴露的工廠
- earlySingletonObjects:執(zhí)行了工廠方法生產(chǎn)出的Bean
- singletonsCurrentlyCreation:創(chuàng)建中的Bean 眨层,用于檢測循環(huán)依賴
所以循環(huán)依賴無法解決的有
構(gòu)造器注入
代理類改變了Bean的版本(見alreadyCreated庙楚,提前暴露的別的Bean 依賴了,之后)
原型Bean (prototype)
參考烏哇哇這里
協(xié)調(diào)不同步的Bean
簡單的說趴樱,一個如果一個singleton的Bean 依賴一個prototype 的Bean的時候馒闷,會產(chǎn)生不同步的情況(因為singleton只創(chuàng)建一次,當(dāng)singleton調(diào)用 prototype 的時候叁征,一般的注入沒辦法讓 Spring 容器每次都返回一個新的 prototype的Bean)纳账。
兩種方法:
- 讓singleton 的bean 實現(xiàn) ApplicationContextAware 接口
public class A implements ApplicationContextAware {
//用于保存ApplicationContext的引用,set方式注入
private ApplicationContext applicationContext;
//模擬業(yè)務(wù)處理的方法
public Object process(){
B b = createB();
return b.execute();
}
private B createB() {
return (B) this.applicationContext.getBean("b"); //
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext=applicationContext;//獲得該ApplicationContext引用
}
}
2 . lookup 方法
public abstract class A{
//模擬業(yè)務(wù)處理的方法
public Object process(){
B b= createB();
return b.execute();
}
protected abstract B createB();
}
<bean id="b" class="com.example.B" scope="prototype"/>
<bean id="a" class="com.example.A">
<lookup-method name="createB" bean="b"/>
</bean>
推薦用第二種方法航揉,這樣和Spring的代碼沒有耦合塞祈。
createB() 方法是個抽象方法,我們并沒有實現(xiàn)它啊帅涂,那它是怎么拿到B類的呢议薪。這里的奧妙就是Srping應(yīng)用了CGLIB(動態(tài)代理)類庫。這個方法是不是抽象都無所謂媳友,不影響CGLIB動態(tài)代理斯议。
在這個方法的代碼簽名處有個標(biāo)準(zhǔn):
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
- public|protected要求方法必須是可以被子類重寫和調(diào)用的;
- abstract可選醇锚,如果是抽象方法哼御,CGLIB的動態(tài)代理類就會實現(xiàn)這個方法,如果不是抽象方法焊唬,就會覆蓋這個方法恋昼,所以沒什么影響;
- return-type就是non-singleton-bean的類型咯赶促,當(dāng)然可以是它的父類或者接口液肌。
- no-arguments不允許有參數(shù)。
AOP
AOP (Aspect-OrientedProgramming鸥滨,面向方面編程)嗦哆,是對OOP(Object-Oriented Programing谤祖,面向?qū)ο缶幊蹋┑难a(bǔ)充。
傳統(tǒng)的OOP模式編程老速,會在每個類調(diào)用一些共同的方法(log粥喜,安全檢查,事務(wù)橘券,性能統(tǒng)計额湘,異常處理等)。
這些方法會造成代碼的冗余约郁,而且曾加的代碼的耦合性(功能邏輯和業(yè)務(wù)邏輯沒有分開)
實現(xiàn)AOP的兩種方式:
- XML風(fēng)格缩挑,使用<aop:config>
首先定義一個要被切的類
public interface PersonService {
public String getPersonName(Integer id);
public void save(String name);
}
public class PersonServiceBean implements PersonService {
@Override
public String getPersonName(Integer id) {
// TODO Auto-generated method stub
return null;
}
@Override
public void save(String name) {
// TODO Auto-generated method stub
System.out.println("您輸入的是" + name);
}
}
然后,我們來定義切點類和切點
public class MyInterceptor {
public void anyMethod(){}
public void doBefore(String name){
System.out.println("前置通知" + name);
}
public void doAfterReturn(String result){
System.out.println("后置通知" + result);
}
public Object doAfter(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("進(jìn)入方法");
Object result = pjp.proceed();
System.out.println("最終通知");
return result;
}
public void doAfterThrowing(Exception e){
System.out.println("異常通知" + e);
}
public void doAround(){
System.out.println("環(huán)繞通知");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:config>
<aop:aspect id="asp" ref="myInterceptor">
<!-- 切點-->
<aop:pointcut id="mycut"
expression="execution(* cn.qdlg.service.impl.PersonServiceBean.*(..))"/>
<aop:before pointcut-ref="mycut" method="doBefore"/>
<aop:after pointcut-ref="mycut" method="doAfter"/>
<aop:after-returning pointcut-ref="mycut" method="doAfterReturn"/>
<aop:around pointcut-ref="mycut" method="doAround"/>
<aop:after-throwing pointcut-ref="mycut" method="doAfterThrowing"/>
</aop:aspect>
</aop:config>
<bean id="myInterceptor" class="cn.qdlg.service.MyInterceptor"></bean>
<bean id="PersonService" class="cn.qdlg.service.impl.PersonServiceBean"></bean>
</beans>
- @Aspect 風(fēng)格
//啟動 aspectj 代理
<aop:aspectj-autoproxy proxy-target-class="true"/>
public class MyInterceptor {
@Pointcut("execution (* cn.qdlg.service.impl.PersonServiceBean.*(..))")
public void anyMethod(){}
@Before("anyMethod()")
public void doBefore(String name){
System.out.println("前置通知" + name);
}
@AfterReturning("anyMethod() && args(name)")
public void doAfterReturn(String result){
System.out.println("后置通知" + result);
}
@After("anyMethod()")
public Object doAfter(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("進(jìn)入方法");
Object result = pjp.proceed();
System.out.println("最終通知");
return result;
}
@AfterThrowing("anyMethod()")
public void doAfterThrowing(Exception e){
System.out.println("異常通知" + e);
}
@Around("anyMethod()")
public void doAround(){
System.out.println("環(huán)繞通知");
}
}
AOP 實現(xiàn)的方式鬓梅,動態(tài)代理----- >http://www.reibang.com/p/6411406ef7c3