Spring IOC与AOP

1.IOC原理

  IOC(Inversion of Control)控制反转。html

  伴随着工业级应用的规模愈来愈庞大,对象之间的依赖关系也愈来愈复杂,常常会出现对象之间的多重依赖性关系,所以,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度太高的系统,必然会出现牵一发而动全身的情形。java

  IOC即借助于“第三方”(IOC容器)实现具备依赖关系的对象之间的解耦。spring

  IOC模式,系统中经过引入实现了IOC模式的IOC容器,便可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特色就是经过文本的配置文件进行应用程序组件间相互关系的配置,而不用从新修改并编译具体的代码。编程

  能够把IoC模式看作是工厂模式的升华,能够把IoC看做是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,而后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把之前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这二者独立分隔开来,目的就是提升灵活性和可维护性。缓存

  IoC中最基本的Java技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是 根据给出的类名(字符串)来生成对象。这种编程方式可让对象在 生成时才决定要生成哪种对象。反射的应用是很普遍的,象Hibernate、Spring中都是用“反射”作为最基本的技术手段。
  在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为何反射技术没有普通应用开来的缘由。但经SUN改良优化后,反射方式生成对象和一般对象生成方式,速度已经相差不大了(但依然有 一倍以上的差距)。

2.Spring IOC实现

  Resource定位过程:使用ClassPathResource来定位,寻找以文件形式存在的BeanDefinition信息。安全

  BeanDefinition的载入:至关于把定义的BeanDefinition在IOC容器中转化成一个Spring内部表示的数据结构的过程。数据结构

  BeanDefinition的解析:按照Spring的Bean定义规则来对这个XML的文档树进行解析。先调用XML的解析器DOM4J进行解析获得ducument对象,而后才按Spring规则进行解析。架构

  BeanDefinition的注册:为IOC容器提供了更友好的使用方式,是经过一个HashMap来持有载入的BeanDefinition的。在Map中能够更方便的进行检索和使用。框架

  依赖注入:函数

  *是否单件模式的Bean,是的话从缓存中取。

  *若是当前没有此Bean,就从双亲BeanFactory链一直向上查找。

  *得到当前Bean的全部依赖Bean,递归调用getBean。

  *工厂方法生成

  对象的生成实际上是应用反射机制或CGLIB生出。

  1)生成对象

  *读取xml文件,获取beans标签下的全部bean标签。

  *利用Class.forName(class).newInstance(),咱们就能够构造了一个bean的实例了。

  2)依赖对象

  其实依赖对象也是bean实例的一个属性,那么其实咱们调用bean实例的setXXX()方法后,就能够为他注入依赖对象了,一样也是利用反射原理。

  *读取bean下面的全部 <property name="p" ref="persondao">标签

  *利用自省原理,得到bean的全部属性的集合,PropertyDescriptor[] ps=Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();

  *判断设置的 <property name="">标签中的name是否有在属性的集合中(ps里面),好比咱们填写了标签 <property name="p"ref="persondao">,那么咱们应该查找类中是否有Persondao p ;这个属性,若是没有的话,就不用设置p的值了。

  *接下去咱们就能够调用属性的set方法,为bean实例设置值了。那么怎么获取set方法呢?咱们能够调用PropertyDescriptor类的getWriteMethod方法,获取set方法,而后执行其invoke(要注入到的对象, 要、注入的值),若是没有set方法的话,咱们天然也就不须要设置依赖对象的值了。

  autowire 

  开发人员都会拿DRY(讨厌重复劳动!Don't Repeat Yourself!)和KISS(保持简单,傻瓜!Keep It Simple, Stupid!)说笑。事实上,这正是优秀开发者必须具有的基本素质,即遵循这两条效率和敏捷原则。为简化DI容器中协做者的管理,Spring引入了Autowiring特性。

  方便之处在减小或者消除属性或构造器参数的设置,这样能够给咱们的配置文件减减肥。

  其实,自动装配就是让咱们少些几个  <ref ="...">.

  eg:当byName时,private Bean3 bean3;  这个bean3必须和<bean id="bean3" class="com.test.model.Bean3"

  parent="abstractBean"> 这个bean3相同.不然不能自动装配。

  CGLIB

  CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库( 比java的反射或动态代理快不少!),它能够在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO字节码的动态生成。
   代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。常常被用来动态地建立代理。JDK的动态代理用起来很是简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。若是想代理没有实现接口的继承的类,该怎么办?如今咱们可使用CGLIB包。
  CGLIB包的底层是经过使用一个小而快的 字节码处理框架ASM,来转换 字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。固然不鼓励直接使用ASM,由于它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

  

3.AOP原理

  AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。

  能够经过预编译方式和运行期动态代理实如今不修改源代码的状况下给程序动态添加功能的一种技术。

  主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,经过对这些行为的分离,咱们但愿能够将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码

  在Spring中提供了面向切面编程的丰富支持,容许经过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该作的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

  面向对象编程主要用于为同一对象层次的公用行为建模。它的弱点是将公共行为应用于多个无关对象模型之间。而这偏偏是面向方面编程适合的地方。有了 AOP,咱们能够定义交叉的关系,并将这些关系应用于跨模块的、彼此不一样的对象模型。AOP 同时还可让咱们层次化功能性而不是嵌入功能性,从而使得代码有更好的可读性和易于维护。它会和面向对象编程合做得很好。

4.Spring AOP

  1)利用Spring AOP接口实现AOP,主要是为了指定自定义通知来供spring AOP机制识别。主要接口:前置通知 MethodBeforeAdvice ,后置通知:AfterReturningAdvice,环绕通知:MethodInterceptor,异常通知:ThrowsAdvice 。

  前置方法会在切入点方法以前执行,后置会在切入点方法执行以后执行,环绕会在切入点先执行around的前处理,而后执行切点方法,再执行return处理。最后执行around的后处理。

  spring 处理顺序是按照xml配置顺序依次处理通知,以队列的方式存放前通知,以压栈的方式存放后通知。因此是前通知依次执行,后通知到切入点执行完以后,从栈里在后进先出的形式把后通知执行。

  Spring AOP 框架对 AOP 代理类的处理原则是:若是目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;若是目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类。

  JDK动态代理为何必须使用接口:

  从建立代理函数看起,即public static Object newProxyInstance(ClassLoader loader,   Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException 

  经过源码能够看到,这个类第一步生成一个代理类(注意,这里的参数就是接口列表),Class cl = getProxyClass(loader, interfaces);

  而后经过代理类找到构造参数为InvocationHandler的构造函数并生成一个新类。

  Constructor cons = cl.getConstructor(constructorParams);//这个有用,在后面细说
  return (Object) cons.newInstance(new Object[] { h });  

  接口起什么做用呢,因而又看getProxyClass方法的代码,这个源码很长,就不细说了。大体分为三段:

    第一:验证

    第二:缓存建立新类的结构,若是建立过,则直接返回。(注意:这里的KEY就是接口列表)

    第三:若是没有建立过,则建立新类

  建立代码以下

      long num;
     //得到代理类数字标识 

     synchronized (nextUniqueNumberLock) {
        num = nextUniqueNumber++;
     }

      //得到建立新类的类名$Proxy,包名为接口包名,但须要注意的是,若是有两个接口并且不在同一个包下,也会报错

      String proxyName = proxyPkg + proxyClassNamePrefix + num;
      //调用class处理文件生成类的字节码,根据接口列表建立一个新类,这个类为代理类,
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces);
      //经过JNI接口,将Class字节码文件定义一个新类

      proxyClass = defineClass0(loader, proxyName,
         proxyClassFile, 0, proxyClassFile.length);

   根据前面的代码Constructor cons = cl.getConstructor(constructorParams);

   能够猜想到接口建立的新类proxyClassFile 无论采用什么接口,都是如下结构

    public class $Proxy1 extends Proxy implements 传入的接口{

    }
  生成新类的看不到源代码,不过猜想它的执行原理颇有多是若是类是Proxy的子类,则调用InvocationHandler进行方法的Invoke。

  因此,JDK动态代理的原理是根据定义好的规则,用传入的接口建立一个新类,这就是为何采用动态代理时为何只能用接口引用指向代理,而不能用传入的类引用执行动态类。

 

参考:http://blog.csdn.net/m13666368773/article/details/7802126

http://javacrazyer.iteye.com/blog/794035

http://www.cnblogs.com/frankliiu-java/articles/1896443.html

百度百科

Spring技术内幕

相关文章
相关标签/搜索