AOP,面向切面编程,主要的作用是可以将那些分散在业务系统中相同的代码抽取出来放到一个地方进行管理
这么做的好处是减少了重复代码的编写,并且软件的可维护性也强
为什么叫做面向切面编程呢?
举个例子:假如我们的代码中,
有许多以update开头的函数的执行都需要管理员权限。如果不使用AOP,那么我们在每个以update开头的函数中都要进行权限验证,这样导致了大量重复代码的产生
与此同时,万一某天需求有变,不再限制只有管理员才能执行这些函数,那么我们又要将原来代码中和这个部分相关的代码逐行移除,十分的麻烦
引入了AOP之后,这项工作就变得简单了
我们可以将权限验证的代码放在某个地方,然后通过某些特定的配置实现在执行系统中以update开头的函数之前,先执行权限验证的代码
如此,万一需求变了,我们也只要改一个地方的代码。那一个个以update开头的函数就是切点,横向地来看,可以把它们抽象成一个切面,所以AOP被称为面向切面编程。
常见的应用场景:日志记录、性能统计、安全认证、事务处理、异常处理等
我们将这些代码从业务逻辑代码中分离出来,通过对这些行为的分离,我们把它们独立到非指导业务逻辑的代码中,进而改变这些代码的时候不会影响到我们的业务逻辑代码
并且业务逻辑代码也感知不到它们的存在,因为业务逻辑代码“被代理了”。
Spring AOP中代理机制的实现主要使用了了JDK动态代理以及CGLIB动态代理
基本的概念
连接点
目标被增强的函数即程序执行过程中的行为,比如方法调用或特定异常被抛出Advice通知
定义在连接点做什么,为切面增强提供织入接口,有Before/After/ThrowsAdvice
在特定的连接点,AOP 框架执行的动作
Spring 以拦截器作通知模型,维护一个围绕连接点的拦截器链Pointcut切点
决定Advice应该作用于哪个连接点,也就是说通过Pointcut来定义需要增强的方法的集合Advisor通知器
将目标方法的切面增强设计(Advice)和关注点的设计(Pointcut)结合起来。通过Advisor,可以定义该使用哪个通知并在哪个关注点使用它
1 Advice
Advice是AOP中的一个基本接口,BeforeAdvice、AfterAdvice、ThrowsAdvice等都继承于它
Advice继承关系图
在BeforeAdvice
的继承关系中,定义类为待增强的目标方法设置的前置增强接口MethodBeforeAdvice
使用这个前置接口需要实现一个回调函数before
,作为回调函数,before方法的实现在Advice中被配置到目标方法后,会在调用目标方法时被回调
MethodBeforeAdvice以及回调函数before
before的参数
Method:目标方法的反射对象
Object[]:包含目标方法的输入参数
同样的,在AfterAdvice继承体系下的AfterReturningAdvice中也有相似的回调函数
图1.3 AfterReturningAdvice及其回调函数afterReturn
2 Pointcut切点
从Pointcut的基本接口定义中可以看到,需要返回一个MethodMatcher
Point的匹配判断,即
判断是否需要对当前方法调用进行增强
或者是否需要对当前调用方法应用配置好的Advice通知
Pointcut的基本接口定义
而在MethodMatcher
接口中,有一个matcher
方法
在匹配连接点的过程中起着至关重要的作用.
MethodMatcher
对象是可以配置成
JdkRegexpMethodPointcut
有一个MethodMatcher接口定义的matches
方法,用正则表达式来对方法名进行匹配判断NameMatchMethodPointcut
完成方法的匹配判断
PointCutadvisor 有 两 个 常 用 实 现 类
NameMatchMethodPointCutadvisor
需要注入 mappedname 和 advice 属性,mappedname 指明要拦截的方法名regexMethodPointCutadvisor
需要注入 pattern 和 advice 属性,pattern 按照正则表达式的方法指明了要拦截的方法名,
advice 定义一个增强,即要加入的操作(需要自己实现 MethodBeforeAdvice、MethodafterAdvice、throwAdvice、Methodinterceptor 接口之一),然后在 ProxyBeanFactory 的拦截器中注入这个 PointCutadvisor。注:一个 ProxyFactoryBean 只能指定一个代理目标。
JdkRegexpMethodPointcut中的matches函数
在NameRegexpMethodPointcut中,给出了matches方法的另一个实现,根据方法的全限定名称进行匹配
NameRegexpMethodPointcut中matches函数的实现
NameMatchMethodPointcut中matches方法的调用关系链
JdkRegexpMethodPointcut中matches方法的调用关系链
从图2.4和图2.5中我们可以看到,在JdkDynamicAopProxy的invoke方法中发出了对matches方法的调用.这个invoke方法就是Proxy对象进行代理回调的入口方法.
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface()) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } /** * Determine whether the supplied {@link AdvisedSupport} has only the * {@link org.springframework.aop.SpringProxy} interface specified * (or no proxy interfaces specified at all). */ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] interfaces = config.getProxiedInterfaces(); return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0]))); } }
JdkDynamicAopProxy的类图
// Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }
作者:芥末无疆sss
链接:https://www.jianshu.com/p/ace2d38d2277
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。