美文网首页
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(一)

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