Spring

来源:互联网 发布:软件配置管理规定 编辑:程序博客网 时间:2024/05/02 21:59

3.1 Spring

3.1.1Ioc容器的实现

1、Ioc容器和依赖反转模式

控制反转:依赖对象的获得被反转了。有的人称之为依赖注入。我理解的是反转和注入其实是两个过程。许多应用程序是两个或者更多的类通过彼此组合彼此依赖才能实现业务逻辑的,使得每个对象都需要拥有与它所依赖的对象的引用。如果这个对象的引用获取靠自己实现,这将导致所写代码高度耦合而且很难测试。有了Ioc容器后这些依赖关系就可以交给框架或Ioc容器来完成。

在Spring中,Ioc容器称为完成这个模式的载体,它的功能是在对象生成或者在初始化时侯直接将数据注入到对象实体中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。

2、Spirng Ioc容器的设计

BeanFactory在Spring中是提供的是最基本的Ioc容器的功能,就像面向接口编程一样。BeanFactory接口定义了Ioc容器基本的形式,并没有给出容器具体的实现。具体的实现类有DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等。

通常在写Spring文件时,利用XML配置文件来实现控制反转的配置,在XML文件中定义一个<bean  id=””/>就可以完成将对象交给Ioc容器管理。Ioc容器把这个bean配置转换为一个BeanDefinition对象,并把它放在一个map中,准确的说是一个ConcurrentHashMap。

构造XmlBeanFactory这个Ioc容器时,需要指定BeanDefinition的信息来源,也就是告诉框架去哪读配置文件。而这个信息封装成了一个类,由Spring中的Resource类来给出。Resource是Spring用来封装I/O操作的类。比如,我们的BeanDefinition信息是以XML文件形式存在的,ClassPathResource res = new ClassPathResource(“bean.xml”)。然后将这个Resource作为构造参数传递给XmlBeanFactory构造函数。

 

以XmlBeanFactory为例,在使用Ioc容器时,需要如下几个步骤:

1)    创建Ioc配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息。

2)    创建一个BeanFactory,这里使用DefaultListableBeanFactory。

3)    创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory

4)    从定义好的资源位置读取配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的Ioc容器就建立起来了。

在Spring中,系统已经为用户提供了许多已经定义好的容器实现,我们常用的ApplicationContext除了提供前面提到容器基本功能外,还为用户提供了附加服务。比如,支持不同的信息源、访问资源、支持应用事件。

 

3、ApplicationContext容器的设计原理

以FileSystemXmlApplicationContext的实现为例。

Ioc容器的初始化通过refresh()这个方法启动,这个启动包括BeanDefinition的Resource定位、载入和注册。

1)    Resource定位:统一接口Resource完成。以FileySystemResource为例:

protected ResourcegetResourceByPath(String path) {

              If(path!= null && path.startsWith(“/”)){

                     path= path.substring(1);

}

return new FileSystemResource(path);

}

2)    BeanDefinition载入:把用户定义好的Bean表示成Ioc容器内部的数据结构。具体来说这个BeanDefinition实际上就是POJO对象在Ioc容器中的抽象。Ioc容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。

这里得到Resource后,通过BeanDefinitionReader来完成读取。这里以XML为例,所以具体使用XmlBeanDefinitionReader来载入BeanDefinition到容器中。内部得到XML的文件对象也就是Document对象后,就可以按照Spring的Bean定义规则来对这个XML的文档树进行解析了。其中把id、name、aliase等属性读取出来后,设置到生成的BeanDefinitionHolder中去。

3)    注册BeanDefinition:在Ioc容器内部将BeanDefinition注入到一个HashMap中。Ioc容器就是通过这个HashMap来持有这些BeanDefinition数据。private finalMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

需要注意:通常初始化过程不包括Bean的依赖注入的实现,可以理解为这时只是将控制反转了。而Bean定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。我们知道Bean有一个lazyinit属性可以设置Bean的依赖注入过程,当lazyinit为false时bean的载入和依赖注入在Ioc容器初始化时就预先完成了,而不需等到整个初始化完成以后,第一次使用getBean时才会触发。

 

4、Ioc容器的依赖注入

依赖注入的过程通常是用户第一次向Ioc容器索要Bean时触发的。在依赖注入之前我们已经完成了初始化过程,也就是容器中的BeanDefinition数据已经建立好了。尽管可以用最简单的方式来描述Ioc容器,将它视为一个HashMap,但只能说这个HashMap是容器的最基本的数据结构,而不是Ioc容器的全部。重点来说,getBean是依赖注入的起点,之后会调用createBean,在这个过程中,Bean对象会依据BeanDefinition定义的要求生成。生成了Bean所包含的java对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些生成方式都是由相关的BeanDefinition来指定的。根据类是否实现接口(clazz.isInterface())来判断使用JVM的反射(动态代理)还是使用CGLIB对Bean进行实例化。CGLIB是一个常用的字节码生成器的类库,它提供了一系列的API来提供生成和转换java的字节码功能。生成对象以及对应的property之后,取得注入属性的set方法,通过反射机制,把对象注入进去。这时可以通过getBean来获取Bean,这些Bean不是简单的java对象,而是已经包含了对象之间依赖关系的Bean,尽管这些依赖注入的过程对用户来说是不可见的。

3.1.2SpringAOP的简述

AOP是Aspect-Oriented Programming(面向切面编程)的简称。Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点。从关注点中分离出横切关注点是面向切面的程序设计的核心概念。分离关注点使解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好的管理起来。在AOP联盟的网站上有以下AOP技术:AspectJ、AspectWerkz、JBoss-AOP、BCEL、Javassit等。在SpringAOP中,使用的是java本身的语言特性,如javaproxy代理类、拦截器等技术,来完成AOP编织的实现。

1、Advice通知

Advice定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,它主要描述SpringAOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。在SpringAOP的实现中,使用了这个统一接口,并通过这个接口,为AOP切面增强的织入功能做了更多的细化和拓展,比如提供了更具体的通知类型,如BeforeAdvice、AfterAdvice、ThrowsAdvice等。

2、Pointcut切点

Pointcut决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut来定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。在这种情况下,Pointcut通常意味着标识方法,例如,这些需要增强的地方可以由某个正则表达式进行标识,或者根据某个方法名进行匹配等。

3、Advisor通知器

完成对目标方法的切面增强设计(Advice)和关注点设计(Pointcut)以后,需要一个对象把它们结合起来,完成这个作用的就是Advisor。通过Advisor,可以定义应该使用哪个通知并在哪个关注点使用它,也就是说通过Advisor,把Advice和Pointcut结合起来。

3.1.2Spring AOP的设计与原理

在Spring AOP的实现中,使用的核心技术是动态代理,而这种动态代理实际上是JDK的一个特性。

在Proxy的调用过程中,如果客户(Client)调用Proxy的request方法,会在调用目标对象的request方法前后调用一系列的处理,而这一系列的处理相对于目标对象来说是透明的。

AopProxy代理对象可以由JDK或CGLIB来生成,而JdkDynamicAopProxy和Cglib2AopProxy实现都是通过AopProxy接口。

在JDK的实现中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class)。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

publicInterface InvocationHandler {

       pubic Object invoke(Object proxy, Methodmethod, Object[] args) throws Throwable;

}

这个invoke方法的第一个参数是代理对象实例,第二个参数是Method方法对象,代表的是当前Proxy被调用的方法,最后一个参数是被调用的方法中的参数。只要在实现通过调用Proxy.newIntance方法生成具体Proxy对象时把InvocationHandler设置到参数里就可以了,剩下的由java虚拟机完成。

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是newProxyInstance方法

public static Object newProxyInstance(ClassLoaderloader, Class<?>[] interfaces,InvocationHandler h) throwsIllegalArgumentException

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载

interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了。

InvocationHandler:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

我们给这个代理对象提供了一组什么接口,那么我这个代理对象就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个。

通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

可以发现利用JVM的动态代理有一个限制,那就是目标对象必须实现接口,生成的代理对象如下:

public class $proxy1 extends Proxy implements interfaces

 

所以在Spring AOP的实现中,默认方式是使用JDK来产生AopProxy代理对象。但是如果遇到配置的目标对象不是接口类的实现,会使用CGLIB来产生AopProxy代理对象。CGLIB通过生成目标类的子类来实现增强手段。

在得到了AopProxy代理对象后,在代理的接口方法被调用执行的时候,也就是当AopProxy暴露代理的方法被调用的时候,前面定义的Proxy机制就起作用了。当Proxy对象暴露的方法被调用时,并不是直接运行目标对象的调用方法,而是根据Proxy的定义,改变原有的目标对象方法调用的运行轨迹。这种改变体现在,首先会触发对这些方法调用进行拦截,这些拦截为对目标调用的功能增强提供了工作空间。具体来说,在回调过程中,首先会根据配置来对拦截器是否与当前的调用方法相匹配进行判断。这个过程是一个遍历的过程,它会遍历在Proxy代理对象中设置的拦截器会被逐一调用,直到整个拦截器的调用完成为止。在拦截器的调用过程中,实际上已经封装了Spring对AOP的实现。

0 0
原创粉丝点击