Spring 框架的设计理念与设计模式分析(2)

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

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

  清单 4. AbstractApplicationContext.finishBeanFactoryInitialization

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> protected void finishBeanFactoryInitialization(
    ConfigurableListableBeanFactory beanFactory) {

    
// Stop using the temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader( null );

    
// Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();

    
// Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}

 

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

  清单 5. DefaultListableBeanFactory.preInstantiateSingletons

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public void preInstantiateSingletons() throws BeansException {
    
if ( this .logger.isInfoEnabled()) {
        
this .logger.info( " Pre-instantiating singletons in " + this );
    }
    
synchronized ( this .beanDefinitionMap) {
        
for (String beanName : this .beanDefinitionNames) {
            RootBeanDefinition bd
= getMergedLocalBeanDefinition(beanName);
            
if ( ! bd.isAbstract() && bd.isSingleton()
                
&& ! bd.isLazyInit()) {
                
if (isFactoryBean(beanName)) {
                    
final FactoryBean factory =
                        (FactoryBean) getBean(FACTORY_BEAN_PREFIX
+ beanName);
                    
boolean isEagerInit;
                    
if (System.getSecurityManager() != null
                        
&& factory instanceof SmartFactoryBean) {
                        isEagerInit
= AccessController.doPrivileged(
                            
new PrivilegedAction < Boolean > () {
                            
public Boolean run() {
                                
return ((SmartFactoryBean) factory).isEagerInit();
                            }
                        }, getAccessControlContext());
                    }
                    
else {
                        isEagerInit
= factory instanceof SmartFactoryBean
                            
&& ((SmartFactoryBean) factory).isEagerInit();
                    }
                    
if (isEagerInit) {
                        getBean(beanName);
                    }
                }
                
else {
                    getBean(beanName);
                }
            }
        }
    }
}

 

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

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

  图 13.Bean 实例创建流程图

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

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

  图 14.Bean 实例创建时序图

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

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

  图 15.Bean 对象关系建立

 如何创建Bean实例并构建 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 类,它正是构造代理类的入口。这个类的结构入下:

  图 16. Proxy 类结构

 动态代理的实现原理

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

  清单 6. Proxy. newProxyInstance

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public static Object newProxyInstance(ClassLoader loader,
    Class
<?> [] interfaces,
    InvocationHandler h)
    
throws IllegalArgumentException {
    
    
if (h == null ) {
        
throw new NullPointerException();
    }
    Class cl
= getProxyClass(loader, interfaces);
    
try {
        Constructor cons
= cl.getConstructor(constructorParams);
        
return (Object) cons.newInstance( new Object[] { h });
    }
catch (NoSuchMethodException e) {
        
throw new InternalError(e.toString());
    }
catch (IllegalAccessException e) {
        
throw new InternalError(e.toString());
    }
catch (InstantiationException e) {
        
throw new InternalError(e.toString());
    }
catch (InvocationTargetException e) {
        
throw new InternalError(e.toString());
    }
}

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

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

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

 动态代理的实现原理

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

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

  清单 7. SimpleProxy 类

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public interface SimpleProxy {
    
public void simpleMethod1();    
    
public void simpleMethod2();
}

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

  清单 8. $Proxy2 类

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public class $Proxy2 extends java.lang.reflect.Proxy implements SimpleProxy{
    java.lang.reflect.Method m0;
    java.lang.reflect.Method m1;
    java.lang.reflect.Method m2;
    java.lang.reflect.Method m3;
    java.lang.reflect.Method m4;

    
int hashCode();
    
boolean equals(java.lang.Object);
    java.lang.String toString();
    
void simpleMethod1();
    
void simpleMethod2();
}

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

 

  Spring AOP 如何实现

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

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

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

  图 18. Jdk 动态代理的类图

 Spring AOP 的实现

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

  清单 9. 配置代理类 Bean

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> < bean id = " testBeanSingleton "
    
class = " org.springframework.aop.framework.ProxyFactoryBean " >
    
< property name = " proxyInterfaces " >
        
< value >
            org.springframework.aop.framework.PrototypeTargetTests$TestBean
        
</ value >
    
</ property >
    
< property name = " target " >< ref local = " testBeanTarget " ></ ref > </ property >
    
< property name = " singleton " >< value > true </ value ></ property >
    
< property name = " interceptorNames " >
        
< list >
            
< value > testInterceptor </ value >
            
< value > testInterceptor2 </ value >
        
</ list >
    
</ property >
</ bean >

 

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

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

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

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

  图 19.Spring 代理对象的产生

 Spring AOP 的实现

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

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

  图 20.Spring 调用拦截器

 Spring AOP 的实现

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

 

  Spring 中设计模式分析

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

  代理模式

  代理模式原理

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

  图 21. 代理模式的结构

 Spring 中设计模式分析

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

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

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

  Spring 中如何实现代理模式

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

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

 Spring 中设计模式分析

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

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

  清单 10 代理对象 $Proxy4

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--> public class $Proxy4 extends java.lang.reflect.Proxy implements
    org.springframework.aop.framework.PrototypeTargetTests$TestBean
    org.springframework.aop.SpringProxy
    org.springframework.aop.framework.Advised
{
    java.lang.reflect.Method m16;
    java.lang.reflect.Method m9;
    java.lang.reflect.Method m25;
    java.lang.reflect.Method m5;
    java.lang.reflect.Method m2;
    java.lang.reflect.Method m23;
    java.lang.reflect.Method m18;
    java.lang.reflect.Method m26;
    java.lang.reflect.Method m6;
    java.lang.reflect.Method m28;
    java.lang.reflect.Method m14;
    java.lang.reflect.Method m12;
    java.lang.reflect.Method m27;
    java.lang.reflect.Method m11;
    java.lang.reflect.Method m22;
    java.lang.reflect.Method m3;
    java.lang.reflect.Method m8;
    java.lang.reflect.Method m4;
    java.lang.reflect.Method m19;
    java.lang.reflect.Method m7;
    java.lang.reflect.Method m15;
    java.lang.reflect.Method m20;
    java.lang.reflect.Method m10;
    java.lang.reflect.Method m1;
    java.lang.reflect.Method m17;
    java.lang.reflect.Method m21;
    java.lang.reflect.Method m0;
    java.lang.reflect.Method m13;
    java.lang.reflect.Method m24;

    
int hashCode();
    
int indexOf(org.springframework.aop.Advisor);
    
int indexOf(org.aopalliance.aop.Advice);
    
boolean equals(java.lang.Object);
    java.lang.String toString();
    
void sayhello();
    
void doSomething();
    
void doSomething2();
    java.lang.Class getProxiedInterfaces();
    java.lang.Class getTargetClass();
    
boolean isProxyTargetClass();
    org.springframework.aop.Advisor; getAdvisors();
    
void addAdvisor( int , org.springframework.aop.Advisor)
        
throws org.springframework.aop.framework.AopConfigException;
    
void addAdvisor(org.springframework.aop.Advisor)
        
throws org.springframework.aop.framework.AopConfigException;
    
void setTargetSource(org.springframework.aop.TargetSource);
    org.springframework.aop.TargetSource getTargetSource();
    
void setPreFiltered( boolean );
    
boolean isPreFiltered();
    
boolean isInterfaceProxied(java.lang.Class);
    
boolean removeAdvisor(org.springframework.aop.Advisor);
    
void removeAdvisor( int ) throws org.springframework.aop.framework.AopConfigException;
    
boolean replaceAdvisor(org.springframework.aop.Advisor,
        org.springframework.aop.Advisor)
        
throws org.springframework.aop.framework.AopConfigException;
    
void addAdvice(org.aopalliance.aop.Advice)
        
throws org.springframework.aop.framework.AopConfigException;
    
void addAdvice( int , org.aopalliance.aop.Advice)
        
throws org.springframework.aop.framework.AopConfigException;
    
boolean removeAdvice(org.aopalliance.aop.Advice);
    java.lang.String toProxyConfigString();
    
boolean isFrozen();
    
void setExposeProxy( boolean );
    
boolean isExposeProxy();
}

 

  策略模式

  策略模式原理

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

  下面是策略模式的结构:

  图 23. 策略模式的结构

 策略模式

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

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

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

  Spring 中策略模式的实现

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

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

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

 策略模式

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

  总结

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值