jjzjj

Spring源码分析之AOP

穹柏 2023-09-16 原文

AOP是什么

面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计剖面导向程序设计),是计算机科学中的一种程序设计思想,旨在将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与装饰。

怎么在Spring里使用AOP

Spring里,AOP通过EnableAspectJAutoProxy注解开启。默认情况下,Spring会通过AopAutoConfiguration自动引入这个注解

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Advice.class)
    static class AspectJAutoProxyingConfiguration {

        @Configuration(proxyBeanMethods = false)
        @EnableAspectJAutoProxy(proxyTargetClass = false)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
        static class JdkDynamicAutoProxyConfiguration {

        }

        @Configuration(proxyBeanMethods = false)
        @EnableAspectJAutoProxy(proxyTargetClass = true)
        @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
                matchIfMissing = true)
        static class CglibAutoProxyConfiguration {

        }

    }
}

可以看到,如果我们不主动设置spring.aop.auto=false。那么Spring默认会启用AOP。接下来,我们可以通过在类上标注Aspect即可使用AOP

package org.example.aspect;

@Aspect
@Component
public class SampleAspect {

    @Pointcut("execution(* org.example.xxx.*.*(..))")
    private void executionPointcut() {

    }

    @After(value = "executionPointcut()")
    public void doAfter() {

    }
}

源码分析

1. AOP初始化

1.1 初始化AspectJAutoProxyRegistrar

EnableAspectJAutoProxy通过Import注解引入了AspectJAutoProxyRegistrar

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrarSpring在初始化AopAutoConfiguration时把所有通过Import注解引入的ImportBeanDefinitionRegistrar实现类拿出来进行初始化,并调用其registerBeanDefinitions函数

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

}

1.2 初始化AnnotationAwareAspectJAutoProxyCreator

AspectJAutoProxyRegistrar 则在registerBeanDefinitions注册了一个AnnotationAwareAspectJAutoProxyCreatorBeanDefinition

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            BeanDefinitionRegistry registry, @Nullable Object source) {

        return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    }

AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessorSpring会在初始化普通Bean之前初始化所有BeanPostProcessor

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {

        public void refresh() throws BeansException, IllegalStateException {
                    // 初始化BeanProcessor来拦截Bean的创建
                    registerBeanPostProcessors(beanFactory);

                    // 初始化所有剩下的非懒加载的Bean,比如我们写的Service
                    finishBeanFactoryInitialization(beanFactory);
        }
}

1.3 初始化切面方法跟切点

另外,AnnotationAwareAspectJAutoProxyCreator实现了InstantiationAwareBeanPostProcessorSpring 会在Bean创建时调用其postProcessBeforeInstantiation方法对Bean进行处理。

在第一次调用该方法时,AnnotationAwareAspectJAutoProxyCreator会初始化切面

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            //判断当前BeanName对应的Bean是否应该被代理
            //并将判断结果保存下来,避免后续的后处理方法重复计算
            //在第一次判断时,会在shouldSkip里扫描所有Bean进行切面初始化
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        //如果为AbstractAutoProxyCreator注入了自定义的TargetSourceCreator
        //则通过TargetSourceCreator创建的Bean都被被AOP代理
        //TargetSourceCreator默认为空
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            if (StringUtils.hasLength(beanName)) {
                this.targetSourcedBeans.add(beanName);
            }
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        return null;
    }
}

切面初始化

public class BeanFactoryAspectJAdvisorsBuilder {

        public List<Advisor> buildAspectJAdvisors() {
                List<String> aspectNames = this.aspectBeanNames;

                //如果还未进行初始化
                if (aspectNames == null) {
                    synchronized (this) {
                        aspectNames = this.aspectBeanNames;
                        if (aspectNames == null) {
                            List<Advisor> advisors = new ArrayList<>();
                            aspectNames = new ArrayList<>();

                            //拿到容器里所有的beanName
                            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                                    this.beanFactory, Object.class, true, false);
                            for (String beanName : beanNames) {

                                Class<?> beanType = this.beanFactory.getType(beanName, false);

                                //判断类上是否标注Aspect,以及判断该class是否已经被代码式的Aspectj处理过
                                if (this.advisorFactory.isAspect(beanType)) {
                                    aspectNames.add(beanName);
                                    AspectMetadata amd = new AspectMetadata(beanType, beanName);
                                    MetadataAwareAspectInstanceFactory factory =
                                                new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

                                        //从类中拿到所有带有Before、Around等注解的方法,
                                        //将这些方法包装成MethodInterceptor放入Advisor,MethodInterceptor#invoke为增强方法的调用入口
                                        //将Advisor排好顺序组成List返回
                                        List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

                                        this.advisorsCache.put(beanName, classAdvisors);
                                        advisors.addAll(classAdvisors);
                                }
                            }
                            this.aspectBeanNames = aspectNames;
                            return advisors;
                        }
                    }
                }
            }
}

Advisor排序

public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
        private static final Comparator<Method> adviceMethodComparator;

        static {

            Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
                    //按照注解顺序设置方法对应的advisor的顺序
                    //在AspectJAfterAdvice里,会先将请求继续向拦截器链后传播,
                    //对增强方法的调用是在后面的finnaly块里。所以这里的After顺序即使在AfterReturning前面也没关系
                    //另外,因为在finnly块里触发,所以即使后续的调用抛出了未捕获的异常,After指定的增强方法也会被执行
                    new InstanceComparator<>(
                            Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),

                    (Converter<Method, Annotation>) method -> {
                            //如果方法上没有标注上面的几个注解,则返回null,null会排在最后
                        AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                        return (ann != null ? ann.getAnnotation() : null);
                    });
            Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
            adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator);
        }
}

判断method是否属于切面方法


public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
        public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
                int declarationOrderInAspect, String aspectName) {

                //获取切点信息,如果candidateAdviceMethod不是切面方法,则返回null
                AspectJExpressionPointcut expressionPointcut = getPointcut(
                        candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
                if (expressionPointcut == null) {
                    return null;
                }

                return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                        this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
        }   

        private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
                //在方法上查找Aspectj的相关注解(Around、After等)
                AspectJAnnotation<?> aspectJAnnotation =
                        AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
                if (aspectJAnnotation == null) {
                    return null;
                }

                ...
        }
}

2. 生成代理对象

AbstractAutoProxyCreator实现了BeanPostProcessor,在创建Bean时,Spring会调用AbstractAutoProxyCreator#postProcessAfterInitializationBean进行处理

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

        @Override
        public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
            if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                    //对bean进行包装,返回代理bean
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
        }

        protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
                //如果bean有TargetSourceCreator创建,说明已经被代理过了,直接返回
                if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
                    return bean;
                }
                //拿出缓存的检测的结果进行判断
                if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
                    return bean;
                }
                //初步判断bean是否可以被代理
                if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
                    this.advisedBeans.put(cacheKey, Boolean.FALSE);
                    return bean;
                }

                //根据切点Point的表达式获得符合当前bean的所有advisor
                //如果当前bean不在切点的指向中,则返回DO_NOT_PROXY
                Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
                if (specificInterceptors != DO_NOT_PROXY) {
                    this.advisedBeans.put(cacheKey, Boolean.TRUE);
                    //创建代理对象,将所有advisor包装成DynamicAdvisedInterceptor,
                    //其intercept方法为所有增强方法的统一入口,这个类来自Spring
                    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;
    }
}

选择代理对象的创建方式

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {  
        @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            if (!NativeDetector.inNativeImage() &&
                    (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
                Class<?> targetClass = config.getTargetClass();

                //如果targetClass是接口类型或者是通过Proxy.getProxyClass生成的或者类名里包含$$Lambda
                //则使用JDK动态代理
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
                //使用cglib,ASM修改字节码的方式生产代理类
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }
}

3. 访问代理对象

DispatcherServlet 找到对应的实例跟方法后通过反射进行调用(前面Spring Mvc博文里已分析过),此时会遍历代理对象上的所有MethodInterceptor,

public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {

        public Object proceed() throws Throwable {
            //如果拦截器遍历完了,则调用目标方法
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }

            //遍历所有增强器MethodInterceptor,
            //遍历方式是在MethodInterceptor里调用MethodInvocation#proceed
            //每次进入该方法,都会使currentInterceptorIndex增加1,从而达成遍历
            Object interceptorOrInterceptionAdvice =
                    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

            //调用MethodInterceptor#invoke
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
}

增强方法实际执行的顺序图

Spring Aop增强方法调用顺序.png

接下来看看每个增强注解对应的MethodInterceptor处理类是怎么进行请求处理与传递的

1. AspectJAroundAdvice

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {

    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {
        ...
        ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
        ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
        JoinPointMatch jpm = getJoinPointMatch(pmi);
        //调用Around对应的增强方法,并将mi传如增强方法
        //mi持有了所有的拦截/增强器信息,通过Joinpoint#proceed实现请求的传递
        //所以,around增强方法里需要注意接收Joinpont的实例并调用其proceed方法
        return invokeAdviceMethod(pjp, jpm, null, null);
    }

}

2. MethodBeforeAdviceInterceptor

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

    private final MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {
        //调用before增强方法
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //向后传递
        return mi.proceed();
    }

}

3. AspectJAfterAdvice

public class AspectJAfterAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {

        public AspectJAfterAdvice(
                Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

            super(aspectJBeforeAdviceMethod, pointcut, aif);
        }

        @Override
        @Nullable
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                //先向后传递
                return mi.proceed();
            }
            finally {
                //执行After方法
                invokeAdviceMethod(getJoinPointMatch(), null, null);
            }
        }

}

4. AfterReturningAdviceInterceptor

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

    private final AfterReturningAdvice advice;

    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {

        //向后传递
        Object retVal = mi.proceed();
        //调用AfterReturning方法
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }

}

5. AspectJAfterThrowingAdvice

public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {

        public AspectJAfterThrowingAdvice(
                Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

            super(aspectJBeforeAdviceMethod, pointcut, aif);
        }

        @Override
        @Nullable
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                //向后传递
                return mi.proceed();
            }
            catch (Throwable ex) {
                //判断增强器上定义的异常类型是否匹配
                if (shouldInvokeOnThrowing(ex)) {
                    //调用AfterThrowing方法
                    invokeAdviceMethod(getJoinPointMatch(), null, ex);
                }
                throw ex;
            }
        }

        /**
         * In AspectJ semantics, after throwing advice that specifies a throwing clause
         * is only invoked if the thrown exception is a subtype of the given throwing type.
         */
        private boolean shouldInvokeOnThrowing(Throwable ex) {
            return getDiscoveredThrowingType().isAssignableFrom(ex.getClass());
        }

}

6. ExposeInvocationInterceptor

public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {

        private static final ThreadLocal<MethodInvocation> invocation =
                    new NamedThreadLocal<>("Current AOP method invocation");

        @Override
        @Nullable
        public Object invoke(MethodInvocation mi) throws Throwable {
            MethodInvocation oldInvocation = invocation.get();
            //将MethodInvocation绑定到当前线程的本地变量里,
            //从而实现在其他地方访问MethodInvocation
            //但不建议这样做,因为AOP对于被代理对象来说应该是无感知的,不应该产生这种依赖
            invocation.set(mi);
            try {
                return mi.proceed();
            }
            finally {
                invocation.set(oldInvocation);
            }
        }

}

有关Spring源码分析之AOP的更多相关文章

  1. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

    我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

  2. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

  3. spring.profiles.active和spring.profiles.include的使用及区别说明 - 2

    转自:spring.profiles.active和spring.profiles.include的使用及区别说明下文笔者讲述spring.profiles.active和spring.profiles.include的区别简介说明,如下所示我们都知道,在日常开发中,开发|测试|生产环境都拥有不同的配置信息如:jdbc地址、ip、端口等此时为了避免每次都修改全部信息,我们则可以采用以上的属性处理此类异常spring.profiles.active属性例:配置文件,可使用以下方式定义application-${profile}.properties开发环境配置文件:application-dev

  4. ruby-on-rails - Spring 不起作用。 [未初始化常量 Spring::SID::DL] - 2

    我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li

  5. 建模分析 | 平面2R机器人(二连杆)运动学与动力学建模(附Matlab仿真) - 2

    目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标

  6. 网站日志分析软件--让网站日志分析工作变得更简单 - 2

    网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.

  7. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  8. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  9. ABB-IRB-1200运动学分析MATLAB RVC工具分析+Simulink-Adams联合仿真 - 2

    一、机器人介绍        此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接

  10. 关于Qt程序打包后运行库依赖的常见问题分析及解决方法 - 2

    目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'

随机推荐