本文翻译自Spring.NET官方文档Version 1.3.2。git
受限于我的知识水平,有些地方翻译可能不许确,可是我仍是但愿个人这些微薄的努力能为他人提供帮助。spring
侵删。数组
若是你正在为你的业务模型使用IoC容器——这是个好主意——你将会想使用某个 Spring.NET's AOP特定的IFactoryObject 的实现(要记住,一个工厂的实例提供了一个间接层,使这个工厂可以建立不一样类型的对象—5.3.9节,“经过使用其余类型和实例建立一个对象”)。app
一个基本的建立Spring.NET's AOP代理的方式是使用Spring.Aop.Framework.ProxyFactoryObject类。这种作法可以让你能灵活配置切入点的顺序,切入点的应用和将会应用到你的业务中的通知。然而,若是你不须要这种这种配置,有其余的更简便的方法生成代理。框架
和其余的Spring.NET IFactoryObject 的实现同样,ProxyFactoryObject引入了一个中间层。若是你定义了一个ProxyFactoryObject实例,名字叫作foo,那么对foo并非ProxyFactoryObject 这个代理对象的引用,而是ProxyFactoryObject这个实例的的GetObject() 方法产生的对象。这个方法会包装目标对象而后产生一个AOP代理。ide
使用ProxyFactoryObject 或者其余的IoC感知类建立AOP代理的最大好处就是咱们可使用IoC来管理通知和切入点。 这个是一个强大的特性,能够完成其余AOP框架不能实现的一些功能。例如,一个通知可能关联到一个应用程序中全部实体(不只仅是AOP框架中的目标实体),这个得益于依赖注入带来的可拔插性。post
和Spring.NET中大多的IFactoryObject实现同样,ProxyFactoryObject 也是一个能够灵活配置的Spring.NET实体。它的配置主要用来:性能
一些关键的属性继承自Spring.Aop.Framework.ProxyConfig 类:这个类是全部Spring.NET中AOP代理工厂的父类。一些关键的配置包括:测试
其余在ProxyFactoryObject 类中定义的属性包括:优化
这些名称都是存在于当前容器中的对象名称,同时也包括容器的全部父类包含的对象。你不能够直接声明容器外的源对象,由于这么作会致使ProxyFactoryObject 忽略通知的单例设置。
让咱们看一个简单的ProxyFactoryObject 的例子,这个例子包括:
1 <object id="personTarget" type="MyCompany.MyApp.Person, MyCompany">
2 <property name="name" value="Tony"/>
3 <property name="age" value="51"/>
4 </object>
5 <object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
6 <property name="customProperty" value="configuration string"/>
7 </object>
8 <object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
9 </object>
10 <object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
11
12 <property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>
13 <property name="target" ref="personTarget"/>
14 <property name="interceptorNames">
15 <list>
16 <value>debugInterceptor</value>
17 <value>myCustomInterceptor</value>
18 </list>
19 </property>
20 </object>
要注意的是,InterceptorNames 属性是一个字符串列表:位于当前上下文的切面名称。通知器,拦截器,前置通知,返回后通知和抛出通知实体都能被应用进去。它们的顺序是很重要的。
你可能会奇怪为何这个列表不是实体的引用。其缘由是若是ProxyFactoryObject的singleton属性设置成false,它必须可以针对每一个被代理的实体都返回独立的代理实例。若是有任何的通知器是一个原型,一个独立的实例须要被返回,因此没有必要再从上下文获取一个原型的实例,由于仅仅使用同一个引用是明显不够的。
上文中“person”对象的定义能够用一个Iperson接口的实现来代替,以下:
IPerson person = (IPerson) factory.GetObject("person");
其余在同一个IoC上下文的实体能够表达一个强类型的依赖注入,和其余普通的.NET实体同样:
1 <object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany">
2 <property name="person" ref="person"/>
3 </object>
这里例子中的PersonUser 类会暴露IPerson接口的一些属性。AOP代理会被用来代替“真正的”person的实现。于是这个类型会是一个代理类型。它能够转换成一个IAdvised接口的实现(下文会讨论)。
可使用内联对象(inline object)来把目标和目标的代理的区别隐藏起来,就像下面这样。(更多关于内联对象的信息可查看5.3.2.3小节, “Inner objects“) 只有ProxyFactoryObject 的定义是不同的;通知也包含在内,仅仅为了完整性须要:
1 <object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
2 <property name="customProperty" value="configuration string"/>
3 </object>
4 <object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
5 </object>
6 <object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
7
8 <property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>
9 <property name="target">
10 <!-- Instead of using a reference to target, just use an inline object -->
11 <object type="MyCompany.MyApp.Person, MyCompany">
12 <property name="name" value="Tony"/>
13 <property name="age" value="51"/>
14 </object>
15 </property>
16 <property name="interceptorNames">
17 <list>
18 <value>debugInterceptor</value>
19 <value>myCustomInterceptor</value>
20 </list>
21 </property>
22 </object>
这样作的一个优势是仅仅存在一个Person类型的对象:当咱们想防止应用程序上下文的使用者得到一个没有被通知的对象的引用,或者想避免Spring IoC 的自动装配形成的模棱两可的定义的时候是很用用的。还有一个有争议的优势是若是这么作,那么ProxyFactoryObject 的定义是独立完整的。然而有些时候可以从工厂中得到没有被通知的目标对象也许也是一个好处:例如在一些特定的测试环境中。
让咱们来看一个经过ProxyFactoryObject配置代理对象的例子。
1 <!-- create the object to reference -->
2 <object id="RealObjectTarget" type="MyRealObject" singleton="false"/>
3 <!-- create the proxied object for everyone to use-->
4 <object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
5 <property name="proxyInterfaces" value="MyInterface" />
6 <property name="isSingleton" value="false"/>
7 <property name="targetName" value="RealObjectTarget" />
8 </object>
9
若是你使用一个原型来做为目标你必须把TargetName属性设置成你对象的名字/id而不能使用一个目标引用对象的属性。这样作接下来可以经过一个新的目标原型实例生成一个对应的新代理。
考虑上面的Spring.Net配置的时候,要注意ProxyFactoryObject 的IsSingleton 属性是设置成false的。这就意味着每个代理对象都是独一无二的。这样你就可使用下面的方式去配置配置每个代理对象的通知类型:
1 // Will return un-advised instance of proxy object
2 MyInterface myProxyObject1 = (MyInterface)ctx.GetObject("MyObject"); 3 // myProxyObject1 instance now has an advice attached to it.
4 IAdvised advised = (IAdvised)myProxyObject1; 5 advised.AddAdvice( new DebugAdvice() ); 6 // Will return a new, un-advised instance of proxy object
7 MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject");
当你想代理一个类,而不是一个或者多个接口的时候,应该怎么办呢?
想象一下上面的例子,若是不存在IPerson 接口,咱们要通知一个没有实现任何接口的Person类。在这种状况下,若是找不到任何实现的接口的话,ProxyFactoryObject 会代理全部的公共的虚方法和虚属性。咱们能够经过设置ProxyTargetType为true来强制Spring.NET 去代理类而不是接口。
类的代理是经过在运行时产生一个目标的子类来实现的。Spring.NET 配置这个子类来委托对原目标对象方法的调用:这个子类被用来实现这种装饰器模式,将通知织入。
类的代理应该对使用者透明。然而须要考虑一个重要问题:非虚的方法不能被通知,就像它们不能被重写同样。这个可能在一些一般不会把方法声明成虚方法的类里面有些限制。
尤为是在定义一个事务代理的时候,若是你没有使用事务名称空间,你最终可能会碰到不少相似的代理定义。使用父子对象定义,再加上一些内联对象定义,可使得代理定义更加精简。
首先,须要为这个代理定义一个父对象模板:
1 <object id="txProxyTemplate" abstract="true"
2 type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
3 <property name="PlatformTransactionManager" ref="adoTransactionManager"/>
4 <property name="TransactionAttributes">
5 <name-values>
6 <add key="*" value="PROPAGATION_REQUIRED"/>
7 </name-values>
8 </property>
9 </object>
这样它没法实例化,因此实际上配置还还没有完成。接下来每一个须要被建立的代理都是一个子对象定义,这个定义把目标代理包装成一个内联对象,由于目标对象永远不会被本身使用:
1 <object name="testObjectManager" parent="txProxyTemplate">
2 <property name="Target">
3 <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
4 <property name="TestObjectDao" ref="testObjectDao"/>
5 </object>
6 </property>
7 </object>
固然也能够在子对象中重写父类模板的属性,就像下面的事务传播配置(transaction propagation settings)案例:
1 <object name="testObjectManager" parent="txProxyTemplate">
2 <property name="Target">
3 <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
4 <property name="TestObjectDao" ref="testObjectDao"/>
5 </object>
6 </property>
7 <property name="TransactionAttributes">
8 <name-values>
9 <add key="Save*" value="PROPAGATION_REQUIRED"/>
10 <add key="Delete*" value="PROPAGATION_REQUIRED"/>
11 <add key="Find*" value="PROPAGATION_REQUIRED,readonly"/>
12 </name-values>
13 </property>
14 </object>
要注意,上面的例子咱们已经显示地把父类对象定义成抽象,因此它其实是不能被实例化的。应用程序上下文(并非简单的对象工厂)会默认预实例化(pre-instantiate )全部的单例。所以,要注意的是(至少对于单例对象),若是你有一个你想做为一个模板的父对象,而且这个模板定义了一个特定的类,你必须保证将abstract 属性设置成true,否则应用程序上下文会尝试着去预实例化它。
Spring在运行时经过使用类型构造器(TypeBuilder )的API建立AOP代理。
两种代理类型会被生成,基于结构(composition based )或者基于继承(inheritance based)。若是目标对象实现了至少一个接口那么就会生成基于结构的代理,否则就会生成基于继承的代理。
基于结构的代理是经过生成一个实现全部目标对象的接口的类型来实现。这个动态类真实的名字是一个相似于”GUID“的东西。经过一个私有字段来保存目标对象的引用,而后这个动态类型的实现会在调用目标类以前或者以后执行通知。
基于继承的代理会生成一个直接继承目标类型的动态类型。这样就能够在须要的时候直接追溯到目标类。要注意的是,两种方式中的目标方法实现若是调用了目标对象中的其余方法,这个方法是不会被通知的。想强制使用基于继承的代理方式,你能够设置ProxyFactory 的ProxyTargetType 属性为true,或者使用将XML的名称控件里的proxy-target-type 设置true。
在.NET 2.0中你能够定义程序集的特性InternalsVisibleTo来使得程序集内部的接口或者类容许被特定的”友好“的程序集访问。若是你须要在一个内部的类或者接口生成一个AOP代理,须要在AssemblyInfo文件中加入如下代码:[assembly: InternalsVisibleTo("Spring.Proxy")] and [assembly: InternalsVisibleTo("Spring.DynamicReflection")]。
在上文提到的基于继承的代理有一个很大的限制,那就是全部的方法都要被声明成虚方法。否则一些方法的调用会直接获取私有的”目标“字段或者基类。winform对象就是这样一个例子,它们的方法都不是虚方法。为了解决这个问题,在1.2版本中引入了一个新的后处理机制(post-processing mechanism ),这种机制生成一个不包含私有的”目标“字段的代理类型。通知在调用基类方法以前直接被添加到方法体中。
想要使用这种新的基于继承的代理,要在你的配置文件中声明一个InheritanceBasedAopConfigurer实例,和一个IObjectFactoryPostProcessor,一个例子:
1 <object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop">
2 <property name="ObjectNames">
3 <list>
4 <value>Form*</value>
5 <value>Control*</value>
6 </list>
7 </property>
8 <property name="InterceptorNames">
9 <list>
10 <value>debugInterceptor</value>
11 </list>
12 </property>
13 </object>
14 <object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>
这个配置风格和自动代理很类似,尤为是在你想为WinForm类添加通知的时候很好用。
在Spring.NET中也很容易地经过代码来建立AOP代理。这样你就不须要依赖Spring.NET 的IoC功能。
如下展现了如何为一个目标对象建立代理,其中包含一个拦截器和一个通知器。目标对象实现的接口会被自动代理:
1 ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl); 2 factory.AddAdvice(myMethodInterceptor); 3 factory.AddAdvisor(myAdvisor); 4 IBusinessInterface tb = (IBusinessInterface) factory.GetProxy();
第一步是构建一个Spring.Aop.Framework.ProxyFactory对象。你能够像上面的例子同样建立一个目标对象,或者在构造器中指定要被代理的接口。
你能够添加拦截器或者通知器,而后配置它们。
ProxyFactory中也有一些方便的方法来容许你添加其余的通知类型。AdvisedSupport 是ProxyFactory 和ProxyFactoryObject的基类。
不管你经过那种方式建立AOP代理,你均可以经过使用Spring.Aop.Framework.IAdvised 来配置它们。全部的AOP代理均可以转换成这个接口类型,不管它实现了其余什么接口。这个接口包括如下的方法和属性:
1 public interface IAdvised 2 { 3 IAdvisor[] Advisors { get; } 4
5 IIntroductionAdvisor[] Introductions { get; } 6
7 void AddInterceptor(IInterceptor interceptor); 8
9 void AddInterceptor(int pos, IInterceptor interceptor); 10 void AddAdvisor(IAdvisor advisor); 11 void AddAdvisor(int pos, IAdvisor advisor); 12
13 void AddIntroduction(IIntroductionAdvisor advisor); 14
15 void AddIntroduction(int pos, IIntroductionAdvisor advisor); 16 int IndexOf(IAdvisor advisor); 17 int IndexOf(IIntroductionAdvisor advisor); 18 bool RemoveAdvisor(IAdvisor advisor); 19 void RemoveAdvisor(int index); 20
21 bool RemoveInterceptor(IInterceptor interceptor); 22 bool RemoveIntroduction(IIntroductionAdvisor advisor); 23
24 void RemoveIntroduction(int index); 25 void ReplaceIntroduction(int index, IIntroductionAdvisor advisor); 26 bool ReplaceAdvisor(IAdvisor a, IAdvisor b); 27 }
通知器属性会被加入到工厂的全部通知器,拦截器或者其余通知类型返回一个IAdvisor接口。若是你添加了一个IAdvisor,返回的通知器就将会是你添加的对象。若是你天界了一个拦截器或者其余的通知类型,Spring.NET将会在一个通知器中使用一个老是返回true的IPointcut 接口包装它。所以若是你添加了一个IMethodInterceptor,通知器就会返回一个DefaultPointcutAdvisor和一个匹配全部类型和方法的IPointcut 接口。
AddAdvisor() 方法能够用来添加任意的IAdvisor接口。一般这个会是一个泛型DefaultPointcutAdvisor,这个通知器能够和任何的通知或者切入点使用(除了引入通知)。
通常来讲,一个代理被生成以后也能够添加或者移除通知器或者拦截器。惟一的限制就是不能添加或者移除引入通知,所以工厂中的现存代理的接口不会改变。(你能够从工厂获取一个新的代理来避免这个问题)。
However you create AOP proxies, you can manipulate them using the Spring.Aop.Framework.IAdvised interface. Any AOP proxy can be cast to this interface, whatever other interfaces it implements. This interface includes the following methods and properties:
The Advisors property will return an IAdvisor for every advisor, interceptor or other advice type that has been added to the factory. If you added an IAdvisor, the returned advisor at this index will be the object that you added. If you added an interceptor or other advice type, Spring.NET will have wrapped this in an advisor with a IPointcut that always returns true. Thus if you added an IMethodInterceptor, the advisor returned for this
index will be a DefaultPointcutAdvisor returning your IMethodInterceptor and an IPointcut that matches all types and methods.
The AddAdvisor() methods can be used to add any IAdvisor. Usually this will be the generic DefaultPointcutAdvisor, which can be used with any advice or pointcut (but not for introduction).
By default, it's possible to add or remove advisors or interceptors even once a proxy has been created. The only restriction is that it's impossible to add or remove an introduction advisor, as existing proxies from the factory will not show the interface change. (You can obtain a new proxy from the factory to avoid this problem.) It's questionable whether it's advisable (no pun intended) to modify advice on a business object in production, although there are no doubt legitimate usage cases. However, it can be very useful in development: for example, in tests. I have sometimes found it very useful to be able to add test code in the form of an interceptor or other advice, getting inside a method invocation I want to test. (For example, the advice can get inside a transaction created for that method: for example, to run SQL to check that a database was correctly updated, before marking the transaction for roll back.)
Depending on how you created the proxy, you can usually set a Frozen flag, in which case the IAdvised IsFrozen property will return true, and any attempts to modify advice through addition or removal will result in an AopConfigException. The ability to freeze the state of an advised object is useful in some cases: For example, to prevent calling code removing a security interceptor.