Spring 框架学习(四):AOP

[TOC]

Spring 框架学习(四):AOP

概述

AOP 是 Aspect Oriented Programming (面向切面编程)的简称,维基百科对 AOP 的描述如下:

Aspect 是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点。从关注点中分离出横切关注点是面向切面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码中不再含有针对特定领域问题代码的调用,业务领域同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。

由 AOP 联盟定义的 AOP 体系结构如下图所示:

AOP 联盟定义的 AOP 体系结构

AOP 体系结构分为从上到下、从使用到实现的三层结构。最高层是语言和开发环境,可以看到几个重要的概念:

  1. 基础,可以视为待增强对象,或者说目标对象。
  2. 切面,是对基础的增强应用。
  3. 配置,负责衔接基础和切面。把基础和切面结合起来,从而完成切面对目标对象的编织实现。

第二个层次是为语言和开发环境提供支持的,在这个层次可以看到 AOP 框架的高层实现,主要包括配置和编织实现两部分内容。

最底层是编织的具体实现模块,是为 AOP 提供技术支持的各种技术:反射、程序预处理、拦截器框架、元数据处理。

对于 Spring 平台来说,AOP 是 Spring 框架的核心功能模块之一。AOP 与 IoC 容器的结合使用,为应用开发和 Spring 自身功能的扩展都提供了许多便利。下面先简单介绍一下 Spring AOP 的几个概念:

  1. Advice 通知:定义在连接点做什么,为切面增强提供织入接口,相当于 AOP 体系结构中的“切面”。
  2. Pointcut 切点:决定 Advice 通知应该作用于哪个连接点,也就是通过 Pointcut 来定义需要增强的方法的集合,集合的选取按照一定规则来完成。这个待增强的方法集合相当于 AOP 体系结构中的“基础”。
  3. Advisor 通知器:负责把 Advice 和 Pointcut 结合起来,相当于 AOP 体系结构中的“配置”。

与 IoC 容器的衔接

在 AopNamespaceHandler 里,注册了对 aspectj-autoproxy 标签的解析类 AspectJAutoProxyBeanDefinitionParser。

public class AopNamespaceHandler extends NamespaceHandlerSupport {
    public void init() {
        // In 2.0 XSD as well as in 2.1 XSD.
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
}

AspectJAutoProxyBeanDefinitionParser 类实现了 BeanDefinitionParser 接口,在其 parse 方法中注册了 AnnotationAwareAspectJAutoProxyCreator 类。

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }   
}

public abstract class AopConfigUtils {
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }
}

AnnotationAwareAspectJAutoProxyCreator 类实现了 BeanPostProcessor 接口,当 Spring 加载 Bean 时,会在实例化后调用其 postProcessAfterInitialization 方法

    // 位于父类 AbstractAutoProxyCreator 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 进行代理
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 部分特殊类,不做代理,直接返回
        if (this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (this.nonAdvisedBeans.contains(cacheKey)) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.nonAdvisedBeans.add(cacheKey);
            return bean;
        }

        // 根据 class 获取对应的增强类 advice
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.add(cacheKey);
            // 创建代理
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.nonAdvisedBeans.add(cacheKey);
        return bean;
    }

getAdvicesAndAdvisorsForBean 方法获取用户定义的 Advice,createProxy 方法负责创建代理,createProxy 主要是由 ProxyFactory 类创建代理。

    protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

        ProxyFactory proxyFactory = new ProxyFactory();
        // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
        proxyFactory.copyFrom(this);

        if (!shouldProxyTargetClass(beanClass, beanName)) {
            // 如果使用接口代理,则设置代理接口
            Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
            for (Class<?> targetInterface : targetInterfaces) {
                proxyFactory.addInterface(targetInterface);
            }
        }

        // 根据 advice 创建 advisor
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        for (Advisor advisor : advisors) {
            proxyFactory.addAdvisor(advisor);
        }
        // 设置要代理的类
        proxyFactory.setTargetSource(targetSource);
        // 定制代理,默认是一个空方法,子类可实现,面向未来设计
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }

        return proxyFactory.getProxy(this.proxyClassLoader);
    }

代理工厂 ProxyFactory

具体创建代理使用的 ProxyFactory 类,在设置了 advisor、targetInterface、targetSource 等之后,调用 getProxy 方法生成代理。

    public Object getProxy(ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }
    // 位于基类 ProxyCreatorSupport 中
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }
    //  AopProxyFactory 目前只有一个实现类 DefaultAopProxyFactory
    public AopProxyFactory getAopProxyFactory() {
        return this.aopProxyFactory;
    }

在 getProxy 方法中,首先要调用 createAopProxy 创建代理类,createAopProxy 最终调用 DefaultAopProxyFactory 来创建代理:

    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);
            }
            if (!cglibAvailable) {
                throw new AopConfigException(
                        "Cannot proxy target class because CGLIB2 is not available. " +
                        "Add CGLIB to the class path or specify proxy interfaces.");
            }
            return CglibProxyFactory.createCglibProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

    
    private static class CglibProxyFactory {
        public static AopProxy createCglibProxy(AdvisedSupport advisedSupport) {
            return new Cglib2AopProxy(advisedSupport);
        }
    }

createAopProxy 中根据一些信息来决定使用 JDK 代理 JdkDynamicAopProxy,还是 Cglib 代理 Cglib2AopProxy?

  1. 如果目标类是接口,则使用 JDK 动态代理。
  2. 如果配置了 < aop:aspectj-autoproxy proxy-target-class="true"> 则优先使用 Cglib 代理。
  3. 如果采用激进优化策略,则使用 Cglib 代理。
  4. 如果目标类没有实现接口,则是以 Cglib 代理。

AopProxy 实现

在上面提到的 JdkDynamicAopProxy 和 Cglib2AopProxy 都是 AopProxy 接口的子类,这两个类分别负责调用 JDK 动态代理和 Cglib 代理来生成代理类。

public interface AopProxy {
    // 创建代理
    Object getProxy();
    // 使用指定的 classLoader 创建代理
    Object getProxy(ClassLoader classLoader);
}

JdkDynamicAopProxy 代理类

JdkDynamicAopProxy 实现了 AopProxy 接口,getProxy 方法中,主要是调用了 Proxy.newProxyInstance 方法来生成代理。

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        // 要代理的接口集合
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
}

Proxy.newProxyInstance 有三个参数:classLoader、代理接口集合、InvocationHandler。第三个参数的值为 this,因为 JdkDynamicAopProxy 同时也是实现了 InvocationHandler 接口,该接口有一个 invoke 方法,负责调用增强方法和目标方法。具体 Proxy.newProxyInstance 是如何生成代理的,可以看看这篇文章:深度剖析JDK动态代理机制

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Class targetClass = null;
        Object target = null;

        try {
            // equals 方法处理
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                return equals(args[0]);
            }
            // hashCode 方法处理
            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                return hashCode();
            }
            if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // May be null. Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }

            // 获取当前方法的拦截器链
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // 如果拦截器链为空则直接调用
            if (chain.isEmpty()) {
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            }
            else {
                // 将拦截器链和目标类等进行封装,并调用 proceed 执行
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                retVal = invocation.proceed();
            }

            if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // 处理特殊情况
                retVal = proxy;
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
}

Cglib2AopProxy 代理类

Cglib2AopProxy 也是类似的,实现了 AopProxy 方法,在 getProxy 中使用 Cglib 的 Enhancer 来生成了代理类。

final class Cglib2AopProxy implements AopProxy, Serializable {
    public Object getProxy(ClassLoader classLoader) {
        try {
            Class rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class[] additionalInterfaces = rootClass.getInterfaces();
                for (Class additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }

            // 校验,final 类不可被代理
            validateClassIfNecessary(proxySuperClass);

            // 创建并设置 Enhancer...
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setInterceptDuringConstruction(false);

            Callback[] callbacks = getCallbacks(rootClass);
            // 设置拦截器
            enhancer.setCallbacks(callbacks);
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));

            Class[] types = new Class[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            enhancer.setCallbackTypes(types);

            // 创建代理类
            Object proxy;
            if (this.constructorArgs != null) {
                proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
            }
            else {
                proxy = enhancer.create();
            }

            return proxy;
        }
        catch (CodeGenerationException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (Exception ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • **** AOP 面向切面编程 底层原理 代理!!! 今天AOP课程1、 Spring 传统 AOP2、 Spri...
    luweicheng24阅读 1,316评论 0 1
  • AOP实现可分为两类(按AOP框架修改源代码的时机): 静态AOP实现:AOP框架在编译阶段对程序进行修改,即实现...
    数独题阅读 2,253评论 0 22
  • 基本知识 其实, 接触了这么久的 AOP, 我感觉, AOP 给人难以理解的一个关键点是它的概念比较多, 而且坑爹...
    永顺阅读 7,816评论 5 114
  • title: Spring_AOP源码分析date: 2016-11-03 01:15:11categories:...
    raincoffee阅读 1,708评论 2 36