美文网首页
7. AOP(一)

7. AOP(一)

作者: 凭窗听风 | 来源:发表于2019-05-12 22:12 被阅读0次

spring源码学习笔记,要点归纳和代码理解

前言

Aop是Spring提供的的另一个核心功能,本节将探讨以下几个问题:

  1. 什么是AOP,为什么要使用AOP;
  2. Spring中AOP是如何实现的

一、什么是AOP,为什么要使用AOP

对于某些注入持久化之类 关注面 倾向于横贯某个领域的天然对象边界,原则上可以从模块、封装的角度推理持久化策略。但在实践中,你却不得不将实现了持久化策略的代码铺展到许多对象中。我们用术语横贯式关注面来形容这类情况。同样,持久化框架和领域逻辑,孤立的看也是可以模块化的。问题在于横贯这些领域的情景。
于是诞生了AOP,AOP为Aspect Oriented Programming的缩写,既面向切面编程。在AOP中,被称为方面(Aspect)的模块构造指明了系统中哪些点的行为会以某种一致的方式被修改,从而支持某种特定的场景。这种说明是用某正简洁的声明或编程机制实现的。

二、Spring中的AOP实现

第一步:注册和解析<aop:aspectj-autoproxy/> 标签

Spring的AOP功能的实现核心类为AnnotationAwareAspectJAutoProxyCreator,其注册过程是通过自定义标签<aop:aspectj-autoproxy/>的机械完成的,主要包括以下几步:

  1. 注册AnnotationAwareAspectJAutoProxyCreator
  2. 处理proxy-target-class和expose-proxy属性

讲一下proxy-target-class和expose-proxy属性两个属性的作用:

  • proxy-target-class:
    Spring实现AOP使用JDK动态来历或者CGLIB为目标创建代理,如果目标类实现了至少一个接口,则Spring将使用JDK动态代理,所有目标实现的接口都将被代理。若该目标没有实现任何接口,则创建一个CGLIB代理。想要强制使用CGLIB代理,可以在配置文件中添加<aop:aspcetj-autoproxy proxy-target-class="true"/> 或在配置类上添加注解 @EnableAspectJAutoProxy(proxyTargetClass = true)。使用CGLIB实现代理将无法通知(advise)final方法,因为不能被复写。
  • expose-proxy:
    有时候目标对象内部的自我调用将无法实施切面中的增强,如下实例:
public class InnerMethodServiceImpl implements InnerMethodService {
    @Override
    public void methodA() {
        this.methodB();
    }

    @Override
    public void methodB() {
        System.out.println("method B is invoked");
    }
}

切面:

@Aspect
public class InnerMethodAspect {

    @Pointcut("execution(* com.pctf.aop.service.InnerMethodService.methodA())")
    public void methodAPoint() {

    }

    @Pointcut("execution(* com.pctf.aop.service.InnerMethodService.methodB())")
    public void methodBPoint() {

    }

    @Before("methodAPoint()")
    public void beforeMethodA() {
        System.out.println("before method A invoked");
    }
    @Before("methodBPoint()")
    public void beforeMethodB() {
        System.out.println("before method B invoked");
    }
}

执行结果如图,可见B方法的增强并没有被执行。



解决方法:

  1. 配置文件中添加<aop:aspectj-autoproxy expose-proxy="true"/>
  2. 将代码中的this.methodB()调用改为((InnerMethodService)AopContext.currentProxy()).methodB();

    可见B方法的增强也被执行了。

第二步:获取增强方法或增强器

AnnotationAwareAspectJAutoProxyCreator通过实现BeanPostProcessor接口,在bean实例化之后调用postProcessorAfterInitialization方法对bean实现代理,我们的分析也从这里开始。

@Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        // 获取增强器
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 创建代理
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        // 获取所有的增强器
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        // 获取适配当前bean 的增强器
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

增强器的获取主要包括以下几个步骤:

  1. 获取普通增强器,包括切点信息的获取和根据切点信息生成增强
  2. 增加同步实例化增强器
  3. 获取DeclareParents注解
    最终生成拦截器链

第三步 创建代理

创建代理的调用栈比较深,这里我就不贴每一步的方法,放一张调用栈的截图


发现其代理类最终通过DefaultAopProxyFactory类的createAopProxy创建代理生成AopProxy对象,然后调用该对象的getProxy方法获得。
拿出createAopProxy方法如下:
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() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

至此,我们看到熟悉的代理生成方法,Jdk代理和Cglib代理了.从if的条件中可以看到三个方面会影响具体使用哪种代理:

  1. optimize: 用来控制通过Cglib创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何优化,否则不推荐使用这个设置。
  2. proxytargetclass: 代理目标类本身而不是接口。
  3. hasNoUserSuppliedProxyInterfaces: 是否存在代理接口
    Jdk代理和Cglib代理有什么区别:
  4. Jdk动态代理只能对实现了接口的类生成代理
  5. Cglib可以针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以类和方法都不应该声明为final
    下面来详解下两种代理

Jdk动态代理

public class JdkProxyTest {

    public static void main(String[] args) {
        LoginService loginService = new LoginServiceImpl();
        loginService = (LoginService) getProxy(loginService);
        loginService.login("lucifer");
    }


    public static Object getProxy(Object target) {
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("method before invoke");
                Object result = method.invoke(target, args);
                System.out.println("method after invoke");
                return result;
            }
        };
        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

上面是一个Jdk代理的实例代码,可以看到实现代理主要包括以下几个步骤:

  1. 构造函数,传入要代理的对象,这里我直接方法参数传入
  2. 重写InvocationHandler中的invoke方法,这里包含了所有增强的逻辑
  3. getProxy方法,传入目标的类加载器,实现的接口和实现的InvocationHandler
    Spring中的实现原理与此相同,我们看JdkDynamicAopProxy中的getProxy方法:
public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

InvocationHandler参数传入的是自己,说明自己实现了invoke方法,并且所有的增强逻辑也在此实现.

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

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

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else 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;

            // 目标中的自我调用将无法实施增强,通过此属性暴露代理,维护一个ThreadLocal对象,保存当前线程正在创建的代理
            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // 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.
                // 没有拦截器链则直接反射调用方法,返回结果
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // We need to create a method invocation...
                // 将拦截器封装在ReflectiveMethodInvocation中
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

其主要逻辑已经注释在代码中了,不再复述。
上面函数最主要的工作是创建了一个拦截器链,并使用ReflectiveMethodInvocation类进行了封装。在proceed方法中注意调用。

public Object proceed() throws Throwable {
        //  We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
            if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

在proceed方法中,维护了拦截器链调用的计数器,依次递归执行拦截器链中的方法.直到执行结束后返回invokeJoinpoint

至此Jdk动态代理结束,返回代理后的对象.

Cglib动态代理
Cglib是一个强大的高性能代码生成包,底层通过使用一个小而快的字节码处理框架ASM,转换字节码并生成新的类。熟悉Jvm的同学应该知道,类的加载并不只依靠.class文件,实际上只要读取一个16进制的字节码文件就可以将类信息加载到Jvm中。
首先来实现一个Cglib代理的实例

public class CglibProxyTest {

    public static void main(String[] arge) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(LoginServiceImpl.class);
        enhancer.setCallback(new MethodInterceptorImpl());
        LoginService loginService = (LoginService) enhancer.create();
        loginService.login("lucifer");
    }

    private static class MethodInterceptorImpl implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before invoke");
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("after invoke");
            return result;
        }
    }
}

包括以下几个步骤:

  1. 创建增强器Enhancer
  2. 设置要代理的类
  3. 设置回调,既要增强的逻辑,实现MethodInterceptor接口的intercept方法
    Spring中Cglib的实现与此相同,其逻辑与Jdk实现代理中的invoke方法大同小异,都是先构造拦截器链,然后封装此链递归调用。区别是Cglib中使用CglibMethodInvocation。以下是CglibAopProxy中getProxy方法的代码。
@Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
        }

        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class<?> proxySuperClass = rootClass;
            if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }

            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass, classLoader);

            // Configure CGLIB 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.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

            Callback[] callbacks = getCallbacks(rootClass);
            Class<?>[] types = new Class<?>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            // fixedInterceptorMap only populated at this point, after getCallbacks call above
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);

            // Generate the proxy class and create a proxy instance.
            return createProxyClassAndInstance(enhancer, callbacks);
        }
        catch (CodeGenerationException | IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                    ": Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (Throwable ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

相关文章

  • 7. AOP(一)

    spring源码学习笔记,要点归纳和代码理解 前言 Aop是Spring提供的的另一个核心功能,本节将探讨以下几个...

  • Spring AOP介绍(一)之 Spring AOP 基础介绍

    Spring AOP介绍(一)之 Spring AOP 基础介绍 AOP是什么? AOP(Aspect Orien...

  • AOP之AspectJ - 代码注入

    AOP之AspectJ - 代码注入 [TOC] 一、AOP简介 1.1 什么是AOP编程 AOP是Aspect ...

  • spring-aop

    aop概念aop概念aop术语AOP实现方式1、spring-aop(使用xml文件实现AOP)2、AspectJ...

  • Spring基础知识 day03 动态代理&Spring中

    一、AOP 的相关概念 1.1 AOP 概述: 1.1.1 什么是 AOP: AOP全称是 Aspect Orie...

  • SpringBoot-AOP

    SpringBoot-AOP 使用AOP统一处理请求日志 1.AOP的概念 AOP:AOP是一种编程范式,与语言无...

  • Spring Boot之AOP

    一、AOP简介(摘抄) aop 全称 Aspect Oriented Programming ,面向切面,AOP主...

  • Spring AOP

    一、 AOP 简介 1.1 什么是 AOP AOP (Aspect Orient Programming),直译过...

  • Spring AOP-基础使用

    零、本文纲要 一、了解AOP1、认识AOP2、AOP作用3、AOP核心概念 二、AOP快速入门1、基础准备2、AO...

  • AOP/Spring AOP/Filter/拦截器 释义

    AOP AOP,面向切面编程,是一种编程思想。 Java中常见的AOP技术 Spring AOP Spring A...

网友评论

      本文标题:7. AOP(一)

      本文链接:https://www.haomeiwen.com/subject/cwihaqtx.html