Spring源码解析


huibin的博客文章 作者: huibin http://huibin.javaeye.com 我的博客文章精选 http://www.javaeye.com - 做最棒的软件开发交流社区 第 1 / 168 页 本书由JavaEye提供的电子书DIY功能自动生成于 2010-03-18 目 录 1. SPRING 1.1 Spring源代码解析(一):IOC容器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .24 1.3 Spring源代码解析(三):Spring JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .41 1.4 Spring源代码解析(四):Spring MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .53 1.5 Spring源代码解析(五):Spring AOP获取Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .75 1.6 Spring源代码解析(六):Spring声明式事务处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .83 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .105 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .136 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .156 http://huibin.javaeye.com 第 2 / 168 页 1.1 Spring源代码解析(一):IOC容器 发表时间: 2010-03-18 在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. http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 3 / 168 页 26. } public interface BeanFactory { //这里是对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象, //如果需要得到工厂本身,需要转义 String FACTORY_BEAN_PREFIX = "&"; //这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。 Object getBean(String name) throws BeansException; //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于它会抛出异常:如果根据名字取得的bean实例的Class类型和需要的不同的话。 Object getBean(String name, Class requiredType) throws BeansException; //这里提供对bean的检索,看看是否在IOC容器有这个名字的bean boolean containsBean(String name); //这里根据bean名字得到bean实例,并同时判断这个bean是不是单件 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; //这里对得到bean实例的Class类型 Class getType(String name) throws NoSuchBeanDefinitionException; //这里得到bean的别名,如果根据别名检索,那么其原名也会被检索出来 String[] getAliases(String name); } 在BeanFactory里只对IOC容器的基本行为作了定义,根本不关心你的bean是怎样定义怎样加载的 - 就像我们 只关心从这个工厂里我们得到到什么产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心这 些。如果要关心工厂是怎样产生对象的,应用程 序需要使用具体的IOC容器实现- 当然你可以自己根据这个 BeanFactory来实现自己的IOC容器,但这个没有必要,因为Spring已经为我们准备好了一系列工厂来让我们使 用。比 如XmlBeanFactory就是针对最基础的BeanFactory的IOC容器的实现 - 这个实现使用xml来定义IOC容 器中的bean。 Spring 提供了一个BeanFactory的基本实现,XmlBeanFactory同样的通过使用模板模式来得到对IOC容器的抽 象- AbstractBeanFactory,DefaultListableBeanFactory这些抽象类为其提供模板服务。其中通过resource 接 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 4 / 168 页 口来抽象bean定义数据,对Xml定义文件的解析通过委托给XmlBeanDefinitionReader来完成。下面我们根据 书上的例子,简单的 演示IOC容器的创建过程: Java代码 1. ClassPathResource res = new ClassPathResource( "beans.xml" ); 2. DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); 3. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); 4. reader.loadBeanDefinitions(res); ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res); 这些代码演示了以下几个步骤: 1. 创建IOC配置文件的抽象资源 2. 创建一个BeanFactory 3. 把读取配置信息的BeanDefinitionReader,这里是XmlBeanDefinitionReader配置给BeanFactory 4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个 载入bean定义的过程。我们的 IoC容器就建立起来了。在BeanFactory的源代码中我们可以看到: Java代码 1. public class XmlBeanFactory extends DefaultListableBeanFactory { 2. //这里为容器定义了一个默认使用的bean定义读取器 3. private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader( this ); 4. public XmlBeanFactory(Resource resource) throws BeansException { 5. this (resource, null ); 6. } 7. //在初始化函数中使用读取器来对资源进行读取,得到bean 定义信息。 8. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { 9. super (parentBeanFactory); 10. this .reader.loadBeanDefinitions(resource); 11. } http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 5 / 168 页 public class XmlBeanFactory extends DefaultListableBeanFactory { //这里为容器定义了一个默认使用的bean定义读取器 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } //在初始化函数中使用读取器来对资源进行读取,得到bean定义信息。 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } 我们在后面会看到读取器读取资源和注册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容器服务。 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 6 / 168 页 下 面我们具体看看通过FileSystemXmlApplicationContext是怎样建立起IOC容器的, 显而易见我们可以通过 new来得到IoC容器: Java代码 1. ApplicationContext = new FileSystemXmlApplicationContext(xmlPath); ApplicationContext = new FileSystemXmlApplicationContext(xmlPath); 调用的是它初始化代码: Java代码 1. public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 2. throws BeansException { 3. super (parent); 4. this .configLocations = configLocations; 5. if (refresh) { 6.   //这里是IoC容器的初始化过程,其初始化过程的大致 步骤由AbstractApplicationContext来定 义 7. refresh(); 8. } 9. } public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); this.configLocations = configLocations; if (refresh) {  //这里是IoC容器的初始化过程,其初始化过程的大致步骤由AbstractApplicationContext来定义 refresh(); } } refresh的模板在AbstractApplicationContext: Java代码 1. public void refresh() throws BeansException, IllegalStateException { http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 7 / 168 页 2. synchronized ( this .startupShutdownMonitor) { 3. synchronized ( this .activeMonitor) { 4. this .active = true ; 5. } 6. 7. // 这里需要子类来协助完成资源位置定义,bean 载入和向IOC容器注册的过程 8. refreshBeanFactory(); 9. ............ 10. } public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { synchronized (this.activeMonitor) { this.active = true; } // 这里需要子类来协助完成资源位置定义,bean载入和向IOC容器注册的过程 refreshBeanFactory(); ............ } 这个方法包含了整个BeanFactory初始化的过程,对于特定的FileSystemXmlBeanFactory,我们看到定位资源 位置由refreshBeanFactory()来实现: 在AbstractXmlApplicationContext中定义了对资源的读取 过程,默认由XmlBeanDefinitionReader来读取: Java代码 1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { 2. // 这里使用 XMLBeanDefinitionReader来载入bean定义信息的XML文件 3. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 4. 5. //这里配置reader的环境,其中 ResourceLoader是我们用来定位bean定义信息资源位置的 6. ///因为上下文本身实现了ResourceLoader接 口,所以可以直接把上下文作为ResourceLoader传 递给XmlBeanDefinitionReader 7. beanDefinitionReader.setResourceLoader( this ); 8. beanDefinitionReader.setEntityResolver( new ResourceEntityResolver( this )); http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 8 / 168 页 9. 10. initBeanDefinitionReader(beanDefinitionReader); 11. //这里转到定义好的 XmlBeanDefinitionReader中对载入bean信息进行处理 12. loadBeanDefinitions(beanDefinitionReader); 13. } protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { // 这里使用XMLBeanDefinitionReader来载入bean定义信息的XML文件 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //这里配置reader的环境,其中ResourceLoader是我们用来定位bean定义信息资源位置的 ///因为上下文本身实现了ResourceLoader接口,所以可以直接把上下文作为ResourceLoader传递给XmlBeanDefinitionReader beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); //这里转到定义好的XmlBeanDefinitionReader中对载入bean信息进行处理 loadBeanDefinitions(beanDefinitionReader); } 转到beanDefinitionReader中进行处理: Java代码 1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { 2. Resource[] configResources = getConfigResources(); 3. if (configResources != null ) { 4. //调用 XmlBeanDefinitionReader来载入bean定义信息。 5. reader.loadBeanDefinitions(configResources); 6. } 7. String[] configLocations = getConfigLocations(); 8. if (configLocations != null ) { 9. reader.loadBeanDefinitions(configLocations); 10. } 11. } http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 9 / 168 页 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { //调用XmlBeanDefinitionReader来载入bean定义信息。 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } } 而在作为其抽象父类的AbstractBeanDefinitionReader中来定义载入过程: Java代码 1. public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { 2. //这里得到当前定义的ResourceLoader,默认的我们使 用DefaultResourceLoader 3. ResourceLoader resourceLoader = getResourceLoader(); 4. ......... //如果没有找到我们需要的 ResourceLoader,直接抛出异常 5. if (resourceLoader instanceof ResourcePatternResolver) { 6. // 这里处理我们在定义位置时使用的各种 pattern,需要ResourcePatternResolver来完成 7. try { 8. Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); 9. int loadCount = loadBeanDefinitions(resources); 10. return loadCount; 11. } 12. ........ 13. } 14. else { 15. // 这里通过ResourceLoader来完成位 置定位 16. Resource resource = resourceLoader.getResource(location); 17. // 这里已经把一个位置定义转化为Resource 接口,可以供XmlBeanDefinitionReader来使用了 18. int loadCount = loadBeanDefinitions(resource); 19. return loadCount; http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 10 / 168 页 20. } 21. } public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //这里得到当前定义的ResourceLoader,默认的我们使用DefaultResourceLoader ResourceLoader resourceLoader = getResourceLoader(); .........//如果没有找到我们需要的ResourceLoader,直接抛出异常 if (resourceLoader instanceof ResourcePatternResolver) { // 这里处理我们在定义位置时使用的各种pattern,需要ResourcePatternResolver来完成 try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); return loadCount; } ........ } else { // 这里通过ResourceLoader来完成位置定位 Resource resource = resourceLoader.getResource(location); // 这里已经把一个位置定义转化为Resource接口,可以供XmlBeanDefinitionReader来使用了 int loadCount = loadBeanDefinitions(resource); return loadCount; } } 当我们通过ResourceLoader来载入资源,别忘了了我们的GenericApplicationContext也实现了 ResourceLoader接口: Java代码 1. public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { 2. public Resource getResource(String location) { 3. //这里调用当前的loader也就是 DefaultResourceLoader来完成载入 4. if ( this .resourceLoader != null ) { 5. return this .resourceLoader.getResource(location); 6. } 7. return super .getResource(location); http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 11 / 168 页 8. } 9. ....... 10. } public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { public Resource getResource(String location) { //这里调用当前的loader也就是DefaultResourceLoader来完成载入 if (this.resourceLoader != null) { return this.resourceLoader.getResource(location); } return super.getResource(location); } ....... } 而我们的FileSystemXmlApplicationContext就是一个DefaultResourceLoader - GenericApplicationContext()通过DefaultResourceLoader: Java代码 1. public Resource getResource(String location) { 2. //如果是类路径的方式,那需要使用 ClassPathResource来得到bean文件的资源对象 3. if (location.startsWith(CLASSPATH_URL_PREFIX)) { 4. return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); 5. } 6. else { 7. try { 8. // 如果是URL方式,使用 UrlResource作为bean文件的资源对象 9. URL url = new URL(location); 10. return new UrlResource(url); 11. } 12. catch (MalformedURLException ex) { 13. // 如果都不是,那我们只能委托给子类由子 类来决定使用什么样的资源对象了 14. return getResourceByPath(location); 15. } 16. } 17. } http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 12 / 168 页 public Resource getResource(String location) { //如果是类路径的方式,那需要使用ClassPathResource来得到bean文件的资源对象 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 如果是URL方式,使用UrlResource作为bean文件的资源对象 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // 如果都不是,那我们只能委托给子类由子类来决定使用什么样的资源对象了 return getResourceByPath(location); } } } 我们的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的实现类, 他实现了以下的接 口: Java代码 1. protected Resource getResourceByPath(String path) { 2. if (path != null && path.startsWith( "/" )) { 3. path = path.substring( 1 ); 4. } 5. //这里使用文件系统资源对象来定义bean文件 6. return new FileSystemResource(path); 7. } protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } //这里使用文件系统资源对象来定义bean文件 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 13 / 168 页 return new FileSystemResource(path); } 这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来 完成从文件系统 得到配置文件的资源定义。这样,就可以从文件系统路径上对IOC配置文件进行加载 - 当然我们可以按照这个逻 辑从任何地方加载,在Spring中我们看到它提供的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource等来供我们使用。上面我们看到的是定位Resource的一个过程,而这只是 加载过程的一 部分 - 我们回到AbstractBeanDefinitionReaderz中的loadDefinitions(resource)来看看得到代 表bean文 件的资源定义以后的载入过程,默认的我们使用XmlBeanDefinitionReader: Java代码 1. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 2. ....... 3. try { 4. //这里通过Resource得到 InputStream的IO流 5. InputStream inputStream = encodedResource.getResource().getInputStream(); 6. try { 7. //从InputStream中得到XML的 解析源 8. InputSource inputSource = new InputSource(inputStream); 9. if (encodedResource.getEncoding() != null ) { 10. inputSource.setEncoding(encodedResource.getEncoding()); 11. } 12. //这里是具体的解析和注册过程 13. return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 14. } 15. finally { 16. //关闭从Resource中得到的IO流 17. inputStream.close(); 18. } 19. } 20. ......... 21. } 22. 23. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 24. throws BeanDefinitionStoreException { http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 14 / 168 页 25. try { 26. int validationMode = getValidationModeForResource(resource); 27. //通过解析得到DOM,然后完成bean在IOC容 器中的注册 28. Document doc = this .documentLoader.loadDocument( 29. inputSource, this .entityResolver, this .errorHandler, validationMode, this .namespaceAware); 30. return registerBeanDefinitions(doc, resource); 31. } 32. ....... 33. } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ....... try { //这里通过Resource得到InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //从InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //这里是具体的解析和注册过程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { //关闭从Resource中得到的IO流 inputStream.close(); } } ......... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //通过解析得到DOM,然后完成bean在IOC容器中的注册 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 15 / 168 页 Document doc = this.documentLoader.loadDocument( inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware); return registerBeanDefinitions(doc, resource); } ....... } 我们看到先把定义文件解析为DOM对象,然后进行具体的注册过程: Java代码 1. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 2. // 这里定义解析器,使用 XmlBeanDefinitionParser来解析xml方式的bean定义文件 - 现在的版本 不用这个解析器了,使用的是 XmlBeanDefinitionReader 3. if ( this .parserClass != null ) { 4. XmlBeanDefinitionParser parser = 5. (XmlBeanDefinitionParser) BeanUtils.instantiateClass( this .parserClass); 6. return parser.registerBeanDefinitions( this , doc, resource); 7. } 8. // 具体的注册过程,首先得到 XmlBeanDefinitionReader,来处理xml的bean定义文件 9. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 10. int countBefore = getBeanFactory().getBeanDefinitionCount(); 11. documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 12. return getBeanFactory().getBeanDefinitionCount() - countBefore; 13. } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 这里定义解析器,使用XmlBeanDefinitionParser来解析xml方式的bean定义文件 - 现在的版本不用这个解析器了,使用的是XmlBeanDefinitionReader if (this.parserClass != null) { XmlBeanDefinitionParser parser = (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass); return parser.registerBeanDefinitions(this, doc, resource); } // 具体的注册过程,首先得到XmlBeanDefinitionReader,来处理xml的bean定义文件 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 16 / 168 页 int countBefore = getBeanFactory().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getBeanFactory().getBeanDefinitionCount() - countBefore; } 具体的在BeanDefinitionDocumentReader中完成对,下面是一个简要的注册过程来完成bean定义文件的解析 和 IOC容器中bean的初始化 Java代码 1. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 2. this .readerContext = readerContext; 3. 4. logger.debug( "Loading bean definitions" ); 5. Element root = doc.getDocumentElement(); 6. 7. BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); 8. 9. preProcessXml(root); 10. parseBeanDefinitions(root, delegate); 11. postProcessXml(root); 12. } 13. 14. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 15. if (delegate.isDefaultNamespace(root.getNamespaceURI())) { 16. //这里得到xml文件的子节点,比如各个bean节 点 17. NodeList nl = root.getChildNodes(); 18. 19. //这里对每个节点进行分析处理 20. for ( int i = 0 ; i < nl.getLength(); i++) { 21. Node node = nl.item(i); 22. if (node instanceof Element) { 23. Element ele = (Element) node; 24. String namespaceUri = ele.getNamespaceURI(); 25. if (delegate.isDefaultNamespace(namespaceUri)) { 26. //这里是解析过程的调用, 对缺省的元素进行分析比如bean元素 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 17 / 168 页 27. parseDefaultElement(ele, delegate); 28. } 29. else { 30. delegate.parseCustomElement(ele); 31. } 32. } 33. } 34. } else { 35. delegate.parseCustomElement(root); 36. } 37. } 38. 39. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 40. //这里对元素Import进行处理 41. if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) { 42. importBeanDefinitionResource(ele); 43. } 44. else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) { 45. String name = ele.getAttribute(NAME_ATTRIBUTE); 46. String alias = ele.getAttribute(ALIAS_ATTRIBUTE); 47. getReaderContext().getReader().getBeanFactory().registerAlias(name, alias); 48. getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); 49. } 50. //这里对我们最熟悉的bean元素进行处理 51. else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { 52. //委托给 BeanDefinitionParserDelegate来完成对bean元素的处理,这个类包含了具体的bean 解析的过程。 53. // 把解析bean文件得到的信息放到 BeanDefinition里,他是bean信息的主要载体,也是IOC容 器的管理对象。 54. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 55. if (bdHolder != null ) { 56. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 57. // 这里是向IOC容器注册,实际上是放到 IOC容器的一个map里 58. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 59. 60. // 这里向IOC容器发送事件,表示解析和 注册完成。 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 18 / 168 页 61. getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder)); 62. } 63. } 64. } public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); preProcessXml(root); parseBeanDefinitions(root, delegate); postProcessXml(root); } protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root.getNamespaceURI())) { //这里得到xml文件的子节点,比如各个bean节点 NodeList nl = root.getChildNodes(); //这里对每个节点进行分析处理 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; String namespaceUri = ele.getNamespaceURI(); if (delegate.isDefaultNamespace(namespaceUri)) { //这里是解析过程的调用,对缺省的元素进行分析比如bean元素 parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 19 / 168 页 } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //这里对元素Import进行处理 if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); getReaderContext().getReader().getBeanFactory().registerAlias(name, alias); getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } //这里对我们最熟悉的bean元素进行处理 else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { //委托给BeanDefinitionParserDelegate来完成对bean元素的处理,这个类包含了具体的bean解析的过程。 // 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体,也是IOC容器的管理对象。 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 这里是向IOC容器注册,实际上是放到IOC容器的一个map里 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); // 这里向IOC容器发送事件,表示解析和注册完成。 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } } } 我们看到在parseBeanDefinition中对具体bean元素的解析式交给 BeanDefinitionParserDelegate来完成的, 下面我们看看解析完的bean是怎样在IOC容器中注册的: 在 BeanDefinitionReaderUtils调用的是: http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 20 / 168 页 Java代码 1. public static void registerBeanDefinition( 2. BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException { 3. 4. // 这里得到需要注册bean的名字; 5. String beanName = bdHolder.getBeanName(); 6. //这是调用IOC来注册的bean的过程,需要得到 BeanDefinition 7. beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()); 8. 9. // 别名也是可以通过IOC容器和bean联系起来的进行注 册 10. String[] aliases = bdHolder.getAliases(); 11. if (aliases != null ) { 12. for ( int i = 0 ; i < aliases.length; i++) { 13. beanFactory.registerAlias(beanName, aliases[i]); 14. } 15. } 16. } public static void registerBeanDefinition( BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeansException { // 这里得到需要注册bean的名字; String beanName = bdHolder.getBeanName(); //这是调用IOC来注册的bean的过程,需要得到BeanDefinition beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()); // 别名也是可以通过IOC容器和bean联系起来的进行注册 String[] aliases = bdHolder.getAliases(); if (aliases != null) { for (int i = 0; i < aliases.length; i++) { beanFactory.registerAlias(beanName, aliases[i]); } } } 我们看看XmlBeanFactory中的注册实现: http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 21 / 168 页 Java代码 1. //--------------------------------------------------------------------- 2. // 这里是IOC容器对BeanDefinitionRegistry接口的实现 3. //--------------------------------------------------------------------- 4. 5. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 6. throws BeanDefinitionStoreException { 7. 8. ..... //这里省略了对BeanDefinition的验 证过程 9. //先看看在容器里是不是已经有了同名的bean,如果有抛出 异常。 10. Object oldBeanDefinition = this .beanDefinitionMap.get(beanName); 11. if (oldBeanDefinition != null ) { 12. if (! this .allowBeanDefinitionOverriding) { 13. ........... 14. } 15. else { 16. //把bean的名字加到IOC容器中去 17. this .beanDefinitionNames.add(beanName); 18. } 19. //这里把bean的名字和Bean定义联系起来放到一个 HashMap中去,IOC容器通过这个Map来维护容 器里的Bean定义信息。 20. this .beanDefinitionMap.put(beanName, beanDefinition); 21. removeSingleton(beanName); 22. } //--------------------------------------------------------------------- // 这里是IOC容器对BeanDefinitionRegistry接口的实现 //--------------------------------------------------------------------- public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { .....//这里省略了对BeanDefinition的验证过程 //先看看在容器里是不是已经有了同名的bean,如果有抛出异常。 Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { ........... http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 22 / 168 页 } else { //把bean的名字加到IOC容器中去 this.beanDefinitionNames.add(beanName); } //这里把bean的名字和Bean定义联系起来放到一个HashMap中去,IOC容器通过这个Map来维护容器里的Bean定义信息。 this.beanDefinitionMap.put(beanName, beanDefinition); removeSingleton(beanName); } 这样就完成了Bean定义在IOC容器中的注册,就可被IOC容器进行管理和使用了。 从上面的代码来看,我们总结一下IOC容器 初始化的基本步骤: * 初始化的入口在容器实现中的refresh()调用来完成 * 对bean 定义载入IOC容器使用的方法是loadBeanDefinition,其中的大致过程如下:通过ResourceLoader 来完成资源文件位置的定 位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了 ResourceLoader的实现,可以从类路径,文件系统, URL等方式来定为资源位置。如果是XmlBeanFactory作为 IOC容器,那么需要为它指定bean定义的资源,也就是说bean定义文件时通过 抽象成Resource来被IOC容器 处理的,容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,往往使用的 是 XmlBeanDefinitionReader来解析bean的xml定义文件 - 实际 http://huibin.javaeye.com 1.1 Spring源代码解析(一):IOC容器 第 23 / 168 页 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 发表时间: 2010-03-18 上面我们分析了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. } public interface WebApplicationContext extends ApplicationContext { //这里定义的常量用于在ServletContext中存取根上下文 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; ...... //对WebApplicationContext来说,需要得到Web容器的ServletContext ServletContext getServletContext(); } 而一般的启动过程,Spring会使用一个默认的实现,XmlWebApplicationContext - 这个上下文实现作为在 web容器中的根上下文容器被建立起来,具体的建立过程在下面我们会详细分析。 Java代码 1. public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { 2. http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 24 / 168 页 3. /** 这是和web部署相关的位置信息,用来作为默认的根上 下文bean定义信息的存放位置*/ 4. public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/ applicationContext.xml" ; 5. public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/" ; 6. public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml" ; 7. 8. //我们又看到了熟悉的 loadBeanDefinition,就像我们前面对IOC容器的分析中一样,这个加载工程 在容器的refresh()的时候启动。 9. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { 10. //对于 XmlWebApplicationContext,当然使用的是XmlBeanDefinitionReader来对bean定义信 息来进行解析 11. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 12. 13. beanDefinitionReader.setResourceLoader( this ); 14. beanDefinitionReader.setEntityResolver( new ResourceEntityResolver( this )); 15. 16. initBeanDefinitionReader(beanDefinitionReader); 17. loadBeanDefinitions(beanDefinitionReader); 18. } 19. 20. protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { 21. } 22. //使用XmlBeanDefinitionReader来读 入bean定义信息 23. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { 24. String[] configLocations = getConfigLocations(); 25. if (configLocations != null ) { 26. for ( int i = 0 ; i < configLocations.length; i++) { 27. reader.loadBeanDefinitions(configLocations[i]); 28. } 29. } 30. } 31. //这里取得bean定义信息位置,默认的地方是/WEB- INF/applicationContext.xml 32. protected String[] getDefaultConfigLocations() { 33. if (getNamespace() != null ) { http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 25 / 168 页 34. return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; 35. } 36. else { 37. return new String[] {DEFAULT_CONFIG_LOCATION}; 38. } 39. } 40. } public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { /** 这是和web部署相关的位置信息,用来作为默认的根上下文bean定义信息的存放位置*/ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; //我们又看到了熟悉的loadBeanDefinition,就像我们前面对IOC容器的分析中一样,这个加载工程在容器的refresh()的时候启动。 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException { //对于XmlWebApplicationContext,当然使用的是XmlBeanDefinitionReader来对bean定义信息来进行解析 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } //使用XmlBeanDefinitionReader来读入bean定义信息 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (int i = 0; i < configLocations.length; i++) { reader.loadBeanDefinitions(configLocations[i]); } http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 26 / 168 页 } } //这里取得bean定义信息位置,默认的地方是/WEB-INF/applicationContext.xml protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } } 对于一个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代码 1. WebApplicationContext getWebApplicationContext(ServletContext sc) WebApplicationContext getWebApplicationContext(ServletContext sc) 以Tomcat作为Servlet容器为例,下面是具体的步骤: 1.Tomcat 启动时需要从web.xml中读取启动参数,在web.xml中我们需要对ContextLoaderListener进行配 置,对于在web应用启动入 口是在ContextLoaderListener中的初始化部分;从Spring MVC上看,实际上在 http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 27 / 168 页 web容器中维护了一系列的IOC容器,其中在ContextLoader中载入的IOC容器作为根上下文而存在于 ServletContext中。 Java代码 1. //这里对根上下文进行初始化。 2. public void contextInitialized(ServletContextEvent event) { 3. //这里创建需要的ContextLoader 4. this .contextLoader = createContextLoader(); 5. //这里使用ContextLoader对根上下文进行载入和 初始化 6. this .contextLoader.initWebApplicationContext(event.getServletContext()); 7. } //这里对根上下文进行初始化。 public void contextInitialized(ServletContextEvent event) { //这里创建需要的ContextLoader this.contextLoader = createContextLoader(); //这里使用ContextLoader对根上下文进行载入和初始化 this.contextLoader.initWebApplicationContext(event.getServletContext()); } 通过ContextLoader建立起根上下文的过程,我们可以在ContextLoader中看到: Java代码 1. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) 2. throws IllegalStateException, BeansException { 3. //这里先看看是不是已经在ServletContext中存 在上下文,如果有说明前面已经被载入过,或者是配 置文件有错误。 4. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null ) { 5. //直接抛出异常 6. ......... 7. } 8. 9. ............... 10. try { 11. // 这里载入根上下文的父上下文 http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 28 / 168 页 12. ApplicationContext parent = loadParentContext(servletContext); 13. 14. //这里创建根上下文作为整个应用的上下文同时把它存 到ServletContext中去,注意这里使用的 ServletContext的属性值是 15. //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 以后的应用都是根据这个属性值来取得 根上下文的 - 往往作为自己上下文的父上下文 16. this .context = createWebApplicationContext(servletContext, parent); 17. servletContext.setAttribute( 18. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this .context); 19. .......... 20. 21. return this .context; 22. } 23. ............ 24. } public WebApplicationContext initWebApplicationContext(ServletContext servletContext) throws IllegalStateException, BeansException { //这里先看看是不是已经在ServletContext中存在上下文,如果有说明前面已经被载入过,或者是配置文件有错误。 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { //直接抛出异常 ......... } ............... try { // 这里载入根上下文的父上下文 ApplicationContext parent = loadParentContext(servletContext); //这里创建根上下文作为整个应用的上下文同时把它存到ServletContext中去,注意这里使用的ServletContext的属性值是 //ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后的应用都是根据这个属性值来取得根上下文的 - 往往作为自己上下文的父上下文 this.context = createWebApplicationContext(servletContext, parent); servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); .......... return this.context; http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 29 / 168 页 } ............ } 建立根上下文的父上下文使用的是下面的代码,取决于在web.xml中定义的参数:locatorFactorySelector,这 是一 个可选参数: Java代码 1. protected ApplicationContext loadParentContext(ServletContext servletContext) 2. throws BeansException { 3. 4. ApplicationContext parentContext = null ; 5. 6. String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM); 7. String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM); 8. 9. if (locatorFactorySelector != null ) { 10. BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector); 11. ........ 12. //得到根上下文的父上下文的引用 13. this .parentContextRef = locator.useBeanFactory(parentContextKey); 14. //这里建立得到根上下文的父上下文 15. parentContext = (ApplicationContext) this .parentContextRef.getFactory(); 16. } 17. 18. return parentContext; 19. } protected ApplicationContext loadParentContext(ServletContext servletContext) throws BeansException { ApplicationContext parentContext = null; http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 30 / 168 页 String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM); String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM); if (locatorFactorySelector != null) { BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector); ........ //得到根上下文的父上下文的引用 this.parentContextRef = locator.useBeanFactory(parentContextKey); //这里建立得到根上下文的父上下文 parentContext = (ApplicationContext) this.parentContextRef.getFactory(); } return parentContext; } 得到根上下文的父上下文以后,就是根上下文的创建过程: Java代码 1. protected WebApplicationContext createWebApplicationContext( 2. ServletContext servletContext, ApplicationContext parent) throws BeansException { 3. //这里需要确定我们载入的根WebApplication的 类型,由在web.xml中配置的contextClass中配置 的参数可以决定我们需要载入什么样的ApplicationContext, 4. //如果没有使用默认的。 5. Class contextClass = determineContextClass(servletContext); 6. ......... 7. //这里就是上下文的创建过程 8. ConfigurableWebApplicationContext wac = 9. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 10. //这里保持对父上下文和ServletContext的引用 到根上下文中 11. wac.setParent(parent); 12. wac.setServletContext(servletContext); 13. 14. //这里从web.xml中取得相关的初始化参数 15. String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM); 16. if (configLocation != null ) { 17. wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation, http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 31 / 168 页 18. ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS)); 19. } 20. //这里对WebApplicationContext进行初始 化,我们又看到了熟悉的refresh调用。 21. wac.refresh(); 22. return wac; 23. } protected WebApplicationContext createWebApplicationContext( ServletContext servletContext, ApplicationContext parent) throws BeansException { //这里需要确定我们载入的根WebApplication的类型,由在web.xml中配置的contextClass中配置的参数可以决定我们需要载入什么样的ApplicationContext, //如果没有使用默认的。 Class contextClass = determineContextClass(servletContext); ......... //这里就是上下文的创建过程 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //这里保持对父上下文和ServletContext的引用到根上下文中 wac.setParent(parent); wac.setServletContext(servletContext); //这里从web.xml中取得相关的初始化参数 String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocation != null) { wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation, ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS)); } //这里对WebApplicationContext进行初始化,我们又看到了熟悉的refresh调用。 wac.refresh(); return wac; } 初始化根ApplicationContext后将其存储到SevletContext中去以后,这样就建立了一个全局的关于整个应用的 上 下文。这个根上下文会被以后的DispatcherServlet初始化自己的时候作为自己ApplicationContext的父上下 文。这个在对 DispatcherServlet做分析的时候我们可以看看到。 3.完成对ContextLoaderListener的初始化以 后, Tomcat开始初始化DispatchServlet,- 还记得我们在 web.xml中队载入次序进行了定义。DispatcherServlet会建立自己的ApplicationContext,同时建立这 个自己 http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 32 / 168 页 的上下文的时候会从ServletContext中得到根上下文作为父上下文,然后再对自己的上下文进行初始化,并最后 存到 ServletContext中去供以后检索和使用。 可以从DispatchServlet的父类FrameworkServlet的代码中看 到大致的初始化过程,整个ApplicationContext 的创建过程和ContextLoder创建的过程相类似: Java代码 1. protected final void initServletBean() throws ServletException, BeansException { 2. ......... 3. try { 4. //这里是对上下文的初始化过程。 5. this .webApplicationContext = initWebApplicationContext(); 6. //在完成对上下文的初始化过程结束后,根据bean 配置信息建立MVC框架的各个主要元素 7. initFrameworkServlet(); 8. } 9. ........ 10. } protected final void initServletBean() throws ServletException, BeansException { ......... try { //这里是对上下文的初始化过程。 this.webApplicationContext = initWebApplicationContext(); //在完成对上下文的初始化过程结束后,根据bean配置信息建立MVC框架的各个主要元素 initFrameworkServlet(); } ........ } 对initWebApplicationContext()调用的代码如下: Java代码 1. protected WebApplicationContext initWebApplicationContext() throws BeansException { 2. //这里调用 WebApplicationContextUtils静态类来得到根上下文 3. WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 4. http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 33 / 168 页 5. //创建当前DispatcherServlet的上下文,其 上下文种类使用默认的在FrameworkServlet定义好 的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; 6. WebApplicationContext wac = createWebApplicationContext(parent); 7. ........ 8. if (isPublishContext()) { 9. //把当前建立的上下文存到 ServletContext中去,注意使用的属性名是和当前Servlet名相关的。 10. String attrName = getServletContextAttributeName(); 11. getServletContext().setAttribute(attrName, wac); 12. } 13. return wac; 14. } protected WebApplicationContext initWebApplicationContext() throws BeansException { //这里调用WebApplicationContextUtils静态类来得到根上下文 WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); //创建当前DispatcherServlet的上下文,其上下文种类使用默认的在FrameworkServlet定义好的:DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; WebApplicationContext wac = createWebApplicationContext(parent); ........ if (isPublishContext()) { //把当前建立的上下文存到ServletContext中去,注意使用的属性名是和当前Servlet名相关的。 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; } 其中我们看到调用了WebApplicationContextUtils的静态方法得到根ApplicationContext: Java代码 1. public static WebApplicationContext getWebApplicationContext(ServletContext sc) { 2. //很简单,直接从ServletContext中通 过属性名得到根上下文 3. Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); 4. ....... 5. return (WebApplicationContext) attr; http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 34 / 168 页 6. } 7. 然后创建DispatcherServlet自己的WebApplicationContext: 8. protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) 9. throws BeansException { 10. ....... 11. //这里使用了BeanUtils直接得到 WebApplicationContext,ContextClass是前面定义好的 DEFAULT_CONTEXT_CLASS = 12. //XmlWebApplicationContext.class; 13. ConfigurableWebApplicationContext wac = 14. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass()); 15. 16. //这里配置父上下文,就是在 ContextLoader中建立的根上下文 17. wac.setParent(parent); 18. 19. //保留ServletContext的引用和相关的 配置信息。 20. wac.setServletContext(getServletContext()); 21. wac.setServletConfig(getServletConfig()); 22. wac.setNamespace(getNamespace()); 23. 24. //这里得到ApplicationContext配 置文件的位置 25. if (getContextConfigLocation() != null ) { 26. wac.setConfigLocations( 27. StringUtils.tokenizeToStringArray( 28. getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS)); 29. } 30. 31. //这里调用ApplicationContext的 初始化过程,同样需要使用refresh() 32. wac.refresh(); 33. return wac; 34. } public static WebApplicationContext getWebApplicationContext(ServletContext sc) { //很简单,直接从ServletContext中通过属性名得到根上下文 Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); ....... http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 35 / 168 页 return (WebApplicationContext) attr; } 然后创建DispatcherServlet自己的WebApplicationContext: protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) throws BeansException { ....... //这里使用了BeanUtils直接得到WebApplicationContext,ContextClass是前面定义好的DEFAULT_CONTEXT_CLASS = //XmlWebApplicationContext.class; ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(getContextClass()); //这里配置父上下文,就是在ContextLoader中建立的根上下文 wac.setParent(parent); //保留ServletContext的引用和相关的配置信息。 wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); //这里得到ApplicationContext配置文件的位置 if (getContextConfigLocation() != null) { wac.setConfigLocations( StringUtils.tokenizeToStringArray( getContextConfigLocation(), ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS)); } //这里调用ApplicationContext的初始化过程,同样需要使用refresh() wac.refresh(); return wac; } 4. 然后就是DispatchServlet中对Spring MVC的配置过程,首先对配置文件中的定义元素进行配置 - 请注意这 个时候我们的WebApplicationContext已经建立起来了,也意味着DispatcherServlet有自己的定义资源,可以 需 要从web.xml中读取bean的配置信息,通常我们会使用单独的xml文件来配置MVC中各个要素定义,这里和 web容器相关的加载过程实际上已经完 成了,下面的处理和普通的Spring应用程序的编写没有什么太大的差 别,我们先看看MVC的初始化过程: http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 36 / 168 页 Java代码 1. protected void initFrameworkServlet() throws ServletException, BeansException { 2. initMultipartResolver(); 3. initLocaleResolver(); 4. initThemeResolver(); 5. initHandlerMappings(); 6. initHandlerAdapters(); 7. initHandlerExceptionResolvers(); 8. initRequestToViewNameTranslator(); 9. initViewResolvers(); 10. } protected void initFrameworkServlet() throws ServletException, BeansException { initMultipartResolver(); initLocaleResolver(); initThemeResolver(); initHandlerMappings(); initHandlerAdapters(); initHandlerExceptionResolvers(); initRequestToViewNameTranslator(); initViewResolvers(); } 5. 这样MVC的框架就建立起来了,DispatchServlet对接受到的HTTP Request进行分发处理由doService()完 成,具体的MVC处理过程我们在doDispatch()中完成,其中包括使用Command模式 建立执行链,显示模型数 据等,这些处理我们都可以在DispatcherServlet的代码中看到: Java代码 1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 2. ...... 3. try { 4. doDispatch(request, response); 5. } 6. ....... 7. } http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 37 / 168 页 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { ...... try { doDispatch(request, response); } ....... } 实际的请求分发由doDispatch(request,response)来完成: Java代码 1. protected void doDispatch( final HttpServletRequest request, HttpServletResponse response) throws Exception { 2. ....... 3. // 这是Spring定义的执行链,里面放了映射关系对应 的handler和定义的相关拦截器。 4. HandlerExecutionChain mappedHandler = null ; 5. 6. ...... 7. try { 8. //我们熟悉的ModelAndView在这里出 现了。 9. ModelAndView mv = null ; 10. try { 11. processedRequest = checkMultipart(request); 12. 13. //这里更具request中的参数和映 射关系定义决定使用的handler 14. mappedHandler = getHandler(processedRequest, false ); 15. 16. ...... 17. //这里是handler的调用过程,类 似于Command模式中的execute. 18. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 19. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 20. 21. ....... 22. //这里将模型数据通过视图进行展现 23. if (mv != null && !mv.wasCleared()) { 24. render(mv, processedRequest, response); http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 38 / 168 页 25. } 26. ........ 27. } protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception { ....... // 这是Spring定义的执行链,里面放了映射关系对应的handler和定义的相关拦截器。 HandlerExecutionChain mappedHandler = null; ...... try { //我们熟悉的ModelAndView在这里出现了。 ModelAndView mv = null; try { processedRequest = checkMultipart(request); //这里更具request中的参数和映射关系定义决定使用的handler mappedHandler = getHandler(processedRequest, false); ...... //这里是handler的调用过程,类似于Command模式中的execute. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ....... //这里将模型数据通过视图进行展现 if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); } ........ } 这样具体的MVC模型的实现就由bean配置文件里定义好的view resolver,handler这些类来实现用户代码的功 能。 总 结上面的过程,我们看到在web容器中,ServletContext可以持有一系列的web上下文,而在整个web上下 文中存在一个根上下文来作为其它 Servlet上下文的父上下文。这个根上下文是由ContextLoader载入并进行初 始化的,对于我们的web应用, DispatcherSerlvet载入并初始化自己的上下文,这个上下文的父上下文是根上下 http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 39 / 168 页 文,并且我们也能从ServletContext中根据 Servlet的名字来检索到我们需要的对应于这个Servlet的上下文,但 是根上下文的名字是由Spring唯一确定的。这个 DispactcherServlet建立的上下文就是我们开发Spring MVC 应用的IOC容器。 具体的web请求处理在上下文体系建立 完成以后由DispactcherServlet来完成,上面对MVC的运作做了一个大 致的描述,下面我们会具体就SpringMVC的框架实现作一个详 细的分析。 http://huibin.javaeye.com 1.2 Spring源代码解析(二):IoC容器在Web容器中的启动 第 40 / 168 页 1.3 Spring源代码解析(三):Spring JDBC 发表时间: 2010-03-18 下面我们看看Spring JDBC相关的实现, 在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. } JdbcTemplate = new JdbcTemplate(datasource); jdbcTemplate.execute(new CallBack(){ public CallbackInterfacedoInAction(){ ...... //用户定义的代码或者说Spring替我们实现的代码 } } 在模板中嵌入的是需要客户化的代码,由Spring来作或者需要客户程序亲自动手完成。下面让我们具体看看在 JdbcTemplate中的 代码是怎样完成使命的,我们举JdbcTemplate.execute()为例,这个方法是在 JdbcTemplate中被其他方法调用的基本方法之 一,客户程序往往用这个方法来执行基本的SQL语句: Java代码 1. public Object execute(ConnectionCallback action) throws DataAccessException { 2. //这里得到数据库联接 3. Connection con = DataSourceUtils.getConnection(getDataSource()); 4. try { http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 41 / 168 页 5. Connection conToUse = con; 6. //有些特殊的数据库,需要我们使用特别的方法取得 datasource 7. if ( this .nativeJdbcExtractor != null ) { 8. // Extract native JDBC Connection, castable to OracleConnection or the like. 9. conToUse = this .nativeJdbcExtractor.getNativeConnection(con); 10. } 11. else { 12. // Create close-suppressing Connection proxy, also preparing returned Statements. 13. conToUse = createConnectionProxy(con); 14. } 15. //这里调用的是传递进来的匿名类的方法,也就是用户程序需要 实现CallBack接口的地方。 16. return action.doInConnection(conToUse); 17. } 18. catch (SQLException ex) { 19. //如果捕捉到数据库异常,把数据库联接释放,同时抛 出一个经过Spring转换过的Spring数据库 异常, 20. //我们知道,Spring做了一个有意义的工作是把 这些数据库异常统一到自己的异常体系里了。 21. DataSourceUtils.releaseConnection(con, getDataSource()); 22. con = null ; 23. throw getExceptionTranslator().translate( "ConnectionCallback" , getSql(action), ex); 24. } 25. finally { 26. //最后不管怎样都会把数据库连接释放 27. DataSourceUtils.releaseConnection(con, getDataSource()); 28. } 29. } public Object execute(ConnectionCallback action) throws DataAccessException { //这里得到数据库联接 Connection con = DataSourceUtils.getConnection(getDataSource()); try { Connection conToUse = con; //有些特殊的数据库,需要我们使用特别的方法取得datasource if (this.nativeJdbcExtractor != null) { // Extract native JDBC Connection, castable to OracleConnection or the like. conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } else { http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 42 / 168 页 // Create close-suppressing Connection proxy, also preparing returned Statements. conToUse = createConnectionProxy(con); } //这里调用的是传递进来的匿名类的方法,也就是用户程序需要实现CallBack接口的地方。 return action.doInConnection(conToUse); } catch (SQLException ex) { //如果捕捉到数据库异常,把数据库联接释放,同时抛出一个经过Spring转换过的Spring数据库异常, //我们知道,Spring做了一个有意义的工作是把这些数据库异常统一到自己的异常体系里了。 DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex); } finally { //最后不管怎样都会把数据库连接释放 DataSourceUtils.releaseConnection(con, getDataSource()); } } 对于JdbcTemplate中给出的其他方法,比如query,update,execute等的实现,我们看看query(): Java代码 1. public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse) 2. throws DataAccessException { 3. .......... 4. //这里调用了我们上面看到的execute()基本方法,然 而这里的回调实现是Spring为我们完成的查询过 程 5. return execute(psc, new PreparedStatementCallback() { 6. public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { 7. //准备查询结果集 8. ResultSet rs = null ; 9. try { 10. //这里配置SQL参数 11. if (pss != null ) { 12. pss.setValues(ps); http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 43 / 168 页 13. } 14. //这里执行的SQL查询 15. rs = ps.executeQuery(); 16. ResultSet rsToUse = rs; 17. if (nativeJdbcExtractor != null ) { 18. rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); 19. } 20. //返回需要的记录集合 21. return rse.extractData(rsToUse); 22. } 23. finally { 24. //最后关闭查询的纪录集,对数据库连接的释放在 execute()中释放,就像我们在上面分析的看到 那样。 25. JdbcUtils.closeResultSet(rs); 26. if (pss instanceof ParameterDisposer) { 27. ((ParameterDisposer) pss).cleanupParameters(); 28. } 29. } 30. } 31. }); 32. } public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse) throws DataAccessException { .......... //这里调用了我们上面看到的execute()基本方法,然而这里的回调实现是Spring为我们完成的查询过程 return execute(psc, new PreparedStatementCallback() { public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { //准备查询结果集 ResultSet rs = null; try { //这里配置SQL参数 if (pss != null) { pss.setValues(ps); } //这里执行的SQL查询 rs = ps.executeQuery(); ResultSet rsToUse = rs; http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 44 / 168 页 if (nativeJdbcExtractor != null) { rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); } //返回需要的记录集合 return rse.extractData(rsToUse); } finally { //最后关闭查询的纪录集,对数据库连接的释放在execute()中释放,就像我们在上面分析的看到那样。 JdbcUtils.closeResultSet(rs); if (pss instanceof ParameterDisposer) { ((ParameterDisposer) pss).cleanupParameters(); } } } }); } 辅助类DataSourceUtils来用来对数据库连接进行管理的主要工具,比如打开和关闭数据库连接等基本操作: Java代码 1. public static Connection doGetConnection(DataSource dataSource) throws SQLException { 2. //把对数据库连接放到事务管理里面进行管理 3. ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); 4. if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { 5. conHolder.requested(); 6. if (!conHolder.hasConnection()) { 7. logger.debug( "Fetching resumed JDBC Connection from DataSource" ); 8. conHolder.setConnection(dataSource.getConnection()); 9. } 10. return conHolder.getConnection(); 11. } 12. // 这里得到需要的数据库连接,在配置文件中定义好的。 13. logger.debug( "Fetching JDBC Connection from DataSource" ); http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 45 / 168 页 14. Connection con = dataSource.getConnection(); 15. 16. if (TransactionSynchronizationManager.isSynchronizationActive()) { 17. logger.debug( "Registering transaction synchronization for JDBC Connection" ); 18. // Use same Connection for further JDBC actions within the transaction. 19. // Thread-bound object will get removed by synchronization at transaction completion. 20. ConnectionHolder holderToUse = conHolder; 21. if (holderToUse == null ) { 22. holderToUse = new ConnectionHolder(con); 23. } 24. else { 25. holderToUse.setConnection(con); 26. } 27. holderToUse.requested(); 28. TransactionSynchronizationManager.registerSynchronization( 29. new ConnectionSynchronization(holderToUse, dataSource)); 30. holderToUse.setSynchronizedWithTransaction( true ); 31. if (holderToUse != conHolder) { 32. TransactionSynchronizationManager.bindResource(dataSource, holderToUse); 33. } 34. } 35. 36. return con; 37. } public static Connection doGetConnection(DataSource dataSource) throws SQLException { //把对数据库连接放到事务管理里面进行管理 ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) { conHolder.requested(); if (!conHolder.hasConnection()) { logger.debug("Fetching resumed JDBC Connection from DataSource"); conHolder.setConnection(dataSource.getConnection()); } return conHolder.getConnection(); } // 这里得到需要的数据库连接,在配置文件中定义好的。 logger.debug("Fetching JDBC Connection from DataSource"); http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 46 / 168 页 Connection con = dataSource.getConnection(); if (TransactionSynchronizationManager.isSynchronizationActive()) { logger.debug("Registering transaction synchronization for JDBC Connection"); // Use same Connection for further JDBC actions within the transaction. // Thread-bound object will get removed by synchronization at transaction completion. ConnectionHolder holderToUse = conHolder; if (holderToUse == null) { holderToUse = new ConnectionHolder(con); } else { holderToUse.setConnection(con); } holderToUse.requested(); TransactionSynchronizationManager.registerSynchronization( new ConnectionSynchronization(holderToUse, dataSource)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != conHolder) { TransactionSynchronizationManager.bindResource(dataSource, holderToUse); } } return con; } 那我们实际的DataSource对象是怎样得到的?很清楚我们需要在上下文中进行配置:它作为JdbcTemplate父 类 JdbcAccessor的属性存在: Java代码 1. public abstract class JdbcAccessor implements InitializingBean { 2. 3. /** 这里是我们依赖注入数据库数据源的地方。 */ 4. private DataSource dataSource; 5. 6. /** Helper to translate SQL exceptions to DataAccessExceptions */ 7. private SQLExceptionTranslator exceptionTranslator; http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 47 / 168 页 8. 9. private boolean lazyInit = true ; 10. 11. ........ 12. } public abstract class JdbcAccessor implements InitializingBean { /** 这里是我们依赖注入数据库数据源的地方。 */ private DataSource dataSource; /** Helper to translate SQL exceptions to DataAccessExceptions */ private SQLExceptionTranslator exceptionTranslator; private boolean lazyInit = true; ........ } 而对于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代码 1. public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException { 2. //如果声明已经被编译过,则该声明无效 3. if (isCompiled()) { 4. throw new InvalidDataAccessApiUsageException( "Cannot add parameters once query is compiled" ); http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 48 / 168 页 5. } 6. //这里对参数值进行声明定义 7. this .declaredParameters.add(param); public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException { //如果声明已经被编译过,则该声明无效 if (isCompiled()) { throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled"); } //这里对参数值进行声明定义 this.declaredParameters.add(param); } 而这个declareParameters维护的是一个列表: Java代码 1. /** List of SqlParameter objects */ 2. private List declaredParameters = new LinkedList(); /** List of SqlParameter objects */ private List declaredParameters = new LinkedList(); 这个列表在以后compile的过程中会被使用。 3.然后用户程序需要实现MappingSqlQuery的mapRow接口, 将具体的ResultSet数据生成我们需要的对象, 这是我们迭代使用的方法。1,2,3步实际上为我们定义好了一个迭代的基本单元作为操作模板。 4. 在应用程序,我们直接调用execute()方法得到我们需要的对象列表,列表中的每一个对象的数据来自于执行 SQL语句得到记录集的每一条记录,事实上 执行的execute在父类SqlQuery中起作用: Java代码 1. public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException { 2. validateNamedParameters(paramMap); 3. Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap); 4. RowMapper rowMapper = newRowMapper(parameters, context); 5. String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap)); http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 49 / 168 页 6. //我们又看到了JdbcTemplate,这里使用 JdbcTemplate来完成对数据库的查询操作,所以我们说 JdbcTemplate是基本的操作类。 7. return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper); 8. } public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException { validateNamedParameters(paramMap); Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap); RowMapper rowMapper = newRowMapper(parameters, context); String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap)); //我们又看到了JdbcTemplate,这里使用JdbcTemplate来完成对数据库的查询操作,所以我们说JdbcTemplate是基本的操作类。 return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper); } 在这里我们可以看到template模式的精彩应用和对JdbcTemplate的灵活使用。通过使用它,我们免去了手工 迭代 ResultSet并将其中的数据转化为对象列表的重复过程。在这里我们只需要定义SQL语句和SqlParameter - 如果需要的话,往往SQL语句就常常能够满足我们的要求了。这是灵活使用JdbcTemplate的一个很好的例子。 Spring还为其他数据 库操作提供了许多服务,比如使用SqlUpdate插入和更新数据库,使用 UpdatableSqlQuery更新ResultSet,生成主键,调用存 储过程等。 书中还给出了对BLOB数据和CLOB数据进行数据库操作的例子: 对BLOB数据的操作通过LobHander来完成,通 过调用JdbcTemplate和RDBMS都可以进行操作: 在JdbcTemplate中,具体的调用可以参考书中的例子 - 是通过以下调用起作用的: Java代码 1. public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException { 2. return execute( new SimplePreparedStatementCreator(sql), action); 3. } public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException { return execute(new SimplePreparedStatementCreator(sql), action); } 然后通过对实现PreparedStatementCallback接口的 AbstractLobCreatingPreparedStatementCallback的回 调函数来完成: http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 50 / 168 页 Java代码 1. public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException { 2. LobCreator lobCreator = this .lobHandler.getLobCreator(); 3. try { 4. //这是一个模板方法,具体需要由客户程序实现 5. setValues(ps, lobCreator); 6. return new Integer(ps.executeUpdate()); 7. } 8. finally { 9. lobCreator.close(); 10. } 11. } 12. //定义的需要客户程序实现的虚函数 13. protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator) 14. throws SQLException, DataAccessException; public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException { LobCreator lobCreator = this.lobHandler.getLobCreator(); try { //这是一个模板方法,具体需要由客户程序实现 setValues(ps, lobCreator); return new Integer(ps.executeUpdate()); } finally { lobCreator.close(); } } //定义的需要客户程序实现的虚函数 protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException, DataAccessException; 而我们注意到setValues()是一个需要实现的抽象方法,应用程序通过实现setValues来定义自己的操作 - 在 setValues中调用lobCreator.setBlobAsBinaryStrem()。让我们看看具体的BLOB操作在LobCreator 是怎样完 成的,我们一般使用DefaultLobCreator作为BLOB操作的驱动: Java代码 http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 51 / 168 页 1. public void setBlobAsBinaryStream( 2. PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength) 3. throws SQLException { 4. //通过JDBC来完成对BLOB数据的操作,对 Oracle,Spring提供了OracleLobHandler来支持BLOB 操作。 5. ps.setBinaryStream(paramIndex, binaryStream, contentLength); 6. ........ 7. } public void setBlobAsBinaryStream( PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength) throws SQLException { //通过JDBC来完成对BLOB数据的操作,对Oracle,Spring提供了OracleLobHandler来支持BLOB操作。 ps.setBinaryStream(paramIndex, binaryStream, contentLength); ........ } 上面提到的是零零碎碎的Spring JDBC使用的例子,可以看到使用Spring JDBC可以帮助我们完成许多数据库的 操作。Spring对数据库操作最基本的服务是通过JdbcTeamplate和他常用的回调函数来实现的,在此之 上,又 提供了许多RMDB的操作来帮助我们更便利的对数据库的数据进行操作 - 注意这里没有引入向Hibernate这样的 O/R方案。对这些O/R方案的支持,Spring由其他包来完成服务。 书中还提到关于 execute和update方法之间的区别,update方法返回的是受影响的记录数目的一个计数,并且 如果传入参数的话,使用的是 java.sql.PreparedStatement,而execute方法总是使用 java.sql.Statement,不接 受参数,而且他不返回受影响记录的计数,更适合于创建和丢弃表的语句,而update方法更适合于插入,更新 和删除操作,这也是我们在使用时需要注意的。 http://huibin.javaeye.com 1.3 Spring源代码解析(三):Spring JDBC 第 52 / 168 页 1.4 Spring源代码解析(四):Spring MVC 发表时间: 2010-03-18 下面我们对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. } //这里是对DispatcherServlet的初始化方法,根据名字我们很方面的看到对各个Spring MVC主要元素的初始化 protected void initFrameworkServlet() throws ServletException, BeansException { initMultipartResolver(); initLocaleResolver(); initThemeResolver(); initHandlerMappings(); initHandlerAdapters(); initHandlerExceptionResolvers(); initRequestToViewNameTranslator(); initViewResolvers(); } 看到注解我们知道,这是DispatcherSerlvet的初始化过程,它是在WebApplicationContext已经存在的情 况 下进行的,也就意味着在初始化它的时候,IOC容器应该已经工作了,这也是我们在web.xml中配置Spring的时 候,需要把 DispatcherServlet的 load-on-startup的属性配置为2的原因。 对于具体的初始化过程,很容易理解,我们拿 initHandlerMappings()来看看: http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 53 / 168 页 Java代码 1. private void initHandlerMappings() throws BeansException { 2. if ( this .detectAllHandlerMappings) { 3. // 这里找到所有在上下文中定义的 HandlerMapping,同时把他们排序 4. // 因为在同一个上下文中可以有不止一个 handlerMapping,所以我们把他们都载入到一个链里 进行维护和管理 5. Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( 6. getWebApplicationContext(), HandlerMapping. class , true , false ); 7. if (!matchingBeans.isEmpty()) { 8. this .handlerMappings = new ArrayList(matchingBeans.values()); 9. // 这里通过order属性来对 handlerMapping来在list中排序 10. Collections.sort( this .handlerMappings, new OrderComparator()); 11. } 12. } 13. else { 14. try { 15. Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping. class ); 16. this .handlerMappings = Collections.singletonList(hm); 17. } 18. catch (NoSuchBeanDefinitionException ex) { 19. // Ignore, we'll add a default HandlerMapping later. 20. } 21. } 22. 23. //如果在上下文中没有定义的话,那么我们使用默认的 BeanNameUrlHandlerMapping 24. if ( this .handlerMappings == null ) { 25. this .handlerMappings = getDefaultStrategies(HandlerMapping. class ); 26. ........ 27. } 28. } private void initHandlerMappings() throws BeansException { if (this.detectAllHandlerMappings) { // 这里找到所有在上下文中定义的HandlerMapping,同时把他们排序 // 因为在同一个上下文中可以有不止一个handlerMapping,所以我们把他们都载入到一个链里进行维护和管理 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( getWebApplicationContext(), HandlerMapping.class, true, false); http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 54 / 168 页 if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList(matchingBeans.values()); // 这里通过order属性来对handlerMapping来在list中排序 Collections.sort(this.handlerMappings, new OrderComparator()); } } else { try { Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } //如果在上下文中没有定义的话,那么我们使用默认的BeanNameUrlHandlerMapping if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(HandlerMapping.class); ........ } } 怎样获得上下文环境,可以参见我们前面的对IOC容器在web环境中加载的分析。 DispatcherServlet把定义了 的所有HandlerMapping都加载了放在一个List里待以后进行使用,这个链的每一个元素都是一个 handlerMapping的配置,而一般每一个handlerMapping可以持有一系列从URL请求到 Spring Controller的 映射,比如SimpleUrl HandlerMaaping中就定义了一个map来持有这一系列的映射关系。 DisptcherServlet 通过HandlerMapping使得Web应用程序确定一个执行路径,就像我们在HanderMapping 中看到的那 样,HandlerMapping只是一个借口: Java代码 1. public interface HandlerMapping { 2. public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = 3. Conventions.getQualifiedAttributeName(HandlerMapping. class , "pathWithinHandlerMapping" ); http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 55 / 168 页 4. //实际上维护一个 HandlerExecutionChain,这是典型的Command的模式的使用,这个执行链里 面维护handler和拦截器 5. HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; 6. } public interface HandlerMapping { public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping"); //实际上维护一个HandlerExecutionChain,这是典型的Command的模式的使用,这个执行链里面维护handler和拦截器 HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; } 他的具体实现只需要实现一个接口方法,而这个接口方法返回的是一个HandlerExecutionChain,实际上就是一 个执行链,就 像在Command模式描述的那样,这个类很简单,就是一个持有一个Interceptor链和一个 Controller: Java代码 1. public class HandlerExecutionChain { 2. 3. private Object handler; 4. 5. private HandlerInterceptor[] interceptors; 6. 7. ........ 8. } public class HandlerExecutionChain { private Object handler; private HandlerInterceptor[] interceptors; ........ } 而这些Handler和Interceptor需要我们定义HandlerMapping的时候配置好,比如对具体的 SimpleURLHandlerMapping,他要做的就是根据URL映射的方式注册Handler和Interceptor,自己维护一个放 http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 56 / 168 页 映映射 的handlerMap,当需要匹配Http请求的时候需要使用这个表里的信息来得到执行链。这个注册的过程 在IOC容器初始化 SimpleUrlHandlerMapping的时候就被完成了,这样以后的解析才可以用到map里的映射 信息,这里的信息和bean文件的信息是等价 的,下面是具体的注册过程: Java代码 1. protected void registerHandlers(Map urlMap) throws BeansException { 2. if (urlMap.isEmpty()) { 3. logger.warn( "Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping" ); 4. } 5. else { 6. //这里迭代在 SimpleUrlHandlerMapping中定义的所有映射元素 7. Iterator it = urlMap.keySet().iterator(); 8. while (it.hasNext()) { 9. //这里取得配置的url 10. String url = (String) it.next(); 11. //这里根据url在bean定义中取得对应 的handler 12. Object handler = urlMap.get(url); 13. // Prepend with slash if not already present. 14. if (!url.startsWith( "/" )) { 15. url = "/" + url; 16. } 17. //这里调用 AbstractHandlerMapping中的注册过程 18. registerHandler(url, handler); 19. } 20. } 21. } protected void registerHandlers(Map urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { //这里迭代在SimpleUrlHandlerMapping中定义的所有映射元素 Iterator it = urlMap.keySet().iterator(); while (it.hasNext()) { //这里取得配置的url String url = (String) it.next(); http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 57 / 168 页 //这里根据url在bean定义中取得对应的handler Object handler = urlMap.get(url); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } //这里调用AbstractHandlerMapping中的注册过程 registerHandler(url, handler); } } } 在AbstractMappingHandler中的注册代码: Java代码 1. protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { 2. //试图从handlerMap中取handler,看看是否 已经存在同样的Url映射关系 3. Object mappedHandler = this .handlerMap.get(urlPath); 4. if (mappedHandler != null ) { 5. ........ 6. } 7. 8. //如果是直接用bean名做映射那就直接从容器中取 handler 9. if (! this .lazyInitHandlers && handler instanceof String) { 10. String handlerName = (String) handler; 11. if (getApplicationContext().isSingleton(handlerName)) { 12. handler = getApplicationContext().getBean(handlerName); 13. } 14. } 15. //或者使用默认的handler. 16. if (urlPath.equals( "/*" )) { 17. setDefaultHandler(handler); 18. } 19. else { 20. //把url和handler的对应关系放到 handlerMap中去 http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 58 / 168 页 21. this .handlerMap.put(urlPath, handler); 22. ........ 23. } 24. } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { //试图从handlerMap中取handler,看看是否已经存在同样的Url映射关系 Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { ........ } //如果是直接用bean名做映射那就直接从容器中取handler if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { handler = getApplicationContext().getBean(handlerName); } } //或者使用默认的handler. if (urlPath.equals("/*")) { setDefaultHandler(handler); } else { //把url和handler的对应关系放到handlerMap中去 this.handlerMap.put(urlPath, handler); ........ } } handlerMap是持有的一个HashMap,里面就保存了具体的映射信息: Java代码 1. private final Map handlerMap = new HashMap(); private final Map handlerMap = new HashMap(); http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 59 / 168 页 而SimpleUrlHandlerMapping对接口HandlerMapping的实现是这样的,这个getHandler根据在初 始化的时 候就得到的映射表来生成DispatcherServlet需要的执行链 Java代码 1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 2. //这里根据request中的参数得到其对应的 handler,具体处理在AbstractUrlHandlerMapping中 3. Object handler = getHandlerInternal(request); 4. //如果找不到对应的,就使用缺省的handler 5. if (handler == null ) { 6. handler = this .defaultHandler; 7. } 8. //如果缺省的也没有,那就没办法了 9. if (handler == null ) { 10. return null ; 11. } 12. // 如果handler不是一个具体的handler,那我 们还要到上下文中取 13. if (handler instanceof String) { 14. String handlerName = (String) handler; 15. handler = getApplicationContext().getBean(handlerName); 16. } 17. //生成一个HandlerExecutionChain,其 中放了我们匹配上的handler和定义好的拦截器,就像我们 在HandlerExecutionChain中看到的那样,它持有一个handler和一 个拦截器组。 18. return new HandlerExecutionChain(handler, this .adaptedInterceptors); 19. } public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //这里根据request中的参数得到其对应的handler,具体处理在AbstractUrlHandlerMapping中 Object handler = getHandlerInternal(request); //如果找不到对应的,就使用缺省的handler if (handler == null) { handler = this.defaultHandler; } //如果缺省的也没有,那就没办法了 if (handler == null) { return null; http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 60 / 168 页 } // 如果handler不是一个具体的handler,那我们还要到上下文中取 if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } //生成一个HandlerExecutionChain,其中放了我们匹配上的handler和定义好的拦截器,就像我们在HandlerExecutionChain中看到的那样,它持有一个handler和一个拦截器组。 return new HandlerExecutionChain(handler, this.adaptedInterceptors); } 我们看看具体的handler查找过程: Java代码 1. protected Object getHandlerInternal(HttpServletRequest request) throws Exception { 2. //这里的HTTP Request传进来的参数进行分析,得 到具体的路径信息。 3. String lookupPath = this .urlPathHelper.getLookupPathForRequest(request); 4. ....... //下面是根据请求信息的查找 5. return lookupHandler(lookupPath, request); 6. } 7. 8. protected Object lookupHandler(String urlPath, HttpServletRequest request) { 9. // 如果能够直接能在 SimpleUrlHandlerMapping的映射表中找到,那最好。 10. Object handler = this .handlerMap.get(urlPath); 11. if (handler == null ) { 12. // 这里使用模式来对map中的所有handler 进行匹配,调用了Jre中的Matcher类来完成匹配处 理。 13. String bestPathMatch = null ; 14. for (Iterator it = this .handlerMap.keySet().iterator(); it.hasNext();) { 15. String registeredPath = (String) it.next(); 16. if ( this .pathMatcher.match(registeredPath, urlPath) && 17. (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) { 18. //这里根据匹配路径找到最象的一个 19. handler = this .handlerMap.get(registeredPath); 20. bestPathMatch = registeredPath; 21. } http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 61 / 168 页 22. } 23. 24. if (handler != null ) { 25. exposePathWithinMapping( this .pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request); 26. } 27. } 28. else { 29. exposePathWithinMapping(urlPath, request); 30. } 31. // 32. return handler; 33. } protected Object getHandlerInternal(HttpServletRequest request) throws Exception { //这里的HTTP Request传进来的参数进行分析,得到具体的路径信息。 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); .......//下面是根据请求信息的查找 return lookupHandler(lookupPath, request); } protected Object lookupHandler(String urlPath, HttpServletRequest request) { // 如果能够直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。 Object handler = this.handlerMap.get(urlPath); if (handler == null) { // 这里使用模式来对map中的所有handler进行匹配,调用了Jre中的Matcher类来完成匹配处理。 String bestPathMatch = null; for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) { String registeredPath = (String) it.next(); if (this.pathMatcher.match(registeredPath, urlPath) && (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) { //这里根据匹配路径找到最象的一个 handler = this.handlerMap.get(registeredPath); bestPathMatch = registeredPath; } } if (handler != null) { http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 62 / 168 页 exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request); } } else { exposePathWithinMapping(urlPath, request); } // return handler; } 我们可以看到,总是在handlerMap这个HashMap中找,当然如果直接找到最好,如果找不到,就看看是不是 能通过Match Pattern的模式找,我们一定还记得在配置HnaderMapping的时候是可以通过ANT语法进行配置 的,其中的处理就在这里。 这样可 以清楚地看到整个HandlerMapping的初始化过程 - 同时,我们也看到了一个具体的handler映射是怎 样被存储和查找的 - 这里生成一个ExecutionChain来储存我们找到的handler和在定义bean的时候定义的 Interceptors. 让我们回到 DispatcherServlet,初始化完成以后,实际的对web请求是在doService()方法中处理的,我们知 道 DispatcherServlet只是一个普通的Servlet: Java代码 1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 2. ....... 3. //这里把属性信息进行保存 4. Map attributesSnapshot = null ; 5. if (WebUtils.isIncludeRequest(request)) { 6. logger.debug( "Taking snapshot of request attributes before include" ); 7. attributesSnapshot = new HashMap(); 8. Enumeration attrNames = request.getAttributeNames(); 9. while (attrNames.hasMoreElements()) { 10. String attrName = (String) attrNames.nextElement(); 11. if ( this .cleanupAfterInclude || attrName.startsWith(DispatcherServlet. class .getName())) { 12. attributesSnapshot.put(attrName, request.getAttribute(attrName)); 13. } 14. } http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 63 / 168 页 15. } 16. 17. // Make framework objects available to handlers and view objects. 18. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 19. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this .localeResolver); 20. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this .themeResolver); 21. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 22. 23. try { 24. //这里使实际的处理入口 25. doDispatch(request, response); 26. } 27. finally { 28. // Restore the original attribute snapshot, in case of an include. 29. if (attributesSnapshot != null ) { 30. restoreAttributesAfterInclude(request, attributesSnapshot); 31. } 32. } 33. } protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { ....... //这里把属性信息进行保存 Map attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { logger.debug("Taking snapshot of request attributes before include"); attributesSnapshot = new HashMap(); Enumeration attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 64 / 168 页 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); try { //这里使实际的处理入口 doDispatch(request, response); } finally { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } 我们看到,对于请求的处理实际上是让doDispatch()来完成的 - 这个方法很长,但是过程很简单明了: Java代码 1. protected void doDispatch( final HttpServletRequest request, HttpServletResponse response) throws Exception { 2. HttpServletRequest processedRequest = request; 3. //这是从handlerMapping中得到的执行链 4. HandlerExecutionChain mappedHandler = null ; 5. int interceptorIndex = - 1 ; 6. 7. ........ 8. try { 9. //我们熟悉的ModelAndView开始出现了。 10. ModelAndView mv = null ; 11. try { 12. processedRequest = checkMultipart(request); 13. 14. // 这是我们得到handler的过程 15. mappedHandler = getHandler(processedRequest, false ); http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 65 / 168 页 16. if (mappedHandler == null || mappedHandler.getHandler() == null ) { 17. noHandlerFound(processedRequest, response); 18. return ; 19. } 20. 21. // 这里取出执行链中的 Interceptor进行前处理 22. if (mappedHandler.getInterceptors() != null ) { 23. for ( int i = 0 ; i < mappedHandler.getInterceptors().length; i++) { 24. HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i]; 25. if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { 26. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null ); 27. return ; 28. } 29. interceptorIndex = i; 30. } 31. } 32. 33. //在执行handler之前,用 HandlerAdapter先检查一下handler的合法性:是不是按Spring的 要求编写的。 34. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 35. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 36. 37. // 这里取出执行链中的 Interceptor进行后处理 38. if (mappedHandler.getInterceptors() != null ) { 39. for ( int i = mappedHandler.getInterceptors().length - 1 ; i >= 0 ; i--) { 40. HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i]; 41. interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); 42. } 43. } 44. } 45. 46. ........ 47. 48. // Did the handler return a view to render? 49. //这里对视图生成进行处理 http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 66 / 168 页 50. if (mv != null && !mv.wasCleared()) { 51. render(mv, processedRequest, response); 52. } 53. ....... 54. } protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; //这是从handlerMapping中得到的执行链 HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; ........ try { //我们熟悉的ModelAndView开始出现了。 ModelAndView mv = null; try { processedRequest = checkMultipart(request); // 这是我们得到handler的过程 mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // 这里取出执行链中的Interceptor进行前处理 if (mappedHandler.getInterceptors() != null) { for (int i = 0; i < mappedHandler.getInterceptors().length; i++) { HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i]; if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) { triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null); return; } interceptorIndex = i; } } http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 67 / 168 页 //在执行handler之前,用HandlerAdapter先检查一下handler的合法性:是不是按Spring的要求编写的。 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 这里取出执行链中的Interceptor进行后处理 if (mappedHandler.getInterceptors() != null) { for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) { HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i]; interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv); } } } ........ // Did the handler return a view to render? //这里对视图生成进行处理 if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); } ....... } 我们很清楚的看到和MVC框架紧密相关的代码,比如如何得到和http请求相对应的执行链,怎样执行执行链和怎 样把模型数据展现到视图中 去。 先看怎样取得Command对象,对我们来说就是Handler - 下面是getHandler的代码: Java代码 1. protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception { 2. //在ServletContext取得执行链 - 实际上第一次 得到它的时候,我们把它放在ServletContext进行了 缓存。 3. HandlerExecutionChain handler = 4. (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); 5. if (handler != null ) { http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 68 / 168 页 6. if (!cache) { 7. request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); 8. } 9. return handler; 10. } 11. //这里的迭代器迭代的时在 initHandlerMapping中载入的上下文所有的HandlerMapping 12. Iterator it = this .handlerMappings.iterator(); 13. while (it.hasNext()) { 14. HandlerMapping hm = (HandlerMapping) it.next(); 15. ....... 16. //这里是实际取得handler的过程,在每个 HandlerMapping中建立的映射表进行检索得到请求对 应的handler 17. handler = hm.getHandler(request); 18. 19. //然后把handler存到 ServletContext中去进行缓存 20. if (handler != null ) { 21. if (cache) { 22. request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler); 23. } 24. return handler; 25. } 26. } 27. return null ; 28. } protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception { //在ServletContext取得执行链 - 实际上第一次得到它的时候,我们把它放在ServletContext进行了缓存。 HandlerExecutionChain handler = (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); if (handler != null) { if (!cache) { request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE); } return handler; } //这里的迭代器迭代的时在initHandlerMapping中载入的上下文所有的HandlerMapping Iterator it = this.handlerMappings.iterator(); while (it.hasNext()) { http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 69 / 168 页 HandlerMapping hm = (HandlerMapping) it.next(); ....... //这里是实际取得handler的过程,在每个HandlerMapping中建立的映射表进行检索得到请求对应的handler handler = hm.getHandler(request); //然后把handler存到ServletContext中去进行缓存 if (handler != null) { if (cache) { request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler); } return handler; } } return null; } 如果在ServletContext中可以取得handler则直接返回,实际上这个handler是缓冲了上次处理的结果 - 总要有 第一次把这个handler放到ServletContext中去: 如果在ServletContext中找不到handler,那就通 过持有的handlerMapping生成一个,我们看到它会迭代当前 持有的所有的 handlerMapping,因为可以定义不止一个,他们在定义的时候也可以指定顺序,直到找到第一 个,然后返回。先找到一个 handlerMapping,然后通过这个handlerMapping返回一个执行链,里面包含了最 终的Handler和我们定义的一连串的 Interceptor。具体的我们可以参考上面的SimpleUrlHandlerMapping的 代码分析知道getHandler是怎样得到一个 HandlerExecutionChain的。 得到HandlerExecutionChain以后,我们通过HandlerAdapter 对这个Handler的合法性进行判断: Java代码 1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 2. Iterator it = this .handlerAdapters.iterator(); 3. while (it.hasNext()) { 4. //同样对持有的所有adapter进行匹配 5. HandlerAdapter ha = (HandlerAdapter) it.next(); 6. if (ha.supports(handler)) { 7. return ha; 8. } 9. } 10. ........ http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 70 / 168 页 11. } protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { Iterator it = this.handlerAdapters.iterator(); while (it.hasNext()) { //同样对持有的所有adapter进行匹配 HandlerAdapter ha = (HandlerAdapter) it.next(); if (ha.supports(handler)) { return ha; } } ........ } 通过判断,我们知道这个handler是不是一个Controller接口的实现,比如对于具体的HandlerAdapter - SimpleControllerHandlerAdapter: Java代码 1. public class SimpleControllerHandlerAdapter implements HandlerAdapter { 2. 3. public boolean supports(Object handler) { 4. return (handler instanceof Controller); 5. } 6. ....... 7. } public class SimpleControllerHandlerAdapter implements HandlerAdapter { public boolean supports(Object handler) { return (handler instanceof Controller); } ....... } 简单的判断一下handler是不是实现了Controller接口。这也体现了一种对配置文件进行验证的机制。 让我们再回到 DispatcherServlet看到代码: http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 71 / 168 页 Java代码 1. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 这个就是对handle的具体调用!相当于Command模式里的Command.execute();理所当然的返回一个 ModelAndView,下面就是一个对View进行处理的过程: Java代码 1. if (mv != null && !mv.wasCleared()) { 2. render(mv, processedRequest, response); 3. } if (mv != null && !mv.wasCleared()) { render(mv, processedRequest, response); } 调用的是render方法: Java代码 1. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) 2. throws Exception {response.setLocale(locale); 3. 4. View view = null ; 5. //这里把默认的视图放到ModelAndView中去。 6. if (!mv.hasView()) { 7. mv.setViewName(getDefaultViewName(request)); 8. } 9. 10. if (mv.isReference()) { 11. // 这里对视图名字进行解析 12. view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); 13. ....... 14. } 15. else { 16. // 有可能在ModelAndView里已经直接 包含了View对象,那我们就直接使用。 http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 72 / 168 页 17. view = mv.getView(); 18. ........ 19. } 20. 21. //得到具体的View对象以后,我们用它来生成视图。 22. view.render(mv.getModelInternal(), request, response); 23. } protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {response.setLocale(locale); View view = null; //这里把默认的视图放到ModelAndView中去。 if (!mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } if (mv.isReference()) { // 这里对视图名字进行解析 view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request); ....... } else { // 有可能在ModelAndView里已经直接包含了View对象,那我们就直接使用。 view = mv.getView(); ........ } //得到具体的View对象以后,我们用它来生成视图。 view.render(mv.getModelInternal(), request, response); } 从整个过程我们看到先在ModelAndView中寻找视图的逻辑名,如果找不到那就使用缺省的视图,如果能够找 到视图的名字,那就对他进 行解析得到实际的需要使用的视图对象。还有一种可能就是在ModelAndView中已 经包含了实际的视图对象,这个视图对象是可以直接使用的。 不 管怎样,得到一个视图对象以后,通过调用视图对象的render来完成数据的显示过程,我们可以看看具体的 JstlView是怎样实现的,我们在 JstlView的抽象父类 AbstractView中找到render方法: http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 73 / 168 页 Java代码 1. public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { 2. ...... 3. // 这里把所有的相关信息都收集到一个Map里 4. Map mergedModel = new HashMap( this http://huibin.javaeye.com 1.4 Spring源代码解析(四):Spring MVC 第 74 / 168 页 1.5 Spring源代码解析(五):Spring AOP获取Proxy 发表时间: 2010-03-18 下面我们来看看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. } http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 75 / 168 页 public final boolean matches(Method method, Class targetClass) { //这里通过放射得到方法的全名 String patt = method.getDeclaringClass().getName() + "." + method.getName(); for (int i = 0; i < this.patterns.length; i++) { // 这里是判断是否和方法名是否匹配的代码 boolean matched = matches(patt, i); if (matched) { for (int j = 0; j < this.excludedPatterns.length; j++) { boolean excluded = matchesExclusion(patt, j); if(excluded) { return false; } } return true; } } return false; } 在JDKRegexpMethodPointcut中通过JDK中的正则表达式匹配来完成pointcut的最终确定: Java代码 1. protected boolean matches(String pattern, int patternIndex) { 2. Matcher matcher = this .compiledPatterns[patternIndex].matcher(pattern); 3. return matcher.matches(); 4. } protected boolean matches(String pattern, int patternIndex) { Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern); return matcher.matches(); } Advisor: 当我们完成额外的动作设计(advice)和额外动作插入点的设计(pointcut)以后,我们需要一个对象把他们 结合 起来,这就是通知器 - advisor,定义应该在哪里应用哪个通知。Advisor的实现有:DefaultPointcutAdvisor他 有两个属性advice和 pointcut来让我们配置advice和pointcut。 http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 76 / 168 页 接着我们就可以通过ProxyFactoryBean来配置我们的代理对象和方 面行为,在ProxyFactoryBean中有 interceptorNames来配置已经定义好的通知器-advisor,虽然这里的名字叫做 interceptNames,但实际上是供 我们配置advisor的地方,具体的代理实现通过JDK 的Proxy或者CGLIB来完成。因为ProxyFactoryBean是一个 FactoryBean,在ProxyFactoryBean中我们通过 getObject()可以直接得到代理对象: Java代码 1. public Object getObject() throws BeansException { 2. //这里初始化通知器链 3. initializeAdvisorChain(); 4. if (isSingleton()) { 5. //根据定义需要生成单件的Proxy 6. return getSingletonInstance(); 7. } 8. else { 9. ....... 10. //这里根据定义需要生成Prototype类型的 Proxy 11. return newPrototypeInstance(); 12. } 13. } public Object getObject() throws BeansException { //这里初始化通知器链 initializeAdvisorChain(); if (isSingleton()) { //根据定义需要生成单件的Proxy return getSingletonInstance(); } else { ....... //这里根据定义需要生成Prototype类型的Proxy return newPrototypeInstance(); } } 我们看看怎样生成单件的代理对象: Java代码 http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 77 / 168 页 1. private synchronized Object getSingletonInstance() { 2. if ( this .singletonInstance == null ) { 3. this .targetSource = freshTargetSource(); 4. if ( this .autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) { 5. // 这里设置代理对象的接口 6. setInterfaces(ClassUtils.getAllInterfacesForClass( this .targetSource.getTargetClass())); 7. } 8. // Eagerly initialize the shared singleton instance. 9. super .setFrozen( this .freezeProxy); 10. // 注意这里的方法会使用ProxyFactory 来生成我们需要的Proxy 11. this .singletonInstance = getProxy(createAopProxy()); 12. // We must listen to superclass advice change events to recache the singleton 13. // instance if necessary. 14. addListener( this ); 15. } 16. return this .singletonInstance; 17. } 18. 19. //使用createAopProxy放回的AopProxy来得到代理对象。 20. protected Object getProxy(AopProxy aopProxy) { 21. return aopProxy.getProxy( this .beanClassLoader); 22. } private synchronized Object getSingletonInstance() { if (this.singletonInstance == null) { this.targetSource = freshTargetSource(); if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) { // 这里设置代理对象的接口 setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass())); } // Eagerly initialize the shared singleton instance. super.setFrozen(this.freezeProxy); // 注意这里的方法会使用ProxyFactory来生成我们需要的Proxy this.singletonInstance = getProxy(createAopProxy()); // We must listen to superclass advice change events to recache the singleton // instance if necessary. addListener(this); http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 78 / 168 页 } return this.singletonInstance; } //使用createAopProxy放回的AopProxy来得到代理对象。 protected Object getProxy(AopProxy aopProxy) { return aopProxy.getProxy(this.beanClassLoader); } ProxyFactoryBean的父类是AdvisedSupport,Spring使用AopProxy接口把AOP代理的实现与框架的其他 部 分分离开来;在AdvisedSupport中通过这样的方式来得到AopProxy,当然这里需要得到AopProxyFactory的帮 助 - 下面我们看到Spring为我们提供的实现,来帮助我们方便的从JDK或者cglib中得到我们想要的代理对象: Java代码 1. protected synchronized AopProxy createAopProxy() { 2. if (! this .isActive) { 3. activate(); 4. } 5. return getAopProxyFactory().createAopProxy( this ); 6. } protected synchronized AopProxy createAopProxy() { if (!this.isActive) { activate(); } return getAopProxyFactory().createAopProxy(this); } 而在ProxyConfig中对使用的AopProxyFactory做了定义: Java代码 1. //这个DefaultAopProxyFactory是Spring用来生成 AopProxy的地方, 2. //当然了它包含JDK和Cglib两种实现方式。 3. private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 79 / 168 页 //这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方, //当然了它包含JDK和Cglib两种实现方式。 private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); 其中在DefaultAopProxyFactory中是这样生成AopProxy的: Java代码 1. public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException { 2. //首先考虑使用cglib来实现代理对象,当然如果同时目标 对象不是接口的实现类的话 3. if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() || 4. advisedSupport.getProxiedInterfaces().length == 0 ) { 5. //这里判断如果不存在cglib库,直接抛出异常。 6. if (!cglibAvailable) { 7. throw new AopConfigException( 8. "Cannot proxy target class because CGLIB2 is not available. " + 9. "Add CGLIB to the class path or specify proxy interfaces." ); 10. } 11. // 这里使用Cglib来生成Proxy,如果 target不是接口的实现的话,返回cglib类型的AopProxy 12. return CglibProxyFactory.createCglibProxy(advisedSupport); 13. } 14. else { 15. // 这里使用JDK来生成Proxy,返回JDK类 型的AopProxy 16. return new JdkDynamicAopProxy(advisedSupport); 17. } 18. } public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException { //首先考虑使用cglib来实现代理对象,当然如果同时目标对象不是接口的实现类的话 if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0) { //这里判断如果不存在cglib库,直接抛出异常。 if (!cglibAvailable) { throw new AopConfigException( "Cannot proxy target class because CGLIB2 is not available. " + "Add CGLIB to the class path or specify proxy interfaces."); http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 80 / 168 页 } // 这里使用Cglib来生成Proxy,如果target不是接口的实现的话,返回cglib类型的AopProxy return CglibProxyFactory.createCglibProxy(advisedSupport); } else { // 这里使用JDK来生成Proxy,返回JDK类型的AopProxy return new JdkDynamicAopProxy(advisedSupport); } } 于是我们就可以看到其中的代理对象可以由JDK或者Cglib来生成,我们看到JdkDynamicAopProxy类和 Cglib2AopProxy都实现的是AopProxy的接口,在JdkDynamicAopProxy实现中我们可以看到Proxy是怎样生 成的: Java代码 1. public Object getProxy(ClassLoader classLoader) { 2. if (logger.isDebugEnabled()) { 3. Class targetClass = this .advised.getTargetSource().getTargetClass(); 4. logger.debug( "Creating JDK dynamic proxy" + 5. (targetClass != null ? " for [" + targetClass.getName() + "]" : "" )); 6. } 7. Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces( this .advised); 8. findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); 9. //这里我们调用JDK Proxy来生成需要的Proxy实 例 10. return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this ); 11. } public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { Class targetClass = this.advised.getTargetSource().getTargetClass(); logger.debug("Creating JDK dynamic proxy" + (targetClass != null ? " for [" + targetClass.getName() + "]" : "")); } Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //这里我们调用JDK Proxy来生成需要的Proxy实例 http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 81 / 168 页 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } 这样用Proxy包装target之后,通过ProxyFactoryBean得到对其方法的调用就被Proxy拦截了, ProxyFactoryBean的getObject()方法得到的实际上是一个Proxy了,我们的target对象已经被封装了。对 ProxyFactoryBean这个工厂bean而言,其生产出来的对象是封装了目标对象的代理对象。 http://huibin.javaeye.com 1.5 Spring源代码解析(五):Spring AOP获取Proxy 第 82 / 168 页 1.6 Spring源代码解析(六):Spring声明式事务处理 发表时间: 2010-03-18 我们看看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. } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 83 / 168 页 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. } public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean implements FactoryBean, BeanFactoryAware { //这里是Spring事务处理而使用的AOP拦截器,中间封装了Spring对事务处理的代码来支持声明式事务处理的实现 private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); private Pointcut pointcut; //这里Spring把TransactionManager注入到TransactionInterceptor中去 public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionInterceptor.setTransactionManager(transactionManager); } //这里把在bean配置文件中读到的事务管理的属性信息注入到TransactionInterceptor中去 public void setTransactionAttributes(Properties transactionAttributes) { this.transactionInterceptor.setTransactionAttributes(transactionAttributes); } .........中间省略了其他一些方法....... //这里创建Spring AOP对事务处理的Advisor http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 84 / 168 页 protected Object createMainInterceptor() { this.transactionInterceptor.afterPropertiesSet(); if (this.pointcut != null) { //这里使用默认的通知器 return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor); } else { // 使用上面定义好的TransactionInterceptor作为拦截器,同时使用TransactionAttributeSourceAdvisor return new TransactionAttributeSourceAdvisor(this.transactionInterceptor); } } } 那什么时候Spring的TransactionInterceptor被注入到Spring AOP中成为Advisor中的一部分呢?我们看到在 TransactionProxyFactoryBean中,这个方法在IOC初始化bean的时候 被执行: Java代码 1. public void afterPropertiesSet() { 2. ....... 3. //TransactionProxyFactoryBean 实际上使用ProxyFactory完成AOP的基本功能。 4. ProxyFactory proxyFactory = new ProxyFactory(); 5. 6. if ( this .preInterceptors != null ) { 7. for ( int i = 0 ; i < this .preInterceptors.length; i++) { 8. proxyFactory.addAdvisor( this .advisorAdapterRegistry.wrap( this .preInterceptors[i])); 9. } 10. } 11. 12. //这里是Spring加入通知器的地方 13. //有两种通知器可以被加入 DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor 14. //这里把Spring处理声明式事务处理的AOP代码都放到 ProxyFactory中去,怎样加入advisor我们 可以参考ProxyFactory的父类AdvisedSupport() 15. //由它来维护一个advice的链表,通过这个链表的增删改 来抽象我们对整个通知器配置的增删改操 作。 16. proxyFactory.addAdvisor( this .advisorAdapterRegistry.wrap(createMainInterceptor())); 17. http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 85 / 168 页 18. if ( this .postInterceptors != null ) { 19. for ( int i = 0 ; i < this .postInterceptors.length; i++) { 20. proxyFactory.addAdvisor( this .advisorAdapterRegistry.wrap( this .postInterceptors[i])); 21. } 22. } 23. 24. proxyFactory.copyFrom( this ); 25. 26. //这里创建AOP的目标源 27. TargetSource targetSource = createTargetSource( this .target); 28. proxyFactory.setTargetSource(targetSource); 29. 30. if ( this .proxyInterfaces != null ) { 31. proxyFactory.setInterfaces( this .proxyInterfaces); 32. } 33. else if (!isProxyTargetClass()) { 34. proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass())); 35. } 36. 37. this .proxy = getProxy(proxyFactory); 38. } public void afterPropertiesSet() { ....... //TransactionProxyFactoryBean实际上使用ProxyFactory完成AOP的基本功能。 ProxyFactory proxyFactory = new ProxyFactory(); if (this.preInterceptors != null) { for (int i = 0; i < this.preInterceptors.length; i++) { proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i])); } } //这里是Spring加入通知器的地方 //有两种通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor //这里把Spring处理声明式事务处理的AOP代码都放到ProxyFactory中去,怎样加入advisor我们可以参考ProxyFactory的父类AdvisedSupport() http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 86 / 168 页 //由它来维护一个advice的链表,通过这个链表的增删改来抽象我们对整个通知器配置的增删改操作。 proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor())); if (this.postInterceptors != null) { for (int i = 0; i < this.postInterceptors.length; i++) { proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i])); } } proxyFactory.copyFrom(this); //这里创建AOP的目标源 TargetSource targetSource = createTargetSource(this.target); proxyFactory.setTargetSource(targetSource); if (this.proxyInterfaces != null) { proxyFactory.setInterfaces(this.proxyInterfaces); } else if (!isProxyTargetClass()) { proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass())); } this.proxy = getProxy(proxyFactory); } Spring 已经定义了一个transctionInterceptor作为拦截器或者AOP advice的实现,在IOC容器中定义的其他属 性比如transactionManager和事务管理的属性都会传到已经定义好的 TransactionInterceptor那里去进行处 理。以上反映了基本的Spring AOP的定义过程,其中pointcut和advice都已经定义好,同时也通过通知器配置 到ProxyFactory中去了。 下面让我们回到 TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎样定义的, 这样 我们可以理解具体的属性是怎样起作用,这里我们分析一下类TransactionAttributeSourceAdvisor: Java代码 1. public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor { 2. //和其他Advisor一样,同样需要定义AOP中的用到的 Interceptor和Pointcut 3. //Interceptor使用传进来的 TransactionInterceptor http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 87 / 168 页 4. //而对于pointcut,这里定义了一个内部类,参见下面 的代码 5. private TransactionInterceptor transactionInterceptor; 6. 7. private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut(); 8. 9. ......... 10. //定义的PointCut内部类 11. private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { 12. ....... 13. //方法匹配的实现,使用了 TransactionAttributeSource类 14. public boolean matches(Method method, Class targetClass) { 15. TransactionAttributeSource tas = getTransactionAttributeSource(); 16. //这里使用 TransactionAttributeSource来对配置属性进行处理 17. return (tas != null && tas.getTransactionAttribute(method, targetClass) != null ); 18. } 19. ........省略了equal,hashcode,tostring的代码 20. } public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor { //和其他Advisor一样,同样需要定义AOP中的用到的Interceptor和Pointcut //Interceptor使用传进来的TransactionInterceptor //而对于pointcut,这里定义了一个内部类,参见下面的代码 private TransactionInterceptor transactionInterceptor; private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut(); ......... //定义的PointCut内部类 private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable { ....... //方法匹配的实现,使用了TransactionAttributeSource类 public boolean matches(Method method, Class targetClass) { TransactionAttributeSource tas = getTransactionAttributeSource(); //这里使用TransactionAttributeSource来对配置属性进行处理 return (tas != null && tas.getTransactionAttribute(method, targetClass) != null); } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 88 / 168 页 ........省略了equal,hashcode,tostring的代码 } 这里我们看看属性值是怎样被读入的:AbstractFallbackTransactionAttributeSource负责具体的属 性读入任 务,我们可以有两种读入方式,比如annotation和直接配置.我们下面看看直接配置的读入方式,在Spring中同 时对读入的属性值进行了 缓存处理,这是一个decorator模式: Java代码 1. public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) { 2. //这里先查一下缓存里有没有事务管理的属性配置,如果有从缓 存中取得TransactionAttribute 3. Object cacheKey = getCacheKey(method, targetClass); 4. Object cached = this .cache.get(cacheKey); 5. if (cached != null ) { 6. if (cached == NULL_TRANSACTION_ATTRIBUTE) { 7. return null ; 8. } 9. else { 10. return (TransactionAttribute) cached; 11. } 12. } 13. else { 14. // 这里通过对方法和目标对象的信息来计算事务缓存 属性 15. TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass); 16. //把得到的事务缓存属性存到缓存中,下次可以直接从 缓存中取得。 17. if (txAtt == null ) { 18. this .cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); 19. } 20. else { 21. ........... 22. this .cache.put(cacheKey, txAtt); 23. } 24. return txAtt; 25. } 26. } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 89 / 168 页 public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) { //这里先查一下缓存里有没有事务管理的属性配置,如果有从缓存中取得TransactionAttribute Object cacheKey = getCacheKey(method, targetClass); Object cached = this.cache.get(cacheKey); if (cached != null) { if (cached == NULL_TRANSACTION_ATTRIBUTE) { return null; } else { return (TransactionAttribute) cached; } } else { // 这里通过对方法和目标对象的信息来计算事务缓存属性 TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass); //把得到的事务缓存属性存到缓存中,下次可以直接从缓存中取得。 if (txAtt == null) { this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE); } else { ........... this.cache.put(cacheKey, txAtt); } return txAtt; } } 别急,基本的处理在computeTransactionAttribute()中: Java代码 1. private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) { 2. //这里检测是不是public方法 3. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { 4. return null ; 5. } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 90 / 168 页 6. 7. Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); 8. 9. // First try is the method in the target class. 10. TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod)); 11. if (txAtt != null ) { 12. return txAtt; 13. } 14. 15. // Second try is the transaction attribute on the target class. 16. txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass())); 17. if (txAtt != null ) { 18. return txAtt; 19. } 20. 21. if (specificMethod != method) { 22. // Fallback is to look at the original method. 23. txAtt = findTransactionAttribute(findAllAttributes(method)); 24. if (txAtt != null ) { 25. return txAtt; 26. } 27. // Last fallback is the class of the original method. 28. return findTransactionAttribute(findAllAttributes(method.getDeclaringClass())); 29. } 30. return null ; 31. } private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) { //这里检测是不是public方法 if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); // First try is the method in the target class. TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod)); if (txAtt != null) { http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 91 / 168 页 return txAtt; } // Second try is the transaction attribute on the target class. txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass())); if (txAtt != null) { return txAtt; } if (specificMethod != method) { // Fallback is to look at the original method. txAtt = findTransactionAttribute(findAllAttributes(method)); if (txAtt != null) { return txAtt; } // Last fallback is the class of the original method. return findTransactionAttribute(findAllAttributes(method.getDeclaringClass())); } return null; } 经过一系列的尝试我们可以通过findTransactionAttribute()通过调用findAllAttribute()得到 TransactionAttribute的对象,如果返回的是null,这说明该方法不是我们需要事务处理的方法。 在完成把需要的通知器加到 ProxyFactory中去的基础上,我们看看具体的看事务处理代码怎样起作用,在 TransactionInterceptor中: Java代码 1. public Object invoke( final MethodInvocation invocation) throws Throwable { 2. //这里得到目标对象 3. Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null ); 4. 5. //这里同样的通过判断是否能够得到 TransactionAttribute来决定是否对当前方法进行事务处理, 有可能该属性已经被缓存, 6. //具体可以参考上面对 getTransactionAttribute的分析,同样是通过TransactionAttributeSource 7. final TransactionAttribute txAttr = http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 92 / 168 页 8. getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass); 9. final String joinpointIdentification = methodIdentification(invocation.getMethod()); 10. 11. //这里判断我们使用了什么 TransactionManager 12. if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) { 13. // 这里创建事务,同时把创建事务过程中得到的信息 放到TransactionInfo中去 14. TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification); 15. Object retVal = null ; 16. try { 17. retVal = invocation.proceed(); 18. } 19. catch (Throwable ex) { 20. // target invocation exception 21. completeTransactionAfterThrowing(txInfo, ex); 22. throw ex; 23. } 24. finally { 25. cleanupTransactionInfo(txInfo); 26. } 27. commitTransactionAfterReturning(txInfo); 28. return retVal; 29. } 30. 31. else { 32. // 使用的是Spring定义的 PlatformTransactionManager同时实现了回调接口,我们通过其回调 函数完成事务处理,就像我们使用编程式事务处理一样。 33. try { 34. Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, 35. new TransactionCallback() { 36. public Object doInTransaction(TransactionStatus status) { 37. //同样的 需要一个TransactonInfo 38. TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); 39. try { 40. return invocation.proceed(); http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 93 / 168 页 41. } 42. .....这里省去了异常处理和事务信息的清理代码 43. }); 44. ........... 45. } 46. } public Object invoke(final MethodInvocation invocation) throws Throwable { //这里得到目标对象 Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null); //这里同样的通过判断是否能够得到TransactionAttribute来决定是否对当前方法进行事务处理,有可能该属性已经被缓存, //具体可以参考上面对getTransactionAttribute的分析,同样是通过TransactionAttributeSource final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass); final String joinpointIdentification = methodIdentification(invocation.getMethod()); //这里判断我们使用了什么TransactionManager if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) { // 这里创建事务,同时把创建事务过程中得到的信息放到TransactionInfo中去 TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification); Object retVal = null; try { retVal = invocation.proceed(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } else { http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 94 / 168 页 // 使用的是Spring定义的PlatformTransactionManager同时实现了回调接口,我们通过其回调函数完成事务处理,就像我们使用编程式事务处理一样。 try { Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { //同样的需要一个TransactonInfo TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); try { return invocation.proceed(); } .....这里省去了异常处理和事务信息的清理代码 }); ........... } } 这里面涉及到事务的创建,我们可以在TransactionAspectSupport实现的事务管理代码: Java代码 1. protected TransactionInfo createTransactionIfNecessary( 2. TransactionAttribute txAttr, final String joinpointIdentification) { 3. 4. // If no name specified, apply method identification as transaction name. 5. if (txAttr != null && txAttr.getName() == null ) { 6. txAttr = new DelegatingTransactionAttribute(txAttr) { 7. public String getName() { 8. return joinpointIdentification; 9. } 10. }; 11. } 12. 13. TransactionStatus status = null ; 14. if (txAttr != null ) { 15. //这里使用了我们定义好的事务配置信息,有事务管理器来创建 事务,同时返回TransactionInfo 16. status = getTransactionManager().getTransaction(txAttr); 17. } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 95 / 168 页 18. return prepareTransactionInfo(txAttr, joinpointIdentification, status); 19. } protected TransactionInfo createTransactionIfNecessary( TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name. if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { public String getName() { return joinpointIdentification; } }; } TransactionStatus status = null; if (txAttr != null) { //这里使用了我们定义好的事务配置信息,有事务管理器来创建事务,同时返回TransactionInfo status = getTransactionManager().getTransaction(txAttr); } return prepareTransactionInfo(txAttr, joinpointIdentification, status); } 首先通过TransactionManager得到需要的事务,事务的创建根据我们定义的事务配置决定,在 AbstractTransactionManager中给出一个标准的创建过程,当然创建什么样的事务还是需要具体的 PlatformTransactionManager来决定,但这里给出了创建事务的模板: Java代码 1. public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { 2. Object transaction = doGetTransaction(); 3. ...... 4. 5. if (definition == null ) { 6. //如果事务信息没有被配置,我们使用Spring默 认的配置方式 7. definition = new DefaultTransactionDefinition(); 8. } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 96 / 168 页 9. 10. if (isExistingTransaction(transaction)) { 11. // Existing transaction found -> check propagation behavior to find out how to behave. 12. return handleExistingTransaction(definition, transaction, debugEnabled); 13. } 14. 15. // Check definition settings for new transaction. 16. //下面就是使用配置信息来创建我们需要的事务;比如传播属性 和同步属性等 17. //最后把创建过程中的信息收集起来放到 TransactionStatus中返回; 18. if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { 19. throw new InvalidTimeoutException( "Invalid transaction timeout" , definition.getTimeout()); 20. } 21. 22. // No existing transaction found -> check propagation behavior to find out how to behave. 23. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { 24. throw new IllegalTransactionStateException( 25. "Transaction propagation 'mandatory' but no existing transaction found" ); 26. } 27. else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || 28. definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || 29. definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { 30. //这里是事务管理器创建事务的地方,并将创建过程中 得到的信息放到TransactionStatus中去, 包括创建出来的事务 31. doBegin(transaction, definition); 32. boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 33. return newTransactionStatus(definition, transaction, true , newSynchronization, debugEnabled, null ); 34. } 35. else { http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 97 / 168 页 36. boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); 37. return newTransactionStatus(definition, null , false , newSynchronization, debugEnabled, null ); 38. } 39. } public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); ...... if (definition == null) { //如果事务信息没有被配置,我们使用Spring默认的配置方式 definition = new DefaultTransactionDefinition(); } if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. //下面就是使用配置信息来创建我们需要的事务;比如传播属性和同步属性等 //最后把创建过程中的信息收集起来放到TransactionStatus中返回; if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to behave. if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "Transaction propagation 'mandatory' but no existing transaction found"); } else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { //这里是事务管理器创建事务的地方,并将创建过程中得到的信息放到TransactionStatus中去,包括创建出来的事务 doBegin(transaction, definition); http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 98 / 168 页 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null); } else { boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return newTransactionStatus(definition, null, false, newSynchronization, debugEnabled, null); } } 接着通过调用prepareTransactionInfo完成事务创建的准备,创建过程中得到的信息存储在 TransactionInfo对 象中进行传递同时把信息和当前线程绑定; Java代码 1. protected TransactionInfo prepareTransactionInfo( 2. TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) { 3. 4. TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification); 5. if (txAttr != null ) { 6. ..... 7. // 同样的需要把在getTransaction中 得到的TransactionStatus放到TransactionInfo中来。 8. txInfo.newTransactionStatus(status); 9. } 10. else { 11. ....... 12. } 13. 14. // 绑定事务创建信息到当前线程 15. txInfo.bindToThread(); 16. return txInfo; 17. } protected TransactionInfo prepareTransactionInfo( TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) { TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification); if (txAttr != null) { http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 99 / 168 页 ..... // 同样的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中来。 txInfo.newTransactionStatus(status); } else { ....... } // 绑定事务创建信息到当前线程 txInfo.bindToThread(); return txInfo; } 将创建事务的信息返回,然后看到其他的事务管理代码: Java代码 1. protected void commitTransactionAfterReturning(TransactionInfo txInfo) { 2. if (txInfo != null && txInfo.hasTransaction()) { 3. if (logger.isDebugEnabled()) { 4. logger.debug( "Invoking commit for transaction on " + txInfo.getJoinpointIdentification()); 5. } 6. this .transactionManager.commit(txInfo.getTransactionStatus()); 7. } 8. } protected void commitTransactionAfterReturning(TransactionInfo txInfo) { if (txInfo != null && txInfo.hasTransaction()) { if (logger.isDebugEnabled()) { logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification()); } this.transactionManager.commit(txInfo.getTransactionStatus()); } } http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 100 / 168 页 通过transactionManager对事务进行处理,包括异常抛出和正常的提交事务,具体的事务管理器由用户程序设 定。 Java代码 1. protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) { 2. if (txInfo != null && txInfo.hasTransaction()) { 3. if (txInfo.transactionAttribute.rollbackOn(ex)) { 4. ...... 5. try { 6. this .transactionManager.rollback(txInfo.getTransactionStatus()); 7. } 8. .......... 9. } 10. else { 11. ......... 12. try { 13. this .transactionManager.commit(txInfo.getTransactionStatus()); 14. } 15. ........... 16. } 17. 18. protected void commitTransactionAfterReturning(TransactionInfo txInfo) { 19. if (txInfo != null && txInfo.hasTransaction()) { 20. ...... 21. this .transactionManager.commit(txInfo.getTransactionStatus()); 22. } 23. } protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.hasTransaction()) { if (txInfo.transactionAttribute.rollbackOn(ex)) { ...... try { this.transactionManager.rollback(txInfo.getTransactionStatus()); } .......... http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 101 / 168 页 } else { ......... try { this.transactionManager.commit(txInfo.getTransactionStatus()); } ........... } protected void commitTransactionAfterReturning(TransactionInfo txInfo) { if (txInfo != null && txInfo.hasTransaction()) { ...... this.transactionManager.commit(txInfo.getTransactionStatus()); } } Spring通过以上代码对transactionManager进行事务处理的过程进行了AOP包装,到这里我们看到为了方便客 户实现声 明式的事务处理,Spring还是做了许多工作的。如果说使用编程式事务处理,过程其实比较清楚,我 们可以参考书中的例子: Java代码 1. TransactionDefinition td = new DefaultTransactionDefinition(); 2. TransactionStatus status = transactionManager.getTransaction(td); 3. try { 4. ...... //这里是我们的业务方法 5. } catch (ApplicationException e) { 6. transactionManager.rollback(status); 7. throw e 8. } 9. transactionManager.commit(status); 10. ........ TransactionDefinition td = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(td); try{ ......//这里是我们的业务方法 http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 102 / 168 页 }catch (ApplicationException e) { transactionManager.rollback(status); throw e } transactionManager.commit(status); ........ 我们看到这里选取了默认的事务配置DefaultTransactionDefinition,同时在创建事物的过程中得到 TransactionStatus,然后通过直接调用事务管理器的相关方法就能完成事务处理。 声明式事务处理也同样实现了类似的过程,只是因 为采用了声明的方法,需要增加对属性的读取处理,并且需 要把整个过程整合到Spring AOP框架中和IoC容器中去的过程。 下面我们选取一 个具体的transactionManager - DataSourceTransactionManager来看看其中事务处理的实 现: 同 样的通过使用AbstractPlatformTransactionManager使用模板方法,这些都体现了对具体平台相关的事务 管理器操作的封装, 比如commit: Java代码 1. public final void commit(TransactionStatus status) throws TransactionException { 2. ...... 3. DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; 4. if (defStatus.isLocalRollbackOnly()) { 5. ...... 6. processRollback(defStatus); 7. return ; 8. } 9. ....... 10. processRollback(defStatus); 11. ...... 12. } 13. 14. processCommit(defStatus); 15. } public final void commit(TransactionStatus status) throws TransactionException { ...... http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 103 / 168 页 DefaultTransactionStatus defStatus = (DefaultT http://huibin.javaeye.com 1.6 Spring源代码解析(六):Spring声明式事务处理 第 104 / 168 页 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 发表时间: 2010-03-18 前面我们分析了Spring AOP实现中得到Proxy对象的过程,下面我们看看在Spring AOP中拦截器链是怎样被调 用的,也就是Proxy模式是怎样起作用的,或者说Spring是怎样为我们提供AOP功能的; 在 JdkDynamicAopProxy中生成Proxy对象的时候: Java代码 1. return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this ); return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 这里的this参数对应的是InvocationHandler对象,这里我们的JdkDynamicAopProxy实现了这个接口,也 就是 说当Proxy对象的函数被调用的时候,这个InvocationHandler的invoke方法会被作为回调函数调用,下面我们 看看这个方法的实 现: Java代码 1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 2. MethodInvocation invocation = null ; 3. Object oldProxy = null ; 4. boolean setProxyContext = false ; 5. 6. TargetSource targetSource = this .advised.targetSource; 7. Class targetClass = null ; 8. Object target = null ; 9. 10. try { 11. // Try special rules for equals() method and implementation of the 12. // Advised AOP configuration interface. 13. 14. if (! this .equalsDefined && AopUtils.isEqualsMethod(method)) { 15. // What if equals throws exception!? 16. // This class implements the equals(Object) method itself. 17. return equals(args[ 0 ]) ? Boolean.TRUE : Boolean.FALSE; 18. } 19. if (! this .hashCodeDefined && AopUtils.isHashCodeMethod(method)) { 20. // This class implements the hashCode() method itself. http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 105 / 168 页 21. return new Integer(hashCode()); 22. } 23. if (Advised. class == method.getDeclaringClass()) { 24. // service invocations on ProxyConfig with the proxy config 25. return AopUtils.invokeJoinpointUsingReflection( this .advised, method, args); 26. } 27. 28. Object retVal = null ; 29. 30. if ( this .advised.exposeProxy) { 31. // make invocation available if necessary 32. oldProxy = AopContext.setCurrentProxy(proxy); 33. setProxyContext = true ; 34. } 35. 36. // May be null. Get as late as possible to minimize the time we "own" the target, 37. // in case it comes from a pool. 38. // 这里是得到目标对象的地方,当然这个目标对象可 能来自于一个实例池或者是一个简单的 JAVA对象 39. target = targetSource.getTarget(); 40. if (target != null ) { 41. targetClass = target.getClass(); 42. } 43. 44. // get the interception chain for this method 45. // 这里获得定义好的拦截器链 46. List chain = this .advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( 47. this .advised, proxy, method, targetClass); 48. 49. // Check whether we have any advice. If we don't, we can fallback on direct 50. // reflective invocation of the target, and avoid creating a MethodInvocation. 51. // 如果没有设定拦截器,那么我们就直接调用目标的 对应方法 52. if (chain.isEmpty()) { 53. // We can skip creating a MethodInvocation: just invoke the target directly 54. // Note that the final invoker must be an InvokerInterceptor so we know it does http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 106 / 168 页 55. // nothing but a reflective operation on the target, and no hot swapping or fancy proxying 56. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); 57. } 58. else { 59. // We need to create a method invocation... 60. // invocation = advised.getMethodInvocationFactory().getMethodInvocation( 61. // proxy, method, targetClass, target, args, chain, advised); 62. // 如果有拦截器的设定,那么需要调用拦截 器之后才调用目标对象的相应方法 63. // 这里通过构造一个 ReflectiveMethodInvocation来实现,下面我们会看这个 ReflectiveMethodInvocation类 64. invocation = new ReflectiveMethodInvocation( 65. proxy, target, method, args, targetClass, chain); 66. 67. // proceed to the joinpoint through the interceptor chain 68. // 这里通过 ReflectiveMethodInvocation来调用拦截器链和相应的目标方法 69. retVal = invocation.proceed(); 70. } 71. 72. // massage return value if necessary 73. if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) { 74. // Special case: it returned "this" and the return type of the method is type-compatible 75. // Note that we can't help if the target sets 76. // a reference to itself in another returned object. 77. retVal = proxy; 78. } 79. return retVal; 80. } 81. finally { 82. if (target != null && !targetSource.isStatic()) { 83. // must have come from TargetSource 84. targetSource.releaseTarget(target); 85. } 86. 87. if (setProxyContext) { 88. // restore old proxy http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 107 / 168 页 89. AopContext.setCurrentProxy(oldProxy); 90. } 91. } 92. } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation = null; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class targetClass = null; Object target = null; try { // Try special rules for equals() method and implementation of the // Advised AOP configuration interface. if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // What if equals throws exception!? // This class implements the equals(Object) method itself. return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE; } if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // This class implements the hashCode() method itself. return new Integer(hashCode()); } if (Advised.class == method.getDeclaringClass()) { // service invocations on ProxyConfig with the proxy config return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal = null; if (this.advised.exposeProxy) { // make invocation available if necessary oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 108 / 168 页 } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. // 这里是得到目标对象的地方,当然这个目标对象可能来自于一个实例池或者是一个简单的JAVA对象 target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // get the interception chain for this method // 这里获得定义好的拦截器链 List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice( this.advised, proxy, method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. // 如果没有设定拦截器,那么我们就直接调用目标的对应方法 if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // We need to create a method invocation... // invocation = advised.getMethodInvocationFactory().getMethodInvocation( // proxy, method, targetClass, target, args, chain, advised); // 如果有拦截器的设定,那么需要调用拦截器之后才调用目标对象的相应方法 // 这里通过构造一个ReflectiveMethodInvocation来实现,下面我们会看这个ReflectiveMethodInvocation类 invocation = new ReflectiveMethodInvocation( proxy, target, method, args, targetClass, chain); // proceed to the joinpoint through the interceptor chain // 这里通过ReflectiveMethodInvocation来调用拦截器链和相应的目标方法 retVal = invocation.proceed(); } http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 109 / 168 页 // massage return value if necessary if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) { // Special case: it returned "this" and the return type of the method is type-compatible // Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // must have come from TargetSource targetSource.releaseTarget(target); } if (setProxyContext) { // restore old proxy AopContext.setCurrentProxy(oldProxy); } } } 我们先看看目标对象方法的调用,这里是通过AopUtils的方法调用 - 使用反射机制来对目标对象的方法进行调 用: Java代码 1. public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) 2. throws Throwable { 3. 4. // Use reflection to invoke the method. 5. // 利用放射机制得到相应的方法,并且调用invoke 6. try { 7. if (!Modifier.isPublic(method.getModifiers()) || 8. !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 110 / 168 页 9. method.setAccessible( true ); 10. } 11. return method.invoke(target, args); 12. } 13. catch (InvocationTargetException ex) { 14. // Invoked method threw a checked exception. 15. // We must rethrow it. The client won't see the interceptor. 16. throw ex.getTargetException(); 17. } 18. catch (IllegalArgumentException ex) { 19. throw new AopInvocationException( "AOP configuration seems to be invalid: tried calling method [" + 20. method + "] on target [" + target + "]" , ex); 21. } 22. catch (IllegalAccessException ex) { 23. throw new AopInvocationException( "Couldn't access method: " + method, ex); 24. } 25. } public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) throws Throwable { // Use reflection to invoke the method. // 利用放射机制得到相应的方法,并且调用invoke try { if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) { method.setAccessible(true); } return method.invoke(target, args); } catch (InvocationTargetException ex) { // Invoked method threw a checked exception. // We must rethrow it. The client won't see the interceptor. throw ex.getTargetException(); } catch (IllegalArgumentException ex) { throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 111 / 168 页 method + "] on target [" + target + "]", ex); } catch (IllegalAccessException ex) { throw new AopInvocationException("Couldn't access method: " + method, ex); } } 对拦截器链的调用处理是在ReflectiveMethodInvocation里实现的: Java代码 1. public Object proceed() throws Throwable { 2. // We start with an index of -1 and increment early. 3. // 这里直接调用目标对象的方法,没有拦截器的调用或者拦截 器已经调用完了,这个 currentInterceptorIndex的初始值是0 4. if ( this .currentInterceptorIndex == this .interceptorsAndDynamicMethodMatchers.size()) { 5. return invokeJoinpoint(); 6. } 7. 8. Object interceptorOrInterceptionAdvice = 9. this .interceptorsAndDynamicMethodMatchers.get( this .currentInterceptorIndex); 10. if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 11. // Evaluate dynamic method matcher here: static part will already have 12. // been evaluated and found to match. 13. // 这里获得相应的拦截器,如果拦截器可以匹配的上 的话,那就调用拦截器的invoke方法 14. InterceptorAndDynamicMethodMatcher dm = 15. (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; 16. if (dm.methodMatcher.matches( this .method, this .targetClass, this .arguments)) { 17. return dm.interceptor.invoke(nextInvocation()); 18. } 19. else { 20. // Dynamic matching failed. 21. // Skip this interceptor and invoke the next in the chain. 22. // 如果拦截器匹配不上,那就调用下一个拦 截器,这个时候拦截器链的位置指示后移并迭代调 用当前的proceed方法 http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 112 / 168 页 23. this .currentInterceptorIndex++; 24. return proceed(); 25. } 26. } 27. else { 28. // It's an interceptor, so we just invoke it: The pointcut will have 29. // been evaluated statically before this object was constructed. 30. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation()); 31. } 32. } public Object proceed() throws Throwable { // We start with an index of -1 and increment early. // 这里直接调用目标对象的方法,没有拦截器的调用或者拦截器已经调用完了,这个currentInterceptorIndex的初始值是0 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. // 这里获得相应的拦截器,如果拦截器可以匹配的上的话,那就调用拦截器的invoke方法 InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(nextInvocation()); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. // 如果拦截器匹配不上,那就调用下一个拦截器,这个时候拦截器链的位置指示后移并迭代调用当前的proceed方法 this.currentInterceptorIndex++; return proceed(); } } http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 113 / 168 页 else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation()); } } 这里把当前的拦截器链以及在拦截器链的位置标志都clone到一个MethodInvocation对象了,作用是当前的拦 截器执行完之 后,会继续沿着得到这个拦截器链执行下面的拦截行为,也就是会迭代的调用上面这个proceed: Java代码 1. private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException { 2. ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone(); 3. invocation.currentInterceptorIndex = this .currentInterceptorIndex + 1 ; 4. invocation.parent = this ; 5. return invocation; 6. } private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException { ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone(); invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1; invocation.parent = this; return invocation; } 这里的nextInvocation就已经包含了当前的拦截链的基本信息,我们看到在Interceptor中的实现比如 TransactionInterceptor的实现中: Java代码 1. public Object invoke( final MethodInvocation invocation) throws Throwable { 2. ...... //这里是 TransactionInterceptor插入的事务处理代码,我们会在后面分析事务处理实现的时候 进行分析 3. try { 4. //这里是对配置的拦截器链进行迭代处理的调 用 5. retVal = invocation.proceed(); http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 114 / 168 页 6. } 7. ...... //省略了和事务处理的异常处理代码 ,也是 TransactionInterceptor插入的处理 8. else { 9. try { 10. Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, 11. new TransactionCallback() { 12. public Object doInTransaction(TransactionStatus status) { 13. //这里 是TransactionInterceptor插入对事务处理的代码 14. TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); 15. //这里是 对配置的拦截器链进行迭代处理的调用,接着顺着拦截器进行处理 16. try { 17. return invocation.proceed(); 18. } 19. ...... //省略了和事务处理的异常处理代码 ,也是 TransactionInterceptor插入的处理 20. } public Object invoke(final MethodInvocation invocation) throws Throwable { ......//这里是TransactionInterceptor插入的事务处理代码,我们会在后面分析事务处理实现的时候进行分析 try { //这里是对配置的拦截器链进行迭代处理的调用 retVal = invocation.proceed(); } ......//省略了和事务处理的异常处理代码 ,也是TransactionInterceptor插入的处理 else { try { Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { //这里是TransactionInterceptor插入对事务处理的代码 TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); //这里是对配置的拦截器链进行迭代处理的调用,接着顺着拦截器进行处理 try { return invocation.proceed(); } ......//省略了和事务处理的异常处理代码 ,也是TransactionInterceptor插入的处理 } http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 115 / 168 页 从上面的分析我们看到了Spring AOP的基本实现,比如Spring怎样得到Proxy,怎样利用JAVA Proxy以及反射 机制对用户定义的拦截器链进行处理。 http://huibin.javaeye.com 1.7 Spring源代码解析(七):Spring AOP中对拦截器调用的实现 第 116 / 168 页 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 发表时间: 2010-03-18 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. } /** * 这是FactoryBean需要实现的接口方法,直接取得当前的sessionFactory的值 */ public Object getObject() { return this.sessionFactory; } 这个值在afterPropertySet中定义: Java代码 1. public void afterPropertiesSet() throws Exception { 2. //这个buildSessionFactory是通过配置信 息得到SessionFactory的地方 3. SessionFactory rawSf = buildSessionFactory(); 4. //这里使用了Proxy方法插入对 getCurrentSession的拦截,得到和事务相关的session 5. this .sessionFactory = wrapSessionFactoryIfNecessary(rawSf); 6. } public void afterPropertiesSet() throws Exception { //这个buildSessionFactory是通过配置信息得到SessionFactory的地方 SessionFactory rawSf = buildSessionFactory(); http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 117 / 168 页 //这里使用了Proxy方法插入对getCurrentSession的拦截,得到和事务相关的session this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf); } 我们先看看SessionFactory是怎样创建的,这个方法很长,包含了创建Hibernate的SessionFactory的详尽步 骤: Java代码 1. protected SessionFactory buildSessionFactory() throws Exception { 2. SessionFactory sf = null ; 3. 4. // Create Configuration instance. 5. Configuration config = newConfiguration(); 6. 7. //这里配置数据源,事务管理器,LobHander到 Holder中,这个Holder是一个ThreadLocal变量, 这样这些资源就和线程绑定了 8. if ( this .dataSource != null ) { 9. // Make given DataSource available for SessionFactory configuration. 10. configTimeDataSourceHolder.set( this .dataSource); 11. } 12. 13. if ( this .jtaTransactionManager != null ) { 14. // Make Spring-provided JTA TransactionManager available. 15. configTimeTransactionManagerHolder.set( this .jtaTransactionManager); 16. } 17. 18. if ( this .lobHandler != null ) { 19. // Make given LobHandler available for SessionFactory configuration. 20. // Do early because because mapping resource might refer to custom types. 21. configTimeLobHandlerHolder.set( this .lobHandler); 22. } 23. 24. //这里是使用Hibernate的各个属性的配置,这里使用 了Configuration类来抽象这些数据 25. try { 26. // Set connection release mode "on_close" as default. 27. // This was the case for Hibernate 3.0; Hibernate 3.1 changed http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 118 / 168 页 28. // it to "auto" (i.e. "after_statement" or "after_transaction"). 29. // However, for Spring's resource management (in particular for 30. // HibernateTransactionManager), "on_close" is the better default. 31. config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString()); 32. 33. if (!isExposeTransactionAwareSessionFactory()) { 34. // Not exposing a SessionFactory proxy with transaction-aware 35. // getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext 36. // implementation instead, providing the Spring-managed Session that way. 37. // Can be overridden by a custom value for corresponding Hibernate property. 38. config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, 39. "org.springframework.orm.hibernate3.SpringSessionContext" ); 40. } 41. 42. if ( this .entityInterceptor != null ) { 43. // Set given entity interceptor at SessionFactory level. 44. config.setInterceptor( this .entityInterceptor); 45. } 46. 47. if ( this .namingStrategy != null ) { 48. // Pass given naming strategy to Hibernate Configuration. 49. config.setNamingStrategy( this .namingStrategy); 50. } 51. 52. if ( this .typeDefinitions != null ) { 53. // Register specified Hibernate type definitions. 54. Mappings mappings = config.createMappings(); 55. for ( int i = 0 ; i < this .typeDefinitions.length; i++) { 56. TypeDefinitionBean typeDef = this .typeDefinitions[i]; 57. mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters()); 58. } 59. } 60. 61. if ( this .filterDefinitions != null ) { 62. // Register specified Hibernate FilterDefinitions. 63. for ( int i = 0 ; i < this .filterDefinitions.length; i++) { http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 119 / 168 页 64. config.addFilterDefinition( this .filterDefinitions[i]); 65. } 66. } 67. 68. if ( this .configLocations != null ) { 69. for ( int i = 0 ; i < this .configLocations.length; i++) { 70. // Load Hibernate configuration from given location. 71. config.configure( this .configLocations[i].getURL()); 72. } 73. } 74. 75. if ( this .hibernateProperties != null ) { 76. // Add given Hibernate properties to Configuration. 77. config.addProperties( this .hibernateProperties); 78. } 79. 80. if ( this .dataSource != null ) { 81. boolean actuallyTransactionAware = 82. ( this .useTransactionAwareDataSource || this .dataSource instanceof TransactionAwareDataSourceProxy); 83. // Set Spring-provided DataSource as Hibernate ConnectionProvider. 84. config.setProperty(Environment.CONNECTION_PROVIDER, 85. actuallyTransactionAware ? 86. TransactionAwareDataSourceConnectionProvider. class .getName() : 87. LocalDataSourceConnectionProvider. class .getName()); 88. } 89. 90. if ( this .jtaTransactionManager != null ) { 91. // Set Spring-provided JTA TransactionManager as Hibernate property. 92. config.setProperty( 93. Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup. class .getName()); 94. } 95. 96. if ( this .mappingLocations != null ) { 97. // Register given Hibernate mapping definitions, contained in resource files. 98. for ( int i = 0 ; i < this .mappingLocations.length; i++) { 99. config.addInputStream( this .mappingLocations[i].getInputStream()); http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 120 / 168 页 100. } 101. } 102. 103. if ( this .cacheableMappingLocations != null ) { 104. // Register given cacheable Hibernate mapping definitions, read from the file system. 105. for ( int i = 0 ; i < this .cacheableMappingLocations.length; i++) { 106. config.addCacheableFile( this .cacheableMappingLocations[i].getFile()); 107. } 108. } 109. 110. if ( this .mappingJarLocations != null ) { 111. // Register given Hibernate mapping definitions, contained in jar files. 112. for ( int i = 0 ; i < this .mappingJarLocations.length; i++) { 113. Resource resource = this .mappingJarLocations[i]; 114. config.addJar(resource.getFile()); 115. } 116. } 117. 118. if ( this .mappingDirectoryLocations != null ) { 119. // Register all Hibernate mapping definitions in the given directories. 120. for ( int i = 0 ; i < this .mappingDirectoryLocations.length; i++) { 121. File file = this .mappingDirectoryLocations[i].getFile(); 122. if (!file.isDirectory()) { 123. throw new IllegalArgumentException( 124. "Mapping directory location [" + this .mappingDirectoryLocations[i] + 125. "] does not denote a directory" ); 126. } 127. config.addDirectory(file); 128. } 129. } 130. 131. if ( this .entityCacheStrategies != null ) { 132. // Register cache strategies for mapped entities. 133. for (Enumeration classNames = this .entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) { 134. String className = (String) classNames.nextElement(); 135. String[] strategyAndRegion = http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 121 / 168 页 136. StringUtils.commaDelimitedListToStringArray( this .entityCacheStrategies.getProperty(className)); 137. if (strategyAndRegion.length > 1 ) { 138. config.setCacheConcurrencyStrategy(className, strategyAndRegion[ 0 ], strategyAndRegion[ 1 ]); 139. } 140. else if (strategyAndRegion.length > 0 ) { 141. config.setCacheConcurrencyStrategy(className, strategyAndRegion[ 0 ]); 142. } 143. } 144. } 145. 146. if ( this .collectionCacheStrategies != null ) { 147. // Register cache strategies for mapped collections. 148. for (Enumeration collRoles = this .collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) { 149. String collRole = (String) collRoles.nextElement(); 150. String[] strategyAndRegion = 151. StringUtils.commaDelimitedListToStringArray( this .collectionCacheStrategies.getProperty(collRole)); 152. if (strategyAndRegion.length > 1 ) { 153. config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[ 0 ], strategyAndRegion[ 1 ]); 154. } 155. else if (strategyAndRegion.length > 0 ) { 156. config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[ 0 ]); 157. } 158. } 159. } 160. 161. if ( this .eventListeners != null ) { 162. // Register specified Hibernate event listeners. 163. for (Iterator it = this .eventListeners.entrySet().iterator(); it.hasNext();) { 164. Map.Entry entry = (Map.Entry) it.next(); 165. Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String" ); 166. String listenerType = (String) entry.getKey(); http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 122 / 168 页 167. Object listenerObject = entry.getValue(); 168. if (listenerObject instanceof Collection) { 169. Collection listeners = (Collection) listenerObject; 170. EventListeners listenerRegistry = config.getEventListeners(); 171. Object[] listenerArray = 172. (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size()); 173. listenerArray = listeners.toArray(listenerArray); 174. config.setListeners(listenerType, listenerArray); 175. } 176. else { 177. config.setListener(listenerType, listenerObject); 178. } 179. } 180. } 181. 182. // Perform custom post-processing in subclasses. 183. postProcessConfiguration(config); 184. 185. // 这里是根据Configuration配置创建 SessionFactory的地方 186. logger.info( "Building new Hibernate SessionFactory" ); 187. this .configuration = config; 188. sf = newSessionFactory(config); 189. } 190. //最后把和线程绑定的资源清空 191. finally { 192. if ( this .dataSource != null ) { 193. // Reset DataSource holder. 194. configTimeDataSourceHolder.set( null ); 195. } 196. 197. if ( this .jtaTransactionManager != null ) { 198. // Reset TransactionManager holder. 199. configTimeTransactionManagerHolder.set( null ); 200. } 201. 202. if ( this .lobHandler != null ) { 203. // Reset LobHandler holder. http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 123 / 168 页 204. configTimeLobHandlerHolder.set( null ); 205. } 206. } 207. 208. // Execute schema update if requested. 209. if ( this .schemaUpdate) { 210. updateDatabaseSchema(); 211. } 212. 213. return sf; 214. } protected SessionFactory buildSessionFactory() throws Exception { SessionFactory sf = null; // Create Configuration instance. Configuration config = newConfiguration(); //这里配置数据源,事务管理器,LobHander到Holder中,这个Holder是一个ThreadLocal变量,这样这些资源就和线程绑定了 if (this.dataSource != null) { // Make given DataSource available for SessionFactory configuration. configTimeDataSourceHolder.set(this.dataSource); } if (this.jtaTransactionManager != null) { // Make Spring-provided JTA TransactionManager available. configTimeTransactionManagerHolder.set(this.jtaTransactionManager); } if (this.lobHandler != null) { // Make given LobHandler available for SessionFactory configuration. // Do early because because mapping resource might refer to custom types. configTimeLobHandlerHolder.set(this.lobHandler); } //这里是使用Hibernate的各个属性的配置,这里使用了Configuration类来抽象这些数据 try { // Set connection release mode "on_close" as default. http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 124 / 168 页 // This was the case for Hibernate 3.0; Hibernate 3.1 changed // it to "auto" (i.e. "after_statement" or "after_transaction"). // However, for Spring's resource management (in particular for // HibernateTransactionManager), "on_close" is the better default. config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString()); if (!isExposeTransactionAwareSessionFactory()) { // Not exposing a SessionFactory proxy with transaction-aware // getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext // implementation instead, providing the Spring-managed Session that way. // Can be overridden by a custom value for corresponding Hibernate property. config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "org.springframework.orm.hibernate3.SpringSessionContext"); } if (this.entityInterceptor != null) { // Set given entity interceptor at SessionFactory level. config.setInterceptor(this.entityInterceptor); } if (this.namingStrategy != null) { // Pass given naming strategy to Hibernate Configuration. config.setNamingStrategy(this.namingStrategy); } if (this.typeDefinitions != null) { // Register specified Hibernate type definitions. Mappings mappings = config.createMappings(); for (int i = 0; i < this.typeDefinitions.length; i++) { TypeDefinitionBean typeDef = this.typeDefinitions[i]; mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters()); } } if (this.filterDefinitions != null) { // Register specified Hibernate FilterDefinitions. for (int i = 0; i < this.filterDefinitions.length; i++) { http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 125 / 168 页 config.addFilterDefinition(this.filterDefinitions[i]); } } if (this.configLocations != null) { for (int i = 0; i < this.configLocations.length; i++) { // Load Hibernate configuration from given location. config.configure(this.configLocations[i].getURL()); } } if (this.hibernateProperties != null) { // Add given Hibernate properties to Configuration. config.addProperties(this.hibernateProperties); } if (this.dataSource != null) { boolean actuallyTransactionAware = (this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy); // Set Spring-provided DataSource as Hibernate ConnectionProvider. config.setProperty(Environment.CONNECTION_PROVIDER, actuallyTransactionAware ? TransactionAwareDataSourceConnectionProvider.class.getName() : LocalDataSourceConnectionProvider.class.getName()); } if (this.jtaTransactionManager != null) { // Set Spring-provided JTA TransactionManager as Hibernate property. config.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName()); } if (this.mappingLocations != null) { // Register given Hibernate mapping definitions, contained in resource files. for (int i = 0; i < this.mappingLocations.length; i++) { config.addInputStream(this.mappingLocations[i].getInputStream()); } http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 126 / 168 页 } if (this.cacheableMappingLocations != null) { // Register given cacheable Hibernate mapping definitions, read from the file system. for (int i = 0; i < this.cacheableMappingLocations.length; i++) { config.addCacheableFile(this.cacheableMappingLocations[i].getFile()); } } if (this.mappingJarLocations != null) { // Register given Hibernate mapping definitions, contained in jar files. for (int i = 0; i < this.mappingJarLocations.length; i++) { Resource resource = this.mappingJarLocations[i]; config.addJar(resource.getFile()); } } if (this.mappingDirectoryLocations != null) { // Register all Hibernate mapping definitions in the given directories. for (int i = 0; i < this.mappingDirectoryLocations.length; i++) { File file = this.mappingDirectoryLocations[i].getFile(); if (!file.isDirectory()) { throw new IllegalArgumentException( "Mapping directory location [" + this.mappingDirectoryLocations[i] + "] does not denote a directory"); } config.addDirectory(file); } } if (this.entityCacheStrategies != null) { // Register cache strategies for mapped entities. for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) { String className = (String) classNames.nextElement(); String[] strategyAndRegion = StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className)); if (strategyAndRegion.length > 1) { http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 127 / 168 页 config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]); } else if (strategyAndRegion.length > 0) { config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]); } } } if (this.collectionCacheStrategies != null) { // Register cache strategies for mapped collections. for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) { String collRole = (String) collRoles.nextElement(); String[] strategyAndRegion = StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole)); if (strategyAndRegion.length > 1) { config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]); } else if (strategyAndRegion.length > 0) { config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]); } } } if (this.eventListeners != null) { // Register specified Hibernate event listeners. for (Iterator it = this.eventListeners.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String"); String listenerType = (String) entry.getKey(); Object listenerObject = entry.getValue(); if (listenerObject instanceof Collection) { Collection listeners = (Collection) listenerObject; EventListeners listenerRegistry = config.getEventListeners(); Object[] listenerArray = (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size()); listenerArray = listeners.toArray(listenerArray); config.setListeners(listenerType, listenerArray); http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 128 / 168 页 } else { config.setListener(listenerType, listenerObject); } } } // Perform custom post-processing in subclasses. postProcessConfiguration(config); // 这里是根据Configuration配置创建SessionFactory的地方 logger.info("Building new Hibernate SessionFactory"); this.configuration = config; sf = newSessionFactory(config); } //最后把和线程绑定的资源清空 finally { if (this.dataSource != null) { // Reset DataSource holder. configTimeDataSourceHolder.set(null); } if (this.jtaTransactionManager != null) { // Reset TransactionManager holder. configTimeTransactionManagerHolder.set(null); } if (this.lobHandler != null) { // Reset LobHandler holder. configTimeLobHandlerHolder.set(null); } } // Execute schema update if requested. if (this.schemaUpdate) { updateDatabaseSchema(); } http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 129 / 168 页 return sf; } 而直接调用org.hibernate.cfg.Configuration来得到需要的SessionFactory: Java代码 1. protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { 2. return config.buildSessionFactory(); 3. } protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { return config.buildSessionFactory(); } 所以我们这里看到LocalSessionFactory大致起到的一个读取资源配置然后生成SessionFactory的作用;当然这 里在得 到 SessionFactory之后,还需要对session的事务管理作一些处理 - 使用了一个Proxy模式对 getCurrentSession方法进行了拦截; Java代码 1. //这里先根据当前的SessionFactory的类型得到Proxy,然后插入 Spring定义好的getCurrentSession 拦截器 2. protected SessionFactory getTransactionAwareSessionFactoryProxy(SessionFactory target) { 3. Class sfInterface = SessionFactory. class ; 4. if (target instanceof SessionFactoryImplementor) { 5. sfInterface = SessionFactoryImplementor. class ; 6. } 7. return (SessionFactory) Proxy.newProxyInstance(sfInterface.getClassLoader(), 8. new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target)); 9. } //这里先根据当前的SessionFactory的类型得到Proxy,然后插入Spring定义好的getCurrentSession拦截器 protected SessionFactory getTransactionAwareSessionFactoryProxy(SessionFactory target) { Class sfInterface = SessionFactory.class; http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 130 / 168 页 if (target instanceof SessionFactoryImplementor) { sfInterface = SessionFactoryImplementor.class; } return (SessionFactory) Proxy.newProxyInstance(sfInterface.getClassLoader(), new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target)); } 拦截器的实现如下: Java代码 1. private static class TransactionAwareInvocationHandler implements InvocationHandler { 2. 3. private final SessionFactory target; 4. 5. public TransactionAwareInvocationHandler(SessionFactory target) { 6. this .target = target; 7. } 8. 9. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 10. // Invocation on SessionFactory/SessionFactoryImplementor interface coming in... 11. // 这里对getCurrentSession方法 进行拦截,得到一个和当前事务绑定的session交给用户 12. if (method.getName().equals( "getCurrentSession" )) { 13. // Handle getCurrentSession method: return transactional Session, if any. 14. try { 15. return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false ); 16. } 17. catch (IllegalStateException ex) { 18. throw new HibernateException(ex.getMessage()); 19. } 20. } 21. else if (method.getName().equals( "equals" )) { 22. // Only consider equal when proxies are identical. 23. return (proxy == args[ 0 ] ? Boolean.TRUE : Boolean.FALSE); 24. } 25. else if (method.getName().equals( "hashCode" )) { 26. // Use hashCode of SessionFactory proxy. http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 131 / 168 页 27. return new Integer(hashCode()); 28. } 29. 30. // 这里是需要运行的SessionFactory 的目标方法 31. try { 32. return method.invoke( this .target, args); 33. } 34. catch (InvocationTargetException ex) { 35. throw ex.getTargetException(); 36. } 37. } 38. } private static class TransactionAwareInvocationHandler implements InvocationHandler { private final SessionFactory target; public TransactionAwareInvocationHandler(SessionFactory target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on SessionFactory/SessionFactoryImplementor interface coming in... // 这里对getCurrentSession方法进行拦截,得到一个和当前事务绑定的session交给用户 if (method.getName().equals("getCurrentSession")) { // Handle getCurrentSession method: return transactional Session, if any. try { return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false); } catch (IllegalStateException ex) { throw new HibernateException(ex.getMessage()); } } else if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); } else if (method.getName().equals("hashCode")) { http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 132 / 168 页 // Use hashCode of SessionFactory proxy. return new Integer(hashCode()); } // 这里是需要运行的SessionFactory的目标方法 try { return method.invoke(this.target, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } 我们看看getCurrentSession的实现,在SessionFactoryUtils中: Java代码 1. private static Session doGetSession( 2. SessionFactory sessionFactory, Interceptor entityInterceptor, 3. SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate) 4. throws HibernateException, IllegalStateException { 5. 6. Assert.notNull(sessionFactory, "No SessionFactory specified" ); 7. 8. //这个 TransactionSynchronizationManager的Resource是一个ThreadLocal变 量,sessionFactory是一个单例,但ThreadLocal是和线程绑定的 9. //这样就实现了Hiberante中常用的通过 ThreadLocal的session管理机制 10. SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); 11. if (sessionHolder != null && !sessionHolder.isEmpty()) { 12. // pre-bound Hibernate Session 13. Session session = null ; 14. if (TransactionSynchronizationManager.isSynchronizationActive() && 15. sessionHolder.doesNotHoldNonDefaultSession()) { 16. // Spring transaction management is active -> 17. // register pre-bound Session with it for transactional flushing. http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 133 / 168 页 18. session = sessionHolder.getValidatedSession(); 19. if (session != null && !sessionHolder.isSynchronizedWithTransaction()) { 20. logger.debug( "Registering Spring transaction synchronization for existing Hibernate Session" ); 21. TransactionSynchronizationManager.registerSynchronization( 22. new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false )); 23. sessionHolder.setSynchronizedWithTransaction( true ); 24. // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session 25. // with FlushMode.NEVER, which needs to allow flushing within the transaction. 26. FlushMode flushMode = session.getFlushMode(); 27. if (flushMode.lessThan(FlushMode.COMMIT) && 28. !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 29. session.setFlushMode(FlushMode.AUTO); 30. sessionHolder.setPreviousFlushMode(flushMode); 31. } 32. } 33. } 34. else { 35. // No Spring transaction management active -> try JTA transaction synchronization. 36. session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator); 37. } 38. if (session != null ) { 39. return session; 40. } 41. } 42. //这里直接打开一个Session 43. logger.debug( "Opening Hibernate Session" ); 44. Session session = (entityInterceptor != null ? 45. sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession()); 46. 47. // Use same Session for further Hibernate actions within the transaction. 48. // Thread object will get removed by synchronization at transaction completion. http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 134 / 168 页 49. // 把新打开的Session放到 SessionHolder,然后放到ThreadLocal里面去和线程绑定起来,这个 ThreadLocal是 在 TransactionSynchronizationManager中配置好的,可以根据sessionFactory来索 取 50. // 同时根据事务处理的状态来配置session的 属性,比如把FlushMode设置为Never,同时把 session和事务处理关联起来 51. if (TransactionSynchronizationManager.isSynchronizationActive()) { 52. // We're within a Spring-managed transaction, possibly from JtaTransactionManager. 53. logger.debug( "Registering Spring transaction synchronization for new Hibernate Session" ); 54. SessionHolder holderToUse = sessionHolder; 55. if (holderToUse == null ) { 56. holderToUse = new SessionHolder(session); 57. } 58. else { 59. holderToUse.addSession(session); 60. } 61. if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 62. session.setFlushMode(FlushMode.NEVER); 63. } 64. TransactionSynchronizationManager.registerSynchronization( 65. new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true )); 66. holderToUse.setSynchronizedWithTransaction( true ); 67. if (holderToUse != se http://huibin.javaeye.com 1.8 Spring源代码解析(八):Spring驱动Hibernate的实现 第 135 / 168 页 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 发表时间: 2010-03-18 简单分析一下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); http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 136 / 168 页 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. } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //这里检验是不是符合ServletRequest/SevletResponse的要求 if (!(request instanceof HttpServletRequest)) { throw new ServletException("Can only process HttpServletRequest"); } if (!(response instanceof HttpServletResponse)) { throw new ServletException("Can only process HttpServletResponse"); } HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; //根据HttpServletRequest和HttpServletResponse来进行验证 if (requiresAuthentication(httpRequest, httpResponse)) { if (logger.isDebugEnabled()) { logger.debug("Request is to process authentication"); } //这里定义Acegi中的Authentication对象来持有相关的用户验证信息 Authentication authResult; http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 137 / 168 页 try { onPreAuthentication(httpRequest, httpResponse); //这里的具体验证过程委托给子类完成,比如AuthenticationProcessingFilter来完成基于Web页面的用户验证 authResult = attemptAuthentication(httpRequest); } catch (AuthenticationException failed) { // Authentication failed unsuccessfulAuthentication(httpRequest, httpResponse, failed); return; } // Authentication success if (continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } //完成验证后的后续工作,比如跳转到相应的页面 successfulAuthentication(httpRequest, httpResponse, authResult); return; } chain.doFilter(request, response); } 在AuthenticationProcessingFilter中的具体验证过程是这样的: Java代码 1. public Authentication attemptAuthentication(HttpServletRequest request) 2. throws AuthenticationException { 3. //这里从HttpServletRequest中得到用户验 证的用户名和密码 4. String username = obtainUsername(request); 5. String password = obtainPassword(request); 6. 7. if (username == null ) { 8. username = "" ; 9. } http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 138 / 168 页 10. 11. if (password == null ) { 12. password = "" ; 13. } 14. //这里根据得到的用户名和密码去构造一个 Authentication对象提供给AuthenticationManager进 行验证,里面包含了用户的用户名和密码信息 15. UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); 16. 17. // Place the last username attempted into HttpSession for views 18. request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username); 19. 20. // Allow subclasses to set the "details" property 21. setDetails(request, authRequest); 22. //这里启动AuthenticationManager进行 验证过程 23. return this .getAuthenticationManager().authenticate(authRequest); 24. } public Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException { //这里从HttpServletRequest中得到用户验证的用户名和密码 String username = obtainUsername(request); String password = obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } //这里根据得到的用户名和密码去构造一个Authentication对象提供给AuthenticationManager进行验证,里面包含了用户的用户名和密码信息 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Place the last username attempted into HttpSession for views request.getSession().setAttribute(ACEGI_SECURITY_LAST_USERNAME_KEY, username); // Allow subclasses to set the "details" property http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 139 / 168 页 setDetails(request, authRequest); //这里启动AuthenticationManager进行验证过程 return this.getAuthenticationManager().authenticate(authRequest); } 在Acegi框架中,进行验证管理的主要类是AuthenticationManager,我们看看它是怎样进行验证管理的 - 验证 的调用入口是authenticate在AbstractAuthenticationManager的实现中: //这是进行验证的函数, 返回一个Authentication对象来记录验证的结果,其中包含了用户的验证信息,权限 配置等,同时这个Authentication会以后被授权 模块使用 Java代码 1. //如果验证失败,那么在验证过程中会直接抛出异常 2. public final Authentication authenticate(Authentication authRequest) 3. throws AuthenticationException { 4. try { //这里是实际的验证处理,我们下面使用ProviderManager来说明具体的验证过程,传入的 参数 authRequest里面已经包含了从HttpServletRequest中得到的用户输入的用户名和密码 5. Authentication authResult = doAuthentication(authRequest); 6. copyDetails(authRequest, authResult); 7. 8. return authResult; 9. } catch (AuthenticationException e) { 10. e.setAuthentication(authRequest); 11. throw e; 12. } 13. } //如果验证失败,那么在验证过程中会直接抛出异常 public final Authentication authenticate(Authentication authRequest) throws AuthenticationException { try {//这里是实际的验证处理,我们下面使用ProviderManager来说明具体的验证过程,传入的参数authRequest里面已经包含了从HttpServletRequest中得到的用户输入的用户名和密码 Authentication authResult = doAuthentication(authRequest); copyDetails(authRequest, authResult); return authResult; } catch (AuthenticationException e) { e.setAuthentication(authRequest); http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 140 / 168 页 throw e; } } 在ProviderManager中进行实际的验证工作,假设这里使用数据库来存取用户信息: Java代码 1. public Authentication doAuthentication(Authentication authentication) 2. throws AuthenticationException { 3. //这里取得配置好的provider链的迭代器,在配置的时 候可以配置多个provider,这里我们配置的是 DaoAuthenticationProvider来说明, 它使用数据库来保存用户的用户名和密码 信息。 4. Iterator iter = providers.iterator(); 5. 6. Class toTest = authentication.getClass(); 7. 8. AuthenticationException lastException = null ; 9. 10. while (iter.hasNext()) { 11. AuthenticationProvider provider = (AuthenticationProvider) iter.next(); 12. 13. if (provider.supports(toTest)) { 14. logger.debug( "Authentication attempt using " + provider.getClass().getName()); 15. //这个result包含了验证中得到的结果 信息 16. Authentication result = null ; 17. 18. try { //这里是provider进行验证处理的过程 19. result = provider.authenticate(authentication); 20. sessionController.checkAuthenticationAllowed(result); 21. } catch (AuthenticationException ae) { 22. lastException = ae; 23. result = null ; 24. } 25. 26. if (result != null ) { 27. sessionController.registerSuccessfulAuthentication(result); 28. publishEvent( new AuthenticationSuccessEvent(result)); http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 141 / 168 页 29. 30. return result; 31. } 32. } 33. } 34. 35. if (lastException == null ) { 36. lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound" , 37. new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}" )); 38. } 39. 40. // 这里发布事件来通知上下文的监听器 41. String className = exceptionMappings.getProperty(lastException.getClass().getName()); 42. AbstractAuthenticationEvent event = null ; 43. 44. if (className != null ) { 45. try { 46. Class clazz = getClass().getClassLoader().loadClass(className); 47. Constructor constructor = clazz.getConstructor( new Class[] { 48. Authentication. class , AuthenticationException. class 49. }); 50. Object obj = constructor.newInstance( new Object[] {authentication, lastException}); 51. Assert.isInstanceOf(AbstractAuthenticationEvent. class , obj, "Must be an AbstractAuthenticationEvent" ); 52. event = (AbstractAuthenticationEvent) obj; 53. } catch (ClassNotFoundException ignored) {} 54. catch (NoSuchMethodException ignored) {} 55. catch (IllegalAccessException ignored) {} 56. catch (InstantiationException ignored) {} 57. catch (InvocationTargetException ignored) {} 58. } 59. 60. if (event != null ) { 61. publishEvent(event); 62. } else { 63. if (logger.isDebugEnabled()) { http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 142 / 168 页 64. logger.debug( "No event was found for the exception " + lastException.getClass().getName()); 65. } 66. } 67. 68. // Throw the exception 69. throw lastException; 70. } public Authentication doAuthentication(Authentication authentication) throws AuthenticationException { //这里取得配置好的provider链的迭代器,在配置的时候可以配置多个provider,这里我们配置的是DaoAuthenticationProvider来说明, 它使用数据库来保存用户的用户名和密码信息。 Iterator iter = providers.iterator(); Class toTest = authentication.getClass(); AuthenticationException lastException = null; while (iter.hasNext()) { AuthenticationProvider provider = (AuthenticationProvider) iter.next(); if (provider.supports(toTest)) { logger.debug("Authentication attempt using " + provider.getClass().getName()); //这个result包含了验证中得到的结果信息 Authentication result = null; try {//这里是provider进行验证处理的过程 result = provider.authenticate(authentication); sessionController.checkAuthenticationAllowed(result); } catch (AuthenticationException ae) { lastException = ae; result = null; } if (result != null) { sessionController.registerSuccessfulAuthentication(result); publishEvent(new AuthenticationSuccessEvent(result)); http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 143 / 168 页 return result; } } } if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound", new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}")); } // 这里发布事件来通知上下文的监听器 String className = exceptionMappings.getProperty(lastException.getClass().getName()); AbstractAuthenticationEvent event = null; if (className != null) { try { Class clazz = getClass().getClassLoader().loadClass(className); Constructor constructor = clazz.getConstructor(new Class[] { Authentication.class, AuthenticationException.class }); Object obj = constructor.newInstance(new Object[] {authentication, lastException}); Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj, "Must be an AbstractAuthenticationEvent"); event = (AbstractAuthenticationEvent) obj; } catch (ClassNotFoundException ignored) {} catch (NoSuchMethodException ignored) {} catch (IllegalAccessException ignored) {} catch (InstantiationException ignored) {} catch (InvocationTargetException ignored) {} } if (event != null) { publishEvent(event); } else { if (logger.isDebugEnabled()) { logger.debug("No event was found for the exception " + lastException.getClass().getName()); } } http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 144 / 168 页 // Throw the exception throw lastException; } 我们下面看看在DaoAuthenticationProvider是怎样从数据库中取出对应的验证信息进行用户验证的,在它的 基类 AbstractUserDetailsAuthenticationProvider定义了验证的处理模板: Java代码 1. public Authentication authenticate(Authentication authentication) 2. throws AuthenticationException { 3. Assert.isInstanceOf(UsernamePasswordAuthenticationToken. class , authentication, 4. messages.getMessage( "AbstractUserDetailsAuthenticationProvider.onlySupports" , 5. "Only UsernamePasswordAuthenticationToken is supported" )); 6. 7. // 这里取得用户输入的用户名 8. String username = (authentication.getPrincipal() == null ) ? "NONE_PROVIDED" : authentication.getName(); 9. // 如果配置了缓存,从缓存中去取以前存入的用户验证信 息 - 这里是UserDetail,是服务器端存在 数据库里的用户信息,这样就不用每次都去数据库中取了 10. boolean cacheWasUsed = true ; 11. UserDetails user = this .userCache.getUserFromCache(username); 12. //没有取到,设置标志位,下面会把这次取到的服务器端用户信 息存入缓存中去 13. if (user == null ) { 14. cacheWasUsed = false ; 15. 16. try { //这里是调用UserDetailService去取用户数据库里信息的地方 17. user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); 18. } catch (UsernameNotFoundException notFound) { 19. if (hideUserNotFoundExceptions) { 20. throw new BadCredentialsException(messages.getMessage( 21. "AbstractUserDetailsAuthenticationProvider.badCredentials" , "Bad credentials" )); 22. } else { 23. throw notFound; http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 145 / 168 页 24. } 25. } 26. 27. Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract" ); 28. } 29. 30. if (!user.isAccountNonLocked()) { 31. throw new LockedException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.locked" , 32. "User account is locked" )); 33. } 34. 35. if (!user.isEnabled()) { 36. throw new DisabledException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.disabled" , 37. "User is disabled" )); 38. } 39. 40. if (!user.isAccountNonExpired()) { 41. throw new AccountExpiredException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.expired" , 42. "User account has expired" )); 43. } 44. 45. // This check must come here, as we don't want to tell users 46. // about account status unless they presented the correct credentials 47. try { //这里是验证过程,在retrieveUser中从数据库中得到用户的信息,在 additionalAuthenticationChecks中进行对比用户输入和服务器端的用户信息 48. //如果验证通过,那么构造一个 Authentication对象来让以后的授权使用,如果验证不通过, 直接抛出异常结束鉴权过程 49. additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); 50. } catch (AuthenticationException exception) { 51. if (cacheWasUsed) { 52. // There was a problem, so try again after checking 53. // we're using latest data (ie not from the cache) 54. cacheWasUsed = false ; http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 146 / 168 页 55. user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); 56. additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); 57. } else { 58. throw exception; 59. } 60. } 61. 62. if (!user.isCredentialsNonExpired()) { 63. throw new CredentialsExpiredException(messages.getMessage( 64. "AbstractUserDetailsAuthenticationProvider.credentialsExpired" , "User credentials have expired" )); 65. } 66. //根据前面的缓存结果决定是不是要把当前的用户信息存入缓存 以供下次验证使用 67. if (!cacheWasUsed) { 68. this .userCache.putUserInCache(user); 69. } 70. 71. Object principalToReturn = user; 72. 73. if (forcePrincipalAsString) { 74. principalToReturn = user.getUsername(); 75. } 76. //最后返回Authentication记录了验证结果供以 后的授权使用 77. return createSuccessAuthentication(principalToReturn, authentication, user); 78. } 79. //这是是调用UserDetailService去加载服务器端用户信息的地方,从什么地方加载 要看设置,这里我 们假设由JdbcDaoImp来从数据中进行加载 80. protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) 81. throws AuthenticationException { 82. UserDetails loadedUser; 83. //这里调用UserDetailService去从数据库中 加载用户验证信息,同时返回从数据库中返回的信息, 这些信息放到了UserDetails对象中去了 84. try { 85. loadedUser = this .getUserDetailsService().loadUserByUsername(username); http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 147 / 168 页 86. } catch (DataAccessException repositoryProblem) { 87. throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); 88. } 89. 90. if (loadedUser == null ) { 91. throw new AuthenticationServiceException( 92. "UserDetailsService returned null, which is an interface contract violation" ); 93. } 94. return loadedUser; 95. } public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); // 这里取得用户输入的用户名 String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName(); // 如果配置了缓存,从缓存中去取以前存入的用户验证信息 - 这里是UserDetail,是服务器端存在数据库里的用户信息,这样就不用每次都去数据库中取了 boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); //没有取到,设置标志位,下面会把这次取到的服务器端用户信息存入缓存中去 if (user == null) { cacheWasUsed = false; try {//这里是调用UserDetailService去取用户数据库里信息的地方 user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); } catch (UsernameNotFoundException notFound) { if (hideUserNotFoundExceptions) { throw new BadCredentialsException(messages.getMessage( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); } else { throw notFound; } } http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 148 / 168 页 Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); } if (!user.isAccountNonLocked()) { throw new LockedException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked")); } if (!user.isEnabled()) { throw new DisabledException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled")); } if (!user.isAccountNonExpired()) { throw new AccountExpiredException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired")); } // This check must come here, as we don't want to tell users // about account status unless they presented the correct credentials try {//这里是验证过程,在retrieveUser中从数据库中得到用户的信息,在additionalAuthenticationChecks中进行对比用户输入和服务器端的用户信息 //如果验证通过,那么构造一个Authentication对象来让以后的授权使用,如果验证不通过,直接抛出异常结束鉴权过程 additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } catch (AuthenticationException exception) { if (cacheWasUsed) { // There was a problem, so try again after checking // we're using latest data (ie not from the cache) cacheWasUsed = false; user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); } else { throw exception; } } if (!user.isCredentialsNonExpired()) { throw new CredentialsExpiredException(messages.getMessage( http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 149 / 168 页 "AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired")); } //根据前面的缓存结果决定是不是要把当前的用户信息存入缓存以供下次验证使用 if (!cacheWasUsed) { this.userCache.putUserInCache(user); } Object principalToReturn = user; if (forcePrincipalAsString) { principalToReturn = user.getUsername(); } //最后返回Authentication记录了验证结果供以后的授权使用 return createSuccessAuthentication(principalToReturn, authentication, user); } //这是是调用UserDetailService去加载服务器端用户信息的地方,从什么地方加载要看设置,这里我们假设由JdbcDaoImp来从数据中进行加载 protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { UserDetails loadedUser; //这里调用UserDetailService去从数据库中加载用户验证信息,同时返回从数据库中返回的信息,这些信息放到了UserDetails对象中去了 try { loadedUser = this.getUserDetailsService().loadUserByUsername(username); } catch (DataAccessException repositoryProblem) { throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); } if (loadedUser == null) { throw new AuthenticationServiceException( "UserDetailsService returned null, which is an interface contract violation"); } return loadedUser; } 下面我们重点分析一下JdbcDaoImp这个类来看看具体是怎样从数据库中得到用户信息的: Java代码 http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 150 / 168 页 1. public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService { 2. //~ Static fields/ initializers ===================================================================================== 3. //这里是预定义好的对查询语句,对应于默认的数据库表结构, 也可以自己定义查询语句对应特定 的用户数据库验证表的设计 4. public static final String DEF_USERS_BY_USERNAME_QUERY = 5. "SELECT username,password,enabled FROM users WHERE username = ?" ; 6. public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = 7. "SELECT username,authority FROM authorities WHERE username = ?" ; 8. 9. //~ Instance fields ================================================================================================ 10. //这里使用Spring JDBC来进行数据库操作 11. protected MappingSqlQuery authoritiesByUsernameMapping; 12. protected MappingSqlQuery usersByUsernameMapping; 13. private String authoritiesByUsernameQuery; 14. private String rolePrefix = "" ; 15. private String usersByUsernameQuery; 16. private boolean usernameBasedPrimaryKey = true ; 17. 18. //~ Constructors =================================================================================================== 19. //在初始化函数中把查询语句设置为预定义的SQL语句 20. public JdbcDaoImpl() { 21. usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY; 22. authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY; 23. } 24. 25. //~ Methods ======================================================================================================== 26. 27. protected void addCustomAuthorities(String username, List authorities) {} 28. 29. public String getAuthoritiesByUsernameQuery() { http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 151 / 168 页 30. return authoritiesByUsernameQuery; 31. } 32. 33. public String getRolePrefix() { 34. return rolePrefix; 35. } 36. 37. public String getUsersByUsernameQuery() { 38. return usersByUsernameQuery; 39. } 40. 41. protected void initDao() throws ApplicationContextException { 42. initMappingSqlQueries(); 43. } 44. 45. /** 46. * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass 47. */ 48. protected void initMappingSqlQueries() { 49. this .usersByUsernameMapping = new UsersByUsernameMapping(getDataSource()); 50. this .authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource()); 51. } 52. 53. public boolean isUsernameBasedPrimaryKey() { 54. return usernameBasedPrimaryKey; 55. } 56. //这里是取得数据库用户信息的具体过程 57. public UserDetails loadUserByUsername(String username) 58. throws UsernameNotFoundException, DataAccessException { 59. //根据用户名在用户表中得到用户信息,包括用户名, 密码和用户是否有效的信息 60. List users = usersByUsernameMapping.execute(username); 61. 62. if (users.size() == 0 ) { 63. throw new UsernameNotFoundException( "User not found" ); 64. } 65. //取集合中的第一个作为有效的用户对象 66. UserDetails user = (UserDetails) users.get( 0 ); // contains no GrantedAuthority[] http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 152 / 168 页 67. //这里在权限表中去取得用户的权限信息,同样的返回 一个权限集合对应于这个用户 68. List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername()); 69. 70. addCustomAuthorities(user.getUsername(), dbAuths); 71. 72. if (dbAuths.size() == 0 ) { 73. throw new UsernameNotFoundException( "User has no GrantedAuthority" ); 74. } 75. //这里根据得到的权限集合来配置返回的User对象 供以后使用 76. GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray( new GrantedAuthority[dbAuths.size()]); 77. 78. String returnUsername = user.getUsername(); 79. 80. if (!usernameBasedPrimaryKey) { 81. returnUsername = username; 82. } 83. 84. return new User(returnUsername, user.getPassword(), user.isEnabled(), true , true , true , arrayAuths); 85. } 86. 87. public void setAuthoritiesByUsernameQuery(String queryString) { 88. authoritiesByUsernameQuery = queryString; 89. } 90. 91. public void setRolePrefix(String rolePrefix) { 92. this .rolePrefix = rolePrefix; 93. } 94. 95. public void setUsernameBasedPrimaryKey( boolean usernameBasedPrimaryKey) { 96. this .usernameBasedPrimaryKey = usernameBasedPrimaryKey; 97. } 98. 99. public void setUsersByUsernameQuery(String usersByUsernameQueryString) { 100. this .usersByUsernameQuery = usersByUsernameQueryString; 101. } 102. http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 153 / 168 页 103. //~ Inner Classes ================================================================================================== 104. 105. /** 106. * 这里是调用Spring JDBC的数据库操作,具体可以参考对 JDBC的分析,这个类的作用是把数据库 查询得到的记录集合转换为对象集合 - 一个很简单的O/R实现 107. */ 108. protected class AuthoritiesByUsernameMapping extends MappingSqlQuery { 109. protected AuthoritiesByUsernameMapping(DataSource ds) { 110. super (ds, authoritiesByUsernameQuery); 111. declareParameter( new SqlParameter(Types.VARCHAR)); 112. compile(); 113. } 114. 115. protected Object mapRow(ResultSet rs, int rownum) 116. throws SQLException { 117. String roleName = rolePrefix + rs.getString( 2 ); 118. GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName); 119. 120. return authority; 121. } 122. } 123. 124. /** 125. * Query object to look up a user. 126. */ 127. protected class UsersByUsernameMapping extends MappingSqlQuery { 128. protected UsersByUsernameMapping(DataSource ds) { 129. super (ds, usersByUsernameQuery); 130. declareParameter( new SqlParameter(Types.VARCHAR)); 131. compile(); 132. } 133. 134. protected Object mapRow(ResultSet rs, int rownum) 135. throws SQLException { 136. String username = rs.getString( 1 ); 137. String password = rs.getString( 2 ); http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 154 / 168 页 138. boolean enabled = rs.getBoolean( 3 ); 139. UserDetails user = new User(username, password, enabled, true , true , true , 140. new GrantedAuthority[] { new GrantedAuthorityImpl( "HOLDER" )}); 141. 142. return user; 143. } 144. } 145. } public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService { //~ Static fields/initializers ===================================================================================== //这里是预定义好的对查询语句,对应于默认的数据库表结构,也可以自己定义查询语句对应特定的用户数据库验证表的设计 public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT username,password,enabled FROM users WHERE username = ?"; public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT username,authority FROM authorities WHERE username = ?"; //~ Instance fields ================================================================================================ //这里使用Spring JDBC来进行数据库操作 protected MappingSqlQuery authoritiesByUsernameMapping; protected MappingSqlQuery usersByUsernameMapping; private String authoritiesByUsernameQuery; private String rolePrefix = ""; private String usersByUsernameQuery; private boolean usernameBasedPrimaryKey = true; //~ Constructors =================================================================================================== //在初始化函数中把查询语句设置为预定义的 http://huibin.javaeye.com 1.9 Spring源代码解析(九):Spring Acegi框架鉴权的实现 第 155 / 168 页 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 发表时间: 2010-03-18 我们从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. } http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 156 / 168 页 //这里是拦截器拦截HTTP请求的入口 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } //这是具体的拦截调用 public void invoke(FilterInvocation fi) throws IOException, ServletException { if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null) && observeOncePerRequest) { //在第一次进行过安全检查之后就不会再做了 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } else { //这是第一次收到相应的请求,需要做安全检测,同时把标志为设置好 - FILTER_APPLIED,下次就再有请求就不会作相同的安全检查了 if (fi.getRequest() != null) { fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE); } //这里是做安全检查的地方 InterceptorStatusToken token = super.beforeInvocation(fi); //接着向拦截器链执行 try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } } 我们看看在AbstractSecurityInterceptor是怎样对HTTP请求作安全检测的: Java代码 1. protected InterceptorStatusToken beforeInvocation(Object object) { 2. Assert.notNull(object, "Object was null" ); 3. 4. if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { 5. throw new IllegalArgumentException( "Security invocation attempted for object " http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 157 / 168 页 6. + object.getClass().getName() 7. + " but AbstractSecurityInterceptor only configured to support secure objects of type: " 8. + getSecureObjectClass()); 9. } 10. //这里读取配置 FilterSecurityInterceptor的ObjectDefinitionSource属性,这些属性配置了资源的 安全设置 11. ConfigAttributeDefinition attr = this .obtainObjectDefinitionSource().getAttributes(object); 12. 13. if (attr == null ) { 14. if (rejectPublicInvocations) { 15. throw new IllegalArgumentException( 16. "No public invocations are allowed via this AbstractSecurityInterceptor. " 17. + "This indicates a configuration error because the " 18. + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'" ); 19. } 20. 21. if (logger.isDebugEnabled()) { 22. logger.debug( "Public object - authentication not attempted" ); 23. } 24. 25. publishEvent( new PublicInvocationEvent(object)); 26. 27. return null ; // no further work post-invocation 28. } 29. 30. 31. if (logger.isDebugEnabled()) { 32. logger.debug( "Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString()); 33. } 34. //这里从SecurityContextHolder中去取 Authentication对象,一般在登录时会放到 SecurityContextHolder中去 35. if (SecurityContextHolder.getContext().getAuthentication() == null ) { 36. credentialsNotFound(messages.getMessage( "AbstractSecurityInterceptor.authenticationNotFound" , 37. "An Authentication object was not found in the SecurityContext" ), object, attr); http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 158 / 168 页 38. } 39. 40. // 如果前面没有处理鉴权,这里需要对鉴权进行处理 41. Authentication authenticated; 42. 43. if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) { 44. try { //调用配置好的AuthenticationManager处理鉴权,如果鉴权不成功,抛出异常结束处理 45. authenticated = this .authenticationManager.authenticate(SecurityContextHolder.getContext() 46. .getAuthentication()); 47. } catch (AuthenticationException authenticationException) { 48. throw authenticationException; 49. } 50. 51. // We don't authenticated.setAuthentication(true), because each provider should do that 52. if (logger.isDebugEnabled()) { 53. logger.debug( "Successfully Authenticated: " + authenticated.toString()); 54. } 55. //这里把鉴权成功后得到的 Authentication保存到SecurityContextHolder中供下次使用 56. SecurityContextHolder.getContext().setAuthentication(authenticated); 57. } else { //这里处理前面已经通过鉴权的请求,先从SecurityContextHolder中去取得 Authentication 58. authenticated = SecurityContextHolder.getContext().getAuthentication(); 59. 60. if (logger.isDebugEnabled()) { 61. logger.debug( "Previously Authenticated: " + authenticated.toString()); 62. } 63. } 64. 65. // 这是处理授权的过程 66. try { 67. //调用配置好的 AccessDecisionManager来进行授权 68. this .accessDecisionManager.decide(authenticated, object, attr); 69. } catch (AccessDeniedException accessDeniedException) { 70. //授权不成功向外发布事件 http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 159 / 168 页 71. AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated, 72. accessDeniedException); 73. publishEvent(event); 74. 75. throw accessDeniedException; 76. } 77. 78. if (logger.isDebugEnabled()) { 79. logger.debug( "Authorization successful" ); 80. } 81. 82. AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated); 83. publishEvent(event); 84. 85. // 这里构建一个RunAsManager来替代当前的 Authentication对象,默认情况下使用的是 NullRunAsManager会把SecurityContextHolder中的 Authentication对象清空 86. Authentication runAs = this .runAsManager.buildRunAs(authenticated, object, attr); 87. 88. if (runAs == null ) { 89. if (logger.isDebugEnabled()) { 90. logger.debug( "RunAsManager did not change Authentication object" ); 91. } 92. 93. // no further work post-invocation 94. return new InterceptorStatusToken(authenticated, false , attr, object); 95. } else { 96. if (logger.isDebugEnabled()) { 97. logger.debug( "Switching to RunAs Authentication: " + runAs.toString()); 98. } 99. 100. SecurityContextHolder.getContext().setAuthentication(runAs); 101. 102. // revert to token.Authenticated post-invocation 103. return new InterceptorStatusToken(authenticated, true , attr, object); 104. } 105. } http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 160 / 168 页 protected InterceptorStatusToken beforeInvocation(Object object) { Assert.notNull(object, "Object was null"); if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName() + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + getSecureObjectClass()); } //这里读取配置FilterSecurityInterceptor的ObjectDefinitionSource属性,这些属性配置了资源的安全设置 ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object); if (attr == null) { if(rejectPublicInvocations) { throw new IllegalArgumentException( "No public invocations are allowed via this AbstractSecurityInterceptor. " + "This indicates a configuration error because the " + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'"); } if (logger.isDebugEnabled()) { logger.debug("Public object - authentication not attempted"); } publishEvent(new PublicInvocationEvent(object)); return null; // no further work post-invocation } if (logger.isDebugEnabled()) { logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString()); } //这里从SecurityContextHolder中去取Authentication对象,一般在登录时会放到SecurityContextHolder中去 if (SecurityContextHolder.getContext().getAuthentication() == null) { credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", "An Authentication object was not found in the SecurityContext"), object, attr); http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 161 / 168 页 } // 如果前面没有处理鉴权,这里需要对鉴权进行处理 Authentication authenticated; if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) { try {//调用配置好的AuthenticationManager处理鉴权,如果鉴权不成功,抛出异常结束处理 authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext() .getAuthentication()); } catch (AuthenticationException authenticationException) { throw authenticationException; } // We don't authenticated.setAuthentication(true), because each provider should do that if (logger.isDebugEnabled()) { logger.debug("Successfully Authenticated: " + authenticated.toString()); } //这里把鉴权成功后得到的Authentication保存到SecurityContextHolder中供下次使用 SecurityContextHolder.getContext().setAuthentication(authenticated); } else {//这里处理前面已经通过鉴权的请求,先从SecurityContextHolder中去取得Authentication authenticated = SecurityContextHolder.getContext().getAuthentication(); if (logger.isDebugEnabled()) { logger.debug("Previously Authenticated: " + authenticated.toString()); } } // 这是处理授权的过程 try { //调用配置好的AccessDecisionManager来进行授权 this.accessDecisionManager.decide(authenticated, object, attr); } catch (AccessDeniedException accessDeniedException) { //授权不成功向外发布事件 AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated, accessDeniedException); publishEvent(event); http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 162 / 168 页 throw accessDeniedException; } if (logger.isDebugEnabled()) { logger.debug("Authorization successful"); } AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated); publishEvent(event); // 这里构建一个RunAsManager来替代当前的Authentication对象,默认情况下使用的是NullRunAsManager会把SecurityContextHolder中的Authentication对象清空 Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr); if (runAs == null) { if (logger.isDebugEnabled()) { logger.debug("RunAsManager did not change Authentication object"); } // no further work post-invocation return new InterceptorStatusToken(authenticated, false, attr, object); } else { if (logger.isDebugEnabled()) { logger.debug("Switching to RunAs Authentication: " + runAs.toString()); } SecurityContextHolder.getContext().setAuthentication(runAs); // revert to token.Authenticated post-invocation return new InterceptorStatusToken(authenticated, true, attr, object); } } 到这里我们假设配置AffirmativeBased作为AccessDecisionManager: Java代码 1. //这里定义了决策机制,需要全票才能通过 http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 163 / 168 页 2. public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) 3. throws AccessDeniedException { 4. //这里取得配置好的迭代器集合 5. Iterator iter = this .getDecisionVoters().iterator(); 6. int deny = 0 ; 7. //依次使用各个投票器进行投票,并对投票结果进行计 票 8. while (iter.hasNext()) { 9. AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); 10. int result = voter.vote(authentication, object, config); 11. //这是对投票结果进行处理,如果遇到其中一 票通过,那就授权通过,如果是弃权或者反对, 那就继续投票 12. switch (result) { 13. case AccessDecisionVoter.ACCESS_GRANTED: 14. return ; 15. 16. case AccessDecisionVoter.ACCESS_DENIED: 17. //这里对反对票进行计数 18. deny++; 19. 20. break ; 21. 22. default : 23. break ; 24. } 25. } 26. //如果有反对票,抛出异常,整个授权不通过 27. if (deny > 0 ) { 28. throw new AccessDeniedException(messages.getMessage( "AbstractAccessDecisionManager.accessDenied" , 29. "Access is denied" )); 30. } 31. 32. // 这里对弃权票进行处理,看看是全是弃权票的决定 情况,默认是不通过,由 allowIfAllAbstainDecisions变量控制 33. checkAllowIfAllAbstainDecisions(); 34. } 35. 具体的投票由投票器进行,我们这里配置了RoleVoter来进行投票: http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 164 / 168 页 36. public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { 37. int result = ACCESS_ABSTAIN; 38. //这里取得资源的安全配置 39. Iterator iter = config.getConfigAttributes(); 40. 41. while (iter.hasNext()) { 42. ConfigAttribute attribute = (ConfigAttribute) iter.next(); 43. 44. if ( this .supports(attribute)) { 45. result = ACCESS_DENIED; 46. 47. // 这里对资源配置的安全授权级别 进行判断,也就是匹配ROLE为前缀的角色配置 48. // 遍历每个配置属性,如果其中一 个匹配该主体持有的GrantedAuthority,则访问被允许。 49. for ( int i = 0 ; i < authentication.getAuthorities().length; i++) { 50. if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) { 51. return ACCESS_GRANTED; 52. } 53. } 54. } 55. } 56. 57. return result; 58. } //这里定义了决策机制,需要全票才能通过 public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException { //这里取得配置好的迭代器集合 Iterator iter = this.getDecisionVoters().iterator(); int deny = 0; //依次使用各个投票器进行投票,并对投票结果进行计票 while (iter.hasNext()) { AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); int result = voter.vote(authentication, object, config); //这是对投票结果进行处理,如果遇到其中一票通过,那就授权通过,如果是弃权或者反对,那就继续投票 http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 165 / 168 页 switch (result) { case AccessDecisionVoter.ACCESS_GRANTED: return; case AccessDecisionVoter.ACCESS_DENIED: //这里对反对票进行计数 deny++; break; default: break; } } //如果有反对票,抛出异常,整个授权不通过 if (deny > 0) { throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied")); } // 这里对弃权票进行处理,看看是全是弃权票的决定情况,默认是不通过,由allowIfAllAbstainDecisions变量控制 checkAllowIfAllAbstainDecisions(); } 具体的投票由投票器进行,我们这里配置了RoleVoter来进行投票: public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { int result = ACCESS_ABSTAIN; //这里取得资源的安全配置 Iterator iter = config.getConfigAttributes(); while (iter.hasNext()) { ConfigAttribute attribute = (ConfigAttribute) iter.next(); if (this.supports(attribute)) { result = ACCESS_DENIED; // 这里对资源配置的安全授权级别进行判断,也就是匹配ROLE为前缀的角色配置 // 遍历每个配置属性,如果其中一个匹配该主体持有的GrantedAuthority,则访问被允许。 http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 166 / 168 页 for (int i = 0; i < authentication.getAuthorities().length; i++) { if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) { return ACCESS_GRANTED; } } } } return result; } 上面就是对整个授权过程的一个分析,从FilterSecurityInterceptor拦截Http请求入手,然后读取对资源的安全 配 置以后,把这些信息交由AccessDecisionManager来进行决策,Spring为我们提供了若干决策器来使用, 在决策器中我们可以配置投票 器来完成投票,我们在上面具体分析了角色投票器的使用过程。 http://huibin.javaeye.com 1.10 Spring源代码解析(十):Spring Acegi框架授权的实现 第 167 / 168 页 QQ:739921140 huibin的博客文章 作者: huibin http://huibin.javaeye.com 本书由JavaEye提供电子书DIY功能制作并发行。 更多精彩博客电子书,请访问:http://www.javaeye.com/blogs/pdf http://www.javaeye.com - 做最棒的软件开发交流社区 第 168 / 168 页
还剩167页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 34 人已下载

下载pdf

pdf贡献者

lize_n

贡献于2010-11-25

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf