Spring IOC与AOP
1.IOC原理
IOC(Inversion of Control)控制反转。
伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。
IOC即借助于“第三方”(IOC容器)实现具有依赖关系的对象之间的解耦。
IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
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
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技术内幕