zoukankan      html  css  js  c++  java
  • Spring AOP实现原理(递归拦截器)

    一、AOP(Aspect Orient Programming,面向切面编程)相关术语:

    1. 切面(Aspect):实现通用问题的类,例如日志,事务管理,定义了切入点和通知的类,通知和切入点共同组成了切面:时间、地点、做什么

    2. 通知(Advice):类似Spring拦截器或者Servlet过滤器,是方法,定义切面要做什么,何时使用,有before,after,around..

    3. 连接点(Joinpoint): 程序能够使用通知(Advice)的一个时机,即程序执行过程中明确的点,一般是方法的调用,或异常被抛出

    4. 切入点(Pointcut): 定义切面发生在哪里,带了通知的连接点,例如某个类或方法的名称,在程序中主要体现为切入点表达式

    5. 引入(Introduction): 向现有的类添加新的属性或方法

    6. 目标(Target): 被通知的对象

    7. 代理(Proxy):AOP框架创建的对象,代理就是目标对象的增强,Advice + Target = Proxy

    8. 织入(Weaving):把切面应用到目标对象时,创建新的代理对象的过程

    a. 编译时织入:AspectJ,静态AOP,生成的字节码融入了增强后的AOP对象,性能更好

    b. 加载时织入: Instrument

    c. 运行时织入:Spring AOP动态代理,在内存中临时生成一个AOP对象,并在特定的切点做增强处理,仅支持方法级别的切点

    二、Spring实现AOP的方式

    1. 经典的基于代理的AOP:通过在xml文件配置进行代理

    2. 自动代理的AOP:在配置文件中,切点跟通知自动匹配

    3. AOP标签配置到切面

    4. @AspectJ注解

    三、支持的通知类型:Before、After-returning、After-throwing、Around、Introduction

    四、Spring AOP工厂

    1. 如果AdvisedSupport通知器没有进行自定义设置,默认使用JDK动态代理

    2. 如果AdvisedSupport进行了设置,就判断要代理的类是不是接口,接口使用JDK动态代理,否则使用Cglib动态代理

    3. JDK动态代理和Cglib动态代理,都是基于 AdvisedSupport 的 MethodInterceptor 链实现的

        @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);
            }
        }

    五、基于JDK动态代理实现的AOP,要去实现InvocationHandler接口,调用invoke()方法

    1. Spring会根据AdvisedSupport获取一个方法拦截器MethodInterceptor的链条chain

    2. 如果chain为空,直接反射调用原方法

    3. 否则,调用proceed()方法,而此方法是一个递归方法

    @Override
        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 {
                if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                    // The target does not implement the equals(Object) method itself.
                    return equals(args[0]);
                }
                if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                    // The target does not implement the hashCode() method itself.
                    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();
                }
    
                // 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();
                }
    
                // Massage return value if necessary.
                Class<?> returnType = method.getReturnType();
                if (retVal != null && retVal == target && 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);
                }
            }
        }

    六、基于Cglib动态代理实现的AOP,也是基于AdvisedSupport实现Callback(),进行方法拦截

    private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
            // Parameters used for optimisation choices...
            boolean exposeProxy = this.advised.isExposeProxy();
            boolean isFrozen = this.advised.isFrozen();
            boolean isStatic = this.advised.getTargetSource().isStatic();
    
            // Choose an "aop" interceptor (used for AOP calls).
            Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
    
                ..............
    }

    七、递归拦截器链

    1. 所有拦截器都执行完了,才会调用真正的防范,否则就递归调用拦截器

    2. Before拦截器,先执行before再执行proceed,是拦截器链的的最后一环

    3. After拦截器,先执行proceed再执行after

    八、java.lang.instrument包进行静态代理实现,LTW加载时织入(Load Time Weaving)

    1.  在JVM启动时会装配并应用ClassTransformer,对类字节码进行转换,进而实现AOP的功能

    2. 使用起来比较麻烦,不推荐使用

    九、AspectJ静态代理

    参考:

    https://www.cnblogs.com/kevin-yuan/p/5571200.html

    https://blog.csdn.net/u012707422/article/details/93894174

    https://blog.csdn.net/u011983531/article/details/80359304

    https://www.jianshu.com/p/012f950206ca

    https://blog.csdn.net/u011983531/article/details/49391129

  • 相关阅读:
    iOS-Core-Animation-Advanced-Techniques(一)
    一个很有借鉴价值的编程故事
    11
    安卓学习路线
    weakSelf和strongSelf
    <二>iOS 开发Push的个人见解 图文并茂 详细到我写的想吐!!!!!!!!!
    <一>iOS 开发Push的个人见解 图文并茂 详细到我写的想吐!!!!!!!!!
    啊哈!算法 学习2 排序
    啊哈!算法 学习1 排序
    转载---CGImageSource对图像数据读取任务的抽象
  • 原文地址:https://www.cnblogs.com/june0816/p/11324491.html
Copyright © 2011-2022 走看看