在認真學習Rod.Johnson的三部曲之一:<<Professional Java Development with the spring framework>>,順便也看了看源代碼想知道個究竟,拋磚引玉,有興趣的同志一起討論研究吧!
在Spring中,IOC容器的重要地位我們就不多說了,對於Spring的使用者而言,IOC容器實際上是什麼呢?我們可以說BeanFactory就 是我們看到的IoC容器,當然了Spring爲我們準備了許多種IoC容器來使用,這樣可以方便我們從不同的層面,不同的資源位置,不同的形式的定義信息 來建立我們需要的IoC容器。
在Spring中,最基本的IOC容器接口是BeanFactory – 這個接口爲具體的IOC容器的實現作了最基本的功能規定 – 不管怎麼着,作爲IOC容器,這些接口你必須要滿足應用程序的最基本要求:
Java代碼
1 public interface BeanFactory {
2
3 //這裏是對FactoryBean的轉義定義,因爲如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,
4 //如果需要得到工廠本身,需要轉義
5 String FACTORY_BEAN_PREFIX = "&";
6
7
8 //這裏根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。
9 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就是針對最基礎的BeanFactory的IOC容器的實現 – 這個實現使用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
* 訪問資源 , 體現在對ResourceLoader和Resource的支持上面,這樣我們可以從不同地方得到bean定義資源
* 支持應用事件,繼承了接口ApplicationEventPublisher,這樣在上下文中引入了事件機制而BeanFactory是沒有的。
ApplicationContext允許上下文嵌套 – 通過保持父上下文可以維持一個上下文體系 – 這個體系我們在以後對Web容器中的上下文環境的分析中可以清楚地看到。對於bean的查找可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上 下文,逐級向上,這樣爲不同的Spring應用提供了一個共享的bean定義環境。這個我們在分析Web容器中的上下文環境時也能看到。
ApplicationContext提供IoC容器的主要接口,在其體系中有許多抽象子類比如AbstractApplicationContext爲 具體的BeanFactory的實現,比如FileSystemXmlApplicationContext和 ClassPathXmlApplicationContext提供上下文的模板,使得他們只需要關心具體的資源定位問題。當應用程序代碼實例化 FileSystemXmlApplicationContext的時候,得到IoC容器的一種具體表現 – ApplicationContext,從而應用程序通過ApplicationContext來管理對bean的操作。
BeanFactory 是一個接口,在實際應用中我們一般使用ApplicationContext來使用IOC容器,它們也是IOC容器展現給應用開發者的使用接口。對應用程 序開發者來說,可以認爲BeanFactory和ApplicationFactory在不同的使用層面上代表了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得到InputStream的IO流
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,然後完成bean在IOC容器中的註冊
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得到InputStream的IO流
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,然後完成bean在IOC容器中的註冊
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,來處理xml的bean定義文件
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,來處理xml的bean定義文件
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來解析bean的xml定義文件 – 實際的處理過程是委託給BeanDefinitionParserDelegate來完成的,從而得到bean的定義信息,這些信息在Spring中使用 BeanDefinition對象來表示 – 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition這些相關的方法 – 他們都是爲處理BeanDefinitin服務的,IoC容器解析得到BeanDefinition以後,需要把它在IOC容器中註冊,這由IOC實現 BeanDefinitionRegistry接口來實現。註冊過程就是在IOC容器內部維護的一個HashMap來保存得到的 BeanDefinition的過程。這個HashMap是IoC容器持有bean信息的場所,以後對bean的操作都是圍繞這個HashMap來實現 的。
* 然後我們就可以通過BeanFactory和ApplicationContext來享受到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接收消息。
比如可以直接使用Spring的ScheduleTimerTask和TimerFactoryBean作爲定時器定時產生消息,具體可以參見《Spring框架高級編程》。
TimerFactoryBean是一個工廠bean,對其中的ScheduleTimerTask進行處理後輸出,參考ScheduleTimerTask的實現發現它最後調用的是jre的TimerTask:
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 }
如果要發送時間我們只需要在定義好的ScheduledTimerTasks中publish定義好的事件就可以了。具體可以參考書中例子的實現,這裏只 是結合FactoryBean的原理做一些解釋。如果結合事件和定時器機制,我們可以很方便的實現heartbeat(看門狗),書中給出了這個例子,這 個例子實際上結合了Spring事件和定時機制的使用兩個方面的知識 – 當然了還有IOC容器的知識(任何Spring應用我想都逃不掉IOC的魔爪:)
上面我們分析了IOC容器本身的實現,下面我們看看在典型的web環境中,Spring IOC容器是怎樣被載入和起作用的。
簡單的說,在web容器中,通過ServletContext爲Spring的IOC容器提供宿主環境,對應的建立起一個IOC容器的體系。其中,首先需 要建立的是根上下文,這個上下文持有的對象可以有業務對象,數據存取對象,資源,事物管理器等各種中間層對象。在這個上下文的基礎上,和web MVC相關還會有一個上下文來保存控制器之類的MVC對象,這樣就構成了一個層次化的上下文結構。在web容器中啓動Spring應用程序就是一個建立這 個上下文體系的過程。Spring爲web應用提供了上下文的擴展接口
WebApplicationContext:
Java代碼
1 public interface WebApplicationContext extends ApplicationContext {
2 //這裏定義的常量用於在ServletContext中存取根上下文
3 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
4 ……
5 //對WebApplicationContext來說,需要得到Web容器的ServletContext
6 ServletContext getServletContext();
7 }
8 public interface WebApplicationContext extends ApplicationContext {
9 //這裏定義的常量用於在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),Spring的ContextLoader是提供這樣性能的類,我們可以使用 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代碼
1 JdbcTemplate = new JdbcTemplate(datasource);
2 jdbcTemplate.execute(new CallBack(){
3 public CallbackInterfacedoInAction(){
4 ……
5 //用戶定義的代碼或者說Spring替我們實現的代碼
6 }
7 }
8 JdbcTemplate = new JdbcTemplate(datasource);
9 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, SqlUpdate和StoredProcedure等類,這些類都是Spring JDBC應用程序可以使用的主要類,但我們要注意使用這些類的時候,用戶需要爲他們配置好一個JdbcTemplate作爲其基本的操作的實現。
比如說我們使用MappingSqlQuery來將表數據直接映射到一個對象集合 – 具體可以參考書中的例子
1.我們需要建立DataSource和sql語句並建立持有這些對象的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.然後用戶程序需要實現MappingSqlQuery的mapRow接口,將具體的ResultSet數據生成我們需要的對象,這是我們迭代使用的方法。1,2,3步實際上爲我們定義好了一個迭代的基本單元作爲操作模板。
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來完成,通過調用JdbcTemplate和RDBMS都可以進行操作:
在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由其他包來完成服務。
書中還提到關於execute和update方法之間的區別,update方法返回的是受影響的記錄數目的一個計數,並且如果傳入參數的話,使用的是 java.sql.PreparedStatement,而execute方法總是使用 java.sql.Statement,不接受參數,而且他不返回受影響記錄的計數,更適合於創建和丟棄表的語句,而update方法更適合於插入,更新 和刪除操作,這也是我們在使用時需要注意的。
本文鏈接地址:Spring源代碼分析(三):Spring JDBC 轉載請保留,謝謝!
您還可能感興趣的文章:
· 解決hibernate中使用new Date() 造成oracle date類型時分秒精準度丟失
· 實現spring的異常處理接口HandlerExceptionResolver 自定義自己的異常處理器
下面我們對Spring MVC框架代碼進行分析,對於webApplicationContext的相關分析可以參見以前的文檔,我們這裏着重分析Spring Web MVC框架的實現.我們從分析DispatcherServlet入手:
Java代碼
1 //這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化
2 protected void initFrameworkServlet() throws ServletException, BeansException {
3 initMultipartResolver();
4 initLocaleResolver();
5 initThemeResolver();
6 initHandlerMappings();
7 initHandlerAdapters();
8 initHandlerExceptionResolvers();
9 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.class, true, false);
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 }
而這些Handler和Interceptor需要我們定義HandlerMapping的時候配置好,比如對具體的 SimpleURLHandlerMapping,他要做的就是根據URL映射的方式註冊Handler和Interceptor,自己維護一個放映映射 的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 //這裏根據url在bean定義中取得對應的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 //這裏根據url在bean定義中取得對應的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 //把url和handler的對應關係放到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 //把url和handler的對應關係放到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特殊的數據處理和設置。
過程是不是很長?我們現在在哪裏了?呵呵,我們剛剛完成的事MVC中View的render,對於InternalResourceView的 render 過程比較簡單只是完成一個資源的重定向處理。需要做的就是得到實際view的internalResource路徑,然後轉發到那個資源中去。怎樣得到資 源的路徑呢通過調用:
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對視圖的名字進行解析 – 除了單純的解析之外,它還根據我們的要求生成了我們實際需要的視圖對象。具體的viewResolver在bean定義文件中進行定義同時在 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 的功能。
下面我們來看看Spring的AOP的一些相關代碼是怎麼得到Proxy的,讓我們我們先看看AOP和Spring AOP的一些基本概念:
Advice:
通知,制定在連接點做什麼,在Sping中,他主要描述Spring圍繞方法調用注入的額外的行爲,Spring提供的通知類型有:
before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice,這些都是Spring AOP定義的接口類,具體的動作實現需要用戶程序來完成。
Pointcut:
切點,其決定一個advice應該應用於哪個連接點,也就是需要插入額外處理的地方的集合,例如,被某個advice作爲目標的一組方法。Spring pointcut通常意味着標示方法,可以選擇一組方法調用作爲pointcut,Spring提供了具體的切點來給用戶使用,比如正則表達式切點 JdkRegexpMethodPointcut通過正則表達式對方法名進行匹配,其通過使用 AbstractJdkRegexpMethodPointcut中的對MethodMatcher接口的實現來完成pointcut功能:
Java代碼
1 public final boolean matches(Method method, Class targetClass) {
2 //這裏通過放射得到方法的全名
3 String patt = method.getDeclaringClass().getName() + "." + method.getName();
4 for (int i = 0; i < this.patterns.length; i++) {
5 // 這裏是判斷是否和方法名是否匹配的代碼
6 boolean matched = matches(patt, i);
7 if (matched) {
8 for (int j = 0; j < this.excludedPatterns.length; j++) {
9 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來讓我們配置advice和pointcut。
接着我們就可以通過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的父類是AdvisedSupport,Spring使用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 //這個DefaultAopProxyFactory是Spring用來生成AopProxy的地方,
128 //當然了它包含JDK和Cglib兩種實現方式。
129 private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
130 //這個DefaultAopProxyFactory是Spring用來生成AopProxy的地方,
131 //當然了它包含JDK和Cglib兩種實現方式。
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攔截了, ProxyFactoryBean的getObject()方法得到的實際上是一個Proxy了,我們的target對象已經被封裝了。對 ProxyFactoryBean這個工廠bean而言,其生產出來的對象是封裝了目標對象的代理對象。
我們看看Spring中的事務處理的代碼,使用Spring管理事務有聲明式和編程式兩種方式,聲明式事務處理通過AOP的實現把事物管理代碼作爲 方面封裝來橫向插入到業務代碼中,使得事務管理代碼和業務代碼解藕。在這種方式我們結合IoC容器和Spirng已有的FactoryBean來對事務管 理進行屬性配置,比如傳播行爲,隔離級別等。其中最簡單的方式就是通過配置TransactionProxyFactoryBean來實現聲明式事物;
在整個源代碼分析中,我們可以大致可以看到Spring實現聲明式事物管理有這麼幾個部分:
* 對在上下文中配置的屬性的處理,這裏涉及的類是TransactionAttributeSourceAdvisor,這是一個通知器,用它來對屬性值進 行處理,屬性信息放在TransactionAttribute中來使用,而這些屬性的處理往往是和對切入點的處理是結合起來的。對屬性的處理放在類 TransactionAttributeSource中完成。
* 創建事物的過程,這個過程是委託給具體的事物管理器來創建的,但Spring通過TransactionStatus來傳遞相關的信息。
* 對事物的處理通過對相關信息的判斷來委託給具體的事物管理器完成。
我們下面看看具體的實現,在TransactionFactoryBean中:
Java代碼
1 public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
2 implements FactoryBean, BeanFactoryAware {
3 //這裏是Spring事務處理而使用的AOP攔截器,中間封裝了Spring對事務處理的代碼來支持聲明式事務處理的實現
4 private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
5
6 private Pointcut pointcut;
7
8 //這裏Spring把TransactionManager注入到TransactionInterceptor中去
9 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 //這裏Spring把TransactionManager注入到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 }
那什麼時候Spring的TransactionInterceptor被注入到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的定義過程,其中pointcut和advice都已經定義好,同時也通過通知器配置到ProxyFactory中去了。
下面讓我們回到TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎 樣定義的,這樣我們可以理解具體的屬性是怎樣起作用,這裏我們分析一下類TransactionAttributeSourceAdvisor:
Java代碼
141 public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {
142 //和其他Advisor一樣,同樣需要定義AOP中的用到的Interceptor和Pointcut
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中的用到的Interceptor和Pointcut
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, null, false, 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代碼
1 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
2 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
這裏的this參數對應的是InvocationHandler對象,這裏我們的JdkDynamicAopProxy實現了這個接口,也就是說當 Proxy對象的函數被調用的時候,這個InvocationHandler的invoke方法會被作爲回調函數調用,下面我們看看這個方法的實現:
Java代碼
3 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
4 MethodInvocation invocation = null;
5 Object oldProxy = null;
6 boolean setProxyContext = false;
7
8 TargetSource targetSource = this.advised.targetSource;
9 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實現做一個簡單的剖析。
Spring對Hinberanate的配置是通過LocalSessionFactoryBean來完成的,這是一個工廠Bean的實現,在基類AbstractSessionFactoryBean中:
Java代碼
1 /**
2 * 這是FactoryBean需要實現的接口方法,直接取得當前的sessionFactory的值
3 */
4 public Object getObject() {
5 return this.sessionFactory;
6 }
7 /**
8 * 這是FactoryBean需要實現的接口方法,直接取得當前的sessionFactory的值
9 */
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是怎樣創建的,這個方法很長,包含了創建Hibernate的SessionFactory的詳盡步驟:
Java代碼
25 protected SessionFactory buildSessionFactory() throws Exception {
26 SessionFactory sf = null;
27
28 // Create Configuration instance.
29 Configuration config = newConfiguration();
30
31 //這裏配置數據源,事務管理器,LobHander到Holder中,這個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 //這個TransactionSynchronizationManager的Resource是一個ThreadLocal變量,sessionFactory是一個單例,但ThreadLocal是和線程綁定的
297 //這樣就實現了Hiberante中常用的通過ThreadLocal的session管理機制
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中爲使用Hiberante的SessionFactory以及Session做的準備工作,在這個基礎上,用戶可以通過使用 HibernateTemplate來使用Hibernate的O/R功能,和以前看到的一樣這是一個execute的回調:
Java代碼
373 public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
374 Assert.notNull(action, "Callback object must not be null");
375 //這裏得到配置好的Hibernate的Session
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代碼
1 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
2 throws IOException, ServletException {
3 //這裏檢驗是不是符合ServletRequest/SevletResponse的要求
4 if (!(request instanceof HttpServletRequest)) {
5 throw new ServletException("Can only process HttpServletRequest");
6 }
7
8 if (!(response instanceof HttpServletResponse)) {
9 throw new ServletException("Can only process HttpServletResponse");
10 }
11
12 HttpServletRequest httpRequest = (HttpServletRequest) request;
13 HttpServletResponse httpResponse = (HttpServletResponse) response;
14 //根據HttpServletRequest和HttpServletResponse來進行驗證
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,我們看看它是怎樣進行驗證管理的 – 驗證的調用入口是authenticate在AbstractAuthenticationManager的實現中:
//這是進行驗證的函數,返回一個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(), true, true, true, 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, true, true, true,
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以及對應的UserDetailService和Encoder類來完成相應的獲取服務器端用戶數據以及與用戶輸入的驗證信息的 比對工作。
我們從FilterSecurityInterceptor我們從入手看看怎樣進行授權的:
Java代碼
1 //這裏是攔截器攔截HTTP請求的入口
2 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
3 throws IOException, ServletException {
4 FilterInvocation fi = new FilterInvocation(request, response, chain);
5 invoke(fi);
6 }
7 //這是具體的攔截調用
8 public void invoke(FilterInvocation fi) throws IOException, ServletException {
9 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 //這裏讀取配置FilterSecurityInterceptor的ObjectDefinitionSource屬性,這些屬性配置了資源的安全設置
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 //這裏讀取配置FilterSecurityInterceptor的ObjectDefinitionSource屬性,這些屬性配置了資源的安全設置
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爲我們提供了若干決策器來使用,在決策器中我們可以配置投票器來完成投票, 我們在上面具體分析了角色投票器的使用過程。