在认真学习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为我们提供了若干决策器来使用,在决策器中我们可以配置投票器来完成投票, 我们在上面具体分析了角色投票器的使用过程。
相关推荐
Spring 源码分析 Spring 框架是 Java 语言中最流行的开源框架之一,它提供了一个强大且灵活的基础设施来构建企业级应用程序。在 Spring 框架中,IOC 容器扮演着核心角色,本文将深入分析 Spring 源码,了解 IOC ...
Spring 源代码分析系列涵盖了多个关键模块,包括事务处理、IoC容器、JDBC、MVC、AOP以及与Hibernate和Acegi安全框架的集成。以下是对这些知识点的详细阐述: 1. **Spring 事务处理**:Spring 提供了声明式事务管理...
spring源码分析,百度云视频。链接如果失效了,请私聊我。
spring源码分析专题,源码分析视频,对spring的源码进行分析
以上只是Spring源码分析的部分内容,实际源码中还包括Spring的其他模块,如Spring Batch(批处理)、Spring Security(安全)、Spring Integration(集成)等。理解并掌握Spring源码,有助于我们更好地利用Spring...
Spring框架是开源的轻量级Java应用程序框架,主要目标是简化企业级应用程序的开发。它通过一套完整的设计模式和约定,实现了业务...通过深入分析Spring源码,我们可以更好地理解和利用这些功能来优化我们的应用程序。
摘自javaEye 学习spring 源码的必备资料. 多多下载
《Spring5 源码分析(第 2 版)》是某Tom老师精心编写的深度解析文档,旨在帮助读者全面理解Spring5的核心机制和设计理念。Spring作为Java领域最为广泛应用的框架之一,其源码的深入理解对于开发者来说至关重要。这篇...
《Spring5 源码分析(第 2 版)》是针对Spring框架第五个主要版本的深度解析著作,由知名讲师倾心打造,旨在帮助读者深入理解Spring框架的内部工作机制,提升对Java企业级应用开发的专业技能。本书涵盖了Spring框架的...
《Spring源码分析》这份资料深入探讨了Spring框架的核心机制,尤其聚焦于Spring5版本。Spring作为Java领域中最重要的轻量级应用框架之一,它的设计理念、实现方式以及工作原理对于任何想成为优秀Java开发者的人都至...
### Spring源码分析_Spring_IOC:深入理解Spring的IOC容器机制 #### 基本概念与核心作用 在探讨Spring框架的核心组件之一——IOC(Inversion of Control,控制反转)容器之前,首先需要理解它在Spring框架中的角色...
《Spring源码分析》 Spring框架作为Java领域中不可或缺的一部分,其强大之处在于它提供了丰富的功能,包括依赖注入(Dependency Injection,简称DI)、面向切面编程(Aspect-Oriented Programming,简称AOP)、事务...
在源码分析的过程中,读者会深入理解Spring的内部工作机制,例如如何解析配置、如何创建bean实例、如何实现AOP代理等。这将有助于开发者编写更高效、更健壮的代码,也能为参与Spring的扩展或定制打下坚实基础。 总...
《Spring源码分析》 Spring框架作为Java领域最流行的开源框架之一,它的设计思想和实现方式一直是许多开发者深入研究的对象。这份"Spring源码分析"资料深入探讨了Spring的核心机制,帮助我们理解其背后的原理,从而...
本压缩包“Spring源码解析”提供了对Spring框架核心组件——IOC(Inversion of Control,控制反转)、AOP(Aspect Oriented Programming,面向切面编程)以及Transaction(事务管理)的源码分析,帮助开发者更全面地...
### Spring 源码分析——设计模式篇 #### 一、引言 Spring框架作为Java企业级开发领域中不可或缺的一部分,其内部集成了多种设计模式,不仅有助于提高系统的可维护性和扩展性,还能够帮助开发者更好地理解和应用...
《Spring AOP 源码分析》 在深入探讨Spring AOP之前,我们先要理解AOP(面向切面编程)的基本概念。AOP是一种编程范式,它将关注点分离,使得我们可以将横切关注点(如日志、事务管理、安全检查等)与业务逻辑解耦...
在深入探讨Spring源码分析流程之前,我们先要理解Spring框架的基本概念。Spring是一个轻量级的Java企业级应用框架,它提供了丰富的功能,包括依赖注入(DI)、面向切面编程(AOP)、数据访问、事务管理等。Spring的...
通过对这些模块的源码分析,我们可以深入了解Spring如何实现其强大的功能,并能更好地运用到实际项目中,提升代码质量和可维护性。无论是新手还是经验丰富的开发者,理解Spring的源码都将是一次宝贵的进阶之旅。
《Spring源码分析——BeanFactory》 在Java的IoC(Inversion of Control)和DI(Dependency Injection)领域,Spring框架扮演着至关重要的角色。BeanFactory是Spring的核心组件之一,它是容器的基石,负责管理应用...