谈谈Spring AOP-阿里云开发者社区

开发者社区> 开发与运维> 正文

谈谈Spring AOP

简介: AOP,面向切面编程,主要的作用是可以将那些分散在业务系统中相同的代码抽取出来放到一个地方进行管理 这么做的好处是减少了重复代码的编写,并且软件的可维护性也强 为什么叫做面向切面编程呢? 举个例子:假如我们的代码中, 有许多以update开头的函数的执行都需要管理员权限。

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等都继承于它


img_df3a097fa26fa443b77b23d6fb649bed.png
Advice继承关系图

BeforeAdvice的继承关系中,定义类为待增强的目标方法设置的前置增强接口MethodBeforeAdvice
使用这个前置接口需要实现一个回调函数before,作为回调函数,before方法的实现在Advice中被配置到目标方法后,会在调用目标方法时被回调

img_fc6f3d12fcec545b620d98b91be0c87e.png
MethodBeforeAdvice以及回调函数before

before的参数

  • Method:目标方法的反射对象
  • Object[]:包含目标方法的输入参数

同样的,在AfterAdvice继承体系下的AfterReturningAdvice中也有相似的回调函数

img_50fd86cc3c43f16326470b1a392e7f36.png
图1.3 AfterReturningAdvice及其回调函数afterReturn

2 Pointcut切点

从Pointcut的基本接口定义中可以看到,需要返回一个
MethodMatcher
Point的匹配判断,即

  • 判断是否需要对当前方法调用进行增强
  • 或者是否需要对当前调用方法应用配置好的Advice通知
    img_5593208cc130191d4ea1fe0a06cce0b1.png
    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 只能指定一个代理目标。

img_e4d754e163b6ed17865c45f19019944a.png
JdkRegexpMethodPointcut中的matches函数

在NameRegexpMethodPointcut中,给出了matches方法的另一个实现,根据方法的全限定名称进行匹配


img_ba791227d6c16042de3a29d0a82d7067.png
NameRegexpMethodPointcut中matches函数的实现

img_d7a576181545fba03e9c8026db7b46db.png
NameMatchMethodPointcut中matches方法的调用关系链

img_fb8fa8de727f9dede68c818eaab9e214.png
JdkRegexpMethodPointcut中matches方法的调用关系链

从图2.4和图2.5中我们可以看到,在JdkDynamicAopProxy的invoke方法中发出了对matches方法的调用.这个invoke方法就是Proxy对象进行代理回调的入口方法.


img_2066c3ee2634f9f02d608b292257303f.png
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])));
    }
}
img_71856d7b6c4bc7bc30f740b6d1e41ec4.png
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();
            }
img_c0eb5cf18963ff1c57256990cbaa4dd6.png
img_91c35aad86756ba1911441884b28bd0f.png

img_3ea637155b5414e06aad1f95d3b57f23.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章