Spring源碼分析(轉)

在認真學習Rod.Johnson的三部曲之一:<<Professional Java Development with the spring framework>>,順便也看了看源代碼想知道個究竟,拋磚引玉,有興趣的同志一起討論研究吧!
 在Spring中,IOC容器的重要地位我們就不多說了,對於Spring的使用者而言,IOC容器實際上是什麼呢?我們可以說BeanFactory就 是我們看到的IoC容器,當然了Spring爲我們準備了許多種IoC容器來使用,這樣可以方便我們從不同的層面,不同的資源位置,不同的形式的定義信息 來建立我們需要的IoC容器。
 在Spring中,最基本的IOC容器接口是BeanFactory – 這個接口爲具體的IOC容器的實現作了最基本的功能規定 – 不管怎麼着,作爲IOC容器,這些接口你必須要滿足應用程序的最基本要求:
  

Java代碼 

public interface BeanFactory {   

  

    //這裏是對FactoryBean的轉義定義,因爲如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,  

    //如果需要得到工廠本身,需要轉義         

    String FACTORY_BEAN_PREFIX = "&";   

  

  

    //這裏根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。  

    Object getBean(String name) throws BeansException;   

10   

11     //這裏根據bean的名字和Class類型來得到bean實例,和上面的方法不同在於它會拋出異常:如果根據名字取得的bean實例的Class類型和需要的不同的話。  

12     Object getBean(String name, Class requiredType) throws BeansException;   

13   

14     //這裏提供對bean的檢索,看看是否在IOC容器有這個名字的bean  

15     boolean containsBean(String name);   

16   

17     //這裏根據bean名字得到bean實例,並同時判斷這個bean是不是單件  

18     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;   

19   

20     //這裏對得到bean實例的Class類型  

21     Class getType(String name) throws NoSuchBeanDefinitionException;   

22   

23     //這裏得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來  

24     String[] getAliases(String name);   

25   

26 }  

27 public interface BeanFactory {  

28   

29     //這裏是對FactoryBean的轉義定義,因爲如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,  

30     //如果需要得到工廠本身,需要轉義  

31     String FACTORY_BEAN_PREFIX = "&";  

32   

33     //這裏根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。  

34     Object getBean(String name) throws BeansException;  

35   

36     //這裏根據bean的名字和Class類型來得到bean實例,和上面的方法不同在於它會拋出異常:如果根據名字取得的bean實例的Class類型和需要的不同的話。  

37     Object getBean(String name, Class requiredType) throws BeansException;  

38   

39     //這裏提供對bean的檢索,看看是否在IOC容器有這個名字的bean  

40     boolean containsBean(String name);  

41   

42     //這裏根據bean名字得到bean實例,並同時判斷這個bean是不是單件  

43     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;  

44   

45     //這裏對得到bean實例的Class類型  

46     Class getType(String name) throws NoSuchBeanDefinitionException;  

47   

48     //這裏得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來  

49     String[] getAliases(String name);  

50   

51 }  

BeanFactory裏只對IOC容器的基本行爲作了定義,根本不關心你的bean是怎樣定義怎樣加載的 – 就像我們只關心從這個工廠裏我們得到到什麼產品對象,至於工廠是怎麼生產這些對象的,這個基本的接口不關心這些。如果要關心工廠是怎樣產生對象的,應用程 序需要使用具體的IOC容器實現當然你可以自己根據這個BeanFactory來實現自己的IOC容器,但這個沒有必要,因爲Spring已經爲我們準備好了一系列工廠來讓我們使用。比 如XmlBeanFactory就是針對最基礎的BeanFactoryIOC容器的實現 – 這個實現使用xml來定義IOC容器中的bean
 Spring提供了一個BeanFactory的基本實現,XmlBeanFactory同樣的通過使用模板模式來得到對IOC容器的抽象- AbstractBeanFactory,DefaultListableBeanFactory這些抽象類爲其提供模板服務。其中通過resource 接口來抽象bean定義數據,對Xml定義文件的解析通過委託給XmlBeanDefinitionReader來完成。下面我們根據書上的例子,簡單的 演示IOC容器的創建過程:
  

Java代碼 

52 ClassPathResource res = new ClassPathResource("beans.xml");   

53 DefaultListableBeanFactory factory = new DefaultListableBeanFactory();   

54 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);   

55 reader.loadBeanDefinitions(res);  

56 ClassPathResource res = new ClassPathResource("beans.xml");  

57 DefaultListableBeanFactory factory = new DefaultListableBeanFactory();  

58 XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);  

59 reader.loadBeanDefinitions(res);  

這些代碼演示了以下幾個步驟:

   1. 創建IOC配置文件的抽象資源
    2. 創建一個BeanFactory
    3. 把讀取配置信息的BeanDefinitionReader,這裏是XmlBeanDefinitionReader配置給BeanFactory
    4. 從定義好的資源位置讀入配置信息,具體的解析過程由XmlBeanDefinitionReader來完成,這樣完成整個載入bean定義的過程。我們的IoC容器就建立起來了。在BeanFactory的源代碼中我們可以看到:
  

Java代碼 

60 public class XmlBeanFactory extends DefaultListableBeanFactory {   

61     //這裏爲容器定義了一個默認使用的bean定義讀取器  

62     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);   

63     public XmlBeanFactory(Resource resource) throws BeansException {   

64         this(resource, null);   

65     }   

66     //在初始化函數中使用讀取器來對資源進行讀取,得到bean定義信息。  

67     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {   

68         super(parentBeanFactory);   

69         this.reader.loadBeanDefinitions(resource);   

70     }  

71 public class XmlBeanFactory extends DefaultListableBeanFactory {  

72     //這裏爲容器定義了一個默認使用的bean定義讀取器  

73     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);  

74     public XmlBeanFactory(Resource resource) throws BeansException {  

75         this(resource, null);  

76     }  

77     //在初始化函數中使用讀取器來對資源進行讀取,得到bean定義信息。  

78     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {  

79         super(parentBeanFactory);  

80         this.reader.loadBeanDefinitions(resource);  

81     }  

我們在後面會看到讀取器讀取資源和註冊bean定義信息的整個過程,基本上是和上下文的處理是一樣的,從這裏我們可以看到上下文和 XmlBeanFactory這兩種IOC容器的區別,BeanFactory往往不具備對資源定義的能力,而上下文可以自己完成資源定義,從這個角度上 看上下文更好用一些。
 仔細分析Spring BeanFactory的結構,我們來看看在BeanFactory基礎上擴展出的ApplicationContext – 我們最常使用的上下文。除了具備BeanFactory的全部能力,上下文爲應用程序又增添了許多便利:

    * 可以支持不同的信息源,我們看到ApplicationContext擴展了MessageSource
     * 訪問資源 體現在對ResourceLoaderResource的支持上面,這樣我們可以從不同地方得到bean定義資源
     * 支持應用事件,繼承了接口ApplicationEventPublisher,這樣在上下文中引入了事件機制而BeanFactory是沒有的。

ApplicationContext允許上下文嵌套 – 通過保持父上下文可以維持一個上下文體系 – 這個體系我們在以後對Web容器中的上下文環境的分析中可以清楚地看到。對於bean的查找可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上 下文,逐級向上,這樣爲不同的Spring應用提供了一個共享的bean定義環境。這個我們在分析Web容器中的上下文環境時也能看到。
 ApplicationContext提供IoC容器的主要接口,在其體系中有許多抽象子類比如AbstractApplicationContext爲 具體的BeanFactory的實現,比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他們只需要關心具體的資源定位問題。當應用程序代碼實例化 FileSystemXmlApplicationContext的時候,得到IoC容器的一種具體表現 – ApplicationContext,從而應用程序通過ApplicationContext來管理對bean的操作。
 BeanFactory 是一個接口,在實際應用中我們一般使用ApplicationContext來使用IOC容器,它們也是IOC容器展現給應用開發者的使用接口。對應用程 序開發者來說,可以認爲BeanFactoryApplicationFactory在不同的使用層面上代表了SPRING提供的IOC容器服務。
 下面我們具體看看通過FileSystemXmlApplicationContext是怎樣建立起IOC容器的顯而易見我們可以通過new來得到IoC容器:
  

Java代碼 

82 ApplicationContext = new FileSystemXmlApplicationContext(xmlPath);  

83 ApplicationContext = new FileSystemXmlApplicationContext(xmlPath);  

調用的是它初始化代碼:
  

Java代碼 

84 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)   

85         throws BeansException {   

86     super(parent);   

87     this.configLocations = configLocations;   

88     if (refresh) {   

89        //這裏是IoC容器的初始化過程,其初始化過程的大致步驟由AbstractApplicationContext來定義  

90         refresh();   

91     }   

92 }  

93 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  

94         throws BeansException {  

95     super(parent);  

96     this.configLocations = configLocations;  

97     if (refresh) {  

98        //這裏是IoC容器的初始化過程,其初始化過程的大致步驟由AbstractApplicationContext來定義  

99         refresh();  

100     }  

101 }  

refresh的模板在AbstractApplicationContext:
  

Java代碼 

102 public void refresh() throws BeansException, IllegalStateException {   

103     synchronized (this.startupShutdownMonitor) {   

104         synchronized (this.activeMonitor) {   

105             this.active = true;   

106         }   

107   

108         // 這裏需要子類來協助完成資源位置定義,bean載入和向IOC容器註冊的過程  

109         refreshBeanFactory();   

110         …………   

111  }  

112 public void refresh() throws BeansException, IllegalStateException {  

113     synchronized (this.startupShutdownMonitor) {  

114         synchronized (this.activeMonitor) {  

115             this.active = true;  

116         }  

117   

118         // 這裏需要子類來協助完成資源位置定義,bean載入和向IOC容器註冊的過程  

119         refreshBeanFactory();  

120         ............  

121  }  

這個方法包含了整個BeanFactory初始化的過程,對於特定的FileSystemXmlBeanFactory,我們看到定位資源位置由refreshBeanFactory()來實現:
 在AbstractXmlApplicationContext中定義了對資源的讀取過程,默認由XmlBeanDefinitionReader來讀取:
  

Java代碼 

122 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {   

123     // 這裏使用XMLBeanDefinitionReader來載入bean定義信息的XML文件  

124     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);   

125   

126     //這裏配置reader的環境,其中ResourceLoader是我們用來定位bean定義信息資源位置的  

127     ///因爲上下文本身實現了ResourceLoader接口,所以可以直接把上下文作爲ResourceLoader傳遞給XmlBeanDefinitionReader  

128     beanDefinitionReader.setResourceLoader(this);   

129     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));   

130   

131     initBeanDefinitionReader(beanDefinitionReader);   

132     //這裏轉到定義好的XmlBeanDefinitionReader中對載入bean信息進行處理  

133     loadBeanDefinitions(beanDefinitionReader);   

134 }  

135 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  

136     // 這裏使用XMLBeanDefinitionReader來載入bean定義信息的XML文件  

137     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  

138   

139     //這裏配置reader的環境,其中ResourceLoader是我們用來定位bean定義信息資源位置的  

140     ///因爲上下文本身實現了ResourceLoader接口,所以可以直接把上下文作爲ResourceLoader傳遞給XmlBeanDefinitionReader  

141     beanDefinitionReader.setResourceLoader(this);  

142     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  

143   

144     initBeanDefinitionReader(beanDefinitionReader);  

145     //這裏轉到定義好的XmlBeanDefinitionReader中對載入bean信息進行處理  

146     loadBeanDefinitions(beanDefinitionReader);  

147 }  

轉到beanDefinitionReader中進行處理:
  

Java代碼 

148 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {   

149     Resource[] configResources = getConfigResources();   

150     if (configResources != null) {   

151         //調用XmlBeanDefinitionReader來載入bean定義信息。  

152         reader.loadBeanDefinitions(configResources);   

153     }   

154     String[] configLocations = getConfigLocations();   

155     if (configLocations != null) {   

156         reader.loadBeanDefinitions(configLocations);   

157     }   

158 }  

159 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  

160     Resource[] configResources = getConfigResources();  

161     if (configResources != null) {  

162         //調用XmlBeanDefinitionReader來載入bean定義信息。  

163         reader.loadBeanDefinitions(configResources);  

164     }  

165     String[] configLocations = getConfigLocations();  

166     if (configLocations != null) {  

167         reader.loadBeanDefinitions(configLocations);  

168     }  

169 }  

而在作爲其抽象父類的AbstractBeanDefinitionReader中來定義載入過程:
  

Java代碼 

170 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {   

171  //這裏得到當前定義的ResourceLoader,默認的我們使用DefaultResourceLoader  

172  ResourceLoader resourceLoader = getResourceLoader();   

173  ………//如果沒有找到我們需要的ResourceLoader,直接拋出異常  

174     if (resourceLoader instanceof ResourcePatternResolver) {   

175         // 這裏處理我們在定義位置時使用的各種pattern,需要ResourcePatternResolver來完成  

176         try {   

177             Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);   

178             int loadCount = loadBeanDefinitions(resources);   

179             return loadCount;   

180         }   

181       ……..   

182     }   

183     else {   

184         // 這裏通過ResourceLoader來完成位置定位  

185         Resource resource = resourceLoader.getResource(location);   

186         // 這裏已經把一個位置定義轉化爲Resource接口,可以供XmlBeanDefinitionReader來使用了  

187         int loadCount = loadBeanDefinitions(resource);   

188         return loadCount;   

189     }   

190 }  

191 public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {  

192  //這裏得到當前定義的ResourceLoader,默認的我們使用DefaultResourceLoader  

193  ResourceLoader resourceLoader = getResourceLoader();  

194  .........//如果沒有找到我們需要的ResourceLoader,直接拋出異常  

195     if (resourceLoader instanceof ResourcePatternResolver) {  

196         // 這裏處理我們在定義位置時使用的各種pattern,需要ResourcePatternResolver來完成  

197         try {  

198             Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);  

199             int loadCount = loadBeanDefinitions(resources);  

200             return loadCount;  

201         }  

202       ........  

203     }  

204     else {  

205         // 這裏通過ResourceLoader來完成位置定位  

206         Resource resource = resourceLoader.getResource(location);  

207         // 這裏已經把一個位置定義轉化爲Resource接口,可以供XmlBeanDefinitionReader來使用了  

208         int loadCount = loadBeanDefinitions(resource);  

209         return loadCount;  

210     }  

211 }  

當我們通過ResourceLoader來載入資源,別忘了了我們的GenericApplicationContext也實現了ResourceLoader接口:
  

Java代碼 

212 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {   

213     public Resource getResource(String location) {   

214         //這裏調用當前的loader也就是DefaultResourceLoader來完成載入  

215         if (this.resourceLoader != null) {   

216             return this.resourceLoader.getResource(location);   

217         }   

218         return super.getResource(location);   

219     }   

220 …….   

221 }  

222 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {  

223     public Resource getResource(String location) {  

224         //這裏調用當前的loader也就是DefaultResourceLoader來完成載入  

225         if (this.resourceLoader != null) {  

226             return this.resourceLoader.getResource(location);  

227         }  

228         return super.getResource(location);  

229     }  

230 .......  

231 }  

而我們的FileSystemXmlApplicationContext就是一個DefaultResourceLoader – GenericApplicationContext()通過DefaultResourceLoader:
  

Java代碼 

232 public Resource getResource(String location) {   

233     //如果是類路徑的方式,那需要使用ClassPathResource來得到bean文件的資源對象  

234     if (location.startsWith(CLASSPATH_URL_PREFIX)) {   

235         return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());   

236     }   

237     else {   

238         try {   

239             // 如果是URL方式,使用UrlResource作爲bean文件的資源對象  

240             URL url = new URL(location);   

241             return new UrlResource(url);   

242         }   

243         catch (MalformedURLException ex) {   

244             // 如果都不是,那我們只能委託給子類由子類來決定使用什麼樣的資源對象了  

245             return getResourceByPath(location);   

246         }   

247     }   

248 }  

249 public Resource getResource(String location) {  

250     //如果是類路徑的方式,那需要使用ClassPathResource來得到bean文件的資源對象  

251     if (location.startsWith(CLASSPATH_URL_PREFIX)) {  

252         return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());  

253     }  

254     else {  

255         try {  

256             // 如果是URL方式,使用UrlResource作爲bean文件的資源對象  

257             URL url = new URL(location);  

258             return new UrlResource(url);  

259         }  

260         catch (MalformedURLException ex) {  

261             // 如果都不是,那我們只能委託給子類由子類來決定使用什麼樣的資源對象了  

262             return getResourceByPath(location);  

263         }  

264     }  

265 }  

我們的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的實現類,他實現了以下的接口:
  

Java代碼 

266 protected Resource getResourceByPath(String path) {   

267     if (path != null && path.startsWith("/")) {   

268         path = path.substring(1);   

269     }   

270     //這裏使用文件系統資源對象來定義bean文件  

271     return new FileSystemResource(path);   

272 }  

273 protected Resource getResourceByPath(String path) {  

274     if (path != null && path.startsWith("/")) {  

275         path = path.substring(1);  

276     }  

277     //這裏使用文件系統資源對象來定義bean文件  

278     return new FileSystemResource(path);  

279 }  

這樣代碼就回到了FileSystemXmlApplicationContext中來,他提供了FileSystemResource來完成從文件系統 得到配置文件的資源定義。這樣,就可以從文件系統路徑上對IOC配置文件進行加載 – 當然我們可以按照這個邏輯從任何地方加載,在Spring中我們看到它提供的各種資源抽象,比如ClassPathResource, URLResource,FileSystemResource等來供我們使用。上面我們看到的是定位Resource的一個過程,而這只是加載過程的一 部分 – 我們回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)來看看得到代表bean文 件的資源定義以後的載入過程,默認的我們使用XmlBeanDefinitionReader
  

Java代碼 

280 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {   

281     …….   

282     try {   

283         //這裏通過Resource得到InputStreamIO  

284         InputStream inputStream = encodedResource.getResource().getInputStream();   

285         try {   

286             //InputStream中得到XML的解析源  

287             InputSource inputSource = new InputSource(inputStream);   

288             if (encodedResource.getEncoding() != null) {   

289                 inputSource.setEncoding(encodedResource.getEncoding());   

290             }   

291             //這裏是具體的解析和註冊過程  

292             return doLoadBeanDefinitions(inputSource, encodedResource.getResource());   

293         }   

294         finally {   

295             //關閉從Resource中得到的IO  

296             inputStream.close();   

297         }   

298     }   

299        ………   

300 }   

301   

302 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)   

303         throws BeanDefinitionStoreException {   

304     try {   

305         int validationMode = getValidationModeForResource(resource);   

306         //通過解析得到DOM,然後完成beanIOC容器中的註冊  

307         Document doc = this.documentLoader.loadDocument(   

308                 inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);   

309         return registerBeanDefinitions(doc, resource);   

310     }   

311 …….   

312 }  

313 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {  

314     .......  

315     try {  

316         //這裏通過Resource得到InputStreamIO流  

317         InputStream inputStream = encodedResource.getResource().getInputStream();  

318         try {  

319             //InputStream中得到XML的解析源  

320             InputSource inputSource = new InputSource(inputStream);  

321             if (encodedResource.getEncoding() != null) {  

322                 inputSource.setEncoding(encodedResource.getEncoding());  

323             }  

324             //這裏是具體的解析和註冊過程  

325             return doLoadBeanDefinitions(inputSource, encodedResource.getResource());  

326         }  

327         finally {  

328             //關閉從Resource中得到的IO流  

329             inputStream.close();  

330         }  

331     }  

332        .........  

333 }  

334   

335 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  

336         throws BeanDefinitionStoreException {  

337     try {  

338         int validationMode = getValidationModeForResource(resource);  

339         //通過解析得到DOM,然後完成beanIOC容器中的註冊  

340         Document doc = this.documentLoader.loadDocument(  

341                 inputSource, this.entityResolver, this.errorHandler, validationMode, <a href="http://this.name" title="http://this.name" target="_blank">this.name</a>spaceAware);  

342         return registerBeanDefinitions(doc, resource);  

343     }  

344 .......  

345 }  

我們看到先把定義文件解析爲DOM對象,然後進行具體的註冊過程:
  

Java代碼 

346 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {   

347     // 這裏定義解析器,使用XmlBeanDefinitionParser來解析xml方式的bean定義文件 現在的版本不用這個解析器了,使用的是XmlBeanDefinitionReader  

348     if (this.parserClass != null) {   

349         XmlBeanDefinitionParser parser =   

350                 (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);   

351         return parser.registerBeanDefinitions(this, doc, resource);   

352     }   

353     // 具體的註冊過程,首先得到XmlBeanDefinitionReader,來處理xmlbean定義文件  

354     BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();   

355     int countBefore = getBeanFactory().getBeanDefinitionCount();   

356     documentReader.registerBeanDefinitions(doc, createReaderContext(resource));   

357     return getBeanFactory().getBeanDefinitionCount() - countBefore;   

358 }  

359 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {  

360     // 這裏定義解析器,使用XmlBeanDefinitionParser來解析xml方式的bean定義文件 現在的版本不用這個解析器了,使用的是XmlBeanDefinitionReader  

361     if (this.parserClass != null) {  

362         XmlBeanDefinitionParser parser =  

363                 (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);  

364         return parser.registerBeanDefinitions(this, doc, resource);  

365     }  

366     // 具體的註冊過程,首先得到XmlBeanDefinitionReader,來處理xmlbean定義文件  

367     BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  

368     int countBefore = getBeanFactory().getBeanDefinitionCount();  

369     documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  

370     return getBeanFactory().getBeanDefinitionCount() - countBefore;  

371 }  

具體的在BeanDefinitionDocumentReader中完成對,下面是一個簡要的註冊過程來完成bean定義文件的解析和IOC容器中bean的初始化
  

Java代碼 

372 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {   

373     this.readerContext = readerContext;   

374   

375     logger.debug("Loading bean definitions");   

376     Element root = doc.getDocumentElement();   

377   

378     BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);   

379   

380     preProcessXml(root);   

381     parseBeanDefinitions(root, delegate);   

382     postProcessXml(root);   

383 }   

384   

385 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {   

386     if (delegate.isDefaultNamespace(root.getNamespaceURI())) {   

387         //這裏得到xml文件的子節點,比如各個bean節點           

388         NodeList nl = root.getChildNodes();   

389   

390         //這裏對每個節點進行分析處理  

391         for (int i = 0; i < nl.getLength(); i++) {   

392             Node node = nl.item(i);   

393             if (node instanceof Element) {   

394                 Element ele = (Element) node;   

395                 String namespaceUri = ele.getNamespaceURI();   

396                 if (delegate.isDefaultNamespace(namespaceUri)) {   

397                     //這裏是解析過程的調用,對缺省的元素進行分析比如bean元素  

398                     parseDefaultElement(ele, delegate);   

399                 }   

400                 else {   

401                     delegate.parseCustomElement(ele);   

402                 }   

403             }   

404         }   

405     } else {   

406         delegate.parseCustomElement(root);   

407     }   

408 }   

409   

410 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {   

411     //這裏對元素Import進行處理  

412     if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) {   

413         importBeanDefinitionResource(ele);   

414     }   

415     else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) {   

416         String name = ele.getAttribute(NAME_ATTRIBUTE);   

417         String alias = ele.getAttribute(ALIAS_ATTRIBUTE);   

418         getReaderContext().getReader().getBeanFactory().registerAlias(name, alias);   

419         getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));   

420     }   

421     //這裏對我們最熟悉的bean元素進行處理  

422     else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {   

423         //委託給BeanDefinitionParserDelegate來完成對bean元素的處理,這個類包含了具體的bean解析的過程。  

424         // 把解析bean文件得到的信息放到BeanDefinition裏,他是bean信息的主要載體,也是IOC容器的管理對象。  

425         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);   

426         if (bdHolder != null) {   

427             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);   

428             // 這裏是向IOC容器註冊,實際上是放到IOC容器的一個map  

429             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());   

430   

431             // 這裏向IOC容器發送事件,表示解析和註冊完成。  

432             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));   

433         }   

434     }   

435 }  

436 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {  

437     this.readerContext = readerContext;  

438   

439     logger.debug("Loading bean definitions");  

440     Element root = doc.getDocumentElement();  

441   

442     BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);  

443   

444     preProcessXml(root);  

445     parseBeanDefinitions(root, delegate);  

446     postProcessXml(root);  

447 }  

448   

449 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {  

450     if (delegate.isDefaultNamespace(root.getNamespaceURI())) {  

451         //這裏得到xml文件的子節點,比如各個bean節點  

452         NodeList nl = root.getChildNodes();  

453   

454         //這裏對每個節點進行分析處理  

455         for (int i = 0; i < nl.getLength(); i++) {  

456             Node node = nl.item(i);  

457             if (node instanceof Element) {  

458                 Element ele = (Element) node;  

459                 String namespaceUri = ele.getNamespaceURI();  

460                 if (delegate.isDefaultNamespace(namespaceUri)) {  

461                     //這裏是解析過程的調用,對缺省的元素進行分析比如bean元素  

462                     parseDefaultElement(ele, delegate);  

463                 }  

464                 else {  

465                     delegate.parseCustomElement(ele);  

466                 }  

467             }  

468         }  

469     } else {  

470         delegate.parseCustomElement(root);  

471     }  

472 }  

473   

474 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {  

475     //這裏對元素Import進行處理  

476     if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) {  

477         importBeanDefinitionResource(ele);  

478     }  

479     else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) {  

480         String name = ele.getAttribute(NAME_ATTRIBUTE);  

481         String alias = ele.getAttribute(ALIAS_ATTRIBUTE);  

482         getReaderContext().getReader().getBeanFactory().registerAlias(name, alias);  

483         getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));  

484     }  

485     //這裏對我們最熟悉的bean元素進行處理  

486     else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {  

487         //委託給BeanDefinitionParserDelegate來完成對bean元素的處理,這個類包含了具體的bean解析的過程。  

488         // 把解析bean文件得到的信息放到BeanDefinition裏,他是bean信息的主要載體,也是IOC容器的管理對象。  

489         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  

490         if (bdHolder != null) {  

491             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  

492             // 這裏是向IOC容器註冊,實際上是放到IOC容器的一個map裏  

493             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  

494   

495             // 這裏向IOC容器發送事件,表示解析和註冊完成。  

496             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  

497         }  

498     }  

499 }  

我們看到在parseBeanDefinition中對具體bean元素的解析式交給BeanDefinitionParserDelegate來完成的,下面我們看看解析完的bean是怎樣在IOC容器中註冊的:
 在BeanDefinitionReaderUtils調用的是:
  

Java代碼 

500 public static void registerBeanDefinition(   

501         BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException {   

502   

503     // 這裏得到需要註冊bean的名字;  

504     String beanName = bdHolder.getBeanName();   

505     //這是調用IOC來註冊的bean的過程,需要得到BeanDefinition  

506     beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());   

507   

508     // 別名也是可以通過IOC容器和bean聯繫起來的進行註冊  

509     String[] aliases = bdHolder.getAliases();   

510     if (aliases != null) {   

511         for (int i = 0; i < aliases.length; i++) {   

512             beanFactory.registerAlias(beanName, aliases[i]);   

513         }   

514     }   

515 }  

516 public static void registerBeanDefinition(  

517         BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException {  

518   

519     // 這裏得到需要註冊bean的名字;  

520     String beanName = bdHolder.getBeanName();  

521     //這是調用IOC來註冊的bean的過程,需要得到BeanDefinition  

522     beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());  

523   

524     // 別名也是可以通過IOC容器和bean聯繫起來的進行註冊  

525     String[] aliases = bdHolder.getAliases();  

526     if (aliases != null) {  

527         for (int i = 0; i < aliases.length; i++) {  

528             beanFactory.registerAlias(beanName, aliases[i]);  

529         }  

530     }  

531 }  

我們看看XmlBeanFactory中的註冊實現:
  

Java代碼 

532 //———————————————————————  

533 // 這裏是IOC容器對BeanDefinitionRegistry接口的實現  

534 //———————————————————————  

535   

536 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)   

537         throws BeanDefinitionStoreException {   

538   

539     …..//這裏省略了對BeanDefinition的驗證過程  

540     //先看看在容器裏是不是已經有了同名的bean,如果有拋出異常。  

541     Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);   

542     if (oldBeanDefinition != null) {   

543         if (!this.allowBeanDefinitionOverriding) {   

544         ………..   

545     }   

546     else {   

547         //bean的名字加到IOC容器中去  

548         this.beanDefinitionNames.add(beanName);   

549     }   

550     //這裏把bean的名字和Bean定義聯繫起來放到一個HashMap中去,IOC容器通過這個Map來維護容器裏的Bean定義信息。  

551     this.beanDefinitionMap.put(beanName, beanDefinition);   

552     removeSingleton(beanName);   

553 }  

554 //---------------------------------------------------------------------  

555 // 這裏是IOC容器對BeanDefinitionRegistry接口的實現  

556 //---------------------------------------------------------------------  

557   

558 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  

559         throws BeanDefinitionStoreException {  

560   

561     .....//這裏省略了對BeanDefinition的驗證過程  

562     //先看看在容器裏是不是已經有了同名的bean,如果有拋出異常。  

563     Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);  

564     if (oldBeanDefinition != null) {  

565         if (!this.allowBeanDefinitionOverriding) {  

566         ...........  

567     }  

568     else {  

569         //bean的名字加到IOC容器中去  

570         this.beanDefinitionNames.add(beanName);  

571     }  

572     //這裏把bean的名字和Bean定義聯繫起來放到一個HashMap中去,IOC容器通過這個Map來維護容器裏的Bean定義信息。  

573     this.beanDefinitionMap.put(beanName, beanDefinition);  

574     removeSingleton(beanName);  

575 }  

這樣就完成了Bean定義在IOC容器中的註冊,就可被IOC容器進行管理和使用了。
 從上面的代碼來看,我們總結一下IOC容器初始化的基本步驟:

    * 初始化的入口在容器實現中的refresh()調用來完成
     * bean 定義載入IOC容器使用的方法是loadBeanDefinition,其中的大致過程如下:通過ResourceLoader來完成資源文件位置的定 位,DefaultResourceLoader是默認的實現,同時上下文本身就給出了ResourceLoader的實現,可以從類路徑,文件系統, URL等方式來定爲資源位置。如果是XmlBeanFactory作爲IOC容器,那麼需要爲它指定bean定義的資源,也就是說bean定義文件時通過 抽象成Resource來被IOC容器處理的,容器通過BeanDefinitionReader來完成定義信息的解析和Bean信息的註冊,往往使用的 是XmlBeanDefinitionReader來解析beanxml定義文件 – 實際的處理過程是委託給BeanDefinitionParserDelegate來完成的,從而得到bean的定義信息,這些信息在Spring中使用 BeanDefinition對象來表示 – 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition這些相關的方法 – 他們都是爲處理BeanDefinitin服務的,IoC容器解析得到BeanDefinition以後,需要把它在IOC容器中註冊,這由IOC實現 BeanDefinitionRegistry接口來實現。註冊過程就是在IOC容器內部維護的一個HashMap來保存得到的 BeanDefinition的過程。這個HashMapIoC容器持有bean信息的場所,以後對bean的操作都是圍繞這個HashMap來實現 的。
     * 然後我們就可以通過BeanFactoryApplicationContext來享受到Spring IOC的服務了.
 在使用IOC容器的時候,我們注意到除了少量粘合代碼,絕大多數以正確IoC風格編寫的應用程序代碼完全不用關心如何到達工廠,因爲容器將把這些對象與容 器管理的其他對象鉤在一起。基本的策略是把工廠放到已知的地方,最好是放在對預期使用的上下文有意義的地方,以及代碼將實際需要訪問工廠的地方。 Spring本身提供了對聲明式載入web應用程序用法的應用程序上下文,並將其存儲在ServletContext中的框架實現。具體可以參見以後的文 章。
 在使用Spring IOC容器的時候我們還需要區別兩個概念:
 Beanfactory Factory bean,其中BeanFactory指的是IOC容器的編程抽象,比如ApplicationContext, XmlBeanFactory等,這些都是IOC容器的具體表現,需要使用什麼樣的容器由客戶決定但Spring爲我們提供了豐富的選擇。而 FactoryBean只是一個可以在IOC容器中被管理的一個bean,是對各種處理過程和資源使用的抽象,Factory bean在需要時產生另一個對象,而不返回FactoryBean本省,我們可以把它看成是一個抽象工廠,對它的調用返回的是工廠生產的產品。所有的 Factory bean都實現特殊的org.springframework.beans.factory.FactoryBean接口,當使用容器中factory bean的時候,該容器不會返回factory bean本身,而是返回其生成的對象。Spring包括了大部分的通用資源和服務訪問抽象的Factory bean的實現,其中包括:
 對JNDI查詢的處理,對代理對象的處理,對事務性代理的處理,對RMI代理的處理等,這些我們都可以看成是具體的工廠,看成是SPRING爲我們建立好 的工廠。也就是說Spring通過使用抽象工廠模式爲我們準備了一系列工廠來生產一些特定的對象,免除我們手工重複的工作,我們要使用時只需要在IOC容 器裏配置好就能很方便的使用了。
 現在我們來看看在Spring的事件機制,Spring中有3個標準事件,ContextRefreshEvent, ContextCloseEvent,RequestHandledEvent他們通過ApplicationEvent接口,同樣的如果需要自定義時間 也只需要實現ApplicationEvent接口,參照ContextCloseEvent的實現可以定製自己的事件實現:
  

Java代碼 

576 public class ContextClosedEvent extends ApplicationEvent {   

577   

578     public ContextClosedEvent(ApplicationContext source) {   

579         super(source);   

580     }   

581   

582     public ApplicationContext getApplicationContext() {   

583         return (ApplicationContext) getSource();   

584     }   

585 }  

586 public class ContextClosedEvent extends ApplicationEvent {  

587   

588     public ContextClosedEvent(ApplicationContext source) {  

589         super(source);  

590     }  

591   

592     public ApplicationContext getApplicationContext() {  

593         return (ApplicationContext) getSource();  

594     }  

595 }  

可以通過顯現ApplicationEventPublishAware接口,將事件發佈器耦合到ApplicationContext這樣可以使用 ApplicationContext框架來傳遞和消費消息,然後在ApplicationContext中配置好bean就可以了,在消費消息的過程 中,接受者通過實現ApplicationListener接收消息。
 比如可以直接使用SpringScheduleTimerTaskTimerFactoryBean作爲定時器定時產生消息,具體可以參見《Spring框架高級編程》。
 TimerFactoryBean是一個工廠bean,對其中的ScheduleTimerTask進行處理後輸出,參考ScheduleTimerTask的實現發現它最後調用的是jreTimerTask
  

Java代碼 

596 public void setRunnable(Runnable timerTask) {   

597     this.timerTask = new DelegatingTimerTask(timerTask);   

598 }  

599 public void setRunnable(Runnable timerTask) {  

600     this.timerTask = new DelegatingTimerTask(timerTask);  

601 }  

在書中給出了一個定時發送消息的例子,當然可以可以通過定時器作其他的動作,有兩種方法:
 1.定義MethodInvokingTimerTaskFactoryBean定義要執行的特定bean的特定方法,對需要做什麼進行封裝定義;
 2.定義TimerTask類,通過extends TimerTask來得到,同時對需要做什麼進行自定義
 然後需要定義具體的定時器參數,通過配置ScheduledTimerTask中的參數和timerTask來完成,以下是它需要定義的具體屬性,timerTask是在前面已經定義好的bean
  

Java代碼 

602 private TimerTask timerTask;   

603   

604 private long delay = 0;   

605   

606 private long period = 0;   

607   

608 private boolean fixedRate = false;  

609 private TimerTask timerTask;  

610   

611 private long delay = 0;  

612   

613 private long period = 0;  

614   

615 private boolean fixedRate = false;  

最後,需要在ApplicationContext中註冊,需要把ScheduledTimerTask配置到FactoryBean – TimerFactoryBean,這樣就由IOC容器來管理定時器了。參照
 TimerFactoryBean的屬性,可以定製一組定時器。
  

Java代碼 

616 public class TimerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {   

617   

618     protected final Log logger = LogFactory.getLog(getClass());   

619   

620     private ScheduledTimerTask[] scheduledTimerTasks;   

621   

622     private boolean daemon = false;   

623   

624     private Timer timer;   

625   

626     ………..   

627 }  

628 public class TimerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {  

629   

630     protected final Log logger = LogFactory.getLog(getClass());  

631   

632     private ScheduledTimerTask[] scheduledTimerTasks;  

633   

634     private boolean daemon = false;  

635   

636     private Timer timer;  

637   

638     ...........  

639 }  

如果要發送時間我們只需要在定義好的ScheduledTimerTaskspublish定義好的事件就可以了。具體可以參考書中例子的實現,這裏只 是結合FactoryBean的原理做一些解釋。如果結合事件和定時器機制,我們可以很方便的實現heartbeat(看門狗),書中給出了這個例子,這 個例子實際上結合了Spring事件和定時機制的使用兩個方面的知識 – 當然了還有IOC容器的知識(任何Spring應用我想都逃不掉IOC的魔爪:)

上面我們分析了IOC容器本身的實現,下面我們看看在典型的web環境中,Spring IOC容器是怎樣被載入和起作用的。
 簡單的說,在web容器中,通過ServletContextSpringIOC容器提供宿主環境,對應的建立起一個IOC容器的體系。其中,首先需 要建立的是根上下文,這個上下文持有的對象可以有業務對象,數據存取對象,資源,事物管理器等各種中間層對象。在這個上下文的基礎上,和web MVC相關還會有一個上下文來保存控制器之類的MVC對象,這樣就構成了一個層次化的上下文結構。在web容器中啓動Spring應用程序就是一個建立這 個上下文體系的過程。Springweb應用提供了上下文的擴展接口
 WebApplicationContext:
  

Java代碼 

public interface WebApplicationContext extends ApplicationContext {   

    //這裏定義的常量用於在ServletContext中存取根上下文  

    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";   

    ……   

    //WebApplicationContext來說,需要得到Web容器的ServletContext  

    ServletContext getServletContext();   

}  

public interface WebApplicationContext extends ApplicationContext {  

    //這裏定義的常量用於在ServletContext中存取根上下文  

10     String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";  

11     ......  

12     //WebApplicationContext來說,需要得到Web容器的ServletContext  

13     ServletContext getServletContext();  

14 }  

而一般的啓動過程,Spring會使用一個默認的實現,XmlWebApplicationContext – 這個上下文實現作爲在web容器中的根上下文容器被建立起來,具體的建立過程在下面我們會詳細分析。
  

Java代碼 

15 public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {   

16   

17     /** 這是和web部署相關的位置信息,用來作爲默認的根上下文bean定義信息的存放位置*/  

18     public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";   

19     public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";   

20     public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";   

21       

22     //我們又看到了熟悉的loadBeanDefinition,就像我們前面對IOC容器的分析中一樣,這個加載工程在容器的refresh()的時候啓動。  

23     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {   

24         //對於XmlWebApplicationContext,當然使用的是XmlBeanDefinitionReader來對bean定義信息來進行解析  

25         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);   

26   

27         beanDefinitionReader.setResourceLoader(this);   

28         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));   

29   

30         initBeanDefinitionReader(beanDefinitionReader);   

31         loadBeanDefinitions(beanDefinitionReader);   

32     }   

33   

34     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {   

35     }   

36     //使用XmlBeanDefinitionReader來讀入bean定義信息  

37     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {   

38         String[] configLocations = getConfigLocations();   

39         if (configLocations != null) {   

40             for (int i = 0; i < configLocations.length; i++) {   

41                 reader.loadBeanDefinitions(configLocations[i]);   

42             }   

43         }   

44     }   

45     //這裏取得bean定義信息位置,默認的地方是/WEB-INF/applicationContext.xml  

46     protected String[] getDefaultConfigLocations() {   

47         if (getNamespace() != null) {   

48             return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};   

49         }   

50         else {   

51             return new String[] {DEFAULT_CONFIG_LOCATION};   

52         }   

53     }   

54 }  

55 public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {  

56   

57     /** 這是和web部署相關的位置信息,用來作爲默認的根上下文bean定義信息的存放位置*/  

58     public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";  

59     public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";  

60     public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";  

61   

62     //我們又看到了熟悉的loadBeanDefinition,就像我們前面對IOC容器的分析中一樣,這個加載工程在容器的refresh()的時候啓動。  

63     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  

64         //對於XmlWebApplicationContext,當然使用的是XmlBeanDefinitionReader來對bean定義信息來進行解析  

65         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  

66   

67         beanDefinitionReader.setResourceLoader(this);  

68         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  

69   

70         initBeanDefinitionReader(beanDefinitionReader);  

71         loadBeanDefinitions(beanDefinitionReader);  

72     }  

73   

74     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  

75     }  

76     //使用XmlBeanDefinitionReader來讀入bean定義信息  

77     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  

78         String[] configLocations = getConfigLocations();  

79         if (configLocations != null) {  

80             for (int i = 0; i < configLocations.length; i++) {  

81                 reader.loadBeanDefinitions(configLocations[i]);  

82             }  

83         }  

84     }  

85     //這裏取得bean定義信息位置,默認的地方是/WEB-INF/applicationContext.xml  

86     protected String[] getDefaultConfigLocations() {  

87         if (getNamespace() != null) {  

88             return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};  

89         }  

90         else {  

91             return new String[] {DEFAULT_CONFIG_LOCATION};  

92         }  

93     }  

94 }  

對於一個Spring激活的web應用程序,可以通過使用Spring代碼聲明式的指定在web應用程序啓動時載入應用程序上下文 (WebApplicationContext),SpringContextLoader是提供這樣性能的類,我們可以使用 ContextLoaderServlet或者ContextLoaderListener的啓動時載入的Servlet來實例化Spring IOC容器 – 爲什麼會有兩個不同的類來裝載它呢,這是因爲它們的使用需要區別不同的Servlet容器支持的Serlvet版本。但不管是 ContextLoaderSevlet還是 ContextLoaderListener都使用ContextLoader來完成實際的WebApplicationContext的初始化工作。這 個ContextLoder就像是Spring Web應用程序在Web容器中的加載器booter。當然這些Servlet的具體使用我們都要藉助web容器中的部署描述符來進行相關的定義。
 下面我們使用ContextLoaderListener作爲載入器作一個詳細的分析,這個Servlet的監聽器是根上下文被載入的地方,也是整個 Spring web應用加載上下文的第一個地方;從加載過程我們可以看到,首先從Servlet事件中得到ServletContext,然後可以讀到 配置好的在web.xml的中的各個屬性值,然後ContextLoder實例化WebApplicationContext並完成其載入和初始化作爲根 上下文。當這個根上下文被載入後,它被綁定到web應用程序的ServletContext上。任何需要訪問該ApplicationContext的應 用程序代碼都可以從WebApplicationContextUtils類的靜態方法來得到:
  

Java代碼 

95 WebApplicationContext getWebApplicationContext(ServletContext sc)  

96 WebApplicationContext getWebApplicationContext(ServletContext sc)  

Tomcat作爲Servlet容器爲例,下面是具體的步驟:
 1.Tomcat 啓動時需要從web.xml中讀取啓動參數,在web.xml中我們需要對ContextLoaderListener進行配置,對於在web應用啓動入 口是在ContextLoaderListener中的初始化部分;從Spring MVC上看,實際上在web容器中維護了一系列的IOC容器,其中在ContextLoader中載入的IOC容器作爲根上下文而存在於 ServletContext中。
  

Java代碼 

97 //這裏對根上下文進行初始化。  

98 public void contextInitialized(ServletContextEvent event) {   

99     //這裏創建需要的ContextLoader  

100     this.contextLoader = createContextLoader();   

101     //這裏使用ContextLoader對根上下文進行載入和初始化  

102     this.contextLoader.initWebApplicationContext(event.getServletContext());   

103 }  

104 //這裏對根上下文進行初始化。  

105 public void contextInitialized(ServletContextEvent event) {  

106     //這裏創建需要的ContextLoader  

107     this.contextLoader = createContextLoader();  

108     //這裏使用ContextLoader對根上下文進行載入和初始化  

109     this.contextLoader.initWebApplicationContext(event.getServletContext());  

110 }  

通過ContextLoader建立起根上下文的過程,我們可以在ContextLoader中看到:
  

Java代碼 

111 public WebApplicationContext initWebApplicationContext(ServletContext servletContext)   

112         throws IllegalStateException, BeansException {   

113     //這裏先看看是不是已經在ServletContext中存在上下文,如果有說明前面已經被載入過,或者是配置文件有錯誤。  

114     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {   

115     //直接拋出異常  

116     ………   

117     }   

118      

119     ……………   

120     try {   

121         // 這裏載入根上下文的父上下文  

122         ApplicationContext parent = loadParentContext(servletContext);   

123   

124         //這裏創建根上下文作爲整個應用的上下文同時把它存到ServletContext中去,注意這裏使用的ServletContext的屬性值是  

125         //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以後的應用都是根據這個屬性值來取得根上下文的 往往作爲自己上下文的父上下文  

126         this.context = createWebApplicationContext(servletContext, parent);   

127         servletContext.setAttribute(   

128                 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);   

129         ……….   

130   

131         return this.context;   

132     }   

133        …………   

134 }  

135 public WebApplicationContext initWebApplicationContext(ServletContext servletContext)  

136         throws IllegalStateException, BeansException {  

137     //這裏先看看是不是已經在ServletContext中存在上下文,如果有說明前面已經被載入過,或者是配置文件有錯誤。  

138     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  

139     //直接拋出異常  

140     .........  

141     }  

142   

143     ...............  

144     try {  

145         // 這裏載入根上下文的父上下文  

146         ApplicationContext parent = loadParentContext(servletContext);  

147   

148         //這裏創建根上下文作爲整個應用的上下文同時把它存到ServletContext中去,注意這裏使用的ServletContext的屬性值是  

149         //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以後的應用都是根據這個屬性值來取得根上下文的 往往作爲自己上下文的父上下文  

150         this.context = createWebApplicationContext(servletContext, parent);  

151         servletContext.setAttribute(  

152                 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  

153         ..........  

154   

155         return this.context;  

156     }  

157        ............  

158 }  

建立根上下文的父上下文使用的是下面的代碼,取決於在web.xml中定義的參數:locatorFactorySelector,這是一個可選參數:
  

Java代碼 

159 protected ApplicationContext loadParentContext(ServletContext servletContext)   

160         throws BeansException {   

161   

162     ApplicationContext parentContext = null;   

163   

164     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);   

165     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);   

166   

167     if (locatorFactorySelector != null) {   

168         BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);   

169         ……..   

170         //得到根上下文的父上下文的引用  

171         this.parentContextRef = locator.useBeanFactory(parentContextKey);   

172         //這裏建立得到根上下文的父上下文  

173         parentContext = (ApplicationContext) this.parentContextRef.getFactory();   

174     }   

175   

176     return parentContext;   

177 }  

178 protected ApplicationContext loadParentContext(ServletContext servletContext)  

179         throws BeansException {  

180   

181     ApplicationContext parentContext = null;  

182   

183     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);  

184     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);  

185   

186     if (locatorFactorySelector != null) {  

187         BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);  

188         ........  

189         //得到根上下文的父上下文的引用  

190         this.parentContextRef = <a href="http://locator.us" title="http://locator.us" target="_blank">locator.us</a>eBeanFactory(parentContextKey);  

191         //這裏建立得到根上下文的父上下文  

192         parentContext = (ApplicationContext) this.parentContextRef.getFactory();  

193     }  

194   

195     return parentContext;  

196 }  

得到根上下文的父上下文以後,就是根上下文的創建過程:
  

Java代碼 

197 protected WebApplicationContext createWebApplicationContext(   

198         ServletContext servletContext, ApplicationContext parent) throws BeansException {   

199     //這裏需要確定我們載入的根WebApplication的類型,由在web.xml中配置的contextClass中配置的參數可以決定我們需要載入什麼樣的ApplicationContext,  

200     //如果沒有使用默認的。  

201     Class contextClass = determineContextClass(servletContext);   

202     ………   

203     //這裏就是上下文的創建過程  

204     ConfigurableWebApplicationContext wac =   

205             (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);   

206     //這裏保持對父上下文和ServletContext的引用到根上下文中  

207     wac.setParent(parent);   

208     wac.setServletContext(servletContext);   

209   

210     //這裏從web.xml中取得相關的初始化參數  

211     String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);   

212     if (configLocation != null) {   

213         wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,   

214                 ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));   

215     }   

216    //這裏對WebApplicationContext進行初始化,我們又看到了熟悉的refresh調用。  

217     wac.refresh();   

218     return wac;   

219 }  

220 protected WebApplicationContext createWebApplicationContext(  

221         ServletContext servletContext, ApplicationContext parent) throws BeansException {  

222     //這裏需要確定我們載入的根WebApplication的類型,由在web.xml中配置的contextClass中配置的參數可以決定我們需要載入什麼樣的ApplicationContext,  

223     //如果沒有使用默認的。  

224     Class contextClass = determineContextClass(servletContext);  

225     .........  

226     //這裏就是上下文的創建過程  

227     ConfigurableWebApplicationContext wac =  

228             (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  

229     //這裏保持對父上下文和ServletContext的引用到根上下文中  

230     wac.setParent(parent);  

231     wac.setServletContext(servletContext);  

232   

233     //這裏從web.xml中取得相關的初始化參數  

234     String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);  

235     if (configLocation != null) {  

236         wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,  

237                 ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));  

238     }  

239    //這裏對WebApplicationContext進行初始化,我們又看到了熟悉的refresh調用。  

240     wac.refresh();  

241     return wac;  

242 }  

初始化根ApplicationContext後將其存儲到SevletContext中去以後,這樣就建立了一個全局的關於整個應用的上下文。這個根上 下文會被以後的DispatcherServlet初始化自己的時候作爲自己ApplicationContext的父上下文。這個在對 DispatcherServlet做分析的時候我們可以看看到。

3.完成對ContextLoaderListener的初始化以後, Tomcat開始初始化DispatchServlet還記得我們在web.xml中隊載入次序進行了定義。DispatcherServlet會建立自己的ApplicationContext,同時建立這 個自己的上下文的時候會從ServletContext中得到根上下文作爲父上下文,然後再對自己的上下文進行初始化,並最後存到 ServletContext中去供以後檢索和使用。
 可以從DispatchServlet的父類FrameworkServlet的代碼中看到大致的初始化過程,整個ApplicationContext的創建過程和ContextLoder創建的過程相類似:
  

Java代碼 

243 protected final void initServletBean() throws ServletException, BeansException {   

244     ………   

245     try {   

246         //這裏是對上下文的初始化過程。  

247         this.webApplicationContext = initWebApplicationContext();   

248         //在完成對上下文的初始化過程結束後,根據bean配置信息建立MVC框架的各個主要元素  

249         initFrameworkServlet();   

250     }   

251    ……..   

252 }  

253 protected final void initServletBean() throws ServletException, BeansException {  

254     .........  

255     try {  

256         //這裏是對上下文的初始化過程。  

257         this.webApplicationContext = initWebApplicationContext();  

258         //在完成對上下文的初始化過程結束後,根據bean配置信息建立MVC框架的各個主要元素  

259         initFrameworkServlet();  

260     }  

261    ........  

262 }  

initWebApplicationContext()調用的代碼如下:
  

Java代碼 

263 protected WebApplicationContext initWebApplicationContext() throws BeansException {   

264     //這裏調用WebApplicationContextUtils靜態類來得到根上下文  

265     WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());   

266       

267     //創建當前DispatcherServlet的上下文,其上下文種類使用默認的在FrameworkServlet定義好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;  

268     WebApplicationContext wac = createWebApplicationContext(parent);   

269     ……..   

270     if (isPublishContext()) {   

271         //把當前建立的上下文存到ServletContext中去,注意使用的屬性名是和當前Servlet名相關的。  

272         String attrName = getServletContextAttributeName();   

273         getServletContext().setAttribute(attrName, wac);   

274     }   

275     return wac;   

276 }  

277 protected WebApplicationContext initWebApplicationContext() throws BeansException {  

278     //這裏調用WebApplicationContextUtils靜態類來得到根上下文  

279     WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());  

280   

281     //創建當前DispatcherServlet的上下文,其上下文種類使用默認的在FrameworkServlet定義好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;  

282     WebApplicationContext wac = createWebApplicationContext(parent);  

283     ........  

284     if (isPublishContext()) {  

285         //把當前建立的上下文存到ServletContext中去,注意使用的屬性名是和當前Servlet名相關的。  

286         String attrName = getServletContextAttributeName();  

287         getServletContext().setAttribute(attrName, wac);  

288     }  

289     return wac;  

290 }  

其中我們看到調用了WebApplicationContextUtils的靜態方法得到根ApplicationContext:
  

Java代碼 

291     public static WebApplicationContext getWebApplicationContext(ServletContext sc) {   

292         //很簡單,直接從ServletContext中通過屬性名得到根上下文  

293         Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);   

294         …….   

295         return (WebApplicationContext) attr;   

296     }   

297 然後創建DispatcherServlet自己的WebApplicationContext:   

298     protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)   

299             throws BeansException {   

300         …….   

301         //這裏使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定義好的DEFAULT_CONTEXT_CLASS =                             

302         //XmlWebApplicationContext.class;  

303         ConfigurableWebApplicationContext wac =   

304                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());   

305   

306         //這裏配置父上下文,就是在ContextLoader中建立的根上下文  

307         wac.setParent(parent);   

308   

309         //保留ServletContext的引用和相關的配置信息。  

310         wac.setServletContext(getServletContext());   

311         wac.setServletConfig(getServletConfig());   

312         wac.setNamespace(getNamespace());   

313   

314         //這裏得到ApplicationContext配置文件的位置  

315         if (getContextConfigLocation() != null) {   

316             wac.setConfigLocations(   

317                 StringUtils.tokenizeToStringArray(   

318                             getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));   

319         }   

320           

321         //這裏調用ApplicationContext的初始化過程,同樣需要使用refresh()  

322         wac.refresh();   

323         return wac;   

324     }  

325     public static WebApplicationContext getWebApplicationContext(ServletContext sc) {  

326         //很簡單,直接從ServletContext中通過屬性名得到根上下文  

327         Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  

328         .......  

329         return (WebApplicationContext) attr;  

330     }  

331 然後創建DispatcherServlet自己的WebApplicationContext:  

332     protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)  

333             throws BeansException {  

334         .......  

335         //這裏使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定義好的DEFAULT_CONTEXT_CLASS =  

336         //XmlWebApplicationContext.class;  

337         ConfigurableWebApplicationContext wac =  

338                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass());  

339   

340         //這裏配置父上下文,就是在ContextLoader中建立的根上下文  

341         wac.setParent(parent);  

342   

343         //保留ServletContext的引用和相關的配置信息。  

344         wac.setServletContext(getServletContext());  

345         wac.setServletConfig(getServletConfig());  

346         wac.setNamespace(getNamespace());  

347   

348         //這裏得到ApplicationContext配置文件的位置  

349         if (getContextConfigLocation() != null) {  

350             wac.setConfigLocations(  

351                 StringUtils.tokenizeToStringArray(  

352                             getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));  

353         }  

354   

355         //這裏調用ApplicationContext的初始化過程,同樣需要使用refresh()  

356         wac.refresh();  

357         return wac;  

358     }  

4. 然後就是DispatchServlet中對Spring MVC的配置過程,首先對配置文件中的定義元素進行配置 – 請注意這個時候我們的WebApplicationContext已經建立起來了,也意味着DispatcherServlet有自己的定義資源,可以需 要從web.xml中讀取bean的配置信息,通常我們會使用單獨的xml文件來配置MVC中各個要素定義,這裏和web容器相關的加載過程實際上已經完 成了,下面的處理和普通的Spring應用程序的編寫沒有什麼太大的差別,我們先看看MVC的初始化過程:
  

Java代碼 

359 protected void initFrameworkServlet() throws ServletException, BeansException {   

360     initMultipartResolver();   

361     initLocaleResolver();   

362     initThemeResolver();   

363     initHandlerMappings();   

364     initHandlerAdapters();   

365     initHandlerExceptionResolvers();   

366     initRequestToViewNameTranslator();   

367     initViewResolvers();   

368 }  

369 protected void initFrameworkServlet() throws ServletException, BeansException {  

370     initMultipartResolver();  

371     initLocaleResolver();  

372     initThemeResolver();  

373     initHandlerMappings();  

374     initHandlerAdapters();  

375     initHandlerExceptionResolvers();  

376     initRequestToViewNameTranslator();  

377     initViewResolvers();  

378 }  

5. 這樣MVC的框架就建立起來了,DispatchServlet對接受到的HTTP Request進行分發處理由doService()完成,具體的MVC處理過程我們在doDispatch()中完成,其中包括使用Command模式 建立執行鏈,顯示模型數據等,這些處理我們都可以在DispatcherServlet的代碼中看到:
  

Java代碼 

379 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {   

380     ……   

381     try {   

382         doDispatch(request, response);   

383     }   

384    …….   

385 }  

386 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  

387     ......  

388     try {  

389         doDispatch(request, response);  

390     }  

391    .......  

392 }  

實際的請求分發由doDispatch(request,response)來完成:
  

Java代碼 

393 protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {   

394      …….   

395      // 這是Spring定義的執行鏈,裏面放了映射關係對應的handler和定義的相關攔截器。  

396      HandlerExecutionChain mappedHandler = null;   

397       

398       ……   

399       try {   

400           //我們熟悉的ModelAndView在這裏出現了。  

401           ModelAndView mv = null;   

402           try {   

403               processedRequest = checkMultipart(request);   

404   

405               //這裏更具request中的參數和映射關係定義決定使用的handler  

406               mappedHandler = getHandler(processedRequest, false);   

407   

408               ……   

409               //這裏是handler的調用過程,類似於Command模式中的execute.  

410               HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());   

411               mv = ha.handle(processedRequest, response, mappedHandler.getHandler());   

412   

413               …….   

414           //這裏將模型數據通過視圖進行展現  

415           if (mv != null && !mv.wasCleared()) {   

416               render(mv, processedRequest, response);   

417           }   

418             ……..   

419   }  

420 protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {  

421      .......  

422      // 這是Spring定義的執行鏈,裏面放了映射關係對應的handler和定義的相關攔截器。  

423      HandlerExecutionChain mappedHandler = null;  

424   

425       ......  

426       try {  

427           //我們熟悉的ModelAndView在這裏出現了。  

428           ModelAndView mv = null;  

429           try {  

430               processedRequest = checkMultipart(request);  

431   

432               //這裏更具request中的參數和映射關係定義決定使用的handler  

433               mappedHandler = getHandler(processedRequest, false);  

434   

435               ......  

436               //這裏是handler的調用過程,類似於Command模式中的execute.  

437               HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  

438               mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

439   

440               .......  

441           //這裏將模型數據通過視圖進行展現  

442           if (mv != null && !mv.wasCleared()) {  

443               render(mv, processedRequest, response);  

444           }  

445             ........  

446   }  

這樣具體的MVC模型的實現就由bean配置文件裏定義好的view resolver,handler這些類來實現用戶代碼的功能。
 總結上面的過程,我們看到在web容器中,ServletContext可以持有一系列的web上下文,而在整個web上下文中存在一個根上下文來作爲其 它 Servlet上下文的父上下文。這個根上下文是由ContextLoader載入並進行初始化的,對於我們的web應用, DispatcherSerlvet載入並初始化自己的上下文,這個上下文的父上下文是根上下文,並且我們也能從ServletContext中根據 Servlet的名字來檢索到我們需要的對應於這個Servlet的上下文,但是根上下文的名字是由Spring唯一確定的。這個 DispactcherServlet建立的上下文就是我們開發Spring MVC應用的IOC容器。
 具體的web請求處理在上下文體系建立完成以後由DispactcherServlet來完成,上面對MVC的運作做了一個大致的描述,下面我們會具體就SpringMVC的框架實現作一個詳細的分析。

Spring中,JdbcTemplate是經常被使用的類來幫助用戶程序操作數據庫,在JdbcTemplate爲用戶程序提供了許多便利的數 據庫操作方法,比如查詢,更新等,而且在Spring中,有許多類似 JdbcTemplate的模板,比如HibernateTemplate等等 – 看來這是Rod.Johnson的慣用手法,一般而言這種Template中都是通過回調函數CallBack類的使用來完成功能的,客戶需要在回調接口 中實現自己需要的定製行爲,比如使用客戶想要用的SQL語句等。不過往往Spring通過這種回調函數的實現已經爲我們提供了許多現成的方法供客戶使用。 一般來說回調函數的用法採用匿名類的方式來實現,比如:
  

Java代碼 

JdbcTemplate = new JdbcTemplate(datasource);   

jdbcTemplate.execute(new CallBack(){   

            public CallbackInterfacedoInAction(){   

               ……   

               //用戶定義的代碼或者說Spring替我們實現的代碼  

            }   

}  

JdbcTemplate = new JdbcTemplate(datasource);  

jdbcTemplate.execute(new CallBack(){  

10             public CallbackInterfacedoInAction(){  

11                ......  

12                //用戶定義的代碼或者說Spring替我們實現的代碼  

13             }  

14 }  

在模板中嵌入的是需要客戶化的代碼,由Spring來作或者需要客戶程序親自動手完成。下面讓我們具體看看在JdbcTemplate中的代碼是怎樣完成 使命的,我們舉JdbcTemplate.execute()爲例,這個方法是在JdbcTemplate中被其他方法調用的基本方法之一,客戶程序往往 用這個方法來執行基本的SQL語句:
  

Java代碼 

15 public Object execute(ConnectionCallback action) throws DataAccessException {   

16     //這裏得到數據庫聯接  

17     Connection con = DataSourceUtils.getConnection(getDataSource());   

18     try {   

19         Connection conToUse = con;   

20         //有些特殊的數據庫,需要我們使用特別的方法取得datasource  

21         if (this.nativeJdbcExtractor != null) {   

22             // Extract native JDBC Connection, castable to OracleConnection or the like.  

23             conToUse = this.nativeJdbcExtractor.getNativeConnection(con);   

24         }   

25         else {   

26             // Create close-suppressing Connection proxy, also preparing returned Statements.  

27             conToUse = createConnectionProxy(con);   

28         }   

29     //這裏調用的是傳遞進來的匿名類的方法,也就是用戶程序需要實現CallBack接口的地方。  

30         return action.doInConnection(conToUse);   

31     }   

32     catch (SQLException ex) {   

33         //如果捕捉到數據庫異常,把數據庫聯接釋放,同時拋出一個經過Spring轉換過的Spring數據庫異常,  

34         //我們知道,Spring做了一個有意義的工作是把這些數據庫異常統一到自己的異常體系裏了。  

35         DataSourceUtils.releaseConnection(con, getDataSource());   

36         con = null;   

37         throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);   

38     }   

39     finally {   

40         //最後不管怎樣都會把數據庫連接釋放  

41         DataSourceUtils.releaseConnection(con, getDataSource());   

42     }   

43 }  

44 public Object execute(ConnectionCallback action) throws DataAccessException {  

45     //這裏得到數據庫聯接  

46     Connection con = DataSourceUtils.getConnection(getDataSource());  

47     try {  

48         Connection conToUse = con;  

49         //有些特殊的數據庫,需要我們使用特別的方法取得datasource  

50         if (this.nativeJdbcExtractor != null) {  

51             // Extract native JDBC Connection, castable to OracleConnection or the like.  

52             conToUse = this.nativeJdbcExtractor.getNativeConnection(con);  

53         }  

54         else {  

55             // Create close-suppressing Connection proxy, also preparing returned Statements.  

56             conToUse = createConnectionProxy(con);  

57         }  

58     //這裏調用的是傳遞進來的匿名類的方法,也就是用戶程序需要實現CallBack接口的地方。  

59         return action.doInConnection(conToUse);  

60     }  

61     catch (SQLException ex) {  

62         //如果捕捉到數據庫異常,把數據庫聯接釋放,同時拋出一個經過Spring轉換過的Spring數據庫異常,  

63         //我們知道,Spring做了一個有意義的工作是把這些數據庫異常統一到自己的異常體系裏了。  

64         DataSourceUtils.releaseConnection(con, getDataSource());  

65         con = null;  

66         throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);  

67     }  

68     finally {  

69         //最後不管怎樣都會把數據庫連接釋放  

70         DataSourceUtils.releaseConnection(con, getDataSource());  

71     }  

72 }  

對於JdbcTemplate中給出的其他方法,比如query,update,execute等的實現,我們看看query()
  

Java代碼 

73 public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)   

74         throws DataAccessException {   

75     ……….   

76     //這裏調用了我們上面看到的execute()基本方法,然而這裏的回調實現是Spring爲我們完成的查詢過程  

77     return execute(psc, new PreparedStatementCallback() {   

78         public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {   

79             //準備查詢結果集  

80             ResultSet rs = null;   

81             try {   

82             //這裏配置SQL參數  

83                 if (pss != null) {   

84                     pss.setValues(ps);   

85                 }   

86           //這裏執行的SQL查詢  

87                 rs = ps.executeQuery();   

88                 ResultSet rsToUse = rs;   

89                 if (nativeJdbcExtractor != null) {   

90                     rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);   

91                 }   

92          //返回需要的記錄集合  

93                 return rse.extractData(rsToUse);   

94             }   

95             finally {   

96         //最後關閉查詢的紀錄集,對數據庫連接的釋放在execute()中釋放,就像我們在上面分析的看到那樣。  

97                 JdbcUtils.closeResultSet(rs);   

98                 if (pss instanceof ParameterDisposer) {   

99                     ((ParameterDisposer) pss).cleanupParameters();   

100                 }   

101             }   

102         }   

103     });   

104 }  

105 public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)  

106         throws DataAccessException {  

107     ..........  

108     //這裏調用了我們上面看到的execute()基本方法,然而這裏的回調實現是Spring爲我們完成的查詢過程  

109     return execute(psc, new PreparedStatementCallback() {  

110         public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {  

111             //準備查詢結果集  

112             ResultSet rs = null;  

113             try {  

114             //這裏配置SQL參數  

115                 if (pss != null) {  

116                     pss.setValues(ps);  

117                 }  

118           //這裏執行的SQL查詢  

119                 rs = ps.executeQuery();  

120                 ResultSet rsToUse = rs;  

121                 if (nativeJdbcExtractor != null) {  

122                     rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);  

123                 }  

124          //返回需要的記錄集合  

125                 return rse.extractData(rsToUse);  

126             }  

127             finally {  

128         //最後關閉查詢的紀錄集,對數據庫連接的釋放在execute()中釋放,就像我們在上面分析的看到那樣。  

129                 JdbcUtils.closeResultSet(rs);  

130                 if (pss instanceof ParameterDisposer) {  

131                     ((ParameterDisposer) pss).cleanupParameters();  

132                 }  

133             }  

134         }  

135     });  

136 }  

輔助類DataSourceUtils來用來對數據庫連接進行管理的主要工具,比如打開和關閉數據庫連接等基本操作:
  

Java代碼 

137 public static Connection doGetConnection(DataSource dataSource) throws SQLException {   

138    //把對數據庫連接放到事務管理裏面進行管理  

139     ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);   

140     if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {   

141         conHolder.requested();   

142         if (!conHolder.hasConnection()) {   

143             logger.debug("Fetching resumed JDBC Connection from DataSource");   

144             conHolder.setConnection(dataSource.getConnection());   

145         }   

146         return conHolder.getConnection();   

147     }   

148     // 這裏得到需要的數據庫連接,在配置文件中定義好的。  

149     logger.debug("Fetching JDBC Connection from DataSource");   

150     Connection con = dataSource.getConnection();   

151   

152     if (TransactionSynchronizationManager.isSynchronizationActive()) {   

153         logger.debug("Registering transaction synchronization for JDBC Connection");   

154         // Use same Connection for further JDBC actions within the transaction.  

155         // Thread-bound object will get removed by synchronization at transaction completion.  

156         ConnectionHolder holderToUse = conHolder;   

157         if (holderToUse == null) {   

158             holderToUse = new ConnectionHolder(con);   

159         }   

160         else {   

161             holderToUse.setConnection(con);   

162         }   

163         holderToUse.requested();   

164         TransactionSynchronizationManager.registerSynchronization(   

165                 new ConnectionSynchronization(holderToUse, dataSource));   

166         holderToUse.setSynchronizedWithTransaction(true);   

167         if (holderToUse != conHolder) {   

168             TransactionSynchronizationManager.bindResource(dataSource, holderToUse);   

169         }   

170     }   

171   

172     return con;   

173 }  

174 public static Connection doGetConnection(DataSource dataSource) throws SQLException {  

175    //把對數據庫連接放到事務管理裏面進行管理  

176     ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);  

177     if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {  

178         conHolder.requested();  

179         if (!conHolder.hasConnection()) {  

180             logger.debug("Fetching resumed JDBC Connection from DataSource");  

181             conHolder.setConnection(dataSource.getConnection());  

182         }  

183         return conHolder.getConnection();  

184     }  

185     // 這裏得到需要的數據庫連接,在配置文件中定義好的。  

186     logger.debug("Fetching JDBC Connection from DataSource");  

187     Connection con = dataSource.getConnection();  

188   

189     if (TransactionSynchronizationManager.isSynchronizationActive()) {  

190         logger.debug("Registering transaction synchronization for JDBC Connection");  

191         // Use same Connection for further JDBC actions within the transaction.  

192         // Thread-bound object will get removed by synchronization at transaction completion.  

193         ConnectionHolder holderToUse = conHolder;  

194         if (holderToUse == null) {  

195             holderToUse = new ConnectionHolder(con);  

196         }  

197         else {  

198             holderToUse.setConnection(con);  

199         }  

200         holderToUse.requested();  

201         TransactionSynchronizationManager.registerSynchronization(  

202                 new ConnectionSynchronization(holderToUse, dataSource));  

203         holderToUse.setSynchronizedWithTransaction(true);  

204         if (holderToUse != conHolder) {  

205             TransactionSynchronizationManager.bindResource(dataSource, holderToUse);  

206         }  

207     }  

208   

209     return con;  

210 }  

那我們實際的DataSource對象是怎樣得到的?很清楚我們需要在上下文中進行配置:它作爲JdbcTemplate父類JdbcAccessor的屬性存在:
  

Java代碼 

211 public abstract class JdbcAccessor implements InitializingBean {   

212   

213     /** 這裏是我們依賴注入數據庫數據源的地方。 */  

214     private DataSource dataSource;   

215   

216     /** Helper to translate SQL exceptions to DataAccessExceptions */  

217     private SQLExceptionTranslator exceptionTranslator;   

218   

219     private boolean lazyInit = true;   

220   

221     ……..   

222 }  

223 public abstract class JdbcAccessor implements InitializingBean {  

224   

225     /** 這裏是我們依賴注入數據庫數據源的地方。 */  

226     private DataSource dataSource;  

227   

228     /** Helper to translate SQL exceptions to DataAccessExceptions */  

229     private SQLExceptionTranslator exceptionTranslator;  

230   

231     private boolean lazyInit = true;  

232   

233     ........  

234 }  

而對於DataSource的緩衝池實現,我們通過定義Apache Jakarta Commons DBCP或者C3P0提供的DataSource來完成,然後只要在上下文中配置好就可以使用了。從上面我們看到JdbcTemplate提供了許多簡單 查詢和更新功能,但是如果需要更高層次的抽象,以及更面向對象的方法來訪問數據庫。Spring爲我們提供了 org.springframework.jdbc.object包,這裏麪包含了SqlQuery,SqlMappingQuery, SqlUpdateStoredProcedure等類,這些類都是Spring JDBC應用程序可以使用的主要類,但我們要注意使用這些類的時候,用戶需要爲他們配置好一個JdbcTemplate作爲其基本的操作的實現。
 比如說我們使用MappingSqlQuery來將表數據直接映射到一個對象集合 – 具體可以參考書中的例子
 1.我們需要建立DataSourcesql語句並建立持有這些對象的MappingSqlQuery對象
 2.然後我們需要定義傳遞的SqlParameter,具體的實現我們在MappingSqlQuery的父類RdbmsOperation中可以找到:
  

Java代碼 

235 public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {   

236    //如果聲明已經被編譯過,則該聲明無效  

237    if (isCompiled()) {   

238        throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");   

239    }   

240    //這裏對參數值進行聲明定義  

241    this.declaredParameters.add(param);   

242 public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {  

243    //如果聲明已經被編譯過,則該聲明無效  

244    if (isCompiled()) {  

245        throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");  

246    }  

247    //這裏對參數值進行聲明定義  

248    this.declaredParameters.add(param);  

而這個declareParameters維護的是一個列表:
  

Java代碼 

249 /** List of SqlParameter objects */  

250 private List declaredParameters = new LinkedList();  

251 /** List of SqlParameter objects */  

252 private List declaredParameters = new LinkedList();  

這個列表在以後compile的過程中會被使用。
 3.然後用戶程序需要實現MappingSqlQuerymapRow接口,將具體的ResultSet數據生成我們需要的對象,這是我們迭代使用的方法。123步實際上爲我們定義好了一個迭代的基本單元作爲操作模板。
 4.在應用程序,我們直接調用execute()方法得到我們需要的對象列表,列表中的每一個對象的數據來自於執行SQL語句得到記錄集的每一條記錄,事實上執行的execute在父類SqlQuery中起作用:
  

Java代碼 

253 public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {   

254     validateNamedParameters(paramMap);   

255     Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);   

256     RowMapper rowMapper = newRowMapper(parameters, context);   

257     String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));   

258     //我們又看到了JdbcTemplate,這裏使用JdbcTemplate來完成對數據庫的查詢操作,所以我們說JdbcTemplate是基本的操作類。  

259      return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);   

260 }  

261 public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {  

262     validateNamedParameters(paramMap);  

263     Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);  

264     RowMapper rowMapper = newRowMapper(parameters, context);  

265     String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));  

266     //我們又看到了JdbcTemplate,這裏使用JdbcTemplate來完成對數據庫的查詢操作,所以我們說JdbcTemplate是基本的操作類。  

267      return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);  

268 }  

在這裏我們可以看到template模式的精彩應用和對JdbcTemplate的靈活使用。通過使用它,我們免去了手工迭代ResultSet並將其中 的數據轉化爲對象列表的重複過程。在這裏我們只需要定義SQL語句和SqlParameter – 如果需要的話,往往SQL語句就常常能夠滿足我們的要求了。這是靈活使用JdbcTemplate的一個很好的例子。
 Spring還爲其他數據庫操作提供了許多服務,比如使用SqlUpdate插入和更新數據庫,使用UpdatableSqlQuery更新ResultSet,生成主鍵,調用存儲過程等。
 書中還給出了對BLOB數據和CLOB數據進行數據庫操作的例子:
 對BLOB數據的操作通過LobHander來完成,通過調用JdbcTemplateRDBMS都可以進行操作:
 在JdbcTemplate中,具體的調用可以參考書中的例子 – 是通過以下調用起作用的:
  

Java代碼 

269 public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {   

270     return execute(new SimplePreparedStatementCreator(sql), action);   

271 }  

272 public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {  

273     return execute(new SimplePreparedStatementCreator(sql), action);  

274 }  

然後通過對實現PreparedStatementCallback接口的AbstractLobCreatingPreparedStatementCallback的回調函數來完成:
  

Java代碼 

275 public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {   

276     LobCreator lobCreator = this.lobHandler.getLobCreator();   

277     try {   

278         //這是一個模板方法,具體需要由客戶程序實現  

279         setValues(ps, lobCreator);   

280         return new Integer(ps.executeUpdate());   

281     }   

282     finally {   

283         lobCreator.close();   

284     }   

285 }   

286 //定義的需要客戶程序實現的虛函數  

287 protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)   

288         throws SQLException, DataAccessException;  

289 public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {  

290     LobCreator lobCreator = this.lobHandler.getLobCreator();  

291     try {  

292         //這是一個模板方法,具體需要由客戶程序實現  

293         setValues(ps, lobCreator);  

294         return new Integer(ps.executeUpdate());  

295     }  

296     finally {  

297         lobCreator.close();  

298     }  

299 }  

300 //定義的需要客戶程序實現的虛函數  

301 protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)  

302         throws SQLException, DataAccessException;  

而我們注意到setValues()是一個需要實現的抽象方法,應用程序通過實現setValues來定義自己的操作 – setValues中調用lobCreator.setBlobAsBinaryStrem()。讓我們看看具體的BLOB操作在LobCreator 是怎樣完成的,我們一般使用DefaultLobCreator作爲BLOB操作的驅動:
  

Java代碼 

303 public void setBlobAsBinaryStream(   

304         PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)   

305         throws SQLException {   

306     //通過JDBC來完成對BLOB數據的操作,對Oracle,Spring提供了OracleLobHandler來支持BLOB操作。  

307     ps.setBinaryStream(paramIndex, binaryStream, contentLength);   

308     ……..   

309 }  

310 public void setBlobAsBinaryStream(  

311         PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)  

312         throws SQLException {  

313     //通過JDBC來完成對BLOB數據的操作,對Oracle,Spring提供了OracleLobHandler來支持BLOB操作。  

314     ps.setBinaryStream(paramIndex, binaryStream, contentLength);  

315     ........  

316 }  

上面提到的是零零碎碎的Spring JDBC使用的例子,可以看到使用Spring JDBC可以幫助我們完成許多數據庫的操作。Spring對數據庫操作最基本的服務是通過JdbcTeamplate和他常用的回調函數來實現的,在此之 上,又提供了許多RMDB的操作來幫助我們更便利的對數據庫的數據進行操作 – 注意這裏沒有引入向Hibernate這樣的O/R方案。對這些O/R方案的支持,Spring由其他包來完成服務。
 書中還提到關於executeupdate方法之間的區別,update方法返回的是受影響的記錄數目的一個計數,並且如果傳入參數的話,使用的是 java.sql.PreparedStatement,execute方法總是使用 java.sql.Statement,不接受參數,而且他不返回受影響記錄的計數,更適合於創建和丟棄表的語句,而update方法更適合於插入,更新 和刪除操作,這也是我們在使用時需要注意的。

本文鏈接地址:Spring源代碼分析()Spring JDBC    轉載請保留,謝謝!

您還可能感興趣的文章:

· Quartz + Spring 簡單實現定時任務

· 解決hibernate中使用new Date() 造成oracle date類型時分秒精準度丟失

· 個人關於 spring rmi 配置的理解

· 實現spring的異常處理接口HandlerExceptionResolver 自定義自己的異常處理器

· spring中配置國際化消息

· Spring Security

下面我們對Spring MVC框架代碼進行分析,對於webApplicationContext的相關分析可以參見以前的文檔,我們這裏着重分析Spring Web MVC框架的實現.我們從分析DispatcherServlet入手:
  

Java代碼 

//這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化  

protected void initFrameworkServlet() throws ServletException, BeansException {   

    initMultipartResolver();   

    initLocaleResolver();   

    initThemeResolver();   

    initHandlerMappings();   

    initHandlerAdapters();   

    initHandlerExceptionResolvers();   

    initRequestToViewNameTranslator();   

10     initViewResolvers();   

11 }  

12 //這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化  

13 protected void initFrameworkServlet() throws ServletException, BeansException {  

14     initMultipartResolver();  

15     initLocaleResolver();  

16     initThemeResolver();  

17     initHandlerMappings();  

18     initHandlerAdapters();  

19     initHandlerExceptionResolvers();  

20     initRequestToViewNameTranslator();  

21     initViewResolvers();  

22 }  

看到註解我們知道,這是DispatcherSerlvet的初始化過程,它是在WebApplicationContext已經存在的情況下進行的,也 就意味着在初始化它的時候,IOC容器應該已經工作了,這也是我們在web.xml中配置Spring的時候,需要把DispatcherServlet 的 load-on-startup的屬性配置爲2的原因。
 對於具體的初始化過程,很容易理解,我們拿initHandlerMappings()來看看:
  

Java代碼 

23 private void initHandlerMappings() throws BeansException {   

24     if (this.detectAllHandlerMappings) {   

25          // 這裏找到所有在上下文中定義的HandlerMapping,同時把他們排序  

26          // 因爲在同一個上下文中可以有不止一個handlerMapping,所以我們把他們都載入到一個鏈裏進行維護和管理  

27         Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(   

28                 getWebApplicationContext(), HandlerMapping.classtruefalse);   

29         if (!matchingBeans.isEmpty()) {   

30             this.handlerMappings = new ArrayList(matchingBeans.values());   

31             // 這裏通過order屬性來對handlerMapping來在list中排序  

32             Collections.sort(this.handlerMappings, new OrderComparator());   

33         }   

34     }   

35     else {   

36         try {   

37             Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);   

38             this.handlerMappings = Collections.singletonList(hm);   

39         }   

40         catch (NoSuchBeanDefinitionException ex) {   

41             // Ignore, we'll add a default HandlerMapping later.  

42         }   

43     }   

44   

45     //如果在上下文中沒有定義的話,那麼我們使用默認的BeanNameUrlHandlerMapping  

46     if (this.handlerMappings == null) {   

47         this.handlerMappings = getDefaultStrategies(HandlerMapping.class);   

48     ……..   

49     }   

50 }  

51 private void initHandlerMappings() throws BeansException {  

52     if (this.detectAllHandlerMappings) {  

53          // 這裏找到所有在上下文中定義的HandlerMapping,同時把他們排序  

54          // 因爲在同一個上下文中可以有不止一個handlerMapping,所以我們把他們都載入到一個鏈裏進行維護和管理  

55         Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(  

56                 getWebApplicationContext(), HandlerMapping.class, true, false);  

57         if (!matchingBeans.isEmpty()) {  

58             this.handlerMappings = new ArrayList(matchingBeans.values());  

59             // 這裏通過order屬性來對handlerMapping來在list中排序  

60             Collections.sort(this.handlerMappings, new OrderComparator());  

61         }  

62     }  

63     else {  

64         try {  

65             Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  

66             this.handlerMappings = Collections.singletonList(hm);  

67         }  

68         catch (NoSuchBeanDefinitionException ex) {  

69             // Ignore, we'll add a default HandlerMapping later.  

70         }  

71     }  

72   

73     //如果在上下文中沒有定義的話,那麼我們使用默認的BeanNameUrlHandlerMapping  

74     if (this.handlerMappings == null) {  

75         this.handlerMappings = getDefaultStrategies(HandlerMapping.class);  

76     ........  

77     }  

78 }  

怎樣獲得上下文環境,可以參見我們前面的對IOC容器在web環境中加載的分析。 DispatcherServlet把定義了的所有HandlerMapping都加載了放在一個List裏待以後進行使用,這個鏈的每一個元素都是一個 handlerMapping的配置,而一般每一個handlerMapping可以持有一系列從URL請求到 Spring Controller的映射,比如SimpleUrl
 HandlerMaaping中就定義了一個map來持有這一系列的映射關係。
 DisptcherServlet通過HandlerMapping使得Web應用程序確定一個執行路徑,就像我們在HanderMapping中看到的那樣,HandlerMapping只是一個藉口:
  

Java代碼 

79 public interface HandlerMapping {   

80   public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =   

81                     Conventions.getQualifiedAttributeName(HandlerMapping.class"pathWithinHandlerMapping");   

82       //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執行鏈裏面維護handler和攔截器  

83     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;   

84 }  

85 public interface HandlerMapping {  

86   public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =  

87                     Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");  

88       //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執行鏈裏面維護handler和攔截器  

89     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;  

90 }  

他的具體實現只需要實現一個接口方法,而這個接口方法返回的是一個HandlerExecutionChain,實際上就是一個執行鏈,就像在Command模式描述的那樣,這個類很簡單,就是一個持有一個Interceptor鏈和一個Controller
  

Java代碼 

91 public class HandlerExecutionChain {   

92   

93     private Object handler;   

94   

95     private HandlerInterceptor[] interceptors;   

96       

97     ……..   

98 }  

99 public class HandlerExecutionChain {  

100   

101     private Object handler;  

102   

103     private HandlerInterceptor[] interceptors;  

104   

105     ........  

106 }  

而這些HandlerInterceptor需要我們定義HandlerMapping的時候配置好,比如對具體的 SimpleURLHandlerMapping,他要做的就是根據URL映射的方式註冊HandlerInterceptor,自己維護一個放映映射 的handlerMap,當需要匹配Http請求的時候需要使用這個表裏的信息來得到執行鏈。這個註冊的過程在IOC容器初始化 SimpleUrlHandlerMapping的時候就被完成了,這樣以後的解析纔可以用到map裏的映射信息,這裏的信息和bean文件的信息是等價 的,下面是具體的註冊過程:
  

Java代碼 

107 protected void registerHandlers(Map urlMap) throws BeansException {   

108     if (urlMap.isEmpty()) {   

109         logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");   

110     }   

111     else {   

112         //這裏迭代在SimpleUrlHandlerMapping中定義的所有映射元素  

113         Iterator it = urlMap.keySet().iterator();   

114         while (it.hasNext()) {   

115             //這裏取得配置的url  

116             String url = (String) it.next();   

117             //這裏根據urlbean定義中取得對應的handler  

118             Object handler = urlMap.get(url);   

119             // Prepend with slash if not already present.  

120             if (!url.startsWith("/")) {   

121                 url = "/" + url;   

122             }   

123             //這裏調用AbstractHandlerMapping中的註冊過程  

124             registerHandler(url, handler);   

125         }   

126     }   

127 }  

128 protected void registerHandlers(Map urlMap) throws BeansException {  

129     if (urlMap.isEmpty()) {  

130         logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");  

131     }  

132     else {  

133         //這裏迭代在SimpleUrlHandlerMapping中定義的所有映射元素  

134         Iterator it = urlMap.keySet().iterator();  

135         while (it.hasNext()) {  

136             //這裏取得配置的url  

137             String url = (String) it.next();  

138             //這裏根據urlbean定義中取得對應的handler  

139             Object handler = urlMap.get(url);  

140             // Prepend with slash if not already present.  

141             if (!url.startsWith("/")) {  

142                 url = "/" + url;  

143             }  

144             //這裏調用AbstractHandlerMapping中的註冊過程  

145             registerHandler(url, handler);  

146         }  

147     }  

148 }  

AbstractMappingHandler中的註冊代碼:
  

Java代碼 

149 protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {   

150     //試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關係  

151     Object mappedHandler = this.handlerMap.get(urlPath);   

152     if (mappedHandler != null) {   

153     ……..   

154     }   

155   

156     //如果是直接用bean名做映射那就直接從容器中取handler  

157     if (!this.lazyInitHandlers && handler instanceof String) {   

158         String handlerName = (String) handler;   

159         if (getApplicationContext().isSingleton(handlerName)) {   

160             handler = getApplicationContext().getBean(handlerName);   

161         }   

162     }   

163     //或者使用默認的handler.  

164     if (urlPath.equals("/*")) {   

165         setDefaultHandler(handler);   

166     }   

167     else {   

168    //urlhandler的對應關係放到handlerMap中去  

169         this.handlerMap.put(urlPath, handler);   

170         ……..   

171     }   

172 }  

173 protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {  

174     //試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關係  

175     Object mappedHandler = this.handlerMap.get(urlPath);  

176     if (mappedHandler != null) {  

177     ........  

178     }  

179   

180     //如果是直接用bean名做映射那就直接從容器中取handler  

181     if (!this.lazyInitHandlers && handler instanceof String) {  

182         String handlerName = (String) handler;  

183         if (getApplicationContext().isSingleton(handlerName)) {  

184             handler = getApplicationContext().getBean(handlerName);  

185         }  

186     }  

187     //或者使用默認的handler.  

188     if (urlPath.equals("/*")) {  

189         setDefaultHandler(handler);  

190     }  

191     else {  

192    //urlhandler的對應關係放到handlerMap中去  

193         this.handlerMap.put(urlPath, handler);  

194         ........  

195     }  

196 }  

handlerMap是持有的一個HashMap,裏面就保存了具體的映射信息:
  

Java代碼 

197 private final Map handlerMap = new HashMap();  

198 private final Map handlerMap = new HashMap();  

SimpleUrlHandlerMapping對接口HandlerMapping的實現是這樣的,這個getHandler根據在初始化的時候就得到的映射表來生成DispatcherServlet需要的執行鏈
  

Java代碼 

199 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {   

200     //這裏根據request中的參數得到其對應的handler,具體處理在AbstractUrlHandlerMapping  

201     Object handler = getHandlerInternal(request);   

202     //如果找不到對應的,就使用缺省的handler  

203     if (handler == null) {   

204         handler = this.defaultHandler;   

205     }   

206     //如果缺省的也沒有,那就沒辦法了  

207     if (handler == null) {   

208         return null;   

209     }   

210     // 如果handler不是一個具體的handler,那我們還要到上下文中取  

211     if (handler instanceof String) {   

212         String handlerName = (String) handler;   

213         handler = getApplicationContext().getBean(handlerName);   

214     }   

215     //生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截器組。  

216     return new HandlerExecutionChain(handler, this.adaptedInterceptors);   

217 }  

218 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  

219     //這裏根據request中的參數得到其對應的handler,具體處理在AbstractUrlHandlerMapping中  

220     Object handler = getHandlerInternal(request);  

221     //如果找不到對應的,就使用缺省的handler  

222     if (handler == null) {  

223         handler = this.defaultHandler;  

224     }  

225     //如果缺省的也沒有,那就沒辦法了  

226     if (handler == null) {  

227         return null;  

228     }  

229     // 如果handler不是一個具體的handler,那我們還要到上下文中取  

230     if (handler instanceof String) {  

231         String handlerName = (String) handler;  

232         handler = getApplicationContext().getBean(handlerName);  

233     }  

234     //生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截器組。  

235     return new HandlerExecutionChain(handler, this.adaptedInterceptors);  

236 }  

我們看看具體的handler查找過程:
  

Java代碼 

237 protected Object getHandlerInternal(HttpServletRequest request) throws Exception {   

238     //這裏的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。  

239     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);   

240     …….//下面是根據請求信息的查找  

241     return lookupHandler(lookupPath, request);   

242 }   

243   

244 protected Object lookupHandler(String urlPath, HttpServletRequest request) {   

245     // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。  

246     Object handler = this.handlerMap.get(urlPath);   

247     if (handler == null) {   

248         // 這裏使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher類來完成匹配處理。  

249         String bestPathMatch = null;   

250         for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {   

251             String registeredPath = (String) it.next();   

252             if (this.pathMatcher.match(registeredPath, urlPath) &&   

253                             (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {   

254                 //這裏根據匹配路徑找到最象的一個  

255                 handler = this.handlerMap.get(registeredPath);   

256                 bestPathMatch = registeredPath;   

257             }   

258         }   

259   

260         if (handler != null) {   

261             exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);   

262         }   

263     }   

264     else {   

265         exposePathWithinMapping(urlPath, request);   

266     }   

267     //  

268     return handler;   

269 }  

270 protected Object getHandlerInternal(HttpServletRequest request) throws Exception {  

271     //這裏的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。  

272     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);  

273     .......//下面是根據請求信息的查找  

274     return lookupHandler(lookupPath, request);  

275 }  

276   

277 protected Object lookupHandler(String urlPath, HttpServletRequest request) {  

278     // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。  

279     Object handler = this.handlerMap.get(urlPath);  

280     if (handler == null) {  

281         // 這裏使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher類來完成匹配處理。  

282         String bestPathMatch = null;  

283         for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {  

284             String registeredPath = (String) it.next();  

285             if (this.pathMatcher.match(registeredPath, urlPath) &&  

286                             (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {  

287                 //這裏根據匹配路徑找到最象的一個  

288                 handler = this.handlerMap.get(registeredPath);  

289                 bestPathMatch = registeredPath;  

290             }  

291         }  

292   

293         if (handler != null) {  

294             exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);  

295         }  

296     }  

297     else {  

298         exposePathWithinMapping(urlPath, request);  

299     }  

300     //  

301     return handler;  

302 }  

我們可以看到,總是在handlerMap這個HashMap中找,當然如果直接找到最好,如果找不到,就看看是不是能通過Match Pattern的模式找,我們一定還記得在配置HnaderMapping的時候是可以通過ANT語法進行配置的,其中的處理就在這裏。
 這樣可以清楚地看到整個HandlerMapping的初始化過程 – 同時,我們也看到了一個具體的handler映射是怎樣被存儲和查找的 – 這裏生成一個ExecutionChain來儲存我們找到的handler和在定義bean的時候定義的Interceptors.
 讓我們回到DispatcherServlet,初始化完成以後,實際的對web請求是在doService()方法中處理的,我們知道DispatcherServlet只是一個普通的Servlet:
  

Java代碼 

303 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {   

304     …….   

305     //這裏把屬性信息進行保存  

306     Map attributesSnapshot = null;   

307     if (WebUtils.isIncludeRequest(request)) {   

308         logger.debug("Taking snapshot of request attributes before include");   

309         attributesSnapshot = new HashMap();   

310         Enumeration attrNames = request.getAttributeNames();   

311         while (attrNames.hasMoreElements()) {   

312             String attrName = (String) attrNames.nextElement();   

313             if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {   

314                 attributesSnapshot.put(attrName, request.getAttribute(attrName));   

315             }   

316         }   

317     }   

318   

319     // Make framework objects available to handlers and view objects.  

320     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());   

321     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);   

322     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);   

323     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());   

324   

325     try {   

326          //這裏使實際的處理入口  

327         doDispatch(request, response);   

328     }   

329     finally {   

330         // Restore the original attribute snapshot, in case of an include.  

331         if (attributesSnapshot != null) {   

332             restoreAttributesAfterInclude(request, attributesSnapshot);   

333         }   

334     }   

335 }  

336 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  

337     .......  

338     //這裏把屬性信息進行保存  

339     Map attributesSnapshot = null;  

340     if (WebUtils.isIncludeRequest(request)) {  

341         logger.debug("Taking snapshot of request attributes before include");  

342         attributesSnapshot = new HashMap();  

343         Enumeration attrNames = request.getAttributeNames();  

344         while (attrNames.hasMoreElements()) {  

345             String attrName = (String) attrNames.nextElement();  

346             if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {  

347                 attributesSnapshot.put(attrName, request.getAttribute(attrName));  

348             }  

349         }  

350     }  

351   

352     // Make framework objects available to handlers and view objects.  

353     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  

354     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);  

355     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  

356     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());  

357   

358     try {  

359          //這裏使實際的處理入口  

360         doDispatch(request, response);  

361     }  

362     finally {  

363         // Restore the original attribute snapshot, in case of an include.  

364         if (attributesSnapshot != null) {  

365             restoreAttributesAfterInclude(request, attributesSnapshot);  

366         }  

367     }  

368 }  

我們看到,對於請求的處理實際上是讓doDispatch()來完成的 – 這個方法很長,但是過程很簡單明瞭:
  

Java代碼 

369 protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {   

370     HttpServletRequest processedRequest = request;   

371     //這是從handlerMapping中得到的執行鏈  

372     HandlerExecutionChain mappedHandler = null;   

373     int interceptorIndex = -1;   

374       

375     ……..   

376     try {   

377         //我們熟悉的ModelAndView開始出現了。  

378         ModelAndView mv = null;   

379         try {   

380             processedRequest = checkMultipart(request);   

381   

382             // 這是我們得到handler的過程  

383             mappedHandler = getHandler(processedRequest, false);   

384             if (mappedHandler == null || mappedHandler.getHandler() == null) {   

385                 noHandlerFound(processedRequest, response);   

386                 return;   

387             }   

388   

389             // 這裏取出執行鏈中的Interceptor進行前處理  

390             if (mappedHandler.getInterceptors() != null) {   

391                 for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {   

392                     HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];   

393                     if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {   

394                         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);   

395                         return;   

396                     }   

397                     interceptorIndex = i;   

398                 }   

399             }   

400   

401             //在執行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的。  

402             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());   

403             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());   

404   

405             // 這裏取出執行鏈中的Interceptor進行後處理  

406             if (mappedHandler.getInterceptors() != null) {   

407                 for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i–) {   

408                     HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];   

409                     interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);   

410                 }   

411             }   

412         }   

413           

414         ……..   

415   

416         // Did the handler return a view to render?  

417         //這裏對視圖生成進行處理  

418         if (mv != null && !mv.wasCleared()) {   

419             render(mv, processedRequest, response);   

420         }   

421         …….   

422 }  

423 protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {  

424     HttpServletRequest processedRequest = request;  

425     //這是從handlerMapping中得到的執行鏈  

426     HandlerExecutionChain mappedHandler = null;  

427     int interceptorIndex = -1;  

428   

429     ........  

430     try {  

431         //我們熟悉的ModelAndView開始出現了。  

432         ModelAndView mv = null;  

433         try {  

434             processedRequest = checkMultipart(request);  

435   

436             // 這是我們得到handler的過程  

437             mappedHandler = getHandler(processedRequest, false);  

438             if (mappedHandler == null || mappedHandler.getHandler() == null) {  

439                 noHandlerFound(processedRequest, response);  

440                 return;  

441             }  

442   

443             // 這裏取出執行鏈中的Interceptor進行前處理  

444             if (mappedHandler.getInterceptors() != null) {  

445                 for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {  

446                     HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];  

447                     if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {  

448                         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  

449                         return;  

450                     }  

451                     interceptorIndex = i;  

452                 }  

453             }  

454   

455             //在執行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的。  

456             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  

457             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

458   

459             // 這裏取出執行鏈中的Interceptor進行後處理  

460             if (mappedHandler.getInterceptors() != null) {  

461                 for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {  

462                     HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];  

463                     interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);  

464                 }  

465             }  

466         }  

467   

468         ........  

469   

470         // Did the handler return a view to render?  

471         //這裏對視圖生成進行處理  

472         if (mv != null && !mv.wasCleared()) {  

473             render(mv, processedRequest, response);  

474         }  

475         .......  

476 }  

我們很清楚的看到和MVC框架緊密相關的代碼,比如如何得到和http請求相對應的執行鏈,怎樣執行執行鏈和怎樣把模型數據展現到視圖中去。
 先看怎樣取得Command對象,對我們來說就是Handler – 下面是getHandler的代碼:
  

Java代碼 

477 protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {   

478   //ServletContext取得執行鏈 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存。  

479   HandlerExecutionChain handler =   

480             (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);   

481     if (handler != null) {   

482         if (!cache) {   

483             request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);   

484         }   

485         return handler;   

486     }   

487     //這裏的迭代器迭代的時在initHandlerMapping中載入的上下文所有的HandlerMapping  

488     Iterator it = this.handlerMappings.iterator();   

489     while (it.hasNext()) {   

490         HandlerMapping hm = (HandlerMapping) it.next();   

491         …….   

492         //這裏是實際取得handler的過程,在每個HandlerMapping中建立的映射表進行檢索得到請求對應的handler  

493         handler = hm.getHandler(request);   

494   

495         //然後把handler存到ServletContext中去進行緩存  

496         if (handler != null) {   

497             if (cache) {   

498                 request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);   

499             }   

500             return handler;   

501         }   

502     }   

503     return null;   

504 }  

505 protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {  

506   //ServletContext取得執行鏈 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存。  

507   HandlerExecutionChain handler =  

508             (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);  

509     if (handler != null) {  

510         if (!cache) {  

511             request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);  

512         }  

513         return handler;  

514     }  

515     //這裏的迭代器迭代的時在initHandlerMapping中載入的上下文所有的HandlerMapping  

516     Iterator it = this.handlerMappings.iterator();  

517     while (it.hasNext()) {  

518         HandlerMapping hm = (HandlerMapping) it.next();  

519         .......  

520         //這裏是實際取得handler的過程,在每個HandlerMapping中建立的映射表進行檢索得到請求對應的handler  

521         handler = hm.getHandler(request);  

522   

523         //然後把handler存到ServletContext中去進行緩存  

524         if (handler != null) {  

525             if (cache) {  

526                 request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);  

527             }  

528             return handler;  

529         }  

530     }  

531     return null;  

532 }  

如果在ServletContext中可以取得handler則直接返回,實際上這個handler是緩衝了上次處理的結果 – 總要有第一次把這個handler放到ServletContext中去:
 如果在ServletContext中找不到handler,那就通過持有的handlerMapping生成一個,我們看到它會迭代當前持有的所有的 handlerMapping,因爲可以定義不止一個,他們在定義的時候也可以指定順序,直到找到第一個,然後返回。先找到一個 handlerMapping,然後通過這個handlerMapping返回一個執行鏈,裏面包含了最終的Handler和我們定義的一連串的 Interceptor。具體的我們可以參考上面的SimpleUrlHandlerMapping的代碼分析知道getHandler是怎樣得到一個 HandlerExecutionChain的。
 得到HandlerExecutionChain以後,我們通過HandlerAdapter對這個Handler的合法性進行判斷:
  

Java代碼 

533 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {   

534     Iterator it = this.handlerAdapters.iterator();   

535     while (it.hasNext()) {   

536         //同樣對持有的所有adapter進行匹配  

537         HandlerAdapter ha = (HandlerAdapter) it.next();   

538         if (ha.supports(handler)) {   

539             return ha;   

540         }   

541     }   

542     ……..   

543 }  

544 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {  

545     Iterator it = this.handlerAdapters.iterator();  

546     while (it.hasNext()) {  

547         //同樣對持有的所有adapter進行匹配  

548         HandlerAdapter ha = (HandlerAdapter) it.next();  

549         if (ha.supports(handler)) {  

550             return ha;  

551         }  

552     }  

553     ........  

554 }  

通過判斷,我們知道這個handler是不是一個Controller接口的實現,比如對於具體的HandlerAdapter – SimpleControllerHandlerAdapter:
  

Java代碼 

555 public class SimpleControllerHandlerAdapter implements HandlerAdapter {   

556       

557     public boolean supports(Object handler) {   

558         return (handler instanceof Controller);   

559     }   

560     …….   

561 }  

562 public class SimpleControllerHandlerAdapter implements HandlerAdapter {  

563   

564     public boolean supports(Object handler) {  

565         return (handler instanceof Controller);  

566     }  

567     .......  

568 }  

簡單的判斷一下handler是不是實現了Controller接口。這也體現了一種對配置文件進行驗證的機制。
 讓我們再回到DispatcherServlet看到代碼:
  

Java代碼 

569 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

570 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

這個就是對handle的具體調用!相當於Command模式裏的Command.execute();理所當然的返回一個ModelAndView,下面就是一個對View進行處理的過程:
  

Java代碼 

571 if (mv != null && !mv.wasCleared()) {   

572     render(mv, processedRequest, response);   

573 }  

574 if (mv != null && !mv.wasCleared()) {  

575     render(mv, processedRequest, response);  

576 }  

調用的是render方法:
  

Java代碼

577 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)   

578          throws Exception {response.setLocale(locale);   

579   

580      View view = null;   

581      //這裏把默認的視圖放到ModelAndView中去。  

582      if (!mv.hasView()) {   

583          mv.setViewName(getDefaultViewName(request));   

584      }   

585   

586      if (mv.isReference()) {   

587          // 這裏對視圖名字進行解析  

588          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);   

589      …….   

590      }   

591      else {   

592          // 有可能在ModelAndView裏已經直接包含了View對象,那我們就直接使用。  

593          view = mv.getView();   

594      ……..   

595      }   

596   

597      //得到具體的View對象以後,我們用它來生成視圖。  

598      view.render(mv.getModelInternal(), request, response);   

599  }  

600 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)  

601          throws Exception {response.setLocale(locale);  

602   

603      View view = null;  

604      //這裏把默認的視圖放到ModelAndView中去。  

605      if (!mv.hasView()) {  

606          mv.setViewName(getDefaultViewName(request));  

607      }  

608   

609      if (mv.isReference()) {  

610          // 這裏對視圖名字進行解析  

611          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);  

612      .......  

613      }  

614      else {  

615          // 有可能在ModelAndView裏已經直接包含了View對象,那我們就直接使用。  

616          view = mv.getView();  

617      ........  

618      }  

619   

620      //得到具體的View對象以後,我們用它來生成視圖。  

621      view.render(mv.getModelInternal(), request, response);  

622  }  

從整個過程我們看到先在ModelAndView中尋找視圖的邏輯名,如果找不到那就使用缺省的視圖,如果能夠找到視圖的名字,那就對他進行解析得到實際 的需要使用的視圖對象。還有一種可能就是在ModelAndView中已經包含了實際的視圖對象,這個視圖對象是可以直接使用的。
 不管怎樣,得到一個視圖對象以後,通過調用視圖對象的render來完成數據的顯示過程,我們可以看看具體的JstlView是怎樣實現的,我們在JstlView的抽象父類 AbstractView中找到render方法:
  

Java代碼

623 public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {   

624     ……   

625     // 這裏把所有的相關信息都收集到一個Map  

626     Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));   

627     mergedModel.putAll(this.staticAttributes);   

628     if (model != null) {   

629         mergedModel.putAll(model);   

630     }   

631   

632     // Expose RequestContext?  

633     if (this.requestContextAttribute != null) {   

634         mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));   

635     }   

636     //這是實際的展現模型數據到視圖的調用。  

637     renderMergedOutputModel(mergedModel, request, response);   

638 }  

639 public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {  

640     ......  

641     // 這裏把所有的相關信息都收集到一個Map裏  

642     Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));  

643     mergedModel.putAll(this.staticAttributes);  

644     if (model != null) {  

645         mergedModel.putAll(model);  

646     }  

647   

648     // Expose RequestContext?  

649     if (this.requestContextAttribute != null) {  

650         mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));  

651     }  

652     //這是實際的展現模型數據到視圖的調用。  

653     renderMergedOutputModel(mergedModel, request, response);  

654 }  

註解寫的很清楚了,先把所有的數據模型進行整合放到一個Map – mergedModel裏,然後調用renderMergedOutputModel();這個renderMergedOutputModel是一個模 板方法,他的實現在InternalResourceView也就是JstlView的父類:
  

Java代碼

655 protected void renderMergedOutputModel(   

656        Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {   

657   

658    // Expose the model object as request attributes.  

659    exposeModelAsRequestAttributes(model, request);   

660   

661    // Expose helpers as request attributes, if any.  

662    exposeHelpers(request);   

663   

664    // 這裏得到InternalResource定義的內部資源路徑。  

665    String dispatcherPath = prepareForRendering(request, response);   

666   

667    //這裏把請求轉發到前面得到的內部資源路徑中去。  

668    RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);   

669    if (rd == null) {   

670        throw new ServletException(   

671                "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");   

672    }   

673    …….   

674 protected void renderMergedOutputModel(  

675        Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {  

676   

677    // Expose the model object as request attributes.  

678    exposeModelAsRequestAttributes(model, request);  

679   

680    // Expose helpers as request attributes, if any.  

681    exposeHelpers(request);  

682   

683    // 這裏得到InternalResource定義的內部資源路徑。  

684    String dispatcherPath = prepareForRendering(request, response);  

685   

686    //這裏把請求轉發到前面得到的內部資源路徑中去。  

687    RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);  

688    if (rd == null) {  

689        throw new ServletException(  

690                "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");  

691    }  

692    .......  

首先對模型數據進行處理,exposeModelAsRequestAttributes是在AbstractView中實現的,這個方法把 ModelAndView中的模型數據和其他request數據統統放到ServletContext當中去,這樣整個模型數據就通過 ServletContext暴露並得到共享使用了:
  

Java代碼

693 protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {   

694       Iterator it = model.entrySet().iterator();   

695       while (it.hasNext()) {   

696           Map.Entry entry = (Map.Entry) it.next();   

697           ……….   

698           String modelName = (String) entry.getKey();   

699           Object modelValue = entry.getValue();   

700           if (modelValue != null) {   

701               request.setAttribute(modelName, modelValue);   

702           ………..   

703           }   

704           else {   

705               request.removeAttribute(modelName);   

706               …….   

707           }   

708       }   

709   }  

710 protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {  

711       Iterator it = model.entrySet().iterator();  

712       while (it.hasNext()) {  

713           Map.Entry entry = (Map.Entry) it.next();  

714           ..........  

715           String modelName = (String) entry.getKey();  

716           Object modelValue = entry.getValue();  

717           if (modelValue != null) {  

718               request.setAttribute(modelName, modelValue);  

719           ...........  

720           }  

721           else {  

722               request.removeAttribute(modelName);  

723               .......  

724           }  

725       }  

726   }  

讓我們回到數據處理部分的exposeHelper();這是一個模板方法,其實現在JstlView中實現:
  

Java代碼

727 public class JstlView extends InternalResourceView {   

728   

729     private MessageSource jstlAwareMessageSource;   

730   

731   

732     protected void initApplicationContext() {   

733         super.initApplicationContext();   

734         this.jstlAwareMessageSource =   

735                 JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());   

736     }   

737   

738     protected void exposeHelpers(HttpServletRequest request) throws Exception {   

739         JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);   

740     }   

741   

742 }  

743 public class JstlView extends InternalResourceView {  

744   

745     private MessageSource jstlAwareMessageSource;  

746   

747     protected void initApplicationContext() {  

748         super.initApplicationContext();  

749         this.jstlAwareMessageSource =  

750                 JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());  

751     }  

752   

753     protected void exposeHelpers(HttpServletRequest request) throws Exception {  

754         JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);  

755     }  

756   

757 }  

JstlUtils中包含了對於其他而言jstl特殊的數據處理和設置。
 過程是不是很長?我們現在在哪裏了?呵呵,我們剛剛完成的事MVCViewrender,對於InternalResourceView的 render 過程比較簡單只是完成一個資源的重定向處理。需要做的就是得到實際viewinternalResource路徑,然後轉發到那個資源中去。怎樣得到資 源的路徑呢通過調用:
  

Java代碼

758 protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)   

759        throws Exception {   

760   

761    return getUrl();   

762 protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)  

763        throws Exception {  

764   

765    return getUrl();  

那這個url在哪裏生成呢?我們在View相關的代碼中沒有找到,實際上,他在ViewRosolve的時候就生成了,在UrlBasedViewResolver中:
  

Java代碼

766 protected AbstractUrlBasedView buildView(String viewName) throws Exception {   

767     AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());   

768     view.setUrl(getPrefix() + viewName + getSuffix());   

769     String contentType = getContentType();   

770     if (contentType != null) {   

771         view.setContentType(contentType);   

772     }   

773     view.setRequestContextAttribute(getRequestContextAttribute());   

774     view.setAttributesMap(getAttributesMap());   

775     return view;   

776 }  

777 protected AbstractUrlBasedView buildView(String viewName) throws Exception {  

778     AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());  

779     view.setUrl(getPrefix() + viewName + getSuffix());  

780     String contentType = getContentType();  

781     if (contentType != null) {  

782         view.setContentType(contentType);  

783     }  

784     view.setRequestContextAttribute(getRequestContextAttribute());  

785     view.setAttributesMap(getAttributesMap());  

786     return view;  

787 }  

這裏是生成View的地方,自然也把生成的url和其他一些和view相關的屬性也配置好了。
 那這個ViewResolve是什麼時候被調用的呢?哈哈,我們這樣又要回到DispatcherServlet中去看看究竟,在DispatcherServlet中:
  

Java代碼

788  protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)   

789          throws Exception {   

790   

791      ……..   

792      View view = null;   

793   

794      // 這裏設置視圖名爲默認的名字  

795      if (!mv.hasView()) {   

796          mv.setViewName(getDefaultViewName(request));   

797      }   

798   

799      if (mv.isReference()) {   

800          //這裏對視圖名進行解析,在解析的過程中根據需要生成實際需要的視圖對象。  

801          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);   

802         ……….   

803      }   

804     ……   

805 }  

806  protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)  

807          throws Exception {  

808   

809      ........  

810      View view = null;  

811   

812      // 這裏設置視圖名爲默認的名字  

813      if (!mv.hasView()) {  

814          mv.setViewName(getDefaultViewName(request));  

815      }  

816   

817      if (mv.isReference()) {  

818          //這裏對視圖名進行解析,在解析的過程中根據需要生成實際需要的視圖對象。  

819          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);  

820         ..........  

821      }  

822     ......  

823 }  

下面是對視圖名進行解析的具體過程:
  

Java代碼

824 protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)   

825             throws Exception {   

826          //我們有可能不止一個視圖解析器  

827         for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {   

828             ViewResolver viewResolver = (ViewResolver) it.next();   

829             //這裏是視圖解析器進行解析並生成視圖的過程。  

830             View view = viewResolver.resolveViewName(viewName, locale);   

831             if (view != null) {   

832                 return view;   

833             }   

834         }   

835         return null;   

836     }  

837 protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)  

838             throws Exception {  

839          //我們有可能不止一個視圖解析器  

840         for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {  

841             ViewResolver viewResolver = (ViewResolver) it.next();  

842             //這裏是視圖解析器進行解析並生成視圖的過程。  

843             View view = viewResolver.resolveViewName(viewName, locale);  

844             if (view != null) {  

845                 return view;  

846             }  

847         }  

848         return null;  

849     }  

這裏調用具體的ViewResolver對視圖的名字進行解析 – 除了單純的解析之外,它還根據我們的要求生成了我們實際需要的視圖對象。具體的viewResolverbean定義文件中進行定義同時在 initViewResolver()方法中被初始化到viewResolver變量中,我們看看具體的 InternalResourceViewResolver是怎樣對視圖名進行處理的並生成V視圖對象的:對resolveViewName的調用模板在 AbstractCachingViewResolver,
  

Java代碼

850 public View resolveViewName(String viewName, Locale locale) throws Exception {   

851     //如果沒有打開緩存設置,那創建需要的視圖  

852     if (!isCache()) {   

853         logger.warn("View caching is SWITCHED OFF – DEVELOPMENT SETTING ONLY: This can severely impair performance");   

854         return createView(viewName, locale);   

855     }   

856     else {   

857         Object cacheKey = getCacheKey(viewName, locale);   

858         // No synchronization, as we can live with occasional double caching.  

859         synchronized (this.viewCache) {   

860             //這裏查找緩存裏的視圖對象  

861             View view = (View) this.viewCache.get(cacheKey);   

862             if (view == null) {   

863                 //如果在緩存中沒有找到,創建一個並把創建的放到緩存中去  

864                 view = createView(viewName, locale);   

865                 this.viewCache.put(cacheKey, view);   

866             ……..   

867             }   

868             return view;   

869         }   

870     }   

871 }  

872 public View resolveViewName(String viewName, Locale locale) throws Exception {  

873     //如果沒有打開緩存設置,那創建需要的視圖  

874     if (!isCache()) {  

875         logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");  

876         return createView(viewName, locale);  

877     }  

878     else {  

879         Object cacheKey = getCacheKey(viewName, locale);  

880         // No synchronization, as we can live with occasional double caching.  

881         synchronized (this.viewCache) {  

882             //這裏查找緩存裏的視圖對象  

883             View view = (View) this.viewCache.get(cacheKey);  

884             if (view == null) {  

885                 //如果在緩存中沒有找到,創建一個並把創建的放到緩存中去  

886                 view = createView(viewName, locale);  

887                 this.viewCache.put(cacheKey, view);  

888             ........  

889             }  

890             return view;  

891         }  

892     }  

893 }  

關於這些createView(),loadView(),buildView()的關係,我們看看Eclipse裏的call hiearchy
 然後我們回到view.render中完成數據的最終對httpResponse的寫入,比如在AbstractExcelView中的實現:
  

Java代碼

894 protected final void renderMergedOutputModel(   

895         Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {   

896     ………   

897     // response.setContentLength(workbook.getBytes().length);  

898     response.setContentType(getContentType());   

899     ServletOutputStream out = response.getOutputStream();   

900     workbook.write(out);   

901     out.flush();   

902 }  

903 protected final void renderMergedOutputModel(  

904         Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {  

905     .........  

906     // response.setContentLength(workbook.getBytes().length);  

907     response.setContentType(getContentType());  

908     ServletOutputStream out = response.getOutputStream();  

909     workbook.write(out);  

910     out.flush();  

911 }  

這樣就和我們前面的分析一致起來了:DispatcherServlet在解析視圖名的時候就根據要求生成了視圖對象,包括在 InternalResourceView中需要使用的url和其他各種和HTTP response相關的屬性都會寫保持在生成的視圖對象中,然後就直接調用視圖對象的render來完成數據的展示。
 這就是整個Spring Web MVC框架的大致流程,整個MVC流程由DispatcherServlet來控制。MVC的關鍵過程包括:
 配置到handler的映射關係和怎樣根據請求參數得到對應的handler,Spring中,這是由handlerMapping通過執行鏈來完成 的,而具體的映射關係我們在bean定義文件中定義並在HandlerMapping載入上下文的時候就被配置好了。然後 DispatcherServlet調用HandlerMapping來得到對應的執行鏈,最後通過視圖來展現模型數據,但我們要注意的是視圖對象是在解 析視圖名的時候生成配置好的。這些作爲核心類的HanderMapping,ViewResolver,View,Handler的緊密協作實現了MVC 的功能。

 

下面我們來看看SpringAOP的一些相關代碼是怎麼得到Proxy的,讓我們我們先看看AOPSpring AOP的一些基本概念:
 Advice
     通知,制定在連接點做什麼,在Sping中,他主要描述Spring圍繞方法調用注入的額外的行爲,Spring提供的通知類型有:
     before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice,這些都是Spring AOP定義的接口類,具體的動作實現需要用戶程序來完成。
 Pointcut:
     切點,其決定一個advice應該應用於哪個連接點,也就是需要插入額外處理的地方的集合,例如,被某個advice作爲目標的一組方法。Spring pointcut通常意味着標示方法,可以選擇一組方法調用作爲pointcut,Spring提供了具體的切點來給用戶使用,比如正則表達式切點 JdkRegexpMethodPointcut通過正則表達式對方法名進行匹配,其通過使用 AbstractJdkRegexpMethodPointcut中的對MethodMatcher接口的實現來完成pointcut功能:
  

Java代碼 

public final boolean matches(Method method, Class targetClass) {   

    //這裏通過放射得到方法的全名  

    String patt = method.getDeclaringClass().getName() + "." + method.getName();   

    for (int i = 0; i < this.patterns.length; i++) {   

        // 這裏是判斷是否和方法名是否匹配的代碼  

        boolean matched = matches(patt, i);   

        if (matched) {   

            for (int j = 0; j < this.excludedPatterns.length; j++) {   

                boolean excluded = matchesExclusion(patt, j);   

10                 if(excluded) {   

11                     return false;   

12                 }   

13             }   

14             return true;   

15         }   

16     }   

17     return false;   

18 }  

19 public final boolean matches(Method method, Class targetClass) {  

20     //這裏通過放射得到方法的全名  

21     String patt = method.getDeclaringClass().getName() + "." + method.getName();  

22     for (int i = 0; i < this.patterns.length; i++) {  

23         // 這裏是判斷是否和方法名是否匹配的代碼  

24         boolean matched = matches(patt, i);  

25         if (matched) {  

26             for (int j = 0; j < this.excludedPatterns.length; j++) {  

27                 boolean excluded = matchesExclusion(patt, j);  

28                 if(excluded) {  

29                     return false;  

30                 }  

31             }  

32             return true;  

33         }  

34     }  

35     return false;  

36 }  

JDKRegexpMethodPointcut中通過JDK中的正則表達式匹配來完成pointcut的最終確定:
  

Java代碼 

37 protected boolean matches(String pattern, int patternIndex) {   

38     Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);   

39     return matcher.matches();   

40 }  

41 protected boolean matches(String pattern, int patternIndex) {  

42     Matcher matcher = <a href="http://this.com" title="http://this.com" target="_blank">this.com</a>piledPatterns[patternIndex].matcher(pattern);  

43     return matcher.matches();  

44 }  

Advisor
 當我們完成額外的動作設計(advice)和額外動作插入點的設計(pointcut)以後,我們需要一個對象把他們結合起來,這就是通知器 – advisor,定義應該在哪裏應用哪個通知。Advisor的實現有:DefaultPointcutAdvisor他有兩個屬性advice和 pointcut來讓我們配置advicepointcut
 接着我們就可以通過ProxyFactoryBean來配置我們的代理對象和方面行爲,在ProxyFactoryBean中有 interceptorNames來配置已經定義好的通知器-advisor,雖然這裏的名字叫做interceptNames,但實際上是供我們配置 advisor的地方,具體的代理實現通過JDK Proxy或者CGLIB來完成。因爲ProxyFactoryBean是一個FactoryBean,ProxyFactoryBean中我們通過 getObject()可以直接得到代理對象:
  

Java代碼 

45 public Object getObject() throws BeansException {   

46     //這裏初始化通知器鏈  

47     initializeAdvisorChain();   

48     if (isSingleton()) {   

49     //根據定義需要生成單件的Proxy  

50         return getSingletonInstance();   

51     }   

52     else {   

53     …….   

54         //這裏根據定義需要生成Prototype類型的Proxy  

55         return newPrototypeInstance();   

56     }   

57 }  

58 public Object getObject() throws BeansException {  

59     //這裏初始化通知器鏈  

60     initializeAdvisorChain();  

61     if (isSingleton()) {  

62     //根據定義需要生成單件的Proxy  

63         return getSingletonInstance();  

64     }  

65     else {  

66     .......  

67         //這裏根據定義需要生成Prototype類型的Proxy  

68         return newPrototypeInstance();  

69     }  

70 }  

我們看看怎樣生成單件的代理對象:
  

Java代碼 

71 private synchronized Object getSingletonInstance() {   

72     if (this.singletonInstance == null) {   

73         this.targetSource = freshTargetSource();   

74         if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {   

75             // 這裏設置代理對象的接口  

76             setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));   

77         }   

78         // Eagerly initialize the shared singleton instance.  

79         super.setFrozen(this.freezeProxy);   

80         // 注意這裏的方法會使用ProxyFactory來生成我們需要的Proxy  

81         this.singletonInstance = getProxy(createAopProxy());   

82         // We must listen to superclass advice change events to recache the singleton  

83         // instance if necessary.  

84         addListener(this);   

85     }   

86     return this.singletonInstance;   

87 }   

88   

89 //使用createAopProxy放回的AopProxy來得到代理對象。  

90 protected Object getProxy(AopProxy aopProxy) {   

91     return aopProxy.getProxy(this.beanClassLoader);   

92 }  

93 private synchronized Object getSingletonInstance() {  

94     if (this.singletonInstance == null) {  

95         this.targetSource = freshTargetSource();  

96         if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {  

97             // 這裏設置代理對象的接口  

98             setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));  

99         }  

100         // Eagerly initialize the shared singleton instance.  

101         super.setFrozen(this.freezeProxy);  

102         // 注意這裏的方法會使用ProxyFactory來生成我們需要的Proxy  

103         this.singletonInstance = getProxy(createAopProxy());  

104         // We must listen to superclass advice change events to recache the singleton  

105         // instance if necessary.  

106         addListener(this);  

107     }  

108     return this.singletonInstance;  

109 }  

110   

111 //使用createAopProxy放回的AopProxy來得到代理對象。  

112 protected Object getProxy(AopProxy aopProxy) {  

113     return aopProxy.getProxy(this.beanClassLoader);  

114 }  

ProxyFactoryBean的父類是AdvisedSupportSpring使用AopProxy接口把AOP代理的實現與框架的其他部分分離 開來;在AdvisedSupport中通過這樣的方式來得到AopProxy,當然這裏需要得到AopProxyFactory的幫助 – 下面我們看到Spring爲我們提供的實現,來幫助我們方便的從JDK或者cglib中得到我們想要的代理對象:
  

Java代碼 

115 protected synchronized AopProxy createAopProxy() {   

116     if (!this.isActive) {   

117         activate();   

118     }   

119     return getAopProxyFactory().createAopProxy(this);   

120 }  

121 protected synchronized AopProxy createAopProxy() {  

122     if (!this.isActive) {  

123         activate();  

124     }  

125     return getAopProxyFactory().createAopProxy(this);  

126 }  

而在ProxyConfig中對使用的AopProxyFactory做了定義:
  

Java代碼 

127 //這個DefaultAopProxyFactorySpring用來生成AopProxy的地方,  

128 //當然了它包含JDKCglib兩種實現方式。  

129 private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();  

130 //這個DefaultAopProxyFactorySpring用來生成AopProxy的地方,  

131 //當然了它包含JDKCglib兩種實現方式。  

132 private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();  

其中在DefaultAopProxyFactory中是這樣生成AopProxy的:
  

Java代碼 

133 public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {   

134     //首先考慮使用cglib來實現代理對象,當然如果同時目標對象不是接口的實現類的話  

135     if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||   

136         advisedSupport.getProxiedInterfaces().length == 0) {   

137         //這裏判斷如果不存在cglib庫,直接拋出異常。  

138         if (!cglibAvailable) {   

139             throw new AopConfigException(   

140                     "Cannot proxy target class because CGLIB2 is not available. " +   

141                     "Add CGLIB to the class path or specify proxy interfaces.");   

142         }   

143         // 這裏使用Cglib來生成Proxy,如果target不是接口的實現的話,返回cglib類型的AopProxy  

144         return CglibProxyFactory.createCglibProxy(advisedSupport);   

145     }   

146     else {   

147         // 這裏使用JDK來生成Proxy,返回JDK類型的AopProxy  

148         return new JdkDynamicAopProxy(advisedSupport);   

149     }   

150 }  

151 public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {  

152     //首先考慮使用cglib來實現代理對象,當然如果同時目標對象不是接口的實現類的話  

153     if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||  

154         advisedSupport.getProxiedInterfaces().length == 0) {  

155         //這裏判斷如果不存在cglib庫,直接拋出異常。  

156         if (!cglibAvailable) {  

157             throw new AopConfigException(  

158                     "Cannot proxy target class because CGLIB2 is not available. " +  

159                     "Add CGLIB to the class path or specify proxy interfaces.");  

160         }  

161         // 這裏使用Cglib來生成Proxy,如果target不是接口的實現的話,返回cglib類型的AopProxy  

162         return CglibProxyFactory.createCglibProxy(advisedSupport);  

163     }  

164     else {  

165         // 這裏使用JDK來生成Proxy,返回JDK類型的AopProxy  

166         return new JdkDynamicAopProxy(advisedSupport);  

167     }  

168 }  

於是我們就可以看到其中的代理對象可以由JDK或者Cglib來生成,我們看到JdkDynamicAopProxy類和Cglib2AopProxy都 實現的是AopProxy的接口,在JdkDynamicAopProxy實現中我們可以看到Proxy是怎樣生成的:
  

Java代碼 

169 public Object getProxy(ClassLoader classLoader) {   

170     if (logger.isDebugEnabled()) {   

171         Class targetClass = this.advised.getTargetSource().getTargetClass();   

172         logger.debug("Creating JDK dynamic proxy" +   

173                 (targetClass != null ? " for [" + targetClass.getName() + "]" : ""));   

174     }   

175     Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);   

176     findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);   

177     //這裏我們調用JDK Proxy來生成需要的Proxy實例  

178     return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);   

179 }  

180 public Object getProxy(ClassLoader classLoader) {  

181     if (logger.isDebugEnabled()) {  

182         Class targetClass = this.advised.getTargetSource().getTargetClass();  

183         logger.debug("Creating JDK dynamic proxy" +  

184                 (targetClass != null ? " for [" + targetClass.getName() + "]" : ""));  

185     }  

186     Class[] proxiedInterfaces = <a href="http://AopProxyUtils.com" title="http://AopProxyUtils.com" target="_blank">AopProxyUtils.com</a>pleteProxiedInterfaces(this.advised);  

187     findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  

188     //這裏我們調用JDK Proxy來生成需要的Proxy實例  

189     return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  

190 }  

這樣用Proxy包裝target之後,通過ProxyFactoryBean得到對其方法的調用就被Proxy攔截了, ProxyFactoryBeangetObject()方法得到的實際上是一個Proxy了,我們的target對象已經被封裝了。對 ProxyFactoryBean這個工廠bean而言,其生產出來的對象是封裝了目標對象的代理對象。

 

我們看看Spring中的事務處理的代碼,使用Spring管理事務有聲明式和編程式兩種方式,聲明式事務處理通過AOP的實現把事物管理代碼作爲 方面封裝來橫向插入到業務代碼中,使得事務管理代碼和業務代碼解藕。在這種方式我們結合IoC容器和Spirng已有的FactoryBean來對事務管 理進行屬性配置,比如傳播行爲,隔離級別等。其中最簡單的方式就是通過配置TransactionProxyFactoryBean來實現聲明式事物;
 在整個源代碼分析中,我們可以大致可以看到Spring實現聲明式事物管理有這麼幾個部分:

    * 對在上下文中配置的屬性的處理,這裏涉及的類是TransactionAttributeSourceAdvisor,這是一個通知器,用它來對屬性值進 行處理,屬性信息放在TransactionAttribute中來使用,而這些屬性的處理往往是和對切入點的處理是結合起來的。對屬性的處理放在類 TransactionAttributeSource中完成。
     * 創建事物的過程,這個過程是委託給具體的事物管理器來創建的,但Spring通過TransactionStatus來傳遞相關的信息。
     * 對事物的處理通過對相關信息的判斷來委託給具體的事物管理器完成。

我們下面看看具體的實現,在TransactionFactoryBean中:
  

Java代碼 

public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean   

        implements FactoryBean, BeanFactoryAware {   

//這裏是Spring事務處理而使用的AOP攔截器,中間封裝了Spring對事務處理的代碼來支持聲明式事務處理的實現  

    private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();   

  

    private Pointcut pointcut;   

  

//這裏SpringTransactionManager注入到TransactionInterceptor中去  

    public void setTransactionManager(PlatformTransactionManager transactionManager) {   

10         this.transactionInterceptor.setTransactionManager(transactionManager);   

11     }   

12   

13 //這裏把在bean配置文件中讀到的事務管理的屬性信息注入到TransactionInterceptor中去  

14     public void setTransactionAttributes(Properties transactionAttributes) {   

15         this.transactionInterceptor.setTransactionAttributes(transactionAttributes);   

16     }   

17   

18     ………中間省略了其他一些方法…….   

19   

20     //這裏創建Spring AOP對事務處理的Advisor  

21     protected Object createMainInterceptor() {   

22         this.transactionInterceptor.afterPropertiesSet();   

23         if (this.pointcut != null) {   

24             //這裏使用默認的通知器  

25             return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);   

26         }   

27         else {   

28             // 使用上面定義好的TransactionInterceptor作爲攔截器,同時使用TransactionAttributeSourceAdvisor  

29             return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);   

30         }   

31     }   

32 }  

33 public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean  

34         implements FactoryBean, BeanFactoryAware {  

35 //這裏是Spring事務處理而使用的AOP攔截器,中間封裝了Spring對事務處理的代碼來支持聲明式事務處理的實現  

36     private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();  

37   

38     private Pointcut pointcut;  

39   

40 //這裏SpringTransactionManager注入到TransactionInterceptor中去  

41     public void setTransactionManager(PlatformTransactionManager transactionManager) {  

42         this.transactionInterceptor.setTransactionManager(transactionManager);  

43     }  

44   

45 //這裏把在bean配置文件中讀到的事務管理的屬性信息注入到TransactionInterceptor中去  

46     public void setTransactionAttributes(Properties transactionAttributes) {  

47         this.transactionInterceptor.setTransactionAttributes(transactionAttributes);  

48     }  

49   

50     .........中間省略了其他一些方法.......  

51   

52     //這裏創建Spring AOP對事務處理的Advisor  

53     protected Object createMainInterceptor() {  

54         this.transactionInterceptor.afterPropertiesSet();  

55         if (this.pointcut != null) {  

56             //這裏使用默認的通知器  

57             return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);  

58         }  

59         else {  

60             // 使用上面定義好的TransactionInterceptor作爲攔截器,同時使用TransactionAttributeSourceAdvisor  

61             return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);  

62         }  

63     }  

64 }  

那什麼時候SpringTransactionInterceptor被注入到Spring AOP中成爲Advisor中的一部分呢?我們看到在TransactionProxyFactoryBean中,這個方法在IOC初始化bean的時候被執行:
  

Java代碼 

65 public void afterPropertiesSet() {   

66     …….   

67     //TransactionProxyFactoryBean實際上使用ProxyFactory完成AOP的基本功能。  

68     ProxyFactory proxyFactory = new ProxyFactory();   

69   

70     if (this.preInterceptors != null) {   

71         for (int i = 0; i < this.preInterceptors.length; i++) {   

72             proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));   

73         }   

74     }   

75   

76     //這裏是Spring加入通知器的地方  

77     //有兩種通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor  

78     //這裏把Spring處理聲明式事務處理的AOP代碼都放到ProxyFactory中去,怎樣加入advisor我們可以參考ProxyFactory的父類AdvisedSupport()  

79     //由它來維護一個advice的鏈表,通過這個鏈表的增刪改來抽象我們對整個通知器配置的增刪改操作。  

80     proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));   

81   

82     if (this.postInterceptors != null) {   

83         for (int i = 0; i < this.postInterceptors.length; i++) {   

84             proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));   

85         }   

86     }   

87   

88     proxyFactory.copyFrom(this);   

89       

90     //這裏創建AOP的目標源  

91     TargetSource targetSource = createTargetSource(this.target);   

92     proxyFactory.setTargetSource(targetSource);   

93   

94     if (this.proxyInterfaces != null) {   

95         proxyFactory.setInterfaces(this.proxyInterfaces);   

96     }   

97     else if (!isProxyTargetClass()) {   

98         proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));   

99     }   

100   

101     this.proxy = getProxy(proxyFactory);   

102 }  

103 public void afterPropertiesSet() {  

104     .......  

105     //TransactionProxyFactoryBean實際上使用ProxyFactory完成AOP的基本功能。  

106     ProxyFactory proxyFactory = new ProxyFactory();  

107   

108     if (this.preInterceptors != null) {  

109         for (int i = 0; i < this.preInterceptors.length; i++) {  

110             proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));  

111         }  

112     }  

113   

114     //這裏是Spring加入通知器的地方  

115     //有兩種通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor  

116     //這裏把Spring處理聲明式事務處理的AOP代碼都放到ProxyFactory中去,怎樣加入advisor我們可以參考ProxyFactory的父類AdvisedSupport()  

117     //由它來維護一個advice的鏈表,通過這個鏈表的增刪改來抽象我們對整個通知器配置的增刪改操作。  

118     proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));  

119   

120     if (this.postInterceptors != null) {  

121         for (int i = 0; i < this.postInterceptors.length; i++) {  

122             proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));  

123         }  

124     }  

125   

126     proxyFactory.copyFrom(this);  

127   

128     //這裏創建AOP的目標源  

129     TargetSource targetSource = createTargetSource(this.target);  

130     proxyFactory.setTargetSource(targetSource);  

131   

132     if (this.proxyInterfaces != null) {  

133         proxyFactory.setInterfaces(this.proxyInterfaces);  

134     }  

135     else if (!isProxyTargetClass()) {  

136         proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));  

137     }  

138   

139     this.proxy = getProxy(proxyFactory);  

140 }  

Spring 已經定義了一個transctionInterceptor作爲攔截器或者AOP advice的實現,在IOC容器中定義的其他屬性比如transactionManager和事務管理的屬性都會傳到已經定義好的 TransactionInterceptor那裏去進行處理。以上反映了基本的Spring AOP的定義過程,其中pointcutadvice都已經定義好,同時也通過通知器配置到ProxyFactory中去了。
 下面讓我們回到TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎 樣定義的,這樣我們可以理解具體的屬性是怎樣起作用,這裏我們分析一下類TransactionAttributeSourceAdvisor:
  

Java代碼 

141 public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {   

142     //和其他Advisor一樣,同樣需要定義AOP中的用到的InterceptorPointcut  

143     //Interceptor使用傳進來的TransactionInterceptor  

144     //而對於pointcut,這裏定義了一個內部類,參見下面的代碼    

145     private TransactionInterceptor transactionInterceptor;   

146   

147     private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();   

148       

149     ………   

150     //定義的PointCut內部類  

151         private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {   

152        …….   

153       //方法匹配的實現,使用了TransactionAttributeSource  

154         public boolean matches(Method method, Class targetClass) {   

155             TransactionAttributeSource tas = getTransactionAttributeSource();   

156             //這裏使用TransactionAttributeSource來對配置屬性進行處理  

157             return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);   

158         }   

159     ……..省略了equal,hashcode,tostring的代碼   

160     }  

161 public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {  

162     //和其他Advisor一樣,同樣需要定義AOP中的用到的InterceptorPointcut  

163     //Interceptor使用傳進來的TransactionInterceptor  

164     //而對於pointcut,這裏定義了一個內部類,參見下面的代碼  

165     private TransactionInterceptor transactionInterceptor;  

166   

167     private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();  

168   

169     .........  

170     //定義的PointCut內部類  

171         private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {  

172        .......  

173       //方法匹配的實現,使用了TransactionAttributeSource類  

174         public boolean matches(Method method, Class targetClass) {  

175             TransactionAttributeSource tas = getTransactionAttributeSource();  

176             //這裏使用TransactionAttributeSource來對配置屬性進行處理  

177             return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);  

178         }  

179     ........省略了equal,hashcode,tostring的代碼  

180     }  

這裏我們看看屬性值是怎樣被讀入的:AbstractFallbackTransactionAttributeSource負責具體的屬性讀入任務,我 們可以有兩種讀入方式,比如annotation和直接配置.我們下面看看直接配置的讀入方式,在Spring中同時對讀入的屬性值進行了緩存處理,這是 一個decorator模式:
  

Java代碼 

181 public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {   

182     //這裏先查一下緩存裏有沒有事務管理的屬性配置,如果有從緩存中取得TransactionAttribute  

183     Object cacheKey = getCacheKey(method, targetClass);   

184     Object cached = this.cache.get(cacheKey);   

185     if (cached != null) {   

186         if (cached == NULL_TRANSACTION_ATTRIBUTE) {   

187             return null;   

188         }   

189         else {   

190             return (TransactionAttribute) cached;   

191         }   

192     }   

193     else {   

194         // 這裏通過對方法和目標對象的信息來計算事務緩存屬性  

195         TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);   

196         //把得到的事務緩存屬性存到緩存中,下次可以直接從緩存中取得。  

197         if (txAtt == null) {   

198             this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);   

199         }   

200         else {   

201             ………..   

202             this.cache.put(cacheKey, txAtt);   

203         }   

204         return txAtt;   

205     }   

206 }  

207 public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {  

208     //這裏先查一下緩存裏有沒有事務管理的屬性配置,如果有從緩存中取得TransactionAttribute  

209     Object cacheKey = getCacheKey(method, targetClass);  

210     Object cached = this.cache.get(cacheKey);  

211     if (cached != null) {  

212         if (cached == NULL_TRANSACTION_ATTRIBUTE) {  

213             return null;  

214         }  

215         else {  

216             return (TransactionAttribute) cached;  

217         }  

218     }  

219     else {  

220         // 這裏通過對方法和目標對象的信息來計算事務緩存屬性  

221         TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);  

222         //把得到的事務緩存屬性存到緩存中,下次可以直接從緩存中取得。  

223         if (txAtt == null) {  

224             this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);  

225         }  

226         else {  

227             ...........  

228             this.cache.put(cacheKey, txAtt);  

229         }  

230         return txAtt;  

231     }  

232 }  

別急,基本的處理在computeTransactionAttribute()中:
  

Java代碼 

233 private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {   

234     //這裏檢測是不是public方法  

235     if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {   

236         return null;   

237     }   

238       

239     Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);   

240       

241     // First try is the method in the target class.  

242     TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));   

243     if (txAtt != null) {   

244         return txAtt;   

245     }   

246   

247     // Second try is the transaction attribute on the target class.  

248     txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));   

249     if (txAtt != null) {   

250         return txAtt;   

251     }   

252   

253     if (specificMethod != method) {   

254         // Fallback is to look at the original method.  

255         txAtt = findTransactionAttribute(findAllAttributes(method));   

256         if (txAtt != null) {   

257             return txAtt;   

258         }   

259         // Last fallback is the class of the original method.  

260         return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));   

261     }   

262     return null;   

263 }  

264 private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {  

265     //這裏檢測是不是public方法  

266     if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {  

267         return null;  

268     }  

269   

270     Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);  

271   

272     // First try is the method in the target class.  

273     TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));  

274     if (txAtt != null) {  

275         return txAtt;  

276     }  

277   

278     // Second try is the transaction attribute on the target class.  

279     txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));  

280     if (txAtt != null) {  

281         return txAtt;  

282     }  

283   

284     if (specificMethod != method) {  

285         // Fallback is to look at the original method.  

286         txAtt = findTransactionAttribute(findAllAttributes(method));  

287         if (txAtt != null) {  

288             return txAtt;  

289         }  

290         // Last fallback is the class of the original method.  

291         return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));  

292     }  

293     return null;  

294 }  

經過一系列的嘗試我們可以通過findTransactionAttribute()通過調用findAllAttribute()得到TransactionAttribute的對象,如果返回的是null,這說明該方法不是我們需要事務處理的方法。
 在完成把需要的通知器加到ProxyFactory中去的基礎上,我們看看具體的看事務處理代碼怎樣起作用,在TransactionInterceptor中:
  

Java代碼 

295 public Object invoke(final MethodInvocation invocation) throws Throwable {   

296     //這裏得到目標對象  

297     Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);   

298   

299     //這裏同樣的通過判斷是否能夠得到TransactionAttribute來決定是否對當前方法進行事務處理,有可能該屬性已經被緩存,  

300     //具體可以參考上面對getTransactionAttribute的分析,同樣是通過TransactionAttributeSource  

301     final TransactionAttribute txAttr =   

302             getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);   

303     final String joinpointIdentification = methodIdentification(invocation.getMethod());   

304   

305     //這裏判斷我們使用了什麼TransactionManager  

306     if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {   

307         // 這裏創建事務,同時把創建事務過程中得到的信息放到TransactionInfo中去  

308         TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);   

309         Object retVal = null;   

310         try {   

311               retVal = invocation.proceed();   

312         }   

313         catch (Throwable ex) {   

314             // target invocation exception  

315             completeTransactionAfterThrowing(txInfo, ex);   

316             throw ex;   

317         }   

318         finally {   

319             cleanupTransactionInfo(txInfo);   

320         }   

321         commitTransactionAfterReturning(txInfo);   

322         return retVal;   

323     }   

324   

325     else {   

326         // 使用的是Spring定義的PlatformTransactionManager同時實現了回調接口,我們通過其回調函數完成事務處理,就像我們使用編程式事務處理一樣。  

327         try {   

328             Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,   

329                     new TransactionCallback() {   

330                         public Object doInTransaction(TransactionStatus status) {   

331                             //同樣的需要一個TransactonInfo  

332                             TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);   

333                             try {   

334                                 return invocation.proceed();   

335                             }   

336                          …..這裏省去了異常處理和事務信息的清理代碼   

337                     });   

338          ………..   

339     }   

340 }  

341 public Object invoke(final MethodInvocation invocation) throws Throwable {  

342     //這裏得到目標對象  

343     Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);  

344   

345     //這裏同樣的通過判斷是否能夠得到TransactionAttribute來決定是否對當前方法進行事務處理,有可能該屬性已經被緩存,  

346     //具體可以參考上面對getTransactionAttribute的分析,同樣是通過TransactionAttributeSource  

347     final TransactionAttribute txAttr =  

348             getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);  

349     final String joinpointIdentification = methodIdentification(invocation.getMethod());  

350   

351     //這裏判斷我們使用了什麼TransactionManager  

352     if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {  

353         // 這裏創建事務,同時把創建事務過程中得到的信息放到TransactionInfo中去  

354         TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);  

355         Object retVal = null;  

356         try {  

357               retVal = invocation.proceed();  

358         }  

359         catch (Throwable ex) {  

360             // target invocation exception  

361             completeTransactionAfterThrowing(txInfo, ex);  

362             throw ex;  

363         }  

364         finally {  

365             cleanupTransactionInfo(txInfo);  

366         }  

367         commitTransactionAfterReturning(txInfo);  

368         return retVal;  

369     }  

370   

371     else {  

372         // 使用的是Spring定義的PlatformTransactionManager同時實現了回調接口,我們通過其回調函數完成事務處理,就像我們使用編程式事務處理一樣。  

373         try {  

374             Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,  

375                     new TransactionCallback() {  

376                         public Object doInTransaction(TransactionStatus status) {  

377                             //同樣的需要一個TransactonInfo  

378                             TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);  

379                             try {  

380                                 return invocation.proceed();  

381                             }  

382                          .....這裏省去了異常處理和事務信息的清理代碼  

383                     });  

384          ...........  

385     }  

386 }  

這裏面涉及到事務的創建,我們可以在TransactionAspectSupport實現的事務管理代碼:
  

Java代碼 

387 protected TransactionInfo createTransactionIfNecessary(   

388         TransactionAttribute txAttr, final String joinpointIdentification) {   

389   

390     // If no name specified, apply method identification as transaction name.  

391     if (txAttr != null && txAttr.getName() == null) {   

392         txAttr = new DelegatingTransactionAttribute(txAttr) {   

393             public String getName() {   

394                 return joinpointIdentification;   

395             }   

396         };   

397     }   

398   

399     TransactionStatus status = null;   

400     if (txAttr != null) {   

401     //這裏使用了我們定義好的事務配置信息,有事務管理器來創建事務,同時返回TransactionInfo  

402         status = getTransactionManager().getTransaction(txAttr);   

403     }   

404     return prepareTransactionInfo(txAttr, joinpointIdentification, status);   

405 }  

406 protected TransactionInfo createTransactionIfNecessary(  

407         TransactionAttribute txAttr, final String joinpointIdentification) {  

408   

409     // If no name specified, apply method identification as transaction name.  

410     if (txAttr != null && txAttr.getName() == null) {  

411         txAttr = new DelegatingTransactionAttribute(txAttr) {  

412             public String getName() {  

413                 return joinpointIdentification;  

414             }  

415         };  

416     }  

417   

418     TransactionStatus status = null;  

419     if (txAttr != null) {  

420     //這裏使用了我們定義好的事務配置信息,有事務管理器來創建事務,同時返回TransactionInfo  

421         status = getTransactionManager().getTransaction(txAttr);  

422     }  

423     return prepareTransactionInfo(txAttr, joinpointIdentification, status);  

424 }  

首先通過TransactionManager得到需要的事務,事務的創建根據我們定義的事務配置決定,在 AbstractTransactionManager中給出一個標準的創建過程,當然創建什麼樣的事務還是需要具體的 PlatformTransactionManager來決定,但這裏給出了創建事務的模板:
  

Java代碼 

425 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {   

426     Object transaction = doGetTransaction();   

427     ……   

428   

429     if (definition == null) {   

430         //如果事務信息沒有被配置,我們使用Spring默認的配置方式  

431         definition = new DefaultTransactionDefinition();   

432     }   

433   

434     if (isExistingTransaction(transaction)) {   

435         // Existing transaction found -> check propagation behavior to find out how to behave.  

436         return handleExistingTransaction(definition, transaction, debugEnabled);   

437     }   

438   

439     // Check definition settings for new transaction.  

440     //下面就是使用配置信息來創建我們需要的事務;比如傳播屬性和同步屬性等  

441     //最後把創建過程中的信息收集起來放到TransactionStatus中返回;    

442     if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {   

443         throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());   

444     }   

445   

446     // No existing transaction found -> check propagation behavior to find out how to behave.  

447     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {   

448         throw new IllegalTransactionStateException(   

449                 "Transaction propagation 'mandatory' but no existing transaction found");   

450     }   

451     else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||   

452             definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||   

453         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {   

454         //這裏是事務管理器創建事務的地方,並將創建過程中得到的信息放到TransactionStatus中去,包括創建出來的事務  

455         doBegin(transaction, definition);   

456         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);   

457         return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);   

458     }   

459     else {   

460         boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);   

461         return newTransactionStatus(definition, nullfalse, newSynchronization, debugEnabled, null);   

462     }   

463 }  

464 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {  

465     Object transaction = doGetTransaction();  

466     ......  

467   

468     if (definition == null) {  

469         //如果事務信息沒有被配置,我們使用Spring默認的配置方式  

470         definition = new DefaultTransactionDefinition();  

471     }  

472   

473     if (isExistingTransaction(transaction)) {  

474         // Existing transaction found -> check propagation behavior to find out how to behave.  

475         return handleExistingTransaction(definition, transaction, debugEnabled);  

476     }  

477   

478     // Check definition settings for new transaction.  

479     //下面就是使用配置信息來創建我們需要的事務;比如傳播屬性和同步屬性等  

480     //最後把創建過程中的信息收集起來放到TransactionStatus中返回;  

481     if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {  

482         throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());  

483     }  

484   

485     // No existing transaction found -> check propagation behavior to find out how to behave.  

486     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {  

487         throw new IllegalTransactionStateException(  

488                 "Transaction propagation 'mandatory' but no existing transaction found");  

489     }  

490     else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||  

491             definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||  

492         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {  

493         //這裏是事務管理器創建事務的地方,並將創建過程中得到的信息放到TransactionStatus中去,包括創建出來的事務  

494         doBegin(transaction, definition);  

495         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);  

496         return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);  

497     }  

498     else {  

499         boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);  

500         return newTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null);  

501     }  

502 }  

接着通過調用prepareTransactionInfo完成事務創建的準備,創建過程中得到的信息存儲在TransactionInfo對象中進行傳遞同時把信息和當前線程綁定;
  

Java代碼 

503 protected TransactionInfo prepareTransactionInfo(   

504         TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {   

505   

506     TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);   

507     if (txAttr != null) {   

508     …..   

509         // 同樣的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中來。  

510         txInfo.newTransactionStatus(status);   

511     }   

512     else {   

513     …….   

514    }   

515   

516     // 綁定事務創建信息到當前線程  

517     txInfo.bindToThread();   

518     return txInfo;   

519 }  

520 protected TransactionInfo prepareTransactionInfo(  

521         TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {  

522   

523     TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);  

524     if (txAttr != null) {  

525     .....  

526         // 同樣的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中來。  

527         txInfo.newTransactionStatus(status);  

528     }  

529     else {  

530     .......  

531    }  

532   

533     // 綁定事務創建信息到當前線程  

534     txInfo.bindToThread();  

535     return txInfo;  

536 }  

將創建事務的信息返回,然後看到其他的事務管理代碼:
  

Java代碼 

537 protected void commitTransactionAfterReturning(TransactionInfo txInfo) {   

538     if (txInfo != null && txInfo.hasTransaction()) {   

539         if (logger.isDebugEnabled()) {   

540             logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());   

541         }   

542         this.transactionManager.commit(txInfo.getTransactionStatus());   

543     }   

544 }  

545 protected void commitTransactionAfterReturning(TransactionInfo txInfo) {  

546     if (txInfo != null && txInfo.hasTransaction()) {  

547         if (logger.isDebugEnabled()) {  

548             logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());  

549         }  

550         <a href="http://this.transactionManager.com" title="http://this.transactionManager.com" target="_blank">this.transactionManager.com</a>mit(txInfo.getTransactionStatus());  

551     }  

552 }  

通過transactionManager對事務進行處理,包括異常拋出和正常的提交事務,具體的事務管理器由用戶程序設定。
  

Java代碼 

553 protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {   

554     if (txInfo != null && txInfo.hasTransaction()) {   

555         if (txInfo.transactionAttribute.rollbackOn(ex)) {   

556             ……   

557             try {   

558                 this.transactionManager.rollback(txInfo.getTransactionStatus());   

559             }   

560             ……….   

561   }   

562         else {   

563             ………   

564             try {   

565                 this.transactionManager.commit(txInfo.getTransactionStatus());   

566             }   

567    ………..   

568 }   

569   

570 protected void commitTransactionAfterReturning(TransactionInfo txInfo) {   

571     if (txInfo != null && txInfo.hasTransaction()) {   

572         ……   

573         this.transactionManager.commit(txInfo.getTransactionStatus());   

574     }   

575 }  

576 protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {  

577     if (txInfo != null && txInfo.hasTransaction()) {  

578         if (txInfo.transactionAttribute.rollbackOn(ex)) {  

579             ......  

580             try {  

581                 this.transactionManager.rollback(txInfo.getTransactionStatus());  

582             }  

583             ..........  

584   }  

585         else {  

586             .........  

587             try {  

588                 <a href="http://this.transactionManager.com" title="http://this.transactionManager.com" target="_blank">this.transactionManager.com</a>mit(txInfo.getTransactionStatus());  

589             }  

590    ...........  

591 }  

592   

593 protected void commitTransactionAfterReturning(TransactionInfo txInfo) {  

594     if (txInfo != null && txInfo.hasTransaction()) {  

595         ......  

596         <a href="http://this.transactionManager.com" title="http://this.transactionManager.com" target="_blank">this.transactionManager.com</a>mit(txInfo.getTransactionStatus());  

597     }  

598 }  

Spring通過以上代碼對transactionManager進行事務處理的過程進行了AOP包裝,到這裏我們看到爲了方便客戶實現聲明式的事務處理,Spring還是做了許多工作的。如果說使用編程式事務處理,過程其實比較清楚,我們可以參考書中的例子:
  

Java代碼 

599 TransactionDefinition td = new DefaultTransactionDefinition();   

600 TransactionStatus status = transactionManager.getTransaction(td);   

601 try{   

602       ……//這裏是我們的業務方法  

603 }catch (ApplicationException e) {   

604    transactionManager.rollback(status);   

605    throw e   

606 }   

607 transactionManager.commit(status);   

608  ……..  

609 TransactionDefinition td = new DefaultTransactionDefinition();  

610 TransactionStatus status = transactionManager.getTransaction(td);  

611 try{  

612       ......//這裏是我們的業務方法  

613 }catch (ApplicationException e) {  

614    transactionManager.rollback(status);  

615    throw e  

616 }  

617 <a href="http://transactionManager.com" title="http://transactionManager.com" target="_blank">transactionManager.com</a>mit(status);  

618  ........  

我們看到這裏選取了默認的事務配置DefaultTransactionDefinition,同時在創建事物的過程中得到TransactionStatus,然後通過直接調用事務管理器的相關方法就能完成事務處理。
 聲明式事務處理也同樣實現了類似的過程,只是因爲採用了聲明的方法,需要增加對屬性的讀取處理,並且需要把整個過程整合到Spring AOP框架中和IoC容器中去的過程。
 下面我們選取一個具體的transactionManager – DataSourceTransactionManager來看看其中事務處理的實現:
 同樣的通過使用AbstractPlatformTransactionManager使用模板方法,這些都體現了對具體平臺相關的事務管理器操作的封裝,比如commit
  

Java代碼 

619 public final void commit(TransactionStatus status) throws TransactionException {   

620     ……   

621     DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;   

622     if (defStatus.isLocalRollbackOnly()) {   

623         ……   

624         processRollback(defStatus);   

625         return;   

626     }   

627          …….   

628         processRollback(defStatus);   

629     ……   

630     }   

631   

632     processCommit(defStatus);   

633 }  

634 public final void commit(TransactionStatus status) throws TransactionException {  

635     ......  

636     DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;  

637     if (defStatus.isLocalRollbackOnly()) {  

638         ......  

639         processRollback(defStatus);  

640         return;  

641     }  

642          .......  

643         processRollback(defStatus);  

644     ......  

645     }  

646   

647     processCommit(defStatus);  

648 }  

通過對TransactionStatus的具體狀態的判斷,來決定具體的事務處理:
  

Java代碼 

649 private void processCommit(DefaultTransactionStatus status) throws TransactionException {   

650     try {   

651         boolean beforeCompletionInvoked = false;   

652         try {   

653             triggerBeforeCommit(status);   

654             triggerBeforeCompletion(status);   

655             beforeCompletionInvoked = true;   

656             boolean globalRollbackOnly = false;   

657             if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {   

658                 globalRollbackOnly = status.isGlobalRollbackOnly();   

659             }   

660             if (status.hasSavepoint()) {   

661             ……..   

662                status.releaseHeldSavepoint();   

663             }   

664             else if (status.isNewTransaction()) {   

665             ……   

666                 doCommit(status);   

667             }   

668         ………   

669 }  

670 private void processCommit(DefaultTransactionStatus status) throws TransactionException {  

671     try {  

672         boolean beforeCompletionInvoked = false;  

673         try {  

674             triggerBeforeCommit(status);  

675             triggerBeforeCompletion(status);  

676             beforeCompletionInvoked = true;  

677             boolean globalRollbackOnly = false;  

678             if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {  

679                 globalRollbackOnly = status.isGlobalRollbackOnly();  

680             }  

681             if (status.hasSavepoint()) {  

682             ........  

683                status.releaseHeldSavepoint();  

684             }  

685             else if (status.isNewTransaction()) {  

686             ......  

687                 doCommit(status);  

688             }  

689         .........  

690 }  

這些模板方法的實現由具體的transactionManager來實現,比如在DataSourceTransactionManager:
  

Java代碼 

691 protected void doCommit(DefaultTransactionStatus status) {   

692     //這裏得到存在TransactionInfo中已經創建好的事務  

693     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   

694   

695     //這裏得到和事務綁定的數據庫連接  

696     Connection con = txObject.getConnectionHolder().getConnection();   

697     ……..   

698     try {   

699     //這裏通過數據庫連接來提交事務  

700         con.commit();   

701     }   

702    …….   

703 }   

704   

705 protected void doRollback(DefaultTransactionStatus status) {   

706     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   

707     Connection con = txObject.getConnectionHolder().getConnection();   

708     if (status.isDebug()) {   

709         logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");   

710     }   

711     try {   

712     //這裏通過數據庫連接來回滾事務  

713         con.rollback();   

714     }   

715     catch (SQLException ex) {   

716         throw new TransactionSystemException("Could not roll back JDBC transaction", ex);   

717     }   

718 }  

719 protected void doCommit(DefaultTransactionStatus status) {  

720     //這裏得到存在TransactionInfo中已經創建好的事務  

721     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();  

722   

723     //這裏得到和事務綁定的數據庫連接  

724     Connection con = txObject.getConnectionHolder().getConnection();  

725     ........  

726     try {  

727     //這裏通過數據庫連接來提交事務  

728         <a href="http://con.com" title="http://con.com" target="_blank">con.com</a>mit();  

729     }  

730    .......  

731 }  

732   

733 protected void doRollback(DefaultTransactionStatus status) {  

734     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();  

735     Connection con = txObject.getConnectionHolder().getConnection();  

736     if (status.isDebug()) {  

737         logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");  

738     }  

739     try {  

740     //這裏通過數據庫連接來回滾事務  

741         con.rollback();  

742     }  

743     catch (SQLException ex) {  

744         throw new TransactionSystemException("Could not roll back JDBC transaction", ex);  

745     }  

746 }  

我們看到在DataSourceTransactionManager中最後還是交給connection來實現事務的提交和rollback。整個聲明 式事務處理是事務處理在Spring AOP中的應用,我們看到了一個很好的使用Spring AOP的例子,在Spring聲明式事務處理的源代碼中我們可以看到:
 1.怎樣封裝各種不同平臺下的事務處理代碼
 2.怎樣讀取屬性值和結合事務處理代碼來完成既定的事務處理策略
 3.怎樣靈活的使用SpringAOP框架。
 如果能夠結合前面的Spring AOP的源代碼來學習,理解可能會更深刻些。

 

前面我們分析了Spring AOP實現中得到Proxy對象的過程,下面我們看看在Spring AOP中攔截器鏈是怎樣被調用的,也就是Proxy模式是怎樣起作用的,或者說Spring是怎樣爲我們提供AOP功能的;
 在JdkDynamicAopProxy中生成Proxy對象的時候:
  

Java代碼 

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  

這裏的this參數對應的是InvocationHandler對象,這裏我們的JdkDynamicAopProxy實現了這個接口,也就是說當 Proxy對象的函數被調用的時候,這個InvocationHandlerinvoke方法會被作爲回調函數調用,下面我們看看這個方法的實現:
  

Java代碼 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   

    MethodInvocation invocation = null;   

    Object oldProxy = null;   

    boolean setProxyContext = false;   

  

    TargetSource targetSource = this.advised.targetSource;   

    Class targetClass = null;   

10     Object target = null;   

11   

12     try {   

13         // Try special rules for equals() method and implementation of the  

14         // Advised AOP configuration interface.  

15   

16         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {   

17             // What if equals throws exception!?  

18             // This class implements the equals(Object) method itself.  

19             return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;   

20         }   

21         if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {   

22             // This class implements the hashCode() method itself.  

23             return new Integer(hashCode());   

24         }   

25         if (Advised.class == method.getDeclaringClass()) {   

26             // service invocations on ProxyConfig with the proxy config  

27             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);   

28         }   

29   

30         Object retVal = null;   

31   

32         if (this.advised.exposeProxy) {   

33             // make invocation available if necessary  

34             oldProxy = AopContext.setCurrentProxy(proxy);   

35             setProxyContext = true;   

36         }   

37   

38         // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,  

39         // in case it comes from a pool.  

40         // 這裏是得到目標對象的地方,當然這個目標對象可能來自於一個實例池或者是一個簡單的JAVA對象  

41         target = targetSource.getTarget();   

42         if (target != null) {   

43             targetClass = target.getClass();   

44         }   

45   

46         // get the interception chain for this method  

47         // 這裏獲得定義好的攔截器鏈  

48         List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(   

49                 this.advised, proxy, method, targetClass);   

50   

51         // Check whether we have any advice. If we don't, we can fallback on direct  

52         // reflective invocation of the target, and avoid creating a MethodInvocation.  

53         // 如果沒有設定攔截器,那麼我們就直接調用目標的對應方法  

54         if (chain.isEmpty()) {   

55             // We can skip creating a MethodInvocation: just invoke the target directly  

56             // Note that the final invoker must be an InvokerInterceptor so we know it does  

57             // nothing but a reflective operation on the target, and no hot swapping or fancy proxying  

58             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);   

59         }   

60         else {   

61             // We need to create a method invocation…  

62             // invocation = advised.getMethodInvocationFactory().getMethodInvocation(  

63             //         proxy, method, targetClass, target, args, chain, advised);  

64             // 如果有攔截器的設定,那麼需要調用攔截器之後才調用目標對象的相應方法  

65             // 這裏通過構造一個ReflectiveMethodInvocation來實現,下面我們會看這個ReflectiveMethodInvocation  

66             invocation = new ReflectiveMethodInvocation(   

67                     proxy, target, method, args, targetClass, chain);   

68   

69             // proceed to the joinpoint through the interceptor chain  

70             // 這裏通過ReflectiveMethodInvocation來調用攔截器鏈和相應的目標方法  

71             retVal = invocation.proceed();   

72         }   

73   

74         // massage return value if necessary  

75         if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {   

76             // Special case: it returned "this" and the return type of the method is type-compatible  

77             // Note that we can't help if the target sets  

78             // a reference to itself in another returned object.  

79             retVal = proxy;   

80         }   

81         return retVal;   

82     }   

83     finally {   

84         if (target != null && !targetSource.isStatic()) {   

85             // must have come from TargetSource  

86             targetSource.releaseTarget(target);   

87         }   

88   

89         if (setProxyContext) {   

90             // restore old proxy  

91             AopContext.setCurrentProxy(oldProxy);   

92         }   

93     }   

94 }  

95 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  

96     MethodInvocation invocation = null;  

97     Object oldProxy = null;  

98     boolean setProxyContext = false;  

99   

100     TargetSource targetSource = this.advised.targetSource;  

101     Class targetClass = null;  

102     Object target = null;  

103   

104     try {  

105         // Try special rules for equals() method and implementation of the  

106         // Advised AOP configuration interface.  

107   

108         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {  

109             // What if equals throws exception!?  

110             // This class implements the equals(Object) method itself.  

111             return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;  

112         }  

113         if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {  

114             // This class implements the hashCode() method itself.  

115             return new Integer(hashCode());  

116         }  

117         if (Advised.class == method.getDeclaringClass()) {  

118             // service invocations on ProxyConfig with the proxy config  

119             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  

120         }  

121   

122         Object retVal = null;  

123   

124         if (this.advised.exposeProxy) {  

125             // make invocation available if necessary  

126             oldProxy = AopContext.setCurrentProxy(proxy);  

127             setProxyContext = true;  

128         }  

129   

130         // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,  

131         // in case it comes from a pool.  

132         // 這裏是得到目標對象的地方,當然這個目標對象可能來自於一個實例池或者是一個簡單的JAVA對象  

133         target = targetSource.getTarget();  

134         if (target != null) {  

135             targetClass = target.getClass();  

136         }  

137   

138         // get the interception chain for this method  

139         // 這裏獲得定義好的攔截器鏈  

140         List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(  

141                 this.advised, proxy, method, targetClass);  

142   

143         // Check whether we have any advice. If we don't, we can fallback on direct  

144         // reflective invocation of the target, and avoid creating a MethodInvocation.  

145         // 如果沒有設定攔截器,那麼我們就直接調用目標的對應方法  

146         if (chain.isEmpty()) {  

147             // We can skip creating a MethodInvocation: just invoke the target directly  

148             // Note that the final invoker must be an InvokerInterceptor so we know it does  

149             // nothing but a reflective operation on the target, and no hot swapping or fancy proxying  

150             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  

151         }  

152         else {  

153             // We need to create a method invocation...  

154             // invocation = advised.getMethodInvocationFactory().getMethodInvocation(  

155             //         proxy, method, targetClass, target, args, chain, advised);  

156             // 如果有攔截器的設定,那麼需要調用攔截器之後才調用目標對象的相應方法  

157             // 這裏通過構造一個ReflectiveMethodInvocation來實現,下面我們會看這個ReflectiveMethodInvocation類  

158             invocation = new ReflectiveMethodInvocation(  

159                     proxy, target, method, args, targetClass, chain);  

160   

161             // proceed to the joinpoint through the interceptor chain  

162             // 這裏通過ReflectiveMethodInvocation來調用攔截器鏈和相應的目標方法  

163             retVal = invocation.proceed();  

164         }  

165   

166         // massage return value if necessary  

167         if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {  

168             // Special case: it returned "this" and the return type of the method is type-compatible  

169             // Note that we can't help if the target sets  

170             // a reference to itself in another returned object.  

171             retVal = proxy;  

172         }  

173         return retVal;  

174     }  

175     finally {  

176         if (target != null && !targetSource.isStatic()) {  

177             // must have come from TargetSource  

178             targetSource.releaseTarget(target);  

179         }  

180   

181         if (setProxyContext) {  

182             // restore old proxy  

183             AopContext.setCurrentProxy(oldProxy);  

184         }  

185     }  

186 }  

我們先看看目標對象方法的調用,這裏是通過AopUtils的方法調用 – 使用反射機制來對目標對象的方法進行調用:
  

Java代碼 

187 public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)   

188     throws Throwable {   

189   

190     // Use reflection to invoke the method.  

191     // 利用放射機制得到相應的方法,並且調用invoke  

192     try {   

193         if (!Modifier.isPublic(method.getModifiers()) ||   

194                 !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {   

195             method.setAccessible(true);   

196         }   

197         return method.invoke(target, args);   

198     }   

199     catch (InvocationTargetException ex) {   

200         // Invoked method threw a checked exception.  

201         // We must rethrow it. The client won't see the interceptor.  

202         throw ex.getTargetException();   

203     }   

204     catch (IllegalArgumentException ex) {   

205         throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +   

206                 method + "] on target [" + target + "]", ex);   

207     }   

208     catch (IllegalAccessException ex) {   

209         throw new AopInvocationException("Couldn't access method: " + method, ex);   

210     }   

211 }  

212 public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)  

213     throws Throwable {  

214   

215     // Use reflection to invoke the method.  

216     // 利用放射機制得到相應的方法,並且調用invoke  

217     try {  

218         if (!Modifier.isPublic(method.getModifiers()) ||  

219                 !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {  

220             method.setAccessible(true);  

221         }  

222         return method.invoke(target, args);  

223     }  

224     catch (InvocationTargetException ex) {  

225         // Invoked method threw a checked exception.  

226         // We must rethrow it. The client won't see the interceptor.  

227         throw ex.getTargetException();  

228     }  

229     catch (IllegalArgumentException ex) {  

230         throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +  

231                 method + "] on target [" + target + "]", ex);  

232     }  

233     catch (IllegalAccessException ex) {  

234         throw new AopInvocationException("Couldn't access method: " + method, ex);  

235     }  

236 }  

對攔截器鏈的調用處理是在ReflectiveMethodInvocation裏實現的:
  

Java代碼 

237 public Object proceed() throws Throwable {   

238     //    We start with an index of -1 and increment early.  

239     // 這裏直接調用目標對象的方法,沒有攔截器的調用或者攔截器已經調用完了,這個currentInterceptorIndex的初始值是0  

240     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) {   

241         return invokeJoinpoint();   

242     }   

243   

244     Object interceptorOrInterceptionAdvice =   

245         this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);   

246     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {   

247         // Evaluate dynamic method matcher here: static part will already have  

248         // been evaluated and found to match.  

249         // 這裏獲得相應的攔截器,如果攔截器可以匹配的上的話,那就調用攔截器的invoke方法  

250         InterceptorAndDynamicMethodMatcher dm =   

251             (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;   

252         if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {   

253             return dm.interceptor.invoke(nextInvocation());   

254         }   

255         else {   

256             // Dynamic matching failed.  

257             // Skip this interceptor and invoke the next in the chain.  

258             // 如果攔截器匹配不上,那就調用下一個攔截器,這個時候攔截器鏈的位置指示後移並迭代調用當前的proceed方法  

259             this.currentInterceptorIndex++;   

260             return proceed();   

261         }   

262     }   

263     else {   

264         // It's an interceptor, so we just invoke it: The pointcut will have  

265         // been evaluated statically before this object was constructed.  

266         return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation());   

267     }   

268 }  

269 public Object proceed() throws Throwable {  

270     //    We start with an index of -1 and increment early.  

271     // 這裏直接調用目標對象的方法,沒有攔截器的調用或者攔截器已經調用完了,這個currentInterceptorIndex的初始值是0  

272     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) {  

273         return invokeJoinpoint();  

274     }  

275   

276     Object interceptorOrInterceptionAdvice =  

277         this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);  

278     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {  

279         // Evaluate dynamic method matcher here: static part will already have  

280         // been evaluated and found to match.  

281         // 這裏獲得相應的攔截器,如果攔截器可以匹配的上的話,那就調用攔截器的invoke方法  

282         InterceptorAndDynamicMethodMatcher dm =  

283             (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;  

284         if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {  

285             return dm.interceptor.invoke(nextInvocation());  

286         }  

287         else {  

288             // Dynamic matching failed.  

289             // Skip this interceptor and invoke the next in the chain.  

290             // 如果攔截器匹配不上,那就調用下一個攔截器,這個時候攔截器鏈的位置指示後移並迭代調用當前的proceed方法  

291             this.currentInterceptorIndex++;  

292             return proceed();  

293         }  

294     }  

295     else {  

296         // It's an interceptor, so we just invoke it: The pointcut will have  

297         // been evaluated statically before this object was constructed.  

298         return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation());  

299     }  

300 }  

這裏把當前的攔截器鏈以及在攔截器鏈的位置標誌都clone到一個MethodInvocation對象了,作用是當前的攔截器執行完之後,會繼續沿着得到這個攔截器鏈執行下面的攔截行爲,也就是會迭代的調用上面這個proceed:
  

Java代碼 

301 private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {   

302     ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();   

303     invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;   

304     invocation.parent = this;   

305     return invocation;   

306 }  

307 private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {  

308     ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();  

309     invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;  

310     invocation.parent = this;  

311     return invocation;  

312 }  

這裏的nextInvocation就已經包含了當前的攔截鏈的基本信息,我們看到在Interceptor中的實現比如TransactionInterceptor的實現中:
  

Java代碼 

313 public Object invoke(final MethodInvocation invocation) throws Throwable {   

314    ……//這裏是TransactionInterceptor插入的事務處理代碼,我們會在後面分析事務處理實現的時候進行分析  

315         try {   

316             //這裏是對配置的攔截器鏈進行迭代處理的調用  

317             retVal = invocation.proceed();   

318         }   

319    ……//省略了和事務處理的異常處理代碼 ,也是TransactionInterceptor插入的處理  

320       else {   

321         try {   

322             Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,   

323                     new TransactionCallback() {   

324                         public Object doInTransaction(TransactionStatus status) {   

325                              //這裏是TransactionInterceptor插入對事務處理的代碼  

326                             TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);   

327                             //這裏是對配置的攔截器鏈進行迭代處理的調用,接着順着攔截器進行處理  

328                             try {                           

329                                 return invocation.proceed();   

330                             }   

331    ……//省略了和事務處理的異常處理代碼 ,也是TransactionInterceptor插入的處理  

332    }  

333 public Object invoke(final MethodInvocation invocation) throws Throwable {  

334    ......//這裏是TransactionInterceptor插入的事務處理代碼,我們會在後面分析事務處理實現的時候進行分析  

335         try {  

336             //這裏是對配置的攔截器鏈進行迭代處理的調用  

337             retVal = invocation.proceed();  

338         }  

339    ......//省略了和事務處理的異常處理代碼 ,也是TransactionInterceptor插入的處理  

340       else {  

341         try {  

342             Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,  

343                     new TransactionCallback() {  

344                         public Object doInTransaction(TransactionStatus status) {  

345                              //這裏是TransactionInterceptor插入對事務處理的代碼  

346                             TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);  

347                             //這裏是對配置的攔截器鏈進行迭代處理的調用,接着順着攔截器進行處理  

348                             try {  

349                                 return invocation.proceed();  

350                             }  

351    ......//省略了和事務處理的異常處理代碼 ,也是TransactionInterceptor插入的處理  

352    }  

從上面的分析我們看到了Spring AOP的基本實現,比如Spring怎樣得到Proxy,怎樣利用JAVA Proxy以及反射機制對用戶定義的攔截器鏈進行處理。

 

O/R工具出現之後,簡化了許多複雜的信息持久化的開發。Spring應用開發者可以通過Spring提供的O/R方案更方便的使用各種持久化工具,比如Hibernate;下面我們就Spring+Hibernate中的Spring實現做一個簡單的剖析。
 SpringHinberanate的配置是通過LocalSessionFactoryBean來完成的,這是一個工廠Bean的實現,在基類AbstractSessionFactoryBean中:
  

Java代碼 

/** 

 * 這是FactoryBean需要實現的接口方法,直接取得當前的sessionFactory的值 

 */  

public Object getObject() {   

    return this.sessionFactory;   

}  

/** 

 * 這是FactoryBean需要實現的接口方法,直接取得當前的sessionFactory的值 

 */  

10 public Object getObject() {  

11     return this.sessionFactory;  

12 }  

這個值在afterPropertySet中定義:
  

Java代碼 

13 public void afterPropertiesSet() throws Exception {   

14     //這個buildSessionFactory是通過配置信息得到SessionFactory的地方  

15     SessionFactory rawSf = buildSessionFactory();   

16     //這裏使用了Proxy方法插入對getCurrentSession的攔截,得到和事務相關的session  

17     this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);   

18 }  

19 public void afterPropertiesSet() throws Exception {  

20     //這個buildSessionFactory是通過配置信息得到SessionFactory的地方  

21     SessionFactory rawSf = buildSessionFactory();  

22     //這裏使用了Proxy方法插入對getCurrentSession的攔截,得到和事務相關的session  

23     this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);  

24 }  

我們先看看SessionFactory是怎樣創建的,這個方法很長,包含了創建HibernateSessionFactory的詳盡步驟:
  

Java代碼 

25 protected SessionFactory buildSessionFactory() throws Exception {   

26     SessionFactory sf = null;   

27   

28     // Create Configuration instance.  

29     Configuration config = newConfiguration();   

30   

31     //這裏配置數據源,事務管理器,LobHanderHolder中,這個Holder是一個ThreadLocal變量,這樣這些資源就和線程綁定了  

32     if (this.dataSource != null) {   

33         // Make given DataSource available for SessionFactory configuration.  

34         configTimeDataSourceHolder.set(this.dataSource);   

35     }   

36   

37     if (this.jtaTransactionManager != null) {   

38         // Make Spring-provided JTA TransactionManager available.  

39         configTimeTransactionManagerHolder.set(this.jtaTransactionManager);   

40     }   

41   

42     if (this.lobHandler != null) {   

43         // Make given LobHandler available for SessionFactory configuration.  

44         // Do early because because mapping resource might refer to custom types.  

45         configTimeLobHandlerHolder.set(this.lobHandler);   

46     }   

47   

48     //這裏是使用Hibernate的各個屬性的配置,這裏使用了Configuration類來抽象這些數據  

49     try {   

50         // Set connection release mode "on_close" as default.  

51         // This was the case for Hibernate 3.0; Hibernate 3.1 changed  

52         // it to "auto" (i.e. "after_statement" or "after_transaction").  

53         // However, for Spring's resource management (in particular for  

54         // HibernateTransactionManager), "on_close" is the better default.  

55         config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString());   

56   

57         if (!isExposeTransactionAwareSessionFactory()) {   

58             // Not exposing a SessionFactory proxy with transaction-aware  

59             // getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext  

60             // implementation instead, providing the Spring-managed Session that way.  

61             // Can be overridden by a custom value for corresponding Hibernate property.  

62             config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS,   

63                     "org.springframework.orm.hibernate3.SpringSessionContext");   

64         }   

65   

66         if (this.entityInterceptor != null) {   

67             // Set given entity interceptor at SessionFactory level.  

68             config.setInterceptor(this.entityInterceptor);   

69         }   

70   

71         if (this.namingStrategy != null) {   

72             // Pass given naming strategy to Hibernate Configuration.  

73             config.setNamingStrategy(this.namingStrategy);   

74         }   

75   

76         if (this.typeDefinitions != null) {   

77             // Register specified Hibernate type definitions.  

78             Mappings mappings = config.createMappings();   

79             for (int i = 0; i < this.typeDefinitions.length; i++) {   

80                 TypeDefinitionBean typeDef = this.typeDefinitions[i];   

81                 mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());   

82             }   

83         }   

84   

85         if (this.filterDefinitions != null) {   

86             // Register specified Hibernate FilterDefinitions.  

87             for (int i = 0; i < this.filterDefinitions.length; i++) {   

88                 config.addFilterDefinition(this.filterDefinitions[i]);   

89             }   

90         }   

91   

92         if (this.configLocations != null) {   

93             for (int i = 0; i < this.configLocations.length; i++) {   

94                 // Load Hibernate configuration from given location.  

95                 config.configure(this.configLocations[i].getURL());   

96             }   

97         }   

98   

99         if (this.hibernateProperties != null) {   

100             // Add given Hibernate properties to Configuration.  

101             config.addProperties(this.hibernateProperties);   

102         }   

103   

104         if (this.dataSource != null) {   

105             boolean actuallyTransactionAware =   

106                     (this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy);   

107             // Set Spring-provided DataSource as Hibernate ConnectionProvider.  

108             config.setProperty(Environment.CONNECTION_PROVIDER,   

109                     actuallyTransactionAware ?   

110                     TransactionAwareDataSourceConnectionProvider.class.getName() :   

111                     LocalDataSourceConnectionProvider.class.getName());   

112         }   

113   

114         if (this.jtaTransactionManager != null) {   

115             // Set Spring-provided JTA TransactionManager as Hibernate property.  

116             config.setProperty(   

117                     Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());   

118         }   

119   

120         if (this.mappingLocations != null) {   

121             // Register given Hibernate mapping definitions, contained in resource files.  

122             for (int i = 0; i < this.mappingLocations.length; i++) {   

123                 config.addInputStream(this.mappingLocations[i].getInputStream());   

124             }   

125         }   

126   

127         if (this.cacheableMappingLocations != null) {   

128             // Register given cacheable Hibernate mapping definitions, read from the file system.  

129             for (int i = 0; i < this.cacheableMappingLocations.length; i++) {   

130                 config.addCacheableFile(this.cacheableMappingLocations[i].getFile());   

131             }   

132         }   

133   

134         if (this.mappingJarLocations != null) {   

135             // Register given Hibernate mapping definitions, contained in jar files.  

136             for (int i = 0; i < this.mappingJarLocations.length; i++) {   

137                 Resource resource = this.mappingJarLocations[i];   

138                 config.addJar(resource.getFile());   

139             }   

140         }   

141   

142         if (this.mappingDirectoryLocations != null) {   

143             // Register all Hibernate mapping definitions in the given directories.  

144             for (int i = 0; i < this.mappingDirectoryLocations.length; i++) {   

145                 File file = this.mappingDirectoryLocations[i].getFile();   

146                 if (!file.isDirectory()) {   

147                     throw new IllegalArgumentException(   

148                             "Mapping directory location [" + this.mappingDirectoryLocations[i] +   

149                             "] does not denote a directory");   

150                 }   

151                 config.addDirectory(file);   

152             }   

153         }   

154   

155         if (this.entityCacheStrategies != null) {   

156             // Register cache strategies for mapped entities.  

157             for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) {   

158                 String className = (String) classNames.nextElement();   

159                 String[] strategyAndRegion =   

160                         StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className));   

161                 if (strategyAndRegion.length > 1) {   

162                     config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]);   

163                 }   

164                 else if (strategyAndRegion.length > 0) {   

165                     config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);   

166                 }   

167             }   

168         }   

169   

170         if (this.collectionCacheStrategies != null) {   

171             // Register cache strategies for mapped collections.  

172             for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) {   

173                 String collRole = (String) collRoles.nextElement();   

174                 String[] strategyAndRegion =   

175                         StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole));   

176                 if (strategyAndRegion.length > 1) {   

177                     config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]);   

178                 }   

179                 else if (strategyAndRegion.length > 0) {   

180                     config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]);   

181                 }   

182             }   

183         }   

184   

185         if (this.eventListeners != null) {   

186             // Register specified Hibernate event listeners.  

187             for (Iterator it = this.eventListeners.entrySet().iterator(); it.hasNext();) {   

188                 Map.Entry entry = (Map.Entry) it.next();   

189                 Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String");   

190                 String listenerType = (String) entry.getKey();   

191                 Object listenerObject = entry.getValue();   

192                 if (listenerObject instanceof Collection) {   

193                     Collection listeners = (Collection) listenerObject;   

194                     EventListeners listenerRegistry = config.getEventListeners();   

195                     Object[] listenerArray =   

196                             (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size());   

197                     listenerArray = listeners.toArray(listenerArray);   

198                     config.setListeners(listenerType, listenerArray);   

199                 }   

200                 else {   

201                     config.setListener(listenerType, listenerObject);   

202                 }   

203             }   

204         }   

205   

206         // Perform custom post-processing in subclasses.  

207         postProcessConfiguration(config);   

208   

209         // 這裏是根據Configuration配置創建SessionFactory的地方  

210         logger.info("Building new Hibernate SessionFactory");   

211         this.configuration = config;   

212         sf = newSessionFactory(config);   

213     }   

214     //最後把和線程綁定的資源清空  

215     finally {   

216         if (this.dataSource != null) {   

217             // Reset DataSource holder.  

218             configTimeDataSourceHolder.set(null);   

219         }   

220   

221         if (this.jtaTransactionManager != null) {   

222             // Reset TransactionManager holder.  

223             configTimeTransactionManagerHolder.set(null);   

224         }   

225   

226         if (this.lobHandler != null) {   

227             // Reset LobHandler holder.  

228             configTimeLobHandlerHolder.set(null);   

229         }   

230     }   

231   

232     // Execute schema update if requested.  

233     if (this.schemaUpdate) {   

234         updateDatabaseSchema();   

235     }   

236   

237     return sf;   

238 }  

而直接調用org.hibernate.cfg.Configuration來得到需要的SessionFactory:
  

Java代碼 

239 protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {   

240     return config.buildSessionFactory();   

241 }  

所以我們這裏看到LocalSessionFactory大致起到的一個讀取資源配置然後生成SessionFactory的作用;當然這裏在得到 SessionFactory之後,還需要對session的事務管理作一些處理 – 使用了一個Proxy模式對getCurrentSession方法進行了攔截;
  

Java代碼 

242 //這裏先根據當前的SessionFactory的類型得到Proxy,然後插入Spring定義好的getCurrentSession攔截器  

243     protected SessionFactory getTransactionAwareSessionFactoryProxy(SessionFactory target) {   

244         Class sfInterface = SessionFactory.class;   

245         if (target instanceof SessionFactoryImplementor) {   

246             sfInterface = SessionFactoryImplementor.class;   

247         }   

248         return (SessionFactory) Proxy.newProxyInstance(sfInterface.getClassLoader(),   

249                 new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target));   

250     }  

攔截器的實現如下:
  

Java代碼 

251 private static class TransactionAwareInvocationHandler implements InvocationHandler {   

252   

253     private final SessionFactory target;   

254   

255     public TransactionAwareInvocationHandler(SessionFactory target) {   

256         this.target = target;   

257     }   

258   

259     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   

260         // Invocation on SessionFactory/SessionFactoryImplementor interface coming in…  

261         // 這裏對getCurrentSession方法進行攔截,得到一個和當前事務綁定的session交給用戶  

262         if (method.getName().equals("getCurrentSession")) {   

263             // Handle getCurrentSession method: return transactional Session, if any.  

264             try {   

265                 return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false);   

266             }   

267             catch (IllegalStateException ex) {   

268                 throw new HibernateException(ex.getMessage());   

269             }   

270         }   

271         else if (method.getName().equals("equals")) {   

272             // Only consider equal when proxies are identical.  

273             return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);   

274         }   

275         else if (method.getName().equals("hashCode")) {   

276             // Use hashCode of SessionFactory proxy.  

277             return new Integer(hashCode());   

278         }   

279   

280         // 這裏是需要運行的SessionFactory的目標方法  

281         try {   

282             return method.invoke(this.target, args);   

283         }   

284         catch (InvocationTargetException ex) {   

285             throw ex.getTargetException();   

286         }   

287     }   

288 }  

我們看看getCurrentSession的實現,在SessionFactoryUtils中:
  

Java代碼 

289 private static Session doGetSession(   

290             SessionFactory sessionFactory, Interceptor entityInterceptor,   

291             SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)   

292             throws HibernateException, IllegalStateException {   

293   

294         Assert.notNull(sessionFactory, "No SessionFactory specified");   

295   

296         //這個TransactionSynchronizationManagerResource是一個ThreadLocal變量,sessionFactory是一個單例,但ThreadLocal是和線程綁定的  

297         //這樣就實現了Hiberante中常用的通過ThreadLocalsession管理機制  

298         SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);   

299         if (sessionHolder != null && !sessionHolder.isEmpty()) {   

300             // pre-bound Hibernate Session  

301             Session session = null;   

302             if (TransactionSynchronizationManager.isSynchronizationActive() &&   

303                     sessionHolder.doesNotHoldNonDefaultSession()) {   

304                 // Spring transaction management is active ->  

305                 // register pre-bound Session with it for transactional flushing.  

306                 session = sessionHolder.getValidatedSession();   

307                 if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {   

308                     logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");   

309                     TransactionSynchronizationManager.registerSynchronization(   

310                             new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));   

311                     sessionHolder.setSynchronizedWithTransaction(true);   

312                     // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session  

313                     // with FlushMode.NEVER, which needs to allow flushing within the transaction.  

314                     FlushMode flushMode = session.getFlushMode();   

315                     if (flushMode.lessThan(FlushMode.COMMIT) &&   

316                             !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {   

317                         session.setFlushMode(FlushMode.AUTO);   

318                         sessionHolder.setPreviousFlushMode(flushMode);   

319                     }   

320                 }   

321             }   

322             else {   

323                 // No Spring transaction management active -> try JTA transaction synchronization.  

324                 session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);   

325             }   

326             if (session != null) {   

327                 return session;   

328             }   

329         }   

330         //這裏直接打開一個Session  

331         logger.debug("Opening Hibernate Session");   

332         Session session = (entityInterceptor != null ?   

333                 sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());   

334   

335         // Use same Session for further Hibernate actions within the transaction.  

336         // Thread object will get removed by synchronization at transaction completion.  

337         // 把 新打開的Session放到SessionHolder,然後放到ThreadLocal裏面去和線程綁定起來,這個ThreadLocal是 在 TransactionSynchronizationManager中配置好的,可以根據sessionFactory來索取  

338         // 同時根據事務處理的狀態來配置session的屬性,比如把FlushMode設置爲Never,同時把session和事務處理關聯起來  

339         if (TransactionSynchronizationManager.isSynchronizationActive()) {   

340             // We're within a Spring-managed transaction, possibly from JtaTransactionManager.  

341             logger.debug("Registering Spring transaction synchronization for new Hibernate Session");   

342             SessionHolder holderToUse = sessionHolder;   

343             if (holderToUse == null) {   

344                 holderToUse = new SessionHolder(session);   

345             }   

346             else {   

347                 holderToUse.addSession(session);   

348             }   

349             if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {   

350                 session.setFlushMode(FlushMode.NEVER);   

351             }   

352             TransactionSynchronizationManager.registerSynchronization(   

353                     new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));   

354             holderToUse.setSynchronizedWithTransaction(true);   

355             if (holderToUse != sessionHolder) {   

356                 TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);   

357             }   

358         }   

359         else {   

360             // No Spring transaction management active -> try JTA transaction synchronization.  

361             registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);   

362         }   

363   

364         // Check whether we are allowed to return the Session.  

365         if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {   

366             closeSession(session);   

367             throw new IllegalStateException("No Hibernate Session bound to thread, " +   

368                 "and configuration does not allow creation of non-transactional one here");   

369         }   

370   

371         return session;   

372     }  

這裏就是在Spring中爲使用HiberanteSessionFactory以及Session做的準備工作,在這個基礎上,用戶可以通過使用 HibernateTemplate來使用HibernateO/R功能,和以前看到的一樣這是一個execute的回調:
  

Java代碼 

373 public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {   

374     Assert.notNull(action, "Callback object must not be null");   

375     //這裏得到配置好的HibernateSession  

376     Session session = getSession();   

377     boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());   

378     if (existingTransaction) {   

379         logger.debug("Found thread-bound Session for HibernateTemplate");   

380     }   

381   

382     FlushMode previousFlushMode = null;   

383     try {   

384         previousFlushMode = applyFlushMode(session, existingTransaction);   

385         enableFilters(session);   

386         Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));   

387         //這裏是回調的入口  

388         Object result = action.doInHibernate(sessionToExpose);   

389         flushIfNecessary(session, existingTransaction);   

390         return result;   

391     }   

392     catch (HibernateException ex) {   

393         throw convertHibernateAccessException(ex);   

394     }   

395     catch (SQLException ex) {   

396         throw convertJdbcAccessException(ex);   

397     }   

398     catch (RuntimeException ex) {   

399         // Callback code threw application exception…  

400         throw ex;   

401     }   

402     finally {   

403         //如果這個調用的方法在一個事務當中,  

404         if (existingTransaction) {   

405             logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");   

406             disableFilters(session);   

407             if (previousFlushMode != null) {   

408                 session.setFlushMode(previousFlushMode);   

409             }   

410         } //否則把Session關閉  

411         else {   

412             // Never use deferred close for an explicitly new Session.  

413             if (isAlwaysUseNewSession()) {   

414                 SessionFactoryUtils.closeSession(session);   

415             }   

416             else {   

417                 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());   

418             }   

419         }   

420     }   

421 }  

我們看看怎樣得到對應的Session的,仍然使用了SessionFactoryUtils的方法doGetSession:
  

Java代碼 

422 protected Session getSession() {   

423     if (isAlwaysUseNewSession()) {   

424         return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());   

425     }   

426     else if (!isAllowCreate()) {   

427         return SessionFactoryUtils.getSession(getSessionFactory(), false);   

428     }   

429     else {   

430         return SessionFactoryUtils.getSession(   

431                 getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());   

432     }   

433 }  

 

簡單分析一下Spring Acegi的源代碼實現: 
 Servlet.Filter的實現AuthenticationProcessingFilter啓動Web頁面的驗證過程 – AbstractProcessingFilter定義了整個驗證過程的模板: 

Java代碼 

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  

    throws IOException, ServletException {  

    //這裏檢驗是不是符合ServletRequest/SevletResponse的要求  

    if (!(request instanceof HttpServletRequest)) {  

        throw new ServletException("Can only process HttpServletRequest");  

    }  

  

    if (!(response instanceof HttpServletResponse)) {  

        throw new ServletException("Can only process HttpServletResponse");  

10     }  

11   

12     HttpServletRequest httpRequest = (HttpServletRequest) request;  

13     HttpServletResponse httpResponse = (HttpServletResponse) response;  

14     //根據HttpServletRequestHttpServletResponse來進行驗證  

15     if (requiresAuthentication(httpRequest, httpResponse)) {  

16         if (logger.isDebugEnabled()) {  

17             logger.debug("Request is to process authentication");  

18         }  

19         //這裏定義Acegi中的Authentication對象來持有相關的用戶驗證信息  

20         Authentication authResult;  

21   

22         try {  

23             onPreAuthentication(httpRequest, httpResponse);  

24             //這裏的具體驗證過程委託給子類完成,比如AuthenticationProcessingFilter來完成基於Web頁面的用戶驗證  

25             authResult = attemptAuthentication(httpRequest);  

26         } catch (AuthenticationException failed) {  

27             // Authentication failed  

28             unsuccessfulAuthentication(httpRequest, httpResponse, failed);  

29   

30             return;  

31         }  

32   

33         // Authentication success  

34         if (continueChainBeforeSuccessfulAuthentication) {  

35             chain.doFilter(request, response);  

36         }  

37         //完成驗證後的後續工作,比如跳轉到相應的頁面  

38         successfulAuthentication(httpRequest, httpResponse, authResult);  

39   

40         return;  

41     }  

42   

43     chain.doFilter(request, response);  

44 }  


 在AuthenticationProcessingFilter中的具體驗證過程是這樣的: 

Java代碼 

45 public Authentication attemptAuthentication(HttpServletRequest request)  

46     throws AuthenticationException {  

47     //這裏從HttpServletRequest中得到用戶驗證的用戶名和密碼  

48     String username = obtainUsername(request);  

49     String password = obtainPassword(request);  

50   

51     if (username == null) {  

52         username = "";  

53     }  

54   

55     if (password == null) {  

56         password = "";  

57     }  

58     //這裏根據得到的用戶名和密碼去構造一個Authentication對象提供給AuthenticationManager進行驗證,裏面包含了用戶的用戶名和密碼信息  

59     UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);  

60   

61     // Place the last username attempted into HttpSession for views  

62     request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username);  

63   

64     // Allow subclasses to set the "details" property  

65     setDetails(request, authRequest);  

66     //這裏啓動AuthenticationManager進行驗證過程  

67     return this.getAuthenticationManager().authenticate(authRequest);  

68 }  


 在Acegi框架中,進行驗證管理的主要類是AuthenticationManager,我們看看它是怎樣進行驗證管理的 – 驗證的調用入口是authenticateAbstractAuthenticationManager的實現中: 
 //這是進行驗證的函數,返回一個Authentication對象來記錄驗證的結果,其中包含了用戶的驗證信息,權限配置等,同時這個Authentication會以後被授權模塊使用 

Java代碼 

69 //如果驗證失敗,那麼在驗證過程中會直接拋出異常  

70     public final Authentication authenticate(Authentication authRequest)  

71         throws AuthenticationException {  

72         try {//這裏是實際的驗證處理,我們下面使用ProviderManager來說明具體的驗證過程,傳入的參數authRequest裏面已經包含了從HttpServletRequest中得到的用戶輸入的用戶名和密碼  

73             Authentication authResult = doAuthentication(authRequest);  

74             copyDetails(authRequest, authResult);  

75   

76             return authResult;  

77         } catch (AuthenticationException e) {  

78             e.setAuthentication(authRequest);  

79             throw e;  

80         }  

81     }  


 在ProviderManager中進行實際的驗證工作,假設這裏使用數據庫來存取用戶信息: 

Java代碼 

82 public Authentication doAuthentication(Authentication authentication)  

83     throws AuthenticationException {  

84     //這裏取得配置好的provider鏈的迭代器,在配置的時候可以配置多個provider,這裏我們配置的是DaoAuthenticationProvider來說明它使用數據庫來保存用戶的用戶名和密碼信息。  

85     Iterator iter = providers.iterator();  

86   

87     Class toTest = authentication.getClass();  

88   

89     AuthenticationException lastException = null;  

90   

91     while (iter.hasNext()) {  

92         AuthenticationProvider provider = (AuthenticationProvider) iter.next();  

93   

94         if (provider.supports(toTest)) {  

95             logger.debug("Authentication attempt using " + provider.getClass().getName());  

96             //這個result包含了驗證中得到的結果信息  

97             Authentication result = null;  

98   

99             try {//這裏是provider進行驗證處理的過程  

100                 result = provider.authenticate(authentication);  

101                 sessionController.checkAuthenticationAllowed(result);  

102             } catch (AuthenticationException ae) {  

103                 lastException = ae;  

104                 result = null;  

105             }  

106   

107             if (result != null) {  

108                 sessionController.registerSuccessfulAuthentication(result);  

109                 publishEvent(new AuthenticationSuccessEvent(result));  

110   

111                 return result;  

112             }  

113         }  

114     }  

115   

116     if (lastException == null) {  

117         lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",  

118                     new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));  

119     }  

120   

121     // 這裏發佈事件來通知上下文的監聽器  

122     String className = exceptionMappings.getProperty(lastException.getClass().getName());  

123     AbstractAuthenticationEvent event = null;  

124   

125     if (className != null) {  

126         try {  

127             Class clazz = getClass().getClassLoader().loadClass(className);  

128             Constructor constructor = clazz.getConstructor(new Class[] {  

129                         Authentication.class, AuthenticationException.class  

130                     });  

131             Object obj = constructor.newInstance(new Object[] {authentication, lastException});  

132             Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj, "Must be an AbstractAuthenticationEvent");  

133             event = (AbstractAuthenticationEvent) obj;  

134         } catch (ClassNotFoundException ignored) {}  

135         catch (NoSuchMethodException ignored) {}  

136         catch (IllegalAccessException ignored) {}  

137         catch (InstantiationException ignored) {}  

138         catch (InvocationTargetException ignored) {}  

139     }  

140   

141     if (event != null) {  

142         publishEvent(event);  

143     } else {  

144         if (logger.isDebugEnabled()) {  

145             logger.debug("No event was found for the exception " + lastException.getClass().getName());  

146         }  

147     }  

148   

149     // Throw the exception  

150     throw lastException;  

151 }  


 我們下面看看在DaoAuthenticationProvider是怎樣從數據庫中取出對應的驗證信息進行用戶驗證的,在它的基類AbstractUserDetailsAuthenticationProvider定義了驗證的處理模板: 

Java代碼 

152 public Authentication authenticate(Authentication authentication)  

153     throws AuthenticationException {  

154     Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,  

155         messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",  

156             "Only UsernamePasswordAuthenticationToken is supported"));  

157   

158     // 這裏取得用戶輸入的用戶名  

159     String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();  

160     // 如果配置了緩存,從緩存中去取以前存入的用戶驗證信息 這裏是UserDetail,是服務器端存在數據庫裏的用戶信息,這樣就不用每次都去數據庫中取了  

161     boolean cacheWasUsed = true;  

162     UserDetails user = this.userCache.getUserFromCache(username);  

163     //沒有取到,設置標誌位,下面會把這次取到的服務器端用戶信息存入緩存中去  

164     if (user == null) {  

165         cacheWasUsed = false;  

166   

167         try {//這裏是調用UserDetailService去取用戶數據庫裏信息的地方  

168             user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);  

169         } catch (UsernameNotFoundException notFound) {  

170             if (hideUserNotFoundExceptions) {  

171                 throw new BadCredentialsException(messages.getMessage(  

172                         "AbstractUserDetailsAuthenticationProvider.badCredentials""Bad credentials"));  

173             } else {  

174                 throw notFound;  

175             }  

176         }  

177   

178         Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");  

179     }  

180   

181     if (!user.isAccountNonLocked()) {  

182         throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked",  

183                 "User account is locked"));  

184     }  

185   

186     if (!user.isEnabled()) {  

187         throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled",  

188                 "User is disabled"));  

189     }  

190   

191     if (!user.isAccountNonExpired()) {  

192         throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired",  

193                 "User account has expired"));  

194     }  

195   

196     // This check must come here, as we don't want to tell users  

197     // about account status unless they presented the correct credentials  

198     try {//這裏是驗證過程,在retrieveUser中從數據庫中得到用戶的信息,在additionalAuthenticationChecks中進行對比用戶輸入和服務器端的用戶信息  

199           //如果驗證通過,那麼構造一個Authentication對象來讓以後的授權使用,如果驗證不通過,直接拋出異常結束鑑權過程  

200         additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);  

201     } catch (AuthenticationException exception) {  

202         if (cacheWasUsed) {  

203             // There was a problem, so try again after checking  

204             // we're using latest data (ie not from the cache)  

205             cacheWasUsed = false;  

206             user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);  

207             additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);  

208         } else {  

209             throw exception;  

210         }  

211     }  

212   

213     if (!user.isCredentialsNonExpired()) {  

214         throw new CredentialsExpiredException(messages.getMessage(  

215                 "AbstractUserDetailsAuthenticationProvider.credentialsExpired""User credentials have expired"));  

216     }  

217     //根據前面的緩存結果決定是不是要把當前的用戶信息存入緩存以供下次驗證使用  

218     if (!cacheWasUsed) {  

219         this.userCache.putUserInCache(user);  

220     }  

221   

222     Object principalToReturn = user;  

223   

224     if (forcePrincipalAsString) {  

225         principalToReturn = user.getUsername();  

226     }  

227     //最後返回Authentication記錄了驗證結果供以後的授權使用  

228     return createSuccessAuthentication(principalToReturn, authentication, user);  

229 }  

230 //這是是調用UserDetailService去加載服務器端用戶信息的地方,從什麼地方加載要看設置,這裏我們假設由JdbcDaoImp來從數據中進行加載  

231 protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)  

232     throws AuthenticationException {  

233     UserDetails loadedUser;  

234     //這裏調用UserDetailService去從數據庫中加載用戶驗證信息,同時返回從數據庫中返回的信息,這些信息放到了UserDetails對象中去了  

235     try {  

236         loadedUser = this.getUserDetailsService().loadUserByUsername(username);  

237     } catch (DataAccessException repositoryProblem) {  

238         throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);  

239     }  

240   

241     if (loadedUser == null) {  

242         throw new AuthenticationServiceException(  

243             "UserDetailsService returned null, which is an interface contract violation");  

244     }  

245     return loadedUser;  

246 }  


 下面我們重點分析一下JdbcDaoImp這個類來看看具體是怎樣從數據庫中得到用戶信息的: 

Java代碼 

247 public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {  

248     //~ Static fields/initializers =====================================================================================  

249     //這裏是預定義好的對查詢語句,對應於默認的數據庫表結構,也可以自己定義查詢語句對應特定的用戶數據庫驗證表的設計  

250     public static final String DEF_USERS_BY_USERNAME_QUERY =  

251             "SELECT username,password,enabled FROM users WHERE username = ?";  

252     public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =  

253             "SELECT username,authority FROM authorities WHERE username = ?";  

254   

255     //~ Instance fields ================================================================================================  

256     //這裏使用Spring JDBC來進行數據庫操作  

257     protected MappingSqlQuery authoritiesByUsernameMapping;  

258     protected MappingSqlQuery usersByUsernameMapping;  

259     private String authoritiesByUsernameQuery;  

260     private String rolePrefix = "";  

261     private String usersByUsernameQuery;  

262     private boolean usernameBasedPrimaryKey = true;  

263   

264     //~ Constructors ===================================================================================================  

265     //在初始化函數中把查詢語句設置爲預定義的SQL語句  

266     public JdbcDaoImpl() {  

267         usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;  

268         authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;  

269     }  

270   

271     //~ Methods ========================================================================================================  

272   

273     protected void addCustomAuthorities(String username, List authorities) {}  

274   

275     public String getAuthoritiesByUsernameQuery() {  

276         return authoritiesByUsernameQuery;  

277     }  

278   

279     public String getRolePrefix() {  

280         return rolePrefix;  

281     }  

282   

283     public String getUsersByUsernameQuery() {  

284         return usersByUsernameQuery;  

285     }  

286   

287     protected void initDao() throws ApplicationContextException {  

288         initMappingSqlQueries();  

289     }  

290   

291     /** 

292      * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass 

293      */  

294     protected void initMappingSqlQueries() {  

295         this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());  

296         this.authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());  

297     }  

298   

299     public boolean isUsernameBasedPrimaryKey() {  

300         return usernameBasedPrimaryKey;  

301     }  

302     //這裏是取得數據庫用戶信息的具體過程  

303     public UserDetails loadUserByUsername(String username)  

304         throws UsernameNotFoundException, DataAccessException {  

305         //根據用戶名在用戶表中得到用戶信息,包括用戶名,密碼和用戶是否有效的信息  

306         List users = usersByUsernameMapping.execute(username);  

307   

308         if (users.size() == 0) {  

309             throw new UsernameNotFoundException("User not found");  

310         }  

311         //取集合中的第一個作爲有效的用戶對象  

312         UserDetails user = (UserDetails) users.get(0); // contains no GrantedAuthority[]  

313         //這裏在權限表中去取得用戶的權限信息,同樣的返回一個權限集合對應於這個用戶  

314         List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());  

315   

316         addCustomAuthorities(user.getUsername(), dbAuths);  

317   

318         if (dbAuths.size() == 0) {  

319             throw new UsernameNotFoundException("User has no GrantedAuthority");  

320         }  

321         //這裏根據得到的權限集合來配置返回的User對象供以後使用  

322         GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);  

323   

324         String returnUsername = user.getUsername();  

325   

326         if (!usernameBasedPrimaryKey) {  

327             returnUsername = username;  

328         }  

329   

330         return new User(returnUsername, user.getPassword(), user.isEnabled(), truetruetrue, arrayAuths);  

331     }  

332   

333     public void setAuthoritiesByUsernameQuery(String queryString) {  

334         authoritiesByUsernameQuery = queryString;  

335     }  

336   

337     public void setRolePrefix(String rolePrefix) {  

338         this.rolePrefix = rolePrefix;  

339     }  

340   

341     public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {  

342         this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;  

343     }  

344   

345     public void setUsersByUsernameQuery(String usersByUsernameQueryString) {  

346         this.usersByUsernameQuery = usersByUsernameQueryString;  

347     }  

348   

349     //~ Inner Classes ==================================================================================================  

350   

351     /** 

352      * 這裏是調用Spring JDBC的數據庫操作,具體可以參考對JDBC的分析,這個類的作用是把數據庫查詢得到的記錄集合轉換爲對象集合 一個很簡單的O/R實現 

353      */  

354     protected class AuthoritiesByUsernameMapping extends MappingSqlQuery {  

355         protected AuthoritiesByUsernameMapping(DataSource ds) {  

356             super(ds, authoritiesByUsernameQuery);  

357             declareParameter(new SqlParameter(Types.VARCHAR));  

358             compile();  

359         }  

360   

361         protected Object mapRow(ResultSet rs, int rownum)  

362             throws SQLException {  

363             String roleName = rolePrefix + rs.getString(2);  

364             GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);  

365   

366             return authority;  

367         }  

368     }  

369   

370     /** 

371      * Query object to look up a user. 

372      */  

373     protected class UsersByUsernameMapping extends MappingSqlQuery {  

374         protected UsersByUsernameMapping(DataSource ds) {  

375             super(ds, usersByUsernameQuery);  

376             declareParameter(new SqlParameter(Types.VARCHAR));  

377             compile();  

378         }  

379   

380         protected Object mapRow(ResultSet rs, int rownum)  

381             throws SQLException {  

382             String username = rs.getString(1);  

383             String password = rs.getString(2);  

384             boolean enabled = rs.getBoolean(3);  

385             UserDetails user = new User(username, password, enabled, truetruetrue,  

386                     new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});  

387   

388             return user;  

389         }  

390     }  

391 }  


 從數據庫中得到用戶信息後,就是一個比對用戶輸入的信息和這個數據庫用戶信息的比對過程,這個比對過程在DaoAuthenticationProvider: 

Java代碼 

392 //這個UserDetail是從數據庫中查詢到的,這個authentication是從用戶輸入中得到的  

393     protected void additionalAuthenticationChecks(UserDetails userDetails,  

394         UsernamePasswordAuthenticationToken authentication)  

395         throws AuthenticationException {  

396         Object salt = null;  

397   

398         if (this.saltSource != null) {  

399             salt = this.saltSource.getSalt(userDetails);  

400         }  

401         //如果用戶沒有輸入密碼,直接拋出異常  

402         if (authentication.getCredentials() == null) {  

403             throw new BadCredentialsException(messages.getMessage(  

404                     "AbstractUserDetailsAuthenticationProvider.badCredentials""Bad credentials"),  

405                     includeDetailsObject ? userDetails : null);  

406         }  

407         //這裏取得用戶輸入的密碼  

408         String presentedPassword = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString();  

409         //這裏判斷用戶輸入的密碼是不是和數據庫裏的密碼相同,這裏可以使用passwordEncoder來對數據庫裏的密碼加解密  

410         // 如果不相同,拋出異常,如果相同則鑑權成功  

411         if (!passwordEncoder.isPasswordValid(  

412                 userDetails.getPassword(), presentedPassword, salt)) {  

413             throw new BadCredentialsException(messages.getMessage(  

414                     "AbstractUserDetailsAuthenticationProvider.badCredentials""Bad credentials"),  

415                     includeDetailsObject ? userDetails : null);  

416         }  

417     }  


 上面分析了整個Acegi進行驗證的過程,從AuthenticationProcessingFilter中攔截Http請求得到用戶輸入的用戶名和密 碼,這些用戶輸入的驗證信息會被放到Authentication對象中持有並傳遞給AuthenticatioManager來對比在服務端的用戶信息 來完成整個鑑權。這個鑑權完成以後會把有效的用戶信息放在一個Authentication中供以後的授權模塊使用。在具體的鑑權過程中,使用了我們配置 好的各種Provider以及對應的UserDetailServiceEncoder類來完成相應的獲取服務器端用戶數據以及與用戶輸入的驗證信息的 比對工作。

 

我們從FilterSecurityInterceptor我們從入手看看怎樣進行授權的:

Java代碼 

//這裏是攔截器攔截HTTP請求的入口   

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)   

        throws IOException, ServletException {   

        FilterInvocation fi = new FilterInvocation(request, response, chain);   

        invoke(fi);   

    }   

//這是具體的攔截調用   

    public void invoke(FilterInvocation fi) throws IOException, ServletException {   

        if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)   

10             && observeOncePerRequest) {   

11            //在第一次進行過安全檢查之後就不會再做了   

12             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());   

13         } else {   

14             //這是第一次收到相應的請求,需要做安全檢測,同時把標誌爲設置好 -  FILTER_APPLIED,下次就再有請求就不會作相同的安全檢查了   

15             if (fi.getRequest() != null) {   

16                 fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);   

17             }   

18             //這裏是做安全檢查的地方   

19             InterceptorStatusToken token = super.beforeInvocation(fi);   

20             //接着向攔截器鏈執行   

21             try {   

22                 fi.getChain().doFilter(fi.getRequest(), fi.getResponse());   

23             } finally {   

24                 super.afterInvocation(token, null);   

25             }   

26         }   

27     }  

Java代碼 

28 //這裏是攔截器攔截HTTP請求的入口  

29     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  

30         throws IOException, ServletException {  

31         FilterInvocation fi = new FilterInvocation(request, response, chain);  

32         invoke(fi);  

33     }  

34 //這是具體的攔截調用  

35     public void invoke(FilterInvocation fi) throws IOException, ServletException {  

36         if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)  

37             && observeOncePerRequest) {  

38            //在第一次進行過安全檢查之後就不會再做了  

39             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  

40         } else {  

41             //這是第一次收到相應的請求,需要做安全檢測,同時把標誌爲設置好 -  FILTER_APPLIED,下次就再有請求就不會作相同的安全檢查了  

42             if (fi.getRequest() != null) {  

43                 fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);  

44             }  

45             //這裏是做安全檢查的地方  

46             InterceptorStatusToken token = super.beforeInvocation(fi);  

47             //接着向攔截器鏈執行  

48             try {  

49                 fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  

50             } finally {  

51                 super.afterInvocation(token, null);  

52             }  

53         }  

54     }  


 我們看看在AbstractSecurityInterceptor是怎樣對HTTP請求作安全檢測的:

Java代碼 

55 protected InterceptorStatusToken beforeInvocation(Object object) {   

56     Assert.notNull(object, "Object was null");   

57   

58     if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {   

59         throw new IllegalArgumentException("Security invocation attempted for object "  

60             + object.getClass().getName()   

61             + " but AbstractSecurityInterceptor only configured to support secure objects of type: "  

62             + getSecureObjectClass());   

63     }   

64     //這裏讀取配置FilterSecurityInterceptorObjectDefinitionSource屬性,這些屬性配置了資源的安全設置   

65     ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);   

66   

67     if (attr == null) {   

68         if(rejectPublicInvocations) {   

69             throw new IllegalArgumentException(   

70                   "No public invocations are allowed via this AbstractSecurityInterceptor. "  

71                 + "This indicates a configuration error because the "  

72                 + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");   

73         }   

74   

75         if (logger.isDebugEnabled()) {   

76             logger.debug("Public object - authentication not attempted");   

77         }   

78   

79         publishEvent(new PublicInvocationEvent(object));   

80   

81         return null// no further work post-invocation   

82     }   

83   

84   

85     if (logger.isDebugEnabled()) {   

86         logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());   

87     }   

88     //這裏從SecurityContextHolder中去取Authentication對象,一般在登錄時會放到SecurityContextHolder中去   

89     if (SecurityContextHolder.getContext().getAuthentication() == null) {   

90         credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",   

91                 "An Authentication object was not found in the SecurityContext"), object, attr);   

92     }   

93   

94     // 如果前面沒有處理鑑權,這裏需要對鑑權進行處理   

95     Authentication authenticated;   

96   

97     if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {   

98         try {//調用配置好的AuthenticationManager處理鑑權,如果鑑權不成功,拋出異常結束處理   

99             authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()   

100                                                                                          .getAuthentication());   

101         } catch (AuthenticationException authenticationException) {   

102             throw authenticationException;   

103         }   

104   

105         // We don't authenticated.setAuthentication(true), because each provider should do that   

106         if (logger.isDebugEnabled()) {   

107             logger.debug("Successfully Authenticated: " + authenticated.toString());   

108         }   

109         //這裏把鑑權成功後得到的Authentication保存到SecurityContextHolder中供下次使用   

110         SecurityContextHolder.getContext().setAuthentication(authenticated);   

111     } else {//這裏處理前面已經通過鑑權的請求,先從SecurityContextHolder中去取得Authentication   

112         authenticated = SecurityContextHolder.getContext().getAuthentication();   

113   

114         if (logger.isDebugEnabled()) {   

115             logger.debug("Previously Authenticated: " + authenticated.toString());   

116         }   

117     }   

118   

119     // 這是處理授權的過程   

120     try {   

121         //調用配置好的AccessDecisionManager來進行授權   

122         this.accessDecisionManager.decide(authenticated, object, attr);   

123     } catch (AccessDeniedException accessDeniedException) {   

124         //授權不成功向外發佈事件   

125         AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,   

126                 accessDeniedException);   

127         publishEvent(event);   

128   

129         throw accessDeniedException;   

130     }   

131   

132     if (logger.isDebugEnabled()) {   

133         logger.debug("Authorization successful");   

134     }   

135   

136     AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);   

137     publishEvent(event);   

138   

139     // 這裏構建一個RunAsManager來替代當前的Authentication對象,默認情況下使用的是NullRunAsManager會把SecurityContextHolder中的Authentication對象清空   

140     Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);   

141   

142     if (runAs == null) {   

143         if (logger.isDebugEnabled()) {   

144             logger.debug("RunAsManager did not change Authentication object");   

145         }   

146   

147         // no further work post-invocation   

148         return new InterceptorStatusToken(authenticated, false, attr, object);   

149     } else {   

150         if (logger.isDebugEnabled()) {   

151             logger.debug("Switching to RunAs Authentication: " + runAs.toString());   

152         }   

153   

154         SecurityContextHolder.getContext().setAuthentication(runAs);   

155   

156         // revert to token.Authenticated post-invocation   

157         return new InterceptorStatusToken(authenticated, true, attr, object);   

158     }   

159 }  

Java代碼 

160 protected InterceptorStatusToken beforeInvocation(Object object) {  

161     Assert.notNull(object, "Object was null");  

162   

163     if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {  

164         throw new IllegalArgumentException("Security invocation attempted for object "  

165             + object.getClass().getName()  

166             + " but AbstractSecurityInterceptor only configured to support secure objects of type: "  

167             + getSecureObjectClass());  

168     }  

169     //這裏讀取配置FilterSecurityInterceptorObjectDefinitionSource屬性,這些屬性配置了資源的安全設置  

170     ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);  

171   

172     if (attr == null) {  

173         if(rejectPublicInvocations) {  

174             throw new IllegalArgumentException(  

175                   "No public invocations are allowed via this AbstractSecurityInterceptor. "  

176                 + "This indicates a configuration error because the "  

177                 + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");  

178         }  

179   

180         if (logger.isDebugEnabled()) {  

181             logger.debug("Public object - authentication not attempted");  

182         }  

183   

184         publishEvent(new PublicInvocationEvent(object));  

185   

186         return null// no further work post-invocation  

187     }  

188   

189   

190     if (logger.isDebugEnabled()) {  

191         logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString());  

192     }  

193     //這裏從SecurityContextHolder中去取Authentication對象,一般在登錄時會放到SecurityContextHolder中去  

194     if (SecurityContextHolder.getContext().getAuthentication() == null) {  

195         credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",  

196                 "An Authentication object was not found in the SecurityContext"), object, attr);  

197     }  

198   

199     // 如果前面沒有處理鑑權,這裏需要對鑑權進行處理  

200     Authentication authenticated;  

201   

202     if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) {  

203         try {//調用配置好的AuthenticationManager處理鑑權,如果鑑權不成功,拋出異常結束處理  

204             authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext()  

205                                                                                          .getAuthentication());  

206         } catch (AuthenticationException authenticationException) {  

207             throw authenticationException;  

208         }  

209   

210         // We don't authenticated.setAuthentication(true), because each provider should do that  

211         if (logger.isDebugEnabled()) {  

212             logger.debug("Successfully Authenticated: " + authenticated.toString());  

213         }  

214         //這裏把鑑權成功後得到的Authentication保存到SecurityContextHolder中供下次使用  

215         SecurityContextHolder.getContext().setAuthentication(authenticated);  

216     } else {//這裏處理前面已經通過鑑權的請求,先從SecurityContextHolder中去取得Authentication  

217         authenticated = SecurityContextHolder.getContext().getAuthentication();  

218   

219         if (logger.isDebugEnabled()) {  

220             logger.debug("Previously Authenticated: " + authenticated.toString());  

221         }  

222     }  

223   

224     // 這是處理授權的過程  

225     try {  

226         //調用配置好的AccessDecisionManager來進行授權  

227         this.accessDecisionManager.decide(authenticated, object, attr);  

228     } catch (AccessDeniedException accessDeniedException) {  

229         //授權不成功向外發佈事件  

230         AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,  

231                 accessDeniedException);  

232         publishEvent(event);  

233   

234         throw accessDeniedException;  

235     }  

236   

237     if (logger.isDebugEnabled()) {  

238         logger.debug("Authorization successful");  

239     }  

240   

241     AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);  

242     publishEvent(event);  

243   

244     // 這裏構建一個RunAsManager來替代當前的Authentication對象,默認情況下使用的是NullRunAsManager會把SecurityContextHolder中的Authentication對象清空  

245     Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);  

246   

247     if (runAs == null) {  

248         if (logger.isDebugEnabled()) {  

249             logger.debug("RunAsManager did not change Authentication object");  

250         }  

251   

252         // no further work post-invocation  

253         return new InterceptorStatusToken(authenticated, false, attr, object);  

254     } else {  

255         if (logger.isDebugEnabled()) {  

256             logger.debug("Switching to RunAs Authentication: " + runAs.toString());  

257         }  

258   

259         SecurityContextHolder.getContext().setAuthentication(runAs);  

260   

261         // revert to token.Authenticated post-invocation  

262         return new InterceptorStatusToken(authenticated, true, attr, object);  

263     }  

264 }  


 到這裏我們假設配置AffirmativeBased作爲AccessDecisionManager

Java代碼 

265 //這裏定義了決策機制,需要全票才能通過   

266     public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)   

267         throws AccessDeniedException {   

268         //這裏取得配置好的迭代器集合   

269         Iterator iter = this.getDecisionVoters().iterator();   

270         int deny = 0;   

271         //依次使用各個投票器進行投票,並對投票結果進行計票   

272         while (iter.hasNext()) {   

273             AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();   

274             int result = voter.vote(authentication, object, config);   

275             //這是對投票結果進行處理,如果遇到其中一票通過,那就授權通過,如果是棄權或者反對,那就繼續投票   

276             switch (result) {   

277             case AccessDecisionVoter.ACCESS_GRANTED:   

278                 return;   

279   

280             case AccessDecisionVoter.ACCESS_DENIED:   

281             //這裏對反對票進行計數   

282                 deny++;   

283   

284                 break;   

285   

286             default:   

287                 break;   

288             }   

289         }   

290         //如果有反對票,拋出異常,整個授權不通過   

291         if (deny > 0) {   

292             throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",   

293                     "Access is denied"));   

294         }   

295   

296         // 這裏對棄權票進行處理,看看是全是棄權票的決定情況,默認是不通過,由allowIfAllAbstainDecisions變量控制   

297         checkAllowIfAllAbstainDecisions();   

298     }   

299 具體的投票由投票器進行,我們這裏配置了RoleVoter來進行投票:   

300     public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {   

301         int result = ACCESS_ABSTAIN;   

302         //這裏取得資源的安全配置   

303         Iterator iter = config.getConfigAttributes();   

304   

305         while (iter.hasNext()) {   

306             ConfigAttribute attribute = (ConfigAttribute) iter.next();   

307                

308             if (this.supports(attribute)) {   

309                 result = ACCESS_DENIED;   

310   

311                 // 這裏對資源配置的安全授權級別進行判斷,也就是匹配ROLE爲前綴的角色配置   

312                 // 遍歷每個配置屬性,如果其中一個匹配該主體持有的GrantedAuthority,則訪問被允許。   

313                 for (int i = 0; i < authentication.getAuthorities().length; i++) {   

314                     if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {   

315                         return ACCESS_GRANTED;   

316                     }   

317                 }   

318             }   

319         }   

320   

321         return result;   

322     }  

Java代碼 

323 //這裏定義了決策機制,需要全票才能通過  

324     public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)  

325         throws AccessDeniedException {  

326         //這裏取得配置好的迭代器集合  

327         Iterator iter = this.getDecisionVoters().iterator();  

328         int deny = 0;  

329         //依次使用各個投票器進行投票,並對投票結果進行計票  

330         while (iter.hasNext()) {  

331             AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();  

332             int result = voter.vote(authentication, object, config);  

333             //這是對投票結果進行處理,如果遇到其中一票通過,那就授權通過,如果是棄權或者反對,那就繼續投票  

334             switch (result) {  

335             case AccessDecisionVoter.ACCESS_GRANTED:  

336                 return;  

337   

338             case AccessDecisionVoter.ACCESS_DENIED:  

339             //這裏對反對票進行計數  

340                 deny++;  

341   

342                 break;  

343   

344             default:  

345                 break;  

346             }  

347         }  

348         //如果有反對票,拋出異常,整個授權不通過  

349         if (deny > 0) {  

350             throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",  

351                     "Access is denied"));  

352         }  

353   

354         // 這裏對棄權票進行處理,看看是全是棄權票的決定情況,默認是不通過,由allowIfAllAbstainDecisions變量控制  

355         checkAllowIfAllAbstainDecisions();  

356     }  

357 具體的投票由投票器進行,我們這裏配置了RoleVoter來進行投票:  

358     public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {  

359         int result = ACCESS_ABSTAIN;  

360         //這裏取得資源的安全配置  

361         Iterator iter = config.getConfigAttributes();  

362   

363         while (iter.hasNext()) {  

364             ConfigAttribute attribute = (ConfigAttribute) iter.next();  

365               

366             if (this.supports(attribute)) {  

367                 result = ACCESS_DENIED;  

368   

369                 // 這裏對資源配置的安全授權級別進行判斷,也就是匹配ROLE爲前綴的角色配置  

370                 // 遍歷每個配置屬性,如果其中一個匹配該主體持有的GrantedAuthority,則訪問被允許。  

371                 for (int i = 0; i < authentication.getAuthorities().length; i++) {  

372                     if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) {  

373                         return ACCESS_GRANTED;  

374                     }  

375                 }  

376             }  

377         }  

378   

379         return result;  

380     }  


 上面就是對整個授權過程的一個分析,從FilterSecurityInterceptor攔截Http請求入手,然後讀取對資源的安全配置以後,把這些 信息交由AccessDecisionManager來進行決策,Spring爲我們提供了若干決策器來使用,在決策器中我們可以配置投票器來完成投票, 我們在上面具體分析了角色投票器的使用過程。

 

 

 

 

 

 

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