<menu id="gc4q0"></menu>
  • <menu id="gc4q0"></menu>
  • <input id="gc4q0"></input>
    <nav id="gc4q0"></nav>
  • <input id="gc4q0"><acronym id="gc4q0"></acronym></input>
  • Spring框架设计理念与设计模式

    多图详解Spring框架的设计理念与设计模式


    spring作为现在最优秀的框架之一,已被广泛的使用,51CTO也曾经针对Spring框架中的JDBC应用做过报道。本文将从另外一个视角试图剖析出Spring框架的作者设计Spring框架的骨骼架构的设计理念。

    AD:51CTO 网+ 第十二期沙龙:大话数据之美_如何用数据驱动用户体验

    Spring作为现在最优秀的框架之一,已被广泛的使用,51CTO也曾经针对Spring框架中的JDBC应用做过报道。本文将从另外一个视角试图剖析出Spring框架的作者设计Spring框架的骨骼架构的设计理念,有那几个核心组件?为什么需要这些组件?它们又是如何结合在一起构成Spring的骨骼架构?Spring的AOP特性又是如何利用这些基础的骨骼架构来工作的?Spring中又使用了那些设计模式来完成它的这种设计的?它的这种 设计理念对对我们以后的软件设计有何启示?本文将详细解答这些问题。

    Spring的骨骼架构

    Spring总共有十几个组件,但是真正核心的组件只有几个,下面是Spring框架的总体架构图:

    Spring框架的总体架构图
    图1.Spring框架的总体架构图

    从上图中可以看出Spring框架中的核心组件只有三个:Core、Context和Beans。它们构建起了整个Spring的骨骼架构。没有它们就不可能有AOP、Web等上层的特性功能。下面也将主要从这三个组件入手分析Spring。

    Spring的设计理念

    前面介绍了Spring的三个核心组件,如果再在它们三个中选出核心的话,那就非Beans组件莫属了,为何这样说,其实Spring就是面向Bean的编程(BOP,Bean Oriented Programming),Bean在Spring 中才是真正的主角。

    Bean在Spring中作用就像Object对OOP的意义一样,没有对象的概念就像没有面向对象编程,Spring中没有Bean也就没有Spring存在的意义。就像一次演出舞台都准备好了但是却没有演员一样。为什 么要Bean这种角色Bean或者为何在Spring如此重要,这由Spring框架的设计目标决定,Spring为何如此流行,我们用Spring的原因是什么,想想你会发现原来Spring解决了一个非常关键的问题他可以让 你把对象之间的依赖关系转而用配置文件来管理,也就是他的依赖注入机制。而这个注入关系在一个叫Ioc容器中管理,那Ioc容器中有又是什么就是被Bean包裹的对象。Spring正是通过把对象包装在 Bean中而达到对这些对象管理以及一些列额外操作的目的。

    它这种设计策略完全类似于Java实现OOP的设计理念,当然了Java本身的设计要比Spring复杂太多太多,但是都是构建一个数据结构,然后根据这个数据结构设计他的生存环境,并让它在这个环境中 按照一定的规律在不停的运动,在它们的不停运动中设计一系列与环境或者与其他个体完成信息交换。这样想来回过头想想我们用到的其他框架都是大慨类似的设计理念。

    核心组件如何协同工作

    前面说Bean是Spring中关键因素,那Context和Core又有何作用呢?前面吧Bean比作一场演出中的演员的话,那Context就是这场演出的舞台背景,而Core应该就是演出的道具了。只有他们在一起才能 具备能演出一场好戏的最基本的条件。当然有最基本的条件还不能使这场演出脱颖而出,还要他表演的节目足够的精彩,这些节目就是Spring能提供的特色功能了。

    我们知道Bean包装的是Object,而Object必然有数据,如何给这些数据提供生存环境就是Context要解决的问题,对Context来说他就是要发现每个Bean之间的关系,为它们建立这种关系并且要维护好 这种关系。所以Context就是一个Bean关系的集合,这个关系集合又叫Ioc容器,一旦建立起这个Ioc容器后Spring就可以为你工作了。那Core组件又有什么用武之地呢?其实Core就是发现、建立和维护每 个Bean之间的关系所需要的一些列的工具,从这个角度看来,Core这个组件叫Util更能让你理解。

    它们之间可以用下图来表示:

    三个组件关系
    图2.三个组件关系

    核心组件详解

    这里将详细介绍每个组件内部类的层次关系,以及它们在运行时的时序顺序。我们在使用Spring是应该注意的地方。

    Bean组件

    前面已经说明了Bean组件对Spring的重要性,下面看看Bean这个组件式怎么设计的。Bean组件在Spring的org.springframework.beans包下。这个包下的所有类主要解决了三件事:Bean的定义、Bean 的创建以及对Bean的解析。对Spring的使用者来说唯一需要关心的就是Bean的创建,其他两个由Spring在内部帮你完成了,对你来说是透明的。

    SpringBean的创建时典型的工厂模式,他的顶级接口是BeanFactory,下图是这个工厂的继承层次关系:

    Bean工厂的继承关系 
    图4.Bean工厂的继承关系

    BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和Autowire Capable Bean Factory。但是从上图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,他实 现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程中,对对象的 数据访问所做的限制。例如ListableBeanFactory接口表示这些Bean是可列表的,而HierarchicalBeanFactory表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean。 AutowireCapableBeanFactory接口定义Bean的自动装配规则。这四个接口共同定义了Bean的集合、Bean之间的关系、以及Bean行为。

    Bean的定义主要有BeanDefinition描述,如下图说明了这些类的层次关系:

    Bean定义的类层次关系图
    图5.Bean定义的类层次关系图

    Bean的定义就是完整的描述了在Spring的配置文件中你定义的节点中所有的信息,包括各种子节点。当Spring成功解析你定义的一个节点后,在Spring的内部他就被转化 成BeanDefinition对象。以后所有的操作都是对这个对象完成的。

    Bean的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。这个解析过程主要通过 下图中的类完成:

    Bean的解析类
    图6.Bean的解析类

    当然还有具体对tag的解析这里并没有列出。

    Context组件

    Context在Spring的org.springframework.context包下,前面已经讲解了Context组件在Spring中的作用,他实际上就是给Spring提供一个运行时的环境,用以保存各个对象的状态。下面看一下这个 环境是如何构建的。

    ApplicationContext是Context的顶级父类,他除了能标识一个应用环境的基本信息外,他还继承了五个接口,这五个接口主要是扩展了Context的功能。下面是Context的类结构图:

    Context相关的类结构图
    图7.Context相关的类结构图

    从上图中可以看出ApplicationContext继承了BeanFactory,这也说明了Spring容器中运行的主体对象是Bean,另外ApplicationContext继承了ResourceLoader接口,使得ApplicationContext可以访 问到任何外部资源,这将在Core中详细说明。

    ApplicationContext的子类主要包含两个方面:

    ConfigurableApplicationContext表示该Context是可修改的,也就是在构建Context中用户可以动态添加或修改已有的配置信息,它下面又有多个子类,其中最经常使用的是可更新的Context,即 AbstractRefreshableApplicationContext类。

    WebApplicationContext顾名思义,就是为web准备的Context他可以直接访问到ServletContext,通常情况下,这个接口使用的少。

    再往下分就是按照构建Context的文件类型,接着就是访问Context的方式。这样一级一级构成了完整的Context等级层次。

    总体来说ApplicationContext必须要完成以下几件事:

    ◆标识一个应用环境

    ◆利用BeanFactory创建Bean对象

    ◆保存对象关系表

    ◆能够捕获各种事件

    Context作为Spring的Ioc容器,基本上整合了Spring的大部分功能,或者说是大部分功能的基础。

    Core组件

    Core组件作为Spring的核心组件,他其中包含了很多的关键类,其中一个重要组成部分就是定义了资源的访问方式。这种把所有资源都抽象成一个接口的方式很值得在以后的设计中拿来学习。下面就 重要看一下这个部分在Spring的作用。

    下图是Resource相关的类结构图:

    Resource相关的类结构图
    图8.Resource相关的类结构图

    从上图可以看出Resource接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。对资源的提供者来说,如何把资源包装起来交给其他人用这也是一个问题,我们看到Resource 接口继承了InputStreamSource接口,这个接口中有个getInputStream方法,返回的是InputStream类。这样所有的资源都被可以通过InputStream这个类来获取,所以也屏蔽了资源的提供者。另外还有一 个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由ResourceLoader接口完成,他屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源, 他的默认实现是DefaultResourceLoader。

    下面看一下Context和Resource是如何建立关系的?首先看一下他们的类关系图:

    Context和Resource的类关系图
    图9.Context和Resource的类关系图

    从上图可以看出,Context是把资源的加载、解析和描述工作委托给了ResourcePatternResolver类来完成,他相当于一个接头人,他把资源的加载、解析和资源的定义整合在一起便于其他组件使用。 Core组件中还有很多类似的方式。

    Ioc容器如何工作

    前面介绍了Core组件、Bean组件和Context组件的结构与相互关系,下面这里从使用者角度看一下他们是如何运行的,以及我们如何让Spring完成各种功能,Spring到底能有那些功能,这些功能是如 何得来的,下面介绍。

    如何创建BeanFactory工厂

    正如图2描述的那样,Ioc容器实际上就是Context组件结合其他两个组件共同构建了一个Bean关系网,如何构建这个关系网?构建的入口就在AbstractApplicationContext类的refresh方法中。这个方 法的代码如下:

    清单1.AbstractApplicationContext.refresh

         
         
    1. public void refresh() throws BeansException, IllegalStateException {  
    2.  
    3.     synchronized (this.startupShutdownMonitor) {  
    4.  
    5.         // Prepare this context for refreshing.  
    6.  
    7.         prepareRefresh();  
    8.  
    9.         // Tell the subclass to refresh the internal bean factory.  
    10.  
    11.         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
    12.  
    13.         // Prepare the bean factory for use in this context.  
    14.  
    15.         prepareBeanFactory(beanFactory);  
    16.  
    17.         try {  
    18.  
    19.             // Allows post- processing of the bean factory in context subclasses.  
    20.  
    21.             postProcessBeanFactory(beanFactory);  
    22.  
    23.             // Invoke factory processors registered as beans in& nbsp;the context.  
    24.  
    25.             invokeBeanFactoryPostProcessors(beanFactory);  
    26.  
    27.             // Register bean processors that intercept bean crea tion.  
    28.  
    29.             registerBeanPostProcessors (beanFactory);  
    30.  
    31.             // Initialize message source for this context.  
    32.  
    33.             initMessageSource();  
    34.  
    35.             // Initialize event multicaster for this context.  
    36.  
    37.             initApplicationEventMulticaster();  
    38.  
    39.             // Initialize other special beans in specific contex t subclasses.  
    40.  
    41.             onRefresh();  
    42.  
    43.             // Check for listener beans and register them.  
    44.  
    45.             registerListeners();  
    46.  
    47.             // Instantiate all remaining (non-lazy-init) singletons.  
    48.  
    49.             finishBeanFactoryInitialization (beanFactory);  
    50.  
    51.             // Last step: publish corresponding event.  
    52.  
    53.             finishRefresh();  
    54.  
    55.         }  
    56.  
    57.         catch (BeansException ex) {  
    58.  
    59.             // Destroy already created singletons to avoid dangl ing resources.  
    60.  
    61.             destroyBeans();  
    62.  
    63.             // Reset 'active' flag.  
    64.  
    65.             cancelRefresh(ex);  
    66.  
    67.             // Propagate exception to caller.  
    68.  
    69.             throw ex;  
    70.  
    71.         }  
    72.  
    73.     }  
    74.  
    75. }  
    76.  

    这个方法就是构建整个Ioc容器过程的完整的代码,了解了里面的每一行代码基本上就了解大部分Spring的原理和功能了。

    这段代码主要包含这样几个步骤:

    ◆构建BeanFactory,以便于产生所需的“演员”

    ◆注册可能感兴趣的事件

    ◆创建Bean实例对象

    ◆触发被监听的事件

    下面就结合代码分析这几个过程。

    第二三句就是在创建和配置BeanFactory。这里是refresh也就是刷新配置,前面介绍了Context有可更新的子类,这里正是实现这个功能,当BeanFactory已存在是就更新,如果没有就新创建。下面是 更新BeanFactory的方法代码:

    清单2. AbstractRefreshableApplicationContext. refreshBeanFactory

         
         
    1. protected final void refreshBeanFactory() throws BeansException {  
    2.  
    3.     if (hasBeanFactory()) {  
    4.  
    5.         destroyBeans();  
    6.  
    7.         closeBeanFactory();  
    8.  
    9.     }  
    10.  
    11.     try {  
    12.  
    13.         DefaultListableBeanFactory beanFactory = createBeanFactory();  
    14.  
    15.         beanFactory.setSerializationId(getId());  
    16.  
    17.         customizeBeanFactory(beanFactory);  
    18.  
    19.         loadBeanDefinitions(beanFactory);  
    20.  
    21.         synchronized (this.beanFactoryMonitor) {  
    22.  
    23.             this.beanFactory = beanFactory;  
    24.  
    25.         }  
    26.  
    27.     }  
    28.  
    29.     catch (IOException ex) {  
    30.  
    31.         throw new ApplicationContextException(  
    32.  
    33.                        "I/O error& nbsp;parsing bean definition source for "  
    34.  
    35.                        + getDisplayName (), ex);  
    36.  
    37.     }  
    38.  
    39. }  
    40.  

    这个方法实现了AbstractApplicationContext的抽象方法refreshBeanFactory,这段代码清楚的说明了BeanFactory的创建过程。注意BeanFactory对象的类型的变化,前 面介绍了他有很多子类,在什么情况下使用不同的子类这非常关键。BeanFactory的原始对象是DefaultListableBeanFactory,这个非常关键,因为他设计到后面对这个对象的多种操作,下面看一下这个 类的继承层次类图:

    DefaultListableBeanFactory类继承关系图
    图10.DefaultListableBeanFactory类继承关系图

    从这个图中发现除了BeanFactory相关的类外,还发现了与Bean的register相关。这在refreshBeanFactory方法中有一行loadBeanDefinitions(beanFactory)将找到答案,这个方法将开始加载、解析 Bean的定义,也就是把用户定义的数据结构转化为Ioc容器中的特定数据结构。

    这个过程可以用下面时序图解释:

    创建BeanFactory时序图
    图11.创建BeanFactory时序图

    Bean的解析和登记流程时序图如下:

    解析和登记Bean对象时序图
    图12.解析和登记Bean对象时序图

    创建好BeanFactory后,接下去添加一些Spring本身需要的一些工具类,这个操作在AbstractApplicationContext的prepareBeanFactory方法完成。

    AbstractApplicationContext中接下来的三行代码对Spring的功能扩展性起了至关重要的作用。前两行主要是让你现在可以对已经构建的BeanFactory的配置做修改,后面一行就是让你可以对以后再 创建Bean的实例对象时添加一些自定义的操作。所以他们都是扩展了Spring的功能,所以我们要学习使用Spring必须对这一部分搞清楚。

    其中在invokeBeanFactoryPostProcessors方法中主要是获取实现BeanFactoryPostProcessor接口的子类。并执行它的postProcessBeanFactory方法,这个方法的声明如下:

    清单3.BeanFactoryPostProcessor.postProcessBeanFactory

         
         
    1. void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)  
    2.  
    3.     throws BeansException;  

    它的参数是beanFactory,说明可以对beanFactory做修改,这里注意这个beanFactory是ConfigurableListableBeanFactory类型的,这也印证了前面介绍的不同BeanFactory所使用的场合不同,这里 只能是可配置的BeanFactory,防止一些数据被用户随意修改。

    registerBeanPostProcessors方法也是可以获取用户定义的实现了BeanPostProcessor接口的子类,并执行把它们注册到BeanFactory对象中的beanPostProcessors变量中。BeanPostProcessor中声明 了两个方法:postProcessBeforeInitialization、postProcessAfterInitialization分别用于在Bean对象初始化时执行。可以执行用户自定义的操作。

    后面的几行代码是初始化监听事件和对系统的其他监听者的注册,监听者必须是ApplicationListener的子类。

    如何创建Bean实例并构建Bean的关系网

    下面就是Bean的实例化代码,是从finishBeanFactoryInitialization方法开始的。

    清单4.AbstractApplicationContext.finishBeanFactoryInitialization

         
         
    1. protected void finishBeanFactoryInitialization(  
    2.  
    3.         ConfigurableListableBeanFactory beanFactory) {  
    4.  
    5.    
    6.  
    7.     // Stop using the temporary ClassLoader for type matching.  
    8.  
    9.     beanFactory.setTempClassLoader(null);  
    10.  
    11.    
    12.  
    13.     // Allow for caching all bean definition metadata, not expecting further changes .  
    14.  
    15.     beanFactory.freezeConfiguration();  
    16.  
    17.    
    18.  
    19.     // Instantiate all remaining (non-lazy-init) singletons.  
    20.  
    21.     beanFactory.preInstantiateSingletons();  
    22.  
    23. }  
    24.  

    从上面代码中可以发现Bean的实例化是在BeanFactory中发生的。preInstantiateSingletons方法的代码如下:

    清单5.DefaultListableBeanFactory.preInstantiateSingletons

         
         
    1. public void preInstantiateSingletons() throws BeansException {  
    2.  
    3.     if (this.logger.isInfoEnabled()) {  
    4.  
    5.         this.logger.info("Pre- instantiating singletons in " + this);  
    6.  
    7.     }  
    8.  
    9.     synchronized (this.beanDefinitionMap) {  
    10.  
    11.         for  (String beanName : this.beanDefinitionNames) {  
    12.  
    13.             RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);  
    14.  
    15.             if (!bd.isAbstract()  && bd.isSingleton()  
    16.  
    17.                 && !bd.isLazyInit()) {  
    18.  
    19.                 if  (isFactoryBean(beanName)) {  
    20.  
    21.                     final FactoryBean factory =  
    22.  
    23.                         (FactoryBean)  getBean(FACTORY_BEAN_PREFIX+ beanName);  
    24.  
    25.                     boolean isEagerInit;  
    26.  
    27.                     if (System.getSecurityManager()  != null  
    28.  
    29.                         &&  ;factory instanceof SmartFactoryBean) {  
    30.  
    31.                         isEagerInit = AccessController.doPrivileged(  
    32.  
    33.                           &nb sp; new PrivilegedAction<Boolean>() {  
    34.  
    35.                           &nb sp; public Boolean run() {  
    36.  
    37.  return ((SmartFactoryBean)  factory).isEagerInit();  
    38.  
    39.                           &nb sp; }  
    40.  
    41.                         }, getAcce ssControlContext());  
    42.  
    43.                     }  
    44.  
    45.                     else {  
    46.  
    47.                         isEagerInit = factory instanceof SmartFactoryBean  
    48.  
    49.                           &nb sp; && ((SmartFactoryBean) factory).isEagerInit();  
    50.  
    51.                     }  
    52.  
    53.                     if (isEagerInit) {  
    54.  
    55.                         getBean (beanName);  
    56.  
    57.                     }  
    58.  
    59.                 }  
    60.  
    61.                 else {  
    62.  
    63.                     getBean(beanName);  
    64.  
    65.                 }  
    66.  
    67.             }  
    68.  
    69.         }  
    70.  
    71.     }  
    72.  
    73. }  
    74.  

    这里出现了一个非常重要的Bean——FactoryBean,可以说Spring一大半的扩展的功能都与这个Bean有关,这是个特殊的Bean他是个工厂Bean,可以产生Bean的Bean,这里的产生Bean是指 Bean的实例,如果一个类继承FactoryBean用户可以自己定义产生实例对象的方法只要实现他的getObject方法。然而在Spring内部这个Bean的实例对象是FactoryBean,通过调用这个对象的getObject方 法就能获取用户自定义产生的对象,从而为Spring提供了很好的扩展性。Spring获取FactoryBean本身的对象是在前面加上&来完成的。

    如何创建Bean的实例对象以及如何构建Bean实例对象之间的关联关系式Spring中的一个核心关键,下面是这个过程的流程图。

    Bean实例创建流程图
    图13.Bean实例创建流程图

    如果是普通的Bean就直接创建他的实例,是通过调用getBean方法。下面是创建Bean实例的时序图:

    Bean实例创建时序图
    图14.Bean实例创建时序图

    还有一个非常重要的部分就是建立Bean对象实例之间的关系,这也是Spring框架的核心竞争力,何时、如何建立他们之间的关系请看下面的时序图:

    Bean对象关系建立
    图15.Bean对象关系建立

    Ioc容器的扩展点

    现在还有一个问题就是如何让这些Bean对象有一定的扩展性,就是可以加入用户的一些操作。那么有哪些扩展点呢?Spring又是如何调用到这些扩展点的?

    对Spring的Ioc容器来说,主要有这么几个。BeanFactoryPostProcessor,BeanPostProcessor。他们分别是在构建BeanFactory和构建Bean对象时调用。还有就是InitializingBean和DisposableBean 他们分别是在Bean实例创建和销毁时被调用。用户可以实现这些接口中定义的方法,Spring就会在适当的时候调用他们。还有一个是FactoryBean他是个特殊的Bean,这个Bean可以被用户更多的控制。

    这些扩展点通常也是我们使用Spring来完成我们特定任务的地方,如何精通Spring就看你有没有掌握好Spring有哪些扩展点,并且如何使用他们,要知道如何使用他们就必须了解他们内在的机理。可 以用下面一个比喻来解释。

    我们把Ioc容器比作一个箱子,这个箱子里有若干个球的模子,可以用这些模子来造很多种不同的球,还有一个造这些球模的机器,这个机器可以产生球模。那么他们的对应关系就是BeanFactory就是 那个造球模的机器,球模就是Bean,而球模造出来的球就是Bean的实例。那前面所说的几个扩展点又在什么地方呢?BeanFactoryPostProcessor对应到当造球模被造出来时,你将有机会可以对其做出设 当的修正,也就是他可以帮你修改球模。而InitializingBean和DisposableBean是在球模造球的开始和结束阶段,你可以完成一些预备和扫尾工作。BeanPostProcessor就可以让你对球模造出来的球做出 适当的修正。最后还有一个FactoryBean,它可是一个神奇的球模。这个球模不是预先就定型了,而是由你来给他确定它的形状,既然你可以确定这个球模型的形状,当然他造出来的球肯定就是你想要的 球了,这样在这个箱子里尼可以发现所有你想要的球

    Ioc容器如何为我所用

    前面的介绍了Spring容器的构建过程,那Spring能为我们做什么,Spring的Ioc容器又能做什么呢?我们使用Spring必须要首先构建Ioc容器,没有它Spring无法工作,ApplicatonContext.xml就是Ioc 容器的默认配置文件,Spring的所有特性功能都是基于这个Ioc容器工作的,比如后面要介绍的AOP。

    Ioc它实际上就是为你构建了一个魔方,Spring为你搭好了骨骼架构,这个魔方到底能变出什么好的东西出来,这必须要有你的参与。那我们怎么参与?这就是前面说的要了解Spring中那有些扩展点 ,我们通过实现那些扩展点来改变Spring的通用行为。至于如何实现扩展点来得到我们想要的个性结果,Spring中有很多例子,其中AOP的实现就是Spring本身实现了其扩展点来达到了它想要的特性功能 ,可以拿来参考。

    Spring中AOP特性详解

    动态代理的实现原理

    要了解Spring的AOP就必须先了解的动态代理的原理,因为AOP就是基于动态代理实现的。动态代理还要从JDK本身说起。

    在Jdk的java.lang.reflect包下有个Proxy类,它正是构造代理类的入口。这个类的结构入下:

    Proxy类结构
    图16.Proxy类结构

    从上图发现最后面四个是公有方法。而最后一个方法newProxyInstance就是创建代理对象的方法。这个方法的源码如下:

    清单6.Proxy.newProxyInstance

         
         
    1. public static Object newProxyInstance(ClassLoader loader,  
    2.  
    3.     Class> [] interfaces,  
    4.  
    5.     InvocationHandler h)  
    6.  
    7.     throws IllegalArgumentException {  
    8.  
    9.      
    10.  
    11.         if (h == null) {  
    12.  
    13.         throw new NullPointerException();  
    14.  
    15.     }  
    16.  
    17.     Class cl = getProxyClass (loader, interfaces);  
    18.  
    19.     try {  
    20.  
    21.         Constructor cons = cl.getConstructor(constructorParams);  
    22.  
    23.         return (Object) cons.newInstance(new Object[]  { h });  
    24.  
    25.     } catch (NoSuchMethodException e) {  
    26.  
    27.         throw new InternalError(e.toString());  
    28.  
    29.     } catch (IllegalAccessException e) {  
    30.  
    31.         throw new InternalError(e.toString());  
    32.  
    33.     } catch (InstantiationException e) {  
    34.  
    35.         throw new InternalError(e.toString());  
    36.  
    37.     } catch (InvocationTargetException e) {  
    38.  
    39.         throw new InternalError(e.toString());  
    40.  
    41.     }  
    42.  
    43. }  
    44.  

    这个方法需要三个参数:ClassLoader,用于加载代理类的Loader类,通常这个Loader和被代理的类是同一个Loader类。Interfaces,是要被代理的那些那些接口。InvocationHandler,就是用于执行 除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到InvocationHandler类中定义的唯一方法invoke中。这在后面再详解。

    下面还是看看Proxy如何产生代理类的过程,他构造出来的代理类到底是什么样子?下面揭晓啦。

    创建代理对象时序图
    图17.创建代理对象时序图

    其实从上图中可以发现正在构造代理类的是在ProxyGenerator的generateProxyClass的方法中。ProxyGenerator类在sun.misc包下,感兴趣的话可以看看他的源码。

    假如有这样一个接口,如下:

    清单7.SimpleProxy类

         
         
    1. public interface SimpleProxy {  
    2.  
    3.    
    4.  
    5.     public void simpleMethod1();  
    6.  
    7.          
    8.  
    9.     public void simpleMethod2();  
    10.  
    11.    
    12.  
    13. }  
    14.  

    代理来生成的类结构如下:

    清单 8.$Proxy2类

         
         
    1. public class $Proxy2 extends java.lang.reflect.Proxy implements SimpleProxy{  
    2.  
    3.     java.lang.reflect.Method m0;  
    4.  
    5.     java.lang.reflect.Method m1;  
    6.  
    7.     java.lang.reflect.Method m2;  
    8.  
    9.     java.lang.reflect.Method m3;  
    10.  
    11.     java.lang.reflect.Method m4;  
    12.  
    13.    
    14.  
    15.     int hashCode();  
    16.  
    17.     boolean equals(java.lang.Object);  
    18.  
    19.     java.lang.String toString();  
    20.  
    21.     void simpleMethod1();  
    22.  
    23.     void simpleMethod2();  
    24.  
    25. }  
    26.  

    这个类中的方法里面将会是调用InvocationHandler的invoke方法,而每个方法也将对应一个属性变量,这个属性变量m也将传给invoke方法中的Method参数。整个代理就是这样实现的。

    SpringAOP如何实现

    从前面代理的原理我们知道,代理的目的是调用目标方法时我们可以转而执行InvocationHandler类的invoke方法,所以如何在InvocationHandler上做文章就是Spring实现Aop的关键所在。

    Spring的Aop实现是遵守Aop联盟的约定。同时Spring又扩展了它,增加了如Pointcut、Advisor等一些接口使得更加灵活。

    下面是Jdk动态代理的类图:

    Jdk动态代理的类图
    图18.Jdk动态代理的类图

    上图清楚的显示了Spring引用了Aop Alliance定义的接口。姑且不讨论Spring如何扩展Aop Alliance,先看看Spring如何实现代理类的,要实现代理类在Spring的配置文件中通常是这样定一个Bean的 ,如下:

    清单9.配置代理类Bean

         
         
    1. <bean id="testBeanSingleton" 
    2.  
    3.     class=value> 
    4.  
    5.     property> 
    6.  
    7.     <property name="target"><"singleton"><value>truevalue>property> 
    8.  
    9.     <property name=<span class="" attribute-"="" style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;">"interceptorNames"> 
    10.  
    11.         <list> 
    12.  
    13.             <value>testInterceptorvalue> 
    14.  
    15.             <value>testInterceptor2value> 
    16.  
    17.         list> 
    18.  
    19.     property> 
    20.  
    21. bean> 
    22.  

    配置上看到要设置被代理的接口,和接口的实现类也就是目标类,以及拦截器也就在执行目标方法之前被调用,这里Spring中定义的各种各样的拦截器,可以选择使用。

    下面看看Spring如何完成了代理以及是如何调用拦截器的。

    前面提到Spring Aop也是实现其自身的扩展点来完成这个特性的,从这个代理类可以看出它正是继承了Factory Bean的ProxyFactoryBean,FactoryBean之所以特别就在它可以让你自定义对象的创建 方法。当然代理对象要通过Proxy类来动态生成。

    下面是Spring创建的代理对象的时序图:

    Spring代理对象的产生
    图19.Spring代理对象的产生

    Spring创建了代理对象后,当你调用目标对象上的方法时,将都会被代理到InvocationHandler类的invoke方法中执行,这在前面已经解释。在这里JdkDynamicAopProxy类实现了InvocationHandler接 口。

    下面再看看Spring是如何调用拦截器的,下面是这个过程的时序图:

    Spring调用拦截器
    图20.Spring调用拦截器

    以上所说的都是Jdk动态代理,Spring还支持一种CGLIB类代理,感兴趣自己看吧。

    Spring中设计模式分析

    Spring中使用的设计模式也很多,比如工厂模式、单例模式、模版模式等,在《Webx框架的系统架构与设计模式》、《Tomcat的系统架构与模式设计分析》已经有介绍,这里就不赘述了。这里主要介 绍代理模式和策略模式。

    代理模式

    代理模式原理

    代理模式就是给某一个对象创建一个代理对象,而由这个代理对象控制对原对象的引用,而创建这个代理对象就是可以在调用原对象是可以增加一些额外的操作。下面是代理模式的结构:

    代理模式的结构
    图21.代理模式的结构

    Subject:抽象主题,它是代理对象的真实对象要实现的接口,当然这可以是多个接口组成。

    ProxySubject:代理类除了实现抽象主题定义的接口外,还必须持有所代理对象的引用

    RealSubject:被代理的类,是目标对象。

    Spring中如何实现代理模式

    Spring Aop中Jdk动态代理就是利用代理模式技术实现的。在Spring中除了实现被代理对象的接口外,还会有org.springframework.aop.SpringProxy和org.springframework.aop.framework.Advised 两个接口。Spring中使用代理模式的结构图如下:

    Spring中使用代理模式的结构图
    图22.Spring中使用代理模式的结构图

    $Proxy就是创建的代理对象,而Subject是抽象主题,代理对象是通过InvocationHandler来持有对目标对象的引用的。

    Spring中一个真实的代理对象结构如下:

    清单10代理对象$Proxy4

         
         
    1. public class $Proxy4 extends java.lang.reflect.Proxy implements  
    2.  
    3.     org.springframework.aop.framework.PrototypeTargetTests$TestBean  
    4.  
    5.         org.springframework.aop.SpringProxy  
    6.  
    7.         org.springframework.aop.framework.Advised  
    8.  
    9. {  
    10.  
    11.     java.lang.reflect.Method m16;  
    12.  
    13.     java.lang.reflect.Method m9;  
    14.  
    15.     java.lang.reflect.Method m25;  
    16.  
    17.     java.lang.reflect.Method m5;  
    18.  
    19.     java.lang.reflect.Method m2;  
    20.  
    21.     java.lang.reflect.Method m23;  
    22.  
    23.     java.lang.reflect.Method m18;  
    24.  
    25.     java.lang.reflect.Method m26;  
    26.  
    27.     java.lang.reflect.Method m6;  
    28.  
    29.     java.lang.reflect.Method m28;  
    30.  
    31.     java.lang.reflect.Method m14;  
    32.  
    33.     java.lang.reflect.Method m12;  
    34.  
    35.     java.lang.reflect.Method m27;  
    36.  
    37.     java.lang.reflect.Method m11;  
    38.  
    39.     java.lang.reflect.Method m22;  
    40.  
    41.     java.lang.reflect.Method m3;  
    42.  
    43.     java.lang.reflect.Method m8;  
    44.  
    45.     java.lang.reflect.Method m4;  
    46.  
    47.     java.lang.reflect.Method m19;  
    48.  
    49.     java.lang.reflect.Method m7;  
    50.  
    51.     java.lang.reflect.Method m15;  
    52.  
    53.     java.lang.reflect.Method m20;  
    54.  
    55.     java.lang.reflect.Method m10;  
    56.  
    57.     java.lang.reflect.Method m1;  
    58.  
    59.     java.lang.reflect.Method m17;  
    60.  
    61.     java.lang.reflect.Method m21;  
    62.  
    63.     java.lang.reflect.Method m0;  
    64.  
    65.     java.lang.reflect.Method m13;  
    66.  
    67.     java.lang.reflect.Method m24;  
    68.  
    69.    
    70.  
    71.     int hashCode();  
    72.  
    73.     int indexOf(org.springframework.aop.Advisor);  
    74.  
    75.     int indexOf(org.aopalliance.aop.Advice);  
    76.  
    77.     boolean equals(java.lang.Object);  
    78.  
    79.     java.lang.String toString();  
    80.  
    81.     void sayhello();  
    82.  
    83.     void doSomething();  
    84.  
    85.     void doSomething2();  
    86.  
    87.     java.lang.Class getProxiedInterfaces();  
    88.  
    89.     java.lang.Class getTargetClass();  
    90.  
    91.     boolean isProxyTargetClass();  
    92.  
    93.     org.springframework.aop.Advisor; getAdvisors();  
    94.  
    95.     void addAdvisor(int, org.springframework.aop.Advisor)  
    96.  
    97.                throws org.springframework.aop.framework.AopConfigException;  
    98.  
    99.     void addAdvisor(org.springframework.aop.Advisor)  
    100.  
    101.                throws org.springframework.aop.framework.AopConfigException;  
    102.  
    103.     void setTargetSource(org.springframework.aop.TargetSource);  
    104.  
    105.     org.springframework.aop.TargetSource getTargetSource();  
    106.  
    107.     void setPreFiltered(boolean);  
    108.  
    109.     boolean isPreFiltered();  
    110.  
    111.     boolean isInterfaceProxied(java.lang.Class);  
    112.  
    113.     boolean removeAdvisor(org.springframework.aop.Advisor);  
    114.  
    115.     void removeAdvisor(int)throws org.springframework.aop.framework.AopConfigException;  
    116.  
    117.     boolean replaceAdvisor(org.springframework.aop.Advisor,  
    118.  
    119.                org.springframework.aop.Advisor)  
    120.  
    121.                throws org.springframework.aop.framework.AopConfigException;  
    122.  
    123.     void addAdvice(org.aopalliance.aop.Advice)  
    124.  
    125.                throws org.springframework.aop.framework.AopConfigException;  
    126.  
    127.     void addAdvice(int, org.aopalliance.aop.Advice)  
    128.  
    129.                throws org.springframework.aop.framework.AopConfigException;  
    130.  
    131.     boolean removeAdvice(org.aopalliance.aop.Advice);  
    132.  
    133.     java.lang.String toProxyConfigString();  
    134.  
    135.     boolean isFrozen();  
    136.  
    137.     void setExposeProxy(boolean);  
    138.  
    139.     boolean isExposeProxy();  
    140.  
    141. }  
    142.  

    策略模式

    策略模式原理

    策略模式顾名思义就是做某事的策略,这在编程上通常是指完成某个操作可能有多种方法,这些方法各有千秋,可能有不同的适应的场合,然而这些操作方法都有可能用到。各一个操作方法都当作一 个实现策略,使用者可能根据需要选择合适的策略。

    下面是策略模式的结构:

    策略模式的结构
    图23.策略模式的结构

    Context:使用不同策略的环境,它可以根据自身的条件选择不同的策略实现类来完成所要的操作。它持有一个策略实例的引用。创建具体策略对象的方法也可以由他完成。

    ◆Strategy:抽象策略,定义每个策略都要实现的策略方法

    ◆ConcreteStrategy:具体策略实现类,实现抽象策略中定义的策略方法

    ◆Spring中策略模式的实现

    ◆Spring中策略模式使用有多个地方,如Bean定义对象的创建以及代理对象的创建等。这里主要看一下代理对象创建的策略模式的实现。

    前面已经了解Spring的代理方式有两个Jdk动态代理和CGLIB代理。这两个代理方式的使用正是使用了策略模式。它的结构图如下所示:

    Spring中策略模式结构图
    图24.Spring中策略模式结构图

    在上面结构图中与标准的策略模式结构稍微有点不同,这里抽象策略是AopProxy接口,Cglib2AopProxy和JdkDynamicAopProxy分别代表两种策略的实现方式,ProxyFactoryBean就是代表Context角色 ,它根据条件选择使用Jdk代理方式还是CGLIB方式,而另外三个类主要是来负责创建具体策略对象,ProxyFactoryBean是通过依赖的方法来关联具体策略对象的,它是通过调用策略对象的getProxy (ClassLoaderclassLoader)方法来完成操作。

    总结

    本文通过从Spring的几个核心组件入手,试图找出构建Spring框架的骨骼架构,进而分析Spring在设计的一些设计理念,是否从中找出一些好的设计思想,对我们以后程序设计能提供一些思路。接着 再详细分析了Spring中是如何实现这些理念的,以及在设计模式上是如何使用的。

    通过分析Spring给我一个很大的启示就是其这套设计理念其实对我们有很强的借鉴意义,它通过抽象复杂多变的对象,进一步做规范,然后根据它定义的这套规范设计出一个容器,容器中构建它们的 复杂关系,其实现在有很多情况都可以用这种类似的处理方法。

    虽然我很想把我对Spring的想法完全阐述清楚,但是所谓“书不尽言,言不尽意。”,有什么不对或者不清楚的地方大家还是看看其源码吧。

    关于作者:

    许令波,现就职于淘宝网,是一名 Java 开发工程师。对大型互联网架构设计颇感兴趣,并对一些开源框架也有比较深入的研究。

    【编辑推荐】

    1. Java持久化框架 DataNucleus 2.1发布
    2. 浅谈Spring框架中的JDBC应用
    3. Spring框架的7个模块
    4. 详细介绍Spring框架
    5. 将Flex与Spring框架集成
    相关文章
    相关标签/搜索
    每日一句
      每一个你不满意的现在,都有一个你没有努力的曾经。
    公众号推荐
       一个历史类的公众号,欢迎关注
    一两拨千金
    香港最快現场开奖结果118图库天下彩天空彩免费大全香港蓝月亮精选资料六合宝典天天彩票新版 泾川县| 营山县| 洛川县| 台南县| 乐清市| 高碑店市| 枣阳市| 容城县| 金沙县| 察雅县| 上虞市| 巧家县| 社会| 沙湾县| 惠来县| 平乡县| 辽中县| 四子王旗| 崇州市| 敦煌市| 天峨县| 吕梁市| 若羌县| 阿鲁科尔沁旗| 工布江达县| 上杭县| 玉田县| 会同县| 克什克腾旗| 永顺县| 平安县| 武乡县| 辰溪县| 南木林县| 彭水| 万山特区| 信宜市| 仪陇县| 田林县| 微山县| 廊坊市| 锦屏县| 桓台县| 萝北县| 东丰县| 于田县| 额尔古纳市| 永安市| 叶城县| 隆昌县| 南涧| 阿图什市| 九龙县| 辽中县| 上蔡县| 武鸣县| 宣威市| 民勤县| 晋州市| 吴桥县| 馆陶县| 日土县| 东安县| 定州市| 德州市| 和田市| 民权县| 泾源县| 永嘉县| 揭西县| 酒泉市| 石家庄市| 特克斯县| 宜宾县| 扶绥县| 从化市| 秭归县| 开封县| 五大连池市| 宁波市| 峨山| 乌拉特中旗| 黄骅市| 新津县| 丰宁| 米泉市| 德清县| 铜川市| 顺平县| 濉溪县| 明溪县| 淮南市| 泉州市| 日照市| 项城市| 南澳县| 宜阳县| 峡江县| 石家庄市| 凭祥市| 鄂伦春自治旗| 民和| 甘肃省| 永兴县| 商都县| 驻马店市| 仁怀市| 津市市| 郧西县| 偏关县| 沭阳县| 日照市| 仁化县| 梧州市| 九龙坡区| 偃师市| 连城县| 原平市| 镇原县| 那坡县| 百色市| 安岳县| 长兴县| 安塞县| 武义县| 金沙县| 来安县| 西充县| 克拉玛依市| 江永县| 资中县| 高陵县| 积石山| 江孜县| 甘泉县| 新宾| 犍为县| 潮州市| 茂名市| 保康县| 七台河市| 墨脱县| 长葛市| 原平市| 景泰县| 蓝山县| 博野县| 峡江县| 武定县| 台安县| 上思县| 固镇县| 安阳市| 陆丰市| 水富县| 龙里县| 天柱县| 呼图壁县| 瓮安县| 师宗县| 电白县| 田阳县| 南昌市| 阿坝| 星子县| 彭阳县| 瑞金市| 大姚县| 湖口县| 岐山县| 新安县| 彩票| 沁水县| 余江县| 娄底市| 灵川县| 甘洛县| 若羌县| 湟源县| 皮山县| 麟游县| 西安市| 隆昌县| 东明县| 吉林市| 柯坪县| 宜章县| 望都县| 辰溪县| 古田县| 昌吉市| 阜南县| 营山县| 图片| 裕民县| 广灵县| 石首市| 墨玉县| 涞水县| 沧州市| 三河市| 柏乡县| 嘉义市| 商南县| 贵港市| 鄂尔多斯市| 平谷区| 孟村| 瓦房店市| 宜春市| 湟中县| 太和县| 云龙县| 阿荣旗| 泗阳县| 清丰县| 麻城市| 色达县| 大竹县| 梧州市| 成都市| 黎川县| 新昌县| 宽甸| 汝城县| 桐乡市| 班玛县| 宁远县| 巴彦淖尔市| 渭南市| 南丰县| 呼图壁县| 鄂伦春自治旗| 兴业县| 乌鲁木齐县| 莱州市| 拉萨市| 都兰县| 闵行区| 高碑店市| 游戏| 南充市| 湖南省| 玉环县| 宝山区| 香格里拉县| 正蓝旗| 布尔津县| 宣恩县| 建湖县| 洛浦县| 松溪县| 澄城县| 磐安县| 白河县| 洪洞县| 米易县| 五峰| 金平| 镇安县| 右玉县| 同江市| 栖霞市| 义乌市| 南靖县| 云安县| 连州市| 卫辉市| 龙泉市| 土默特右旗| 兰西县| 平武县| 文登市| 娱乐| 鄂伦春自治旗| 称多县| 高安市| 拉萨市| 安义县| 常德市| 泸水县| 温泉县| 河津市| 徐汇区| 南汇区| 广德县| 泰顺县| 建昌县| 秦安县| 石渠县| 淄博市| 肃宁县| 遂宁市| 普陀区| 永年县| 遂宁市| 青河县| 通州市| 岑巩县| 巴林右旗| 济阳县| 萨迦县| 曲水县| 墨竹工卡县| 左权县| 宁安市| 公安县| 安仁县| 东源县| 安义县| 井陉县| 嘉黎县| 汕尾市| 铜鼓县| 河北区| 紫金县| 扬中市| 习水县| 乾安县| 汤阴县| 天全县| 彭水| 常州市| 封丘县| 新龙县| 桑日县| 阿克陶县| 靖边县| 久治县| 镇沅| 清涧县| 玛多县| 长治市| 武川县| 固阳县| 枣强县| 山丹县| 临颍县| 大名县| 渝中区| 巢湖市| 金平| 九台市| 江孜县| 柳林县| 肥城市| 宜君县| 涞源县| 兴国县| 马山县| 纳雍县| 屏山县| 巴中市| 金阳县| 雷山县| 习水县| 广宁县| 武穴市| 神池县| 灌阳县| 永泰县| 固阳县| 都江堰市| 介休市| 郸城县| 湾仔区| 保定市| 尉氏县| 贡觉县| 崇文区| 宜兴市| 武夷山市| 洮南市| 长乐市| 鄱阳县| 南丰县| 平和县| 虹口区| 宁河县| 连山| 定陶县| 安新县| 金沙县| 日照市| 昌图县| 礼泉县| 阜阳市| 阳春市| 平顺县| 邵东县| 论坛| 喀喇沁旗| 德清县| 通许县| 衢州市| 梁山县| 隆回县| 广宁县| 新丰县| 汝阳县| 花垣县| 安仁县| 卢氏县| 临清市| 金山区| 永宁县| 开平市| 北宁市| 自贡市| 红桥区| 上犹县| 石林| 洮南市| 平潭县| 永福县| 利辛县| 怀来县| 墨玉县| 深水埗区| 德保县| 黔西县| 尚义县| 宁晋县| 海淀区| 南郑县| 博爱县| 调兵山市| 冀州市| 陇南市| 靖江市| 沙湾县| 诏安县| 白银市| 方山县| 鹤壁市| 开阳县| 娱乐| 张家口市| 额济纳旗| 无为县| 都昌县| 余姚市| 涡阳县| 高安市| 青田县| 丽水市| 庆安县| 梅河口市| 通辽市| 台中市| 浏阳市| 澎湖县| 乐昌市| 晋中市| 增城市| 邮箱| 逊克县| 仁寿县| 高雄县| 肥西县| 广汉市| 留坝县| 武安市| 安达市| 宁国市| 平原县| 巴塘县| 渝北区| 分宜县| 漳浦县| 巴彦淖尔市| 凌海市| 西吉县| 龙州县| 临汾市| 紫阳县| 随州市| 饶阳县| 剑阁县| 怀安县| 伊川县| 永善县| 博乐市| 宁城县| 兴化市| 江山市| 海盐县| 德州市| 临邑县| 高安市| 溧水县| 繁峙县| 德惠市| 吴忠市| 儋州市| 青冈县| 安宁市| 太湖县| 广河县| 东阿县| 泽普县| 沅江市| 尼玛县| 唐山市| 黄山市| 栾川县| 隆昌县| 天气| 西乌珠穆沁旗| 兰考县| 兴宁市| 德保县| 洪湖市| 双鸭山市| 沙河市| 黄陵县| 楚雄市| 吉安县| 晋州市| 九龙县| 青阳县| 改则县| 北宁市| 达州市| 岢岚县| 吉首市| 密云县| 安徽省| 陇川县| 社会| 柳林县| 灵山县| 扶绥县| 宜宾市| 宣汉县| 营山县| 怀集县| 吉首市| 巴塘县| 浏阳市| 龙井市| 乡宁县| 乡城县| 东乌珠穆沁旗| 宽甸| 鸡西市| 冷水江市| 正定县| 兖州市| 洪雅县| 青海省| 佛学| 新晃| 德令哈市| 临西县| 张家口市| 新疆| 开化县| 胶南市| 宜阳县| 东平县| 宁国市| 安达市| 铜山县| 旅游| 马尔康县| 独山县| 余江县| 外汇| 蒙阴县| 顺平县| 榕江县| 青浦区| 泸溪县| 大荔县| 万荣县| 青州市| 乐至县| 沧源| 海兴县| 山阴县| 昌平区| 历史| 若羌县| 乌兰县| 阿拉尔市| 临泽县| 剑川县| 湘潭县| 金山区| 桃园县| 老河口市| 孝昌县| 阜平县| 衡阳县| 天气| 静安区| 普安县| 神木县| 宣化县| 乐陵市| 凤凰县| 乌什县| 锦州市| 黄浦区| 青阳县| 郴州市| 县级市| 衡东县| 泗洪县| 连城县| 普兰店市| 定兴县| 独山县| 余干县| http://wap.oiswja.fit http://www.tmmvia.fit http://m.butwmb.fit http://wap.qdcmqf.fit http://www.hviunj.fit http://wap.bm1961xusclez.fit http://m.pennfb.fit http://m.rbvauu.fit http://lhxtyn.fit http://www.rqxxxw.fit http://www.lozioa.fit http://wap.hkcvae.fit http://www.bm1961xailz.fit http://m.kfhykq.fit http://wap.rhkypl.fit http://www.ckkbsx.fit http://www.dufbjv.fit http://wap.bm1961xinisterz.fit