前言:
AutoFac的AOP和Spring的AOP一样都是基于动态代理的设计模式(废话)
其中Spring的动态代理有两种方式
Dynamic Proxy |
---|
JDK在1.5版本提供的原生动态代理,主要为InvocationHandler接口(切面)及产生代理类的Proxy类(代理对象PS:不是被代理对象) ,其本质是基于接口的生产方式,如果被代理类没有实现任何接口则无法使用 |
CGLib 则是通过子类生产代理对象,如果被代理类被final修饰,则无法产生代理 |
现在的Spring已经支持这两种方式并可以在其中切换了
当然 AutoFac的动态代理也支持上述的两种方式,但也稍微有些不同
1.AutoFac的接口动态代理
一.首先需要一个接口及实现类(这个类就是被代理对象,一般称为Target)
public interface DogeAction
{
string EatSomething(string food);
}
public class Doge : DogeAction
{
public string EatSomething(string food)
{
Console.WriteLine("Doge is Eating "+food);
return food;
}
}
二.其次需要一个切面,我们可以在里面定义各种增强的方法(Advice)
在Spring中的步骤一般是这样的
定义一个切面打上@Aspect
定义你想要织入的方法(Advice),打上@Before,@After,@Around然后指定PointCut切点
即可
在AutoFac中只要实现IInterceptor即可,需要在实现的 Intercept方法中执行invocation参数(其实就是代理对象)的Proceed方法,在执行前执行的方法可以为前置通知,后执行则反之
/// <summary>
/// 这个类为AOP的切面类,在这里面可以在代理对象(target)方法执行的前后添加ADVICE METHOD(增强方法)
/// </summary>
public class DogAspect : IInterceptor
{
/// <summary>
/// 前置通知
/// </summary>
public void BeforeAdvice()
{
Console.WriteLine("before doge Action");
}
/// <summary>
/// 后置通知
/// </summary>
public void AfterAdvice()
{
Console.WriteLine("after doge Action");
}
/// <summary>
/// 接口方法本身可以作为环绕通知 AroundAdvice,其中的Invocation 无疑就是JavaAOP中类似JoinPoint的作用
/// </summary>
/// <param name="invocation"></param>
public void Intercept(IInvocation invocation)
{
BeforeAdvice();
invocation.Proceed();
AfterAdvice();
}
}
三.将切面注册进容器并配置
static void Main(string[] args)
{
ContainerBuilder Builder = new ContainerBuilder();
//注册切面
Builder.RegisterType<DogAspect>();
//给注册的接口实现类配置切面,
Builder.RegisterType<Doge>().As<DogeAction>().InterceptedBy(typeof(DogAspect)).EnableInterfaceInterceptors();
//开启接口AOP
IContainer container = Builder.Build();
using(var scope = container.BeginLifetimeScope())
{
DogeAction doge = scope.Resolve<DogeAction>();
doge.EatSomething("shit");
}
}
结果:
before doge Action
Doge is Eating shit
after doge Action
2.AutoFac的子类动态代理
要使用子类代理,
首先,你的目标类target不能实现任何接口
public class Doge
{
public string EatSomething(string food)
{
Console.WriteLine("Doge is Eating "+food);
return food;
}
}
然后再该一下注册的代理生成方式即可,非常的简单
static void Main(string[] args)
{
ContainerBuilder Builder = new ContainerBuilder();
Builder.RegisterType<DogAspect>();
Builder.RegisterType<Doge>().InterceptedBy(typeof(DogAspect)).EnableClassInterceptors();
//只需要在这改一下
IContainer container = Builder.Build();
using(var scope = container.BeginLifetimeScope())
{
Doge doge = scope.Resolve<Doge>();
doge.EatSomething("shit");
}
}
结果:
before doge Action
Doge is Eating shit
after doge Action
3.二者对于切入点(PointCut)的处理
在Spring的AOP中,明确有着切入点的概念,
使用Execute表达式可以很自由的方法或缩小切入点的范围
在AOP的概念中,Aspect是我们想要织入的功能,
它必须要有一个切入的地方叫切入点(PontCut)
切点可大可小
大到一整个类,小到方法中的一个参数(Execute表达式都能够表示)
像上例中,织入的方法会在整个被代理类的所有实现方法上生效
public interface DogeAction
{
string EatSomething(string food);
void DogePee();
}
public class Doge:DogeAction
{
public void DogePee()
{
Console.WriteLine("Doge is Peeing");
}
public string EatSomething(string food)
{
Console.WriteLine("Doge is Eating "+food);
return food;
}
}
如果只需要在特定的方法上(指定方法,或者去匹配方法),这需要手动完成,也就是去切面的Intercept里判断
public void Intercept(IInvocation invocation)
{
//判断需要被织入的方法
if(invocation.Method.Name== "EatSomething")
{
BeforeAdvice();
invocation.Proceed();
AfterAdvice();
}
else
{ //不需要织入的方法则直接让它正常执行
invocation.Proceed();
}
}
测试:
static void Main(string[] args)
{
ContainerBuilder Builder = new ContainerBuilder();
Builder.RegisterType<DogAspect>();
Builder.RegisterType<Doge>().As<DogeAction>().InterceptedBy(typeof(DogAspect)).EnableInterfaceInterceptors();
IContainer container = Builder.Build();
using(var scope = container.BeginLifetimeScope())
{
DogeAction doge = scope.Resolve<DogeAction>();
doge.EatSomething("shit");
doge.DogePee();
}
}
结果:
before doge Action:EAT
Doge is Eating shit
after doge Action:EAT
Doge is Peeing