ioc和aop相关资料.docx
《ioc和aop相关资料.docx》由会员分享,可在线阅读,更多相关《ioc和aop相关资料.docx(21页珍藏版)》请在冰豆网上搜索。
ioc和aop相关资料
SpringIOC核心源码学习
(一)
本文主要以springioc容器基本代码骨架为切入点,理解ioc容器的基本代码组件结构,各代码组件细节剖析将放在后面的学习文章里。
关于IOC容器
IoC容器:
最主要是完成了完成对象的创建和依赖的管理注入等等。
先从我们自己设计这样一个视角来考虑:
所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依赖的代码,反转给容器来帮忙实现。
那么必然的我们需要创建一个容器,同时需要一种描述来让容器知道需要创建的对象与对象的关系。
这个描述最具体表现就是我们可配置的文件。
对象和对象关系怎么表示?
可以用xml,properties文件等语义化配置文件表示。
描述对象关系的文件存放在哪里?
可能是classpath,filesystem,或者是URL网络资源,servletContext等。
回到正题,有了配置文件,还需要对配置文件解析。
不同的配置文件对对象的描述不一样,如标准的,自定义声明式的,如何统一?
在内部需要有一个统一的关于对象的定义,所有外部的描述都必须转化成统一的描述定义。
如何对不同的配置文件进行解析?
需要对不同的配置文件语法,采用不同的解析器。
基于以上问题,对应过来,刚好是springioc容器抽象的的几个主要接口:
Resource
BeanDefinition
BeanDefinitionReader
BeanFactory
ApplicationContext
以上五个都是接口,都有各式各样的实现,正是这5个接口定义了springioc容器的基本代码组件结构。
而其组件各种实现的组合关系组成了一个运行时的具体容器。
各代码组件详解
1.Resource
是对资源的抽象,每一个接口实现类都代表了一种资源类型,如ClasspathResource、URLResource,FileSystemResource等。
每一个资源类型都封装了对某一种特定资源的访问策略。
它是spring资源访问策略的一个基础实现,应用在很多场景。
具体可以参考文章:
Spring资源访问剖析和策略模式应用
2.BeanDefinition
用来抽象和描述一个具体bean对象。
是描述一个bean对象的基本数据结构。
3.BeanDefinitionReader
BeanDefinitionReader将外部资源对象描述的bean定义统一转化为统一的内部数据结构BeanDefinition。
对应不同的描述需要有不同的Reader。
如XmlBeanDefinitionReader用来读取xml描述配置的bean对象。
4.BeanFactory
用来定义一个很纯粹的bean容器。
它是一个bean容器的必备结构。
同时和外部应用环境等隔离。
BeanDefinition是它的基本数据结构。
它维护一个BeanDefinitionsMap,并可根据BeanDefinition的描述进行bean的创建和管理。
5.ApplicationContext
从名字来看叫应用上下文,是和应用环境息息相关的。
没错这个就是我们平时开发中经常直接使用打交道的一个类,应用上下文,或者也叫做spring容器。
其实它的基本实现是会持有一个BeanFactory对象,并基于此提供一些包装和功能扩展。
为什么要这么做呢?
因为BeanFactory实现了一个容器基本结构和功能,但是与外部环境隔离。
那么读取配置文件,并将配置文件解析成BeanDefinition,然后注册到BeanFactory的这一个过程的封装自然就需要ApplicationContext。
ApplicationContext和应用环境细细相关,常见实现有ClasspathXmlApplicationContext,FileSystemXmlApplicationContext,WebApplicationContext等。
Classpath、xml、FileSystem、Web等词都代表了应用和环境相关的一些意思,从字面上不难理解各自代表的含义。
当然ApplicationContext和BeanFactory的区别远不止于此,有:
1.资源访问功能:
在Resource和ResourceLoader的基础上可以灵活的访问不同的资源。
2.支持不同的信息源。
3.支持应用事件:
继承了接口ApplicationEventPublisher,这样在上下文中为bean之间提供了事件机制。
……
以上5个组件基本代表了ioc容器的一个最基本组成,而组件的组合是放在ApplicationContext的实现这一层来完成。
以ClasspathXmlApplicationContext容器实现为例,其组合关系如下:
ClassPathXmlApplicationContext的refresh()方法负责完成了整个容器的初始化。
为什么叫refresh?
也就是说其实是刷新的意思,该IOC容器里面维护了一个单例的BeanFactory,如果bean的配置有修改,也可以直接调用refresh方法,它将销毁之前的BeanFactory,重新创建一个BeanFactory。
所以叫refresh也是能理解的。
以下是Refresh的基本步骤:
1.把配置xml文件转换成resource。
resource的转换是先通过ResourcePatternResolver来解析可识别格式的配置文件的路径
(如"classpath*:
"等),如果没有指定格式,默认会按照类路径的资源来处理。
2.利用XmlBeanDefinitionReader完成对xml的解析,将xmlResource里定义的bean对象转换成统一的BeanDefinition。
3.将BeanDefinition注册到BeanFactory,完成对BeanFactory的初始化。
BeanFactory里将会维护一个BeanDefinition的Map。
当getBean的时候就会根据调用BeanFactory,根据bean的BeanDifinition来实例化一个bean。
当然根据bean的lazy-init、protetype等属性设置不同以上过程略有差别。
refresh()代码如下:
Java代码
1publicvoidrefresh()throwsBeansException,IllegalStateException{
2synchronized(this.startupShutdownMonitor){
3//Preparethiscontextforrefreshing.
4prepareRefresh();
5
6//Tellthesubclasstorefreshtheinternalbeanfactory.
7ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();
8
9//Preparethebeanfactoryforuseinthiscontext.
10prepareBeanFactory(beanFactory);
11
12try{
13//Allowspost-processingofthebeanfactoryincontextsubclasses.
14postProcessBeanFactory(beanFactory);
15
16//Invokefactoryprocessorsregisteredasbeansinthecontext.
17invokeBeanFactoryPostProcessors(beanFactory);
18
19//Registerbeanprocessorsthatinterceptbeancreation.
20registerBeanPostProcessors(beanFactory);
21
22//Initializemessagesourceforthiscontext.
23initMessageSource();
24
25//Initializeeventmulticasterforthiscontext.
26initApplicationEventMulticaster();
27
28//Initializeotherspecialbeansinspecificcontextsubclasses.
29onRefresh();
30
31//Checkforlistenerbeansandregisterthem.
32registerListeners();
33
34//Instantiateallremaining(non-lazy-init)singletons.
35finishBeanFactoryInitialization(beanFactory);
36
37//Laststep:
publishcorrespondingevent.
38finishRefresh();
39}
40
41catch(BeansExceptionex){
42//Destroyalreadycreatedsingletonstoavoiddanglingresources.
43beanFactory.destroySingletons();
44
45//Reset'active'flag.
46cancelRefresh(ex);
47
48//Propagateexceptiontocaller.
49throwex;
50}
51}
52}
以上的obtainFreshBeanFactory是很关键的一个方法,里面会调用loadBeanDefinition方法,如下:
Java代码
53protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsIOException{
54//CreateanewXmlBeanDefinitionReaderforthegivenBeanFactory.
55XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);
56
57//Configurethebeandefinitionreaderwiththiscontext's
58//resourceloadingenvironment.
59beanDefinitionReader.setResourceLoader(this);
60beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));
61
62//Allowasubclasstoprovidecustominitializationofthereader,
63//thenproceedwithactuallyloadingthebeandefinitions.
64initBeanDefinitionReader(beanDefinitionReader);
65loadBeanDefinitions(beanDefinitionReader);
66}
LoadBeanDifinition方法很关键,这里特定于整个IOC容器,实例化了一个XmlBeanDefinitionReader来解析Resource文件。
关于Resource文件如何初始化和xml文件如何解析都在
Java代码
67loadBeanDefinitions(beanDefinitionReader);
里面的层层调用完成,这里不在累述。
小结
Spring的扩展性是毋庸置疑的,学习spring的设计是一个很好的实践理论结合。
主要个人觉得有几点:
1.框架顶层的设计有着很好的抽象,遵循面向接口编程的规范。
Resource、BeanFactory、ApplicationContext都是非常好的接口抽象,非常明确的定义了该组件的一些功能。
2.利用组合模式。
3.个组件的实现里大量使用了模板方法模式,提升了同一组件代码的复用性。
4.各种设计保留了扩展的接口,很多基于spring的框架都可以很容易的介入实现了自己的一些扩展。
5.框架里采用里很多经典的设计模式,如代理、装饰、策略等等。
本文的目标:
从实现的角度来认识SpringAOP框架。
观察的角度:
从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringAOP框架。
本文的风格:
首先列出AOP的基本概念;
其次介绍框架所涉及到的核心组件列表,组件之间的结构关系图;
然后细化结构图中的部分;
接下来是一个简单的sample;
最后是后记部分。
注:
1.本文的源代码基于Spring2.x。
Spring的源代码也处于演变中,但对基础代码的影响并不大。
2.本文是对SpringIoC容器实现的结构分析的姊妹帖。
正文:
SpringAOP框架涉及的基本概念介绍:
关注点(concern):
一个关注点可以是一个特定的问题、概念、或是应用程序的兴趣区间--总而言之,应用程序必须达到的一个目标。
核心关注点(coreconcern):
业务功能模块,如:
存款模块,取款模块,转账模块等,
横切关注点(crosscuttingconcern):
非功能性的、横切性模块,如:
安全性管理,事务管理,性能监控等。
方面(aspect):
一个方面是对一个横切关注点的模块化,它将那些原本散落在各处的、用于实现这个关注点的代码归整到一处。
连接点(joinpoint):
程序执行过程中的一点,如:
字段访问:
读、写实例变量;
方法调用:
对方法(包括构造方法)的调用;
异常抛出:
特定的异常被抛出。
切入点(pointcut):
一组连接点的总称,用于指定某个增强应该在何时被调用。
切入点常用正则表达式或别的通配符语法来描述,有些AOP实现技术还支持切入点的组合。
增强(advice):
在特定连接点执行的动作。
很多AOP框架都以拦截器(interceptor)的形式来表现增强--所谓拦截器是这样的一个
对象:
当连接点被调用时,它会收到一个回调消息。
基本的增强有:
前增强(BeforeAdvice):
在连接点调用之前,首先调用增强;
后增强(AfterAdvice):
在连接点调用之后,再调用增强,在AspectJ中,后增强又分为三种:
AfterReturningAdvice:
在调用成功完成(没有异常抛出)之后。
AfterThrowingAdvice:
在抛出某种特定类型(或其子类型)的异常之后。
AfterAdvice:
在连接点的任何调用之后,不管调用是否抛出异常。
环绕增强(AroundAdvice):
这类增强可以完全控制执行流程。
除了完成本身的工作之外,它还需要负责主动调用连接点,促使真实的操作发生(proceed)--这通常是通过调用某个特定的方法来完成的。
引介(introduction):
为一个现有的Java类或接口添加方法或字段。
这种技术可以用于实现Java中的多继承,或者给现有对象模型附加新的API。
混入继承(mixininheritance):
一个“混入类”封装了一组功能,这组功能可以被"混入"到现有的类当中,并且无须使用传统的继承手段。
在AOP这里,混入是通过引介来实现的。
在Java语言中,可以通过混入来模拟多继承。
织入(weaving):
将方面整合到完整的执行流程(或完整的类,此时被织入的便是引介中)。
拦截器(initerceptor):
很多AOP框架用它来实现字段和方法的拦截(interception)。
随之而来的就是在连接点(如方法拦截)处挂接一条拦截器链(interceptorchain),链条上的每个拦截器通常会调用下一个拦截器。
AOP代理(AOPproxy):
即被增强(advise)的对象引用--也就是说,AOP增强将在其上执行的这样一个对象引用。
目标对象(targetobject):
位于拦截器链末端的对象实例--这个概念只存在于那些使用了拦截机制的框架之中。
注:
上述概念描述引自《ExpertOne-on-OneJ2EEDevelopmentwithoutEJB》中第八章对AOP概念描述部分,更多精彩部分可以参阅本章的完整内容。
上述概念已被SpringAOP框架很好的实现,相关组件:
Advisor组件,
Advice组件,
Pointcut组件,
Advised组件,
AopProxy组件,
AopProxyFactory组件,
图1.
图1是对增强、切入点、方面、AOP代理之间依赖关系的全景图。
增强和切入点组成一个方面,方面信息与目标对象信息被组织到Advised中,AopProxyFactory通过Advised中保存的信息生成AopProxy
对象,调用AopProxy.getProxy()方法即可获得增强后的对象。
这里要着重了解的是不同的增强子类型,不同的切入点子类型,
对于不同的切入点子类型最重要的两种子类型:
静态切入点,动态切入点,
静态切入点:
根据部署阶段的信息选择增强,如“拦截特定类的所有getter方法”;
动态切入点:
根据运行时的信息选择增强,如“如果某方法的返回值为null,则将其纳入某切入点”。
图2.
图2是对图1中Advisor与Pointcut的实现细化,图中类之间的关系直观上有点乱,但细看下关系还是相当清晰的,
以Advisor结尾的是方面类型,以Pointcut结尾的是切入点类型,
Advisor与Pointcut的复用关系分两类:
一类是组合复用,另一类是具体继承复用,
组合复用例子如:
RegexpMethodPointcutAdvisor与AbstractRegexpMethodPointcut之间的关系,
NameMatchMethodPointcutAdvisor与NameMatchMethodPointcut之间的关系,
具体继承复用例子如:
StaticMethodMatcherPointcutAdvisor与StaticMethodMatcherPointcut之间的关系,
DynamicMethodMatcherPointcutAdvisor与DynamicMethodMatcherPointcut之间的关系,
图3.
图3是对图1中生成AopProxy对象的实现细化,
AopProxyFactory通过AdvisedSupport提供的信息生成AopProxy对象,AopProxy对象的生成分两类方式:
一类是动态代理,另一类是字节码增强;
需要注意的是,ProxyFactory与ProxyFactoryBean并不是功能实现的必要部分,主要目的为编程式使用代理提供便利的API。
下面是一个简单的sample:
Java代码
1//目标对象接口.
2publicinterfaceTarget{
3publicStringplay(intarg);
4}
5//目标对象实现.
6publicclassTargetImplimplementsTarget{
7
8publicStringplay(intarg){
9System.out.println("playmethod....");
10return"[Target:
]"+arg;
11}
12}
13//前置增强
14publicclassMyBeforeAdviceimplementsMethodBeforeAdvice{
15publicvoidbefore(Methodmethod,Object[]args,Objecttarget)
16throwsThrowable{
17System.out.println(method.getName());
18System.out.println("beforemethod!
");
19}
20}
21//后置增强
22publicclassMyAfterAdviceimplementsAfterReturningAdvice{
23publicvoidafterReturning(ObjectreturnValue,Methodmethod,
24Object[]args,Objecttarget)throwsThrowable{
25System.out.println(returnValue+":
aftermethod");
26}
27}
28//切入点实现
29publicclassMyPointcutimplementsPointcut{
30
31publicClassFiltergetClassFilter(){
32
33returnnewClassFilter(){
34
35publicbooleanmatches(Classarg0){
36if(arg0==TargetImpl.class){
37returntrue;
38}
39returnfalse;
40}
41
42};
43}
44
45publicMethodMatchergetMethodMatcher(){
46
47returnnewMethodMatcher(){
48
49publicbooleanisRuntime(){
50
51return