zoukankan      html  css  js  c++  java
  • Spring Aop之Cglib实现原理详解

    Spring Aop实现对目标对象的代理,AOP的两种实现方式:Jdk代理和Cglib代理。这两种代理的区别在于,Jdk代理与目标类都会实现同一个接口,并且在代理类中会调用目标类中被代理的方法,调用者实际调用的则是代理类的方法,通过这种方式我们就可以在代理类中织入切面逻辑;Jdk代理存在的问题在于目标类被代理的方法必须实现某个接口,Cglib代理则是为了解决这个问题而存在的,其实现代理的方式是通过为目标类动态生成一个子类,通过在子类中织入相应逻辑来达到织入代理逻辑的目的。

    关于Jdk代理和Cglib代理,其优缺点主要在于:

    • Jdk代理生成的代理类只有一个,因而其编译速度是非常快的;而由于被代理的目标类是动态传入代理类中的,Jdk代理的执行效率相对来说低一点,这也是Jdk代理被称为动态代理的原因;
    • Cglib代理需要为每个目标类生成相应的子类,因而在实际运行过程中,其可能会生成非常多的子类,过多的子类始终不是太好的,因为这影响了虚拟机编译类的效率;但由于在调用过程中,代理类的方法是已经静态编译生成了的,因而Cglib代理的执行效率相对来说高一些。

    本文主要讲解Spring Aop是如何通过Cglib代理实现将切面逻辑织入目标类的。

    1. AopProxy织入对象生成

    前面我们讲过,Spring Aop织入切面逻辑的入口方法是AbstractAutoProxyCreator.createProxy()方法,如下是该方法的源码:

    
    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
        
        // 如果当前beanFactory实现了ConfigurableListableBeanFactory接口,则将需要被代理的
        // 对象暴露出来
        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) 
                this.beanFactory, beanName, beanClass);
        }
    
        // 创建代理工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        // 复制proxyTargetClass,exposeProxy等属性
        proxyFactory.copyFrom(this);
    
        // 如果当前设置了不使用Cglib代理目标类,则判断目标类是否设置了preserveTargetClass属性,
        // 如果设置了,则还是强制使用Cglib代理目标类;如果没有设置,则判断目标类是否实现了相关接口,
        // 没有设置,则还是使用Cglib代理。需要注意的是Spring默认使用的是Jdk代理来织入切面逻辑。
        if (!proxyFactory.isProxyTargetClass()) {
            // 判断目标类是否设置了preserveTargetClass属性
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            } else {
                // 判断目标类是否实现了相关接口
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
    
        // 将需要织入的切面逻辑都转换为Advisor对象
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        // 提供的hook方法,供子类实现以实现对代理工厂的定制
        customizeProxyFactory(proxyFactory);
    
        proxyFactory.setFrozen(this.freezeProxy);
        // 当前判断逻辑默认返回false,子类可进行重写,对于AnnotationAwareAspectJAutoProxyCreator,
        // 其重写了该方法返回true,因为其已经对获取到的Advisor进行了过滤,后面不需要在对目标类进行重新
        // 匹配了
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
    
        // 生成代理类
        return proxyFactory.getProxy(getProxyClassLoader());
    }
    
    

    可以看到,在生成代理类之前,主要做了两件事:①判断使用Jdk代理还是Cglib代理;②设置相关的属性。这里我们继续看最后的ProxyFactory.getProxy()方法:

    
    public Object getProxy(@Nullable ClassLoader classLoader) {
        // 首先获取AopProxy对象,其主要有两个实现:JdkDynamicAopProxy和ObjenesisCglibAopProxy,
        // 分别用于Jdk和Cglib代理类的生成,其getProxy()方法则用于获取具体的代理对象
        return createAopProxy().getProxy(classLoader);
    }
    
    

    上面的createAopProxy()方法可以理解为一个工厂方法,返回值是一个AopProxy类型的对象,其内部根据具体的条件生成相应的子类对象,即JdkDynamicAopProxy和ObjenesisCglibAopProxy。后面则通过调用AopProxy.getProxy()方法获取代理过的对象。如下是createAopProxy()方法的实现逻辑:

    
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 判断当前类是否需要进行运行时优化,或者是指定了使用Cglib代理的方式,再或者是目标类没有用户提供的
        // 相关接口,则使用Cglib代理实现代理逻辑的织入
        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.");
            }
            // 如果被代理的类是一个接口,或者被代理的类是使用Jdk代理生成的类,此时还是使用Jdk代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            
            // 返回Cglib代理织入类对象
            return new ObjenesisCglibAopProxy(config);
        } else {
            // 返回Jdk代理织入类对象
            return new JdkDynamicAopProxy(config);
        }
    }
    
    

    这里可以看到,本文需要讲解的Cglib代理逻辑的织入就在ObjenesisCglibAopProxy.getProxy()方法中。

    2. 代理逻辑的织入

    关于代理逻辑的织入,其实现主体还是通过Enhancer来实现,即通过需要织入的Advisor列表,生成Callback对象,并将其设置到Enhancer对象中,最后通过Enhancer生成目标对象。如下是AopProxy.getProxy()方法的源码:

    
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " 
                + this.advised.getTargetSource());
        }
    
        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, 
                         "Target class must be available for creating a CGLIB proxy");
    
            // 判断当前类是否是已经通过Cglib代理生成的类,如果是的,则获取其原始父类,
            // 并将其接口设置到需要代理的接口中
            Class<?> proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                // 获取父类
                proxySuperClass = rootClass.getSuperclass();
                // 获取父类实现的接口,并将其设置到需要代理的接口中
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }
    
            // 对目标类进行检查,主要检查点有三个:
            // 1. 目标方法不能使用final修饰;
            // 2. 目标方法不能是private类型的;
            // 3. 目标方法不能是包访问权限的;
            // 这三个点满足任何一个,当前方法就不能被代理,此时该方法就会被略过
            validateClassIfNecessary(proxySuperClass, classLoader);
    
            // 创建Enhancer对象,并且设置ClassLoader
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            // 这里AopProxyUtils.completeProxiedInterfaces()方法的主要目的是为要生成的代理类
            // 增加SpringProxy,Advised,DecoratingProxy三个需要实现的接口。这里三个接口的作用如下:
            // 1. SpringProxy:是一个空接口,用于标记当前生成的代理类是Spring生成的代理类;
            // 2. Advised:Spring生成代理类所使用的属性都保存在该接口中,
            //    包括Advisor,Advice和其他相关属性;
            // 3. DecoratingProxy:该接口用于获取当前代理对象所代理的目标对象的Class类型。
            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();
            }
    
            // 设置代理类中各个方法将要使用的切面逻辑,这里ProxyCallbackFilter.accept()方法返回
            // 的整型值正好一一对应上面Callback数组中各个切面逻辑的下标,也就是说这里的CallbackFilter
            // 的作用正好指定了代理类中各个方法将要使用Callback数组中的哪个或哪几个切面逻辑
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, 
                this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);
    
            // 生成代理对象
            return createProxyClassAndInstance(enhancer, callbacks);
        } catch (CodeGenerationException | 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 (Throwable ex) {
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }
    
    

    可以看到,这里的AopProxy.getProxy()方法就是生成代理对象的主干逻辑。上面的逻辑中主要有两个部分需要重点讲解:①如果获取Callback数组;②CallbackFilter的作用。关于第一点,我们后面会进行重点讲解,至于第二点,这里我们需要理解的就是CallbackFilter.accept()方法接收一个Method类型的参数,该参数也即当前要生成的代理逻辑的方法,这里的accept()方法将返回目标当前要织入代理逻辑的方法所需要使用的切面逻辑,也即Callback对象在Callback数组中的下标。关于CallbackFilter的使用原理,读者可以阅读实战CGLib系列之proxy篇(二):回调过滤CallbackFilter这篇文章。下面我们继续阅读getCallbacks()的源码:

    
    private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();
    
        // 用户自定义的代理逻辑的主要织入类
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
    
        Callback targetInterceptor;
        // 判断如果要暴露代理对象,如果是,则使用AopContext设置将代理对象设置到ThreadLocal中
        // 用户则可以通过AopContext获取目标对象
        if (exposeProxy) {
            // 判断被代理的对象是否是静态的,如果是静态的,则将目标对象缓存起来,每次都使用该对象即可,
            // 如果目标对象是动态的,则在DynamicUnadvisedExposedInterceptor中每次都生成一个新的
            // 目标对象,以织入后面的代理逻辑
            targetInterceptor = isStatic ?
              new StaticUnadvisedExposedInterceptor(
                this.advised.getTargetSource().getTarget()) :
              new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
        } else {
            // 下面两个类与上面两个的唯一区别就在于是否使用AopContext暴露生成的代理对象
            targetInterceptor = isStatic ?
                new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
            new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
        }
    
        // 当前Callback用于一般的不用背代理的方法,这些方法
        Callback targetDispatcher = isStatic ?
            new StaticDispatcher(this.advised.getTargetSource().getTarget()) 
            : new SerializableNoOp();
    
        // 将获取到的callback组装为一个数组
        Callback[] mainCallbacks = new Callback[] {
            aopInterceptor,  // 用户自己定义的拦截器
            targetInterceptor,  // 根据条件是否暴露代理对象的拦截器
            new SerializableNoOp(),  // 不做任何操作的拦截器
            targetDispatcher, this.advisedDispatcher,  // 用于存储Advised对象的分发器
            new EqualsInterceptor(this.advised),  // 针对equals方法调用的拦截器
            new HashCodeInterceptor(this.advised)  // 针对hashcode方法调用的拦截器
        };
    
        Callback[] callbacks;
        // 如果目标对象是静态的,也即可以缓存的,并且切面逻辑的调用链是固定的,
        // 则对目标对象和整个调用链进行缓存
        if (isStatic && isFrozen) {
            Method[] methods = rootClass.getMethods();
            Callback[] fixedCallbacks = new Callback[methods.length];
            this.fixedInterceptorMap = new HashMap<>(methods.length);
    
            for (int x = 0; x < methods.length; x++) {
                // 获取目标对象的切面逻辑
                List<Object> chain = 
                    this.advised.getInterceptorsAndDynamicInterceptionAdvice(
                    methods[x], rootClass);
                fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                    chain, this.advised.getTargetSource().getTarget(), 
                    this.advised.getTargetClass());
                // 对调用链进行缓存
                this.fixedInterceptorMap.put(methods[x].toString(), x);
            }
    
            // 将生成的静态调用链存入Callback数组中
            callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
            System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
            System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, 
                fixedCallbacks.length);
            // 这里fixedInterceptorOffset记录了当前静态的调用链的切面逻辑的起始位置,
            // 这里记录的用处在于后面使用CallbackFilter的时候,如果发现是静态的调用链,
            // 则直接通过该参数获取相应的调用链,而直接略过了前面的动态调用链
            this.fixedInterceptorOffset = mainCallbacks.length;
        } else {
            callbacks = mainCallbacks;
        }
        return callbacks;
    }
    
    

    这里的getCallbacks()方法主要做了三件事:①获取目标对象的动态调用链;②判断是否设置了exposeProxy属性,如果设置了,则生成一个可以暴露代理对象的Callback对象,否则生成一个不做任何处理直接调用目标对象的Callback对象;③判断目标对象是否是静态的,并且当前的切面逻辑是否是固定的,如果是,则将目标对象和调用链进行缓存,以便后续直接调用。这里需要说明的一个点在于第三点,因为在判断目标对象为静态对象,并且调用链是固定的时候,会将目标对象和调用链进行缓存,并且封装到指定的Callback对象中。这里读者可能会疑问为什么动态调用链和静态调用链都进行了缓存,这和前面讲解的CallbackFilter是息息相关的,因为上述代码最后使用fixedInterceptorOffset记录了当前静态调用链在数组中存储的位置,我们前面也讲了,Enhancer可以通过CallbackFilter返回的整数值来动态的指定从当前对象Callback数组中的第几个环绕逻辑开始织入,这里就会使用到fixedInterceptorOffset。从上述代码中可以看出,用户自定义的调用链是在DynamicAdvisedInterceptor中生成的(关于静态调用链的生成实际上是同样的逻辑,只不过静态调用链会被缓存),这里我们看看DynamicAdvisedInterceptor的实现源码:

    
    public Object intercept(Object proxy, Method method, Object[] args, 
            MethodProxy methodProxy) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        Object target = null;
        // 通过TargetSource获取目标对象
        TargetSource targetSource = this.advised.getTargetSource();
        try {
            // 判断如果需要暴露代理对象,则将当前代理对象设置到ThreadLocal中
            if (this.advised.exposeProxy) {
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
    
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);
            // 获取目标对象切面逻辑的环绕链
            List<Object> chain = this.advised
                .getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            Object retVal;
            if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                // 对参数进行处理,以使其与目标方法的参数类型一致,尤其对于数组类型,
                // 会单独处理其数据类型与实际类型一致
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                // 因为没有切面逻辑需要织入,这里直接调用目标方法
                retVal = methodProxy.invoke(target, argsToUse);
            } else {
                // 通过生成的调用链,对目标方法进行环绕调用
                retVal = new CglibMethodInvocation(proxy, target, method, 
                    args, targetClass, chain, methodProxy).proceed();
            }
            
            // 对返回值进行处理,如果返回值就是当前目标对象,那么将代理生成的代理对象返回;
            // 如果返回值为空,并且返回值类型是非void的基本数据类型,则抛出异常;
            // 如果上述两个条件都不符合,则直接将生成的返回值返回
            retVal = processReturnType(proxy, target, method, retVal);
            return retVal;
        } finally {
            // 如果目标对象不是静态的,则调用TargetSource.releaseTarget()方法释放目标对象
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }
            
            // 将代理对象设置为前面(外层逻辑)调用设置的对象,以防止暴露出来的代理对象不一致
            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
    
    

    这里intercept()方法里主要逻辑有两点:①为目标对象生成切面逻辑调用链;②通过切面逻辑对目标对象进行环绕,并且进行调用。关于这两点,我们都会进行讲解,这里我们首先看看Cglib是如何生成调用链的,如下是getInterceptorsAndDynamicInterceptionAdvice()方法最终调用的源码,中间略过了部分比较简单的调用:

    
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    			Advised config, Method method, @Nullable Class<?> targetClass) {
    
        List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? 
            targetClass : method.getDeclaringClass());
        // 判断切面逻辑中是否有IntroductionAdvisor类型的Advisor
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    
        for (Advisor advisor : config.getAdvisors()) {
            if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 这里判断切面逻辑的调用链是否提前进行过过滤,如果进行过,则不再进行目标方法的匹配,
                // 如果没有,则再进行一次匹配。这里我们使用的AnnotationAwareAspectJAutoProxyCreator
                // 在生成切面逻辑的时候就已经进行了过滤,因而这里返回的是true,本文最开始也对这里进行了讲解
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut()
                    .getClassFilter().matches(actualClass)) {
                    // 将Advisor对象转换为MethodInterceptor数组
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    // 这里进行匹配的时候,首先会检查是否为IntroductionAwareMethodMatcher类型的
                    // Matcher,如果是,则调用其定义的matches()方法进行匹配,如果不是,则直接调用
                    // 当前切面的matches()方法进行匹配。这里由于前面进行匹配时可能存在部分在静态匹配时
                    // 无法确认的方法匹配结果,因而这里调用是必要的,而对于能够确认的匹配逻辑,这里调用
                    // 也是非常迅速的,因为前面已经对匹配结果进行了缓存
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        // 判断如果是动态匹配,则使用InterceptorAndDynamicMethodMatcher对其进行封装
                        if (mm.isRuntime()) {
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(
                                    new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        } else {
                            // 如果是静态匹配,则直接将调用链返回
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            } else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                // 判断如果为IntroductionAdvisor类型的Advisor,则将调用链封装为Interceptor数组
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            } else {
                // 这里是提供的使用自定义的转换器对Advisor进行转换的逻辑,因为getInterceptors()方法中
                // 会使用相应的Adapter对目标Advisor进行匹配,如果能匹配上,通过其getInterceptor()方法
                // 将自定义的Advice转换为MethodInterceptor对象
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
    
        return interceptorList;
    }
    
    

    这里获取调用链的逻辑其实比较简单,其最终的目的就是将Advisor数组一个一个的封装为Interceptor对象。在进行Advisor封装的时候,这里分为了三种类型:

    • 如果目标切面逻辑是一般的切面逻辑,即PointcutAdvisor,则会在运行时对目标方法进行动态匹配,因为前面可能存在还不能确认的是否应该应用切面逻辑的方法;
    • 如果切面逻辑是IntroductionAdvisor的,则将其封装为Interceptor类型的数组;
    • 如果以上两个都不是,说明切面逻辑可能是用户自定义的切面逻辑,这里就通过注册的AdvisorAdapter进行匹配,如果某个Adapter能够支持当前Advisor的转换,则调用其getInterceptor()方法将Advisor转换为MethodInterceptor返回。

    下面我们看看Cglib是如何通过生成的切面调用链将目标对象进行环绕的。前面我们讲了,将切面逻辑进行织入的逻辑在CglibMethodInvocation中,实际上其调用逻辑在其proceed()方法中,这里我们直接看该方法的源码:

    
    public Object proceed() throws Throwable {
        // 这里currentInterceptorIndex记录了当前调用链中正在调用的Intercepor的下标,该数值初始为-1
        if (this.currentInterceptorIndex == 
            this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            // 如果调用链为空,则直接调用目标方法
            return invokeJoinpoint();
        }
    
        // 获取下一个需要织入的Interceptor逻辑
        Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            // 对动态的方法进行匹配,如果匹配成功,才进行调用,否则直接进行下一个Interceptor的调用
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            } else {
                return proceed();
            }
        } else {
            // 如果不需要进行动态匹配,则直接进行下一步的调用
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }
    
    

    这里proceed()方法的逻辑比较简单,其使用一个索引记录了当前正在调用的Interceptor在调用链中的位置,并且依次对调用链进行调用,从而实现将切面逻辑织入目标对象的目的。这里最终对目标对象的调用的逻辑在invokeJoinpoint()方法中。

    3. 小结

    本文首先讲解Spring是如何通过配置的参数来选择使用哪种代理方式的,然后重点讲解了Spring Aop是如何使用Cglib代理实现代理逻辑的织入的。

    原文链接:https://my.oschina.net/zhangxufeng/blog/1933830

  • 相关阅读:
    布局(layout)文件图形界面不能显示:An error has occurred. See error log for more details. java.lang.NullPointe
    Mac下无法推出硬盘
    Excel导入导出数据库(MVC)
    json导入数据库
    XML导入数据库
    Excel表格导入数据库
    Lambda高级查询
    Linq高级查询
    多线程
    反射
  • 原文地址:https://www.cnblogs.com/qixidi/p/10251210.html
Copyright © 2011-2022 走看看