AOP的特点
AOP主要用于横切关注点分离和织入,因此需要理解横切关注点和织入:
-
关注点: 可以认为是所关注的任何东西,比如上边的支付组件;
-
关注点分离: 将问题细化从而单独部分,即可以理解为不可再分割的组件,如上边的日志组件和支付组件;
-
横切关注点: 一个组件无法完成需要的功能,需要其他组件协作完成,如日志组件横切于支付组件;
-
织入: 横切关注点分离后,需要通过某种技术将横切关注点融合到系统中从而完成需要的功能,因此需要织入,织入可能在编译期、加载期、运行期等进行。
横切关注点可能包含很多,比如非业务的:日志、事务处理、缓存、性能统计、权限控制等等这些非业务的基础功能;还可能是业务的:如某个业务组件横切于多个模块。如:
传统支付形式,流水方式:
面向切面方式,先将横切关注点分离,再将横切关注点织入到支付系统中:
AOP的原理
Spring内部创建代理对象的过程
在Spring的底层,如果我们配置了代理模式,Spring会为每一个Bean创建一个对应的ProxyFactoryBean的FactoryBean来创建某个对象的代理对象。
假定我们现在有一个接口TicketService及其实现类RailwayStation,我们打算创建一个代理类,在执行TicketService的方法时的各个阶段,插入对应的业务代码。
public interface TicketService {
//售票
public void sellTicket();
//问询
public void inquire();
//退票
public void withdraw();
}
public class RailwayStation implements TicketService {
public void sellTicket(){
System.out.println("售票............");
}
public void inquire() {
System.out.println("问询.............");
}
public void withdraw() {
System.out.println("退票.............");
}
}
/**
* 执行RealSubject对象的方法之前的处理意见
*/
public class TicketServiceBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("BEFORE_ADVICE: 欢迎光临代售点....");
}
}
/**
* 返回结果时后的处理意见
*/
public class TicketServiceAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("AFTER_RETURNING:本次服务已结束....");
}
}
/**
* 抛出异常时的处理意见
*/
public class TicketServiceThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){
System.out.println("AFTER_THROWING....");
}
public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
System.out.println("调用过程出错啦!!!!!");
}
}
/**
*
* AroundAdvice
*/
public class TicketServiceAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("AROUND_ADVICE:BEGIN....");
Object returnValue = invocation.proceed();
System.out.println("AROUND_ADVICE:END.....");
return returnValue;
}
}
现在,我们来手动使用ProxyFactoryBean来创建Proxy对象,并将相应的几种不同的Advice加入这个proxy对应的各个执行阶段中:
/**
* 通过ProxyFactoryBean 手动创建 代理对象
* Created by louis on 2016/4/14.
*/
public class App {
public static void main(String[] args) throws Exception {
//1.针对不同的时期类型,提供不同的Advice
Advice beforeAdvice = new TicketServiceBeforeAdvice();
Advice afterReturningAdvice = new TicketServiceAfterReturningAdvice();
Advice aroundAdvice = new TicketServiceAroundAdvice();
Advice throwsAdvice = new TicketServiceThrowsAdvice();
RailwayStation railwayStation = new RailwayStation();
//2.创建ProxyFactoryBean,用以创建指定对象的Proxy对象
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//3.设置Proxy的接口
proxyFactoryBean.setInterfaces(TicketService.class);
//4. 设置RealSubject
proxyFactoryBean.setTarget(railwayStation);
//5.使用JDK基于接口实现机制的动态代理生成Proxy代理对象,如果想使用CGLIB,需要将这个flag设置成true
proxyFactoryBean.setProxyTargetClass(true);
//6. 添加不同的Advice
proxyFactoryBean.addAdvice(afterReturningAdvice);
proxyFactoryBean.addAdvice(aroundAdvice);
proxyFactoryBean.addAdvice(throwsAdvice);
proxyFactoryBean.addAdvice(beforeAdvice);
proxyFactoryBean.setProxyTargetClass(false);
//7通过ProxyFactoryBean生成Proxy对象
TicketService ticketService = (TicketService) proxyFactoryBean.getObject();
ticketService.sellTicket();
}
}
输出:
AROUND_ADVICE:BEGIN....
BEFORE_ADVICE: 欢迎光临代售点....
售票............
AROUND_ADVICE:END.....
AFTER_RETURNING:本次服务已结束....
你会看到,我们成功地创建了一个通过一个ProxyFactoryBean和 真实的实例对象创建出了对应的代理对象,并将各个Advice加入到proxy代理对象中。
你会发现,在调用RailwayStation的sellticket()之前,成功插入了BeforeAdivce逻辑,而调用RailwayStation的sellticket()之后,AfterReturning逻辑也成功插入了。
AroundAdvice也成功包裹了sellTicket()方法,只不过这个AroundAdvice发生的时机有点让人感到迷惑。实际上,这个背后的执行逻辑隐藏了Spring AOP关于AOP的关于Advice调度最为核心的算法机制,这个将在本文后面详细阐述。
另外,本例中ProxyFactoryBean是通过JDK的针对接口的动态代理模式生成代理对象的,具体机制,请看下面关于ProxyFactoryBean的介绍。
Spring AOP的核心—ProxyFactoryBean
上面我们通过了纯手动使用ProxyFactoryBean实现了AOP的功能。现在来分析一下上面的代码:我们为ProxyFactoryBean提供了如下信息:
-
Proxy应该感兴趣的Adivce列表;
-
真正的实例对象引用ticketService;
-
告诉ProxyFactoryBean使用基于接口实现的JDK动态代理机制实现proxy:
-
Proxy应该具备的Interface接口:TicketService;
-
根据这些信息,ProxyFactoryBean就能给我们提供我们想要的Proxy对象了!那么,ProxyFactoryBean帮我们做了什么?
Spring 使用工厂Bean模式创建每一个Proxy,对应每一个不同的Class类型,在Spring中都会有一个相对应的ProxyFactoryBean. 以下是ProxyFactoryBean的类图。
如上所示,对于生成Proxy的工厂Bean而言,它要知道对其感兴趣的Advice信息,而这类的信息,被维护到Advised中。Advised可以根据特定的类名和方法名返回对应的AdviceChain,用以表示需要执行的Advice串。
基于JDK面向接口的动态代理JdkDynamicAopProxy生成代理对象
JdkDynamicAopProxy类实现了AopProxy,能够返回Proxy,并且,其自身也实现了InvocationHandler角色。也就是说,当我们使用proxy时,我们对proxy对象调用的方法,都最终被转到这个类的invoke()方法中。
各种Advice是的执行顺序是如何和方法调用进行结合的?
JdkDynamicAopProxy 和CglibAopProxy只是创建代理方式的两种方式而已,实际上我们为方法调用添加的各种Advice的执行逻辑都是统一的。在Spring的底层,会把我们定义的各个Adivce分别 包裹成一个 MethodInterceptor,这些Advice按照加入Advised顺序,构成一个AdivseChain.
各种Advice本质而言是一个方法调用拦截器,现在让我们看看各个Advice拦截器都干了什么?
关于AroundAdivce,其本身就是一个MethodInterceptor,所以不需要额外做转换了。
细心的你会发现,在拦截器串中,每个拦截器最后都会调用MethodInvocation的proceed()方法。如果按照简单的拦截器的执行串来执行的话,MethodInvocation的proceed()方法至少要执行N次(N表示拦截器Interceptor的个数),因为每个拦截器都会调用一次proceed()方法。更直观地讲,比如我们调用了ticketService.sellTicket()方法,那么,按照这个逻辑,我们会打印出四条记录:
售票............
售票............
售票............
售票............
_这样我们肯定不是我们需要的结果!!!!_因为按照我们的理解,只应该有一条”售票…………“才对。真实的Spring的方法调用过程能够控制这个逻辑按照我们的思路执行,Spring将这个整个方法调用过程连同若干个Advice组成的拦截器链组合成ReflectiveMethodInvocation对象,让我们来看看这一执行逻辑是怎么控制的:
实例分析
根据上面的执行链上的逻辑,我们将我们上面举的例子的输出结果在整理一下:
Advice拦截器的添加顺序:
proxyFactoryBean.addAdvice(afterReturningAdvice);
proxyFactoryBean.addAdvice(aroundAdvice);
proxyFactoryBean.addAdvice(throwsAdvice);
proxyFactoryBean.addAdvice(beforeAdvice);
还可以使用PointCut与Advice 结合 加入条件执行判断
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution( * sellTicket(..))");
//传入创建的beforeAdvice和pointcut
FilteredAdvisor sellBeforeAdvior = new FilteredAdvisor(pointcut,beforeAdvice);
//添加到FactoryBean中
proxyFactoryBean.addAdvisor(sellBeforeAdvior);
本文摘要: https://blog.csdn.net/luanlouis/article/details/51155821