范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

SpringAOP源码解析注解式切面增强机制

  IoC 和 AOP 被称为 Spring 两大基础模块,支撑着上层扩展的实现和运行。虽然 AOP 同样建立在 IoC 的实现基础之上,但是作为对 OOP(Object-Oriented Programing) 的补充,AOP(Aspect-Oriented Programming) 在程序设计领域拥有其不可替代的适用场景和地位。Spring AOP 作为 AOP 思想的实现,被誉为 Spring 框架的基础模块也算是实至名归。Spring 在 1.0 版本的时候就引入了对 AOP 的支持,并且随着版本的迭代逐渐提供了基于 XML 配置、注解,以及 schema 配置的使用方式,考虑到实际开发中使用注解配置的方式相对较多,所以本文主要分析注解式 AOP 的实现和运行机制。注解式 AOP 示例
  首先我们还是通过一个简单的示例演示一下注解式 AOP 的具体使用。假设我们声明了一个 IService 接口,并提供了相应的实现类 ServiceImpl,如下:public interface IService {     void sayHello();     void sayHelloTo(String name);     void sayByebye();     void sayByebyeTo(String name); }  @Service public class ServiceImpl implements IService {      @Override     public void sayHello() {         this.sayHelloTo("zhenchao");     }      @Override     public void sayHelloTo(String name) {         System.out.println("hello, " + name);     }      @Override     public void sayByebye() {         this.sayByebyeTo("zhenchao");     }      @Override     public void sayByebyeTo(String name) {         System.out.println("byebye, " + name);     }  }
  现在我们希望借助 Spring AOP 实现对方法调用的打点功能。首先我们需要定义一个切面:@Aspect @Component public class MetricAspect {      @Before("execution(* sayHello*(..))")     public void beforeMetrics4sayHello(JoinPoint point) {         System.out.println("[BEFORE] metrics for method: " + point.getSignature().getName());     }      @Around("execution(* say*(..))")     public Object aroundMetrics4say(ProceedingJoinPoint point) throws Throwable {         System.out.println("[AROUND] before metrics for method: " + point.getSignature().getName());         Object obj = point.proceed();         System.out.println("[AROUND] after metrics for method: " + point.getSignature().getName());         return obj;     }      @After("execution(* sayByebye*(..))")     public void afterMetrics4sayByebye(JoinPoint point) {         System.out.println("[AFTER] metrics for method: " + point.getSignature().getName());     }  }
  通过 @Aspect 注解标记 MetricAspect 是一个切面,通过注解 @Before、@After,以及 @Around,我们在切面中定义了相应的前置、后置,以及环绕增强。然后我们需要在 XML 配置中添加一行如下配置以启用注解式 AOP:
  现在,我们就算大功告成了。
  当然,上面的实现只是注解式 AOP 使用的一个简单示例,并没有覆盖所有的特性。对于 Spring AOP 特性的介绍不属于本文的范畴,不过我们还是会在下面分析源码的过程中进行针对性的介绍。注解式 AOP 实现机制
  下面从启用注解式 AOP 的那一行配置切入,即  标签。前面在分析 Spring IoC 实现的文章中,曾专门分析过 Spring 默认标签和自定义标签的解析过程。对于一个标签而言,除了标签的定义,还需要有对应的标签的解析器,并在 Spring 启动时将标签及其解析器注册到 Spring 容器中。标签  的注册过程由 AopNamespaceHandler#init 方法实现:// 注册  标签及其解析器 this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
  AspectJAutoProxyBeanDefinitionParser 类是标签  的解析器,该类实现了 BeanDefinitionParser 接口,并实现了 BeanDefinitionParser#parse 接口方法,属于标准的标签解析器定义。Spring 容器在启动时会调用 AspectJAutoProxyBeanDefinitionParser#parse 方法解析标签,实现如下:public BeanDefinition parse(Element element, ParserContext parserContext) {     // 注册标签解析器,默认使用 AnnotationAwareAspectJAutoProxyCreator     AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);     // 解析  子标签,记录到 BeanDefinition 到 includePatterns 属性中     this.extendBeanDefinition(element, parserContext);     return null; }
  该方法做了两件事情:注册标签解析器和处理  子标签。本文我们重点来看标签解析器的注册过程,即 AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法:public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {     // 1. 注册或更新代理创建器 ProxyCreator 的 BeanDefinition 对象     BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(             parserContext.getRegistry(), parserContext.extractSource(sourceElement));     // 2. 获取并处理标签的 proxy-target-class 和 expose-proxy 属性     useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);     // 3. 注册组件,并发布事件通知     registerComponentIfNecessary(beanDefinition, parserContext); }
  我们在代码注释中标明了该方法所做的 3 件事情,其中 1 和 2 是我们分析的关键,首先来看 1 过程所做的事情:public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(         BeanDefinitionRegistry registry, @Nullable Object source) {     return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); }  private static BeanDefinition registerOrEscalateApcAsRequired(         Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {      Assert.notNull(registry, "BeanDefinitionRegistry must not be null");      // 如果名为 org.springframework.aop.config.internalAutoProxyCreator 的 bean 已经在册     if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {         BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);         // 已经在册的 ProxyCreator 与当前期望的类型不一致,则依据优先级进行选择         if (!cls.getName().equals(apcDefinition.getBeanClassName())) {             int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());             int requiredPriority = findPriorityForClass(cls);             // 选择优先级高的 ProxyCreator 更新注册             if (currentPriority < requiredPriority) {                 apcDefinition.setBeanClassName(cls.getName());             }         }         return null;     }      // 没有对应在册的 ProxyCreator,注册一个新的     RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);     beanDefinition.setSource(source);     beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);     beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);     registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);     return beanDefinition; }
  上述实现的逻辑还是挺简单的,即注册一个名为 org.springframework.aop.config.internalAutoProxyCreator 的 BeanDefinition,我们称之为代理创建器(ProxyCreator)。这里使用的默认实现为 AnnotationAwareAspectJAutoProxyCreator 类,如果存在多个候选实现,则选择优先级最高的进行注册。
  接下来看一下过程 2,这一步主要是用来解析标签  的 proxy-target-class 和 expose-proxy 属性配置,由 AopNamespaceUtils#useClassProxyingIfNecessary 方法实现:private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {     if (sourceElement != null) {         /*          * 获取并处理 proxy-target-class 属性:          * - false 表示使用 java 原生动态代理          * - true 表示使用 CGLib 动态          *          * 但是对于一些没有接口实现的类来说,即使设置为 false 也会使用 CGlib 进行代理          */         boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));         if (proxyTargetClass) {             // 为之前注册的 ProxyCreator 添加一个名为 proxyTargetClass 的属性,值为 true             AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);         }          /*          * 获取并处理 expose-proxy 标签,实现对于内部方法调用的 AOP 增强          */         boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));         if (exposeProxy) {             // 为之前注册的 ProxyCreator 添加一个名为 exposeProxy 的属性,值为 true             AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);         }     } }
  其中 proxy-target-class 属性用来配置是否使用 CGLib 代理,而 expose-proxy 属性则用来配置是否对内部方法调用启用 AOP 增强。属性 proxy-target-class 的作用大家应该都比较熟悉,下面介绍一下 expose-proxy 属性。前面给出的 AOP 示例中,我们在 IService#sayHello 方法中调用了 IService#sayHelloTo 方法,虽然两个方法都满足对应的 AOP 增强定义,但是只有 IService#sayHello 方法被增强了,这主要是因为 IService#sayHelloTo 方法是在对象内部调用的,调用该方法的对象并不是代理对象。如果期望内部调用时也能够被增强,我们需要配置 expose-proxy=true,并修改 IService#sayHello 方法对于 IService#sayHelloTo 方法的调用方式:public void sayHello() {     ((IService) AopContext.currentProxy()).sayHelloTo("zhenchao"); }
  上面分析了这么多,总的说来就是向 Spring 容器中注册了一个 AnnotationAwareAspectJAutoProxyCreator 类型的 ProxyCreator,并将配置的 proxy-target-class 和 expose-proxy 属性添加到对应 BeanDefinition 的属性列表中。那么 AnnotationAwareAspectJAutoProxyCreator 到底是来做什么的呢?我们先来看一下它的类继承关系图:
  从类继承关系图中可以看到该类实现了 BeanPostProcessor 接口,该接口定义如下:public interface BeanPostProcessor {      @Nullable     default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {         return bean;     }      @Nullable     default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {         return bean;     }  }
  由之前对 Spring IoC 容器启动过程的分析,我们知道在容器启动过程中会在初始化 bean 实例的前后分别调用 BeanPostProcessor 中定义的这两个方法。针对这两个方法的实现主要位于继承链的 AbstractAutoProxyCreator 类中,并且主要是实现了 BeanPostProcessor#postProcessAfterInitialization 方法:public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {     if (bean != null) {         // 如果 beanName 不为空则直接使用 beanName(FactoryBean 则使用 &{beanName}),否则使用 bean 的 className         Object cacheKey = this.getCacheKey(bean.getClass(), beanName);         if (!this.earlyProxyReferences.contains(cacheKey)) {             // 尝试对 bean 进行增强,创建返回增强后的代理对象             return this.wrapIfNecessary(bean, beanName, cacheKey);         }     }     return bean; }
  该方法的核心在于调用 AbstractAutoProxyCreator#wrapIfNecessary 方法尝试基于 AOP 配置对当前 bean 进行增强,并返回增强后的代理对象。方法 AbstractAutoProxyCreator#wrapIfNecessary 的实现如下:protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {     // 已经处理过,直接返回     if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {         return bean;     }     // 不需要进行增强的 bean 实例,直接跳过     if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {         return bean;     }     // 对于 AOP 的基础支撑类,或者指定不需要被代理的类,设置为不进行代理     if (this.isInfrastructureClass(bean.getClass()) || this.shouldSkip(bean.getClass(), beanName)) {         this.advisedBeans.put(cacheKey, Boolean.FALSE);         return bean;     }      // 获取适用于当前 bean 的 Advisor     Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);     // 基于获取到的 Advisor 为当前 bean 创建代理对象     if (specificInterceptors != DO_NOT_PROXY) {         this.advisedBeans.put(cacheKey, Boolean.TRUE);         Object proxy = this.createProxy(                 bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));         this.proxyTypes.put(cacheKey, proxy.getClass());         return proxy;     }      this.advisedBeans.put(cacheKey, Boolean.FALSE);     return bean; }
  上述方法主要的工作是对 bean 实例进行筛选,过滤掉那些已经增强过的、支持 AOP 基础运行的,以及指定不需要被代理的 bean 实例。对于剩下的 bean 实例来说,首先会调用 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法获取适用于当前 bean 的增强器(Advisor),并基于这些增强器调用 AbstractAutoProxyCreator#createProxy 方法为当前 bean 创建增强后的代理对象。筛选适用于 bean 的增强器
  我们首先来看一下筛选适用于当前 bean 的合格增强器的过程,实现位于 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法中:protected Object[] getAdvicesAndAdvisorsForBean(         Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {     // 获取适用于当前 bean 的 Advisor     List advisors = this.findEligibleAdvisors(beanClass, beanName);     // 没有合格的 Advisor,不进行代理     if (advisors.isEmpty()) {         return DO_NOT_PROXY; // null     }     return advisors.toArray(); }  protected List findEligibleAdvisors(Class<?> beanClass, String beanName) {     // 获取所有候选的 Advisor(包括注解的、XML 中配置的)     List candidateAdvisors = this.findCandidateAdvisors();     // 从所有 Advisor 中寻找适用于当前 bean 的 Advisor     List eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);     // 如果 Advisor 不为空,则在最前面追加一个 ExposeInvocationInterceptor     this.extendAdvisors(eligibleAdvisors);     // 对 Advisor 进行排序     if (!eligibleAdvisors.isEmpty()) {         eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);     }     return eligibleAdvisors; }
  整个方法的执行流程很简单,获取所有的候选增强器,并从中找出适用于当前 bean 的增强器。首先来看获取所有候选增强器的过程,实现位于 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors 方法中:protected List findCandidateAdvisors() {     // 调用父类的 findCandidateAdvisors 方法,兼容父类查找 Advisor 的规则     List advisors = super.findCandidateAdvisors();     // 获取所有注解定义的 Advisor     if (this.aspectJAdvisorsBuilder != null) {         advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());     }     return advisors; }
  方法首先调用了父类的实现,这主要是为了兼容父类查找候选增强器的规则,例如我们的示例中使用的是注解方式定义的增强,但是父类却是基于 XML 配置的方式查找增强器,这里的兼容能够让我们在以注解方式编程时兼容其它以 XML 配置的方式定义的增强。下面还是将主要精力放在解析注解式增强定义上,该过程位于 BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors 方法中。不过该方法实现比较冗长,但是逻辑却很清晰,所以这里主要概括一下其执行流程:获取所有类型 bean 实例对应的 beanName 集合;过滤不是切面类型的 bean 对应的 beanName,即没有被 @Aspect 注解,或包含以 ajc$ 开头的字段,同时支持覆盖 BeanFactoryAspectJAdvisorsBuilder#isEligibleBean 方法扩展过滤规则;对于切面 bean 类型,获取 bean 中定义的所有切点,并为每个切点生成对应的增强器;缓存解析得到的增强器,避免重复解析。
  上述流程中我们重点看一下过程 3,实现位于 ReflectiveAspectJAdvisorFactory#getAdvisors 方法中:public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {     // 获取切面 aspect 对应的 class 和 beanName     Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();     String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();     // 校验切面定义的合法性     this.validate(aspectClass);      // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator     // so that it will only instantiate once.     MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =             new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);      List advisors = new ArrayList<>();      // 1. 遍历处理切面中除被 @Pointcut 注解以外的方法     for (Method method : this.getAdvisorMethods(aspectClass)) {         Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);         if (advisor != null) {             advisors.add(advisor);         }     }      // 2. 如果增强器不为空,同时又配置了增强延迟初始化,则需要追加实例化增强器 SyntheticInstantiationAdvisor     if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {         Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);         advisors.add(0, instantiationAdvisor);     }      // 3. 获取所有引介增强定义     for (Field field : aspectClass.getDeclaredFields()) {         // 创建引介增强器 DeclareParentsAdvisor         Advisor advisor = this.getDeclareParentsAdvisor(field);         if (advisor != null) {             advisors.add(advisor);         }     }      return advisors; }
  上述实现的整体执行流程如代码注释。拿到一个切面定义,Spring 首先会遍历获取切面中的增强方法,即被 @Around、@Before、@After、@AfterReturning,以及 @AfterThrowing 注解的方法,并调用 ReflectiveAspectJAdvisorFactory#getAdvisor 方法为每一个增强方法生成对应的增强器:public Advisor getAdvisor(Method candidateAdviceMethod,                           MetadataAwareAspectInstanceFactory aspectInstanceFactory,                           int declarationOrderInAspect,                           String aspectName) {      // 校验切面类定义的合法性     this.validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());      // 获取注解配置的切点信息,封装成 AspectJExpressionPointcut 对象     AspectJExpressionPointcut expressionPointcut = this.getPointcut(             candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());     if (expressionPointcut == null) {         return null;     }      // 依据切点信息生成对应的增强器     return new InstantiationModelAwarePointcutAdvisorImpl(             expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
  上述实现首先对当前切面定义执行合法性校验,如果切面配置合法则获取目标方法上的切点注解定义,并封装成 AspectJExpressionPointcut 对象。该过程位于 ReflectiveAspectJAdvisorFactory#getPointcut 方法中,实现比较简单。
  拿到切点注解定义之后,方法会依据切点的配置信息使用 InstantiationModelAwarePointcutAdvisorImpl 实现类创建对应的增强器。类 InstantiationModelAwarePointcutAdvisorImpl 的实例化过程除了初始化了一些基本属性之外,主要是调用了 InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice 方法,依据增强类型对增强器实施相应的初始化操作:private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {     Advice advice = this.aspectJAdvisorFactory.getAdvice(             this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);     return (advice != null ? advice : EMPTY_ADVICE); }  // org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvice public Advice getAdvice(Method candidateAdviceMethod,                         AspectJExpressionPointcut expressionPointcut,                         MetadataAwareAspectInstanceFactory aspectInstanceFactory,                         int declarationOrder,                         String aspectName) {      // 获取切面 class 对象,并校验切面定义     Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();     this.validate(candidateAspectClass);      // 获取方法的切点注解定义     AspectJAnnotation<?> aspectJAnnotation =             AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);     if (aspectJAnnotation == null) {         return null;     }      // If we get here, we know we have an AspectJ method.     // Check that it"s an AspectJ-annotated class     if (!this.isAspect(candidateAspectClass)) {         throw new AopConfigException("Advice must be declared inside an aspect type: " +                 "Offending method "" + candidateAdviceMethod + "" in class [" + candidateAspectClass.getName() + "]");     }      AbstractAspectJAdvice springAdvice;      // 依据切点注解类型使用对应的增强类进行封装     switch (aspectJAnnotation.getAnnotationType()) {         // @Pointcut         case AtPointcut:             if (logger.isDebugEnabled()) {                 logger.debug("Processing pointcut "" + candidateAdviceMethod.getName() + """);             }             return null;         // @Around         case AtAround:             springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);             break;         // @Before         case AtBefore:             springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);             break;         // @After         case AtAfter:             springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);             break;         // @AfterReturning         case AtAfterReturning:             springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);             AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();             if (StringUtils.hasText(afterReturningAnnotation.returning())) {                 springAdvice.setReturningName(afterReturningAnnotation.returning());             }             break;         // @AfterThrowing         case AtAfterThrowing:             springAdvice = new AspectJAfterThrowingAdvice(                     candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);             AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();             if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {                 springAdvice.setThrowingName(afterThrowingAnnotation.throwing());             }             break;         default:             throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);     }      // Now to configure the advice...     springAdvice.setAspectName(aspectName);     springAdvice.setDeclarationOrder(declarationOrder);     String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);     if (argNames != null) {         springAdvice.setArgumentNamesFromStringArray(argNames);     }     springAdvice.calculateArgumentBindings();      return springAdvice; }
  方法的整体执行流程如代码注释,逻辑比较清晰,Spring 会依据具体的增强注解类型,选择相应的增强类对切点定义进行封装。这里我们以 @Before 为例说明一下增强的执行流程,AspectJMethodBeforeAdvice 增强类关联注册的处理器是 MethodBeforeAdviceInterceptor,当我们调用一个被前置增强的目标方法时,MethodBeforeAdviceInterceptor#invoke 方法会被触发:public Object invoke(MethodInvocation mi) throws Throwable {     // 执行增强方法     this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());     // 执行目标方法     return mi.proceed(); }
  这里执行的增强方法就对应着 AspectJMethodBeforeAdvice#before 方法,该方法会依据切点配置将相应的参数绑定传递给我们自定义的增强方法,并最终通过反射调用触发执行。
  上面分析了普通方法级别增强的处理过程,对于另外一类增强(引介增强),方法 ReflectiveAspectJAdvisorFactory#getAdvisors 则使用专门的 DeclareParentsAdvisor 类创建对应的增强器:// 3. 获取所有引介增强定义 for (Field field : aspectClass.getDeclaredFields()) {     // 创建引介增强器     Advisor advisor = this.getDeclareParentsAdvisor(field);     if (advisor != null) {         advisors.add(advisor);     } }  private Advisor getDeclareParentsAdvisor(Field introductionField) {     // 获取 @DeclareParents 注解定义     DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);     if (declareParents == null) {         return null;     }      // 没有指定默认的接口实现类     if (DeclareParents.class == declareParents.defaultImpl()) {         throw new IllegalStateException(""defaultImpl" attribute must be set on DeclareParents");     }      // 使用 DeclareParentsAdvisor 类型创建对应的引介增强器     return new DeclareParentsAdvisor(             introductionField.getType(), declareParents.value(), declareParents.defaultImpl()); }
  对于引介增强来说,Spring 会注入 DelegatePerTargetObjectIntroductionInterceptor 处理器对其进行专门的处理,思想上与前面分析前置增强大同小异,这里不再展开。
  继续回到 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors 方法,上面的过程我们分析了获取所有类型增强器的过程,但是这些增强器不一定都适用于当前 bean 实例,我们需要依据切点配置信息对其进行筛选。这一过程位于 AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply 方法中:protected List findAdvisorsThatCanApply(         List candidateAdvisors, Class<?> beanClass, String beanName) {     ProxyCreationContext.setCurrentProxiedBeanName(beanName);     try {         return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);     } finally {         ProxyCreationContext.setCurrentProxiedBeanName(null);     } }  // org.springframework.aop.support.AopUtils#findAdvisorsThatCanApply public static List findAdvisorsThatCanApply(List candidateAdvisors, Class<?> clazz) {     // 没有候选的增强器,直接返回     if (candidateAdvisors.isEmpty()) {         return candidateAdvisors;     }     List eligibleAdvisors = new ArrayList<>();      // 1. 筛选引介增强器     for (Advisor candidate : candidateAdvisors) {         if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {             eligibleAdvisors.add(candidate);         }     }     // 表示是否含有引介增强     boolean hasIntroductions = !eligibleAdvisors.isEmpty();      // 2. 筛选其它类型的增强器     for (Advisor candidate : candidateAdvisors) {         // 引介增强已经处理过,这里直接跳过         if (candidate instanceof IntroductionAdvisor) {             // already processed             continue;         }         // 筛选其它类型的增强器         if (canApply(candidate, clazz, hasIntroductions)) {             eligibleAdvisors.add(candidate);         }     }     return eligibleAdvisors; }
  方法首先会使用类过滤器(ClassFilter)筛选引介增强器,除了我们手动注册的类过滤器外,这里默认还会使用 TypePatternClassFilter 类过滤器执行过滤操作。然后,方法会过滤筛选其它类型的增强器,这里除了使用类过滤器外,考虑方法级别增强的定义形式,还会使用方法匹配器(MethodMatcher)进行筛选。如果增强器适用于当前 bean 类型,则将其加入到集合中用于下一步为当前 bean 创建增强代理对象。如果没有任何一个增强器适用于当前 bean 类型,则方法 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean 最终会返回值为 null 的 DO_NOT_PROXY 数组对象,表示当前 bean 不需要被增强。为 bean 创建增强代理对象
  完成了对于当前 bean 增强器的筛选,接下来我们继续回到 AbstractAutoProxyCreator#wrapIfNecessary 方法,看一下基于前面筛选出的增强器为当前 bean 创建增强代理对象的过程,实现位于 AbstractAutoProxyCreator#createProxy 方法中:protected Object createProxy(Class<?> beanClass,                              @Nullable String beanName,                              @Nullable Object[] specificInterceptors,                              TargetSource targetSource) {      if (this.beanFactory instanceof ConfigurableListableBeanFactory) {         AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);     }      // ProxyFactory 用于为目标 bean 实例创建代理对象     ProxyFactory proxyFactory = new ProxyFactory();     proxyFactory.copyFrom(this);      // proxy-target-class = false,表示使用 JDK 原生动态代理     if (!proxyFactory.isProxyTargetClass()) {         // 检测当前 bean 是否应该基于类而非接口生成代理对象,即包含 preserveTargetClass=true 属性         if (this.shouldProxyTargetClass(beanClass, beanName)) {             proxyFactory.setProxyTargetClass(true);         }         // 如果是基于接口生成代理,则添加需要代理的接口到 ProxyFactory 中(除内置 callback 接口、语言内在接口,以及标记接口)         else {             this.evaluateProxyInterfaces(beanClass, proxyFactory);         }     }      // 将拦截器封装成 Advisor 对象     Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);     proxyFactory.addAdvisors(advisors);     proxyFactory.setTargetSource(targetSource);     // 模板方法,定制代理工厂     this.customizeProxyFactory(proxyFactory);      // 设置代理工厂被配置之后是否还允许修改,默认为 false,表示不允许修改     proxyFactory.setFrozen(this.freezeProxy);     if (this.advisorsPreFiltered()) {         proxyFactory.setPreFiltered(true);     }      // 基于 ProxyFactory 创建代理类     return proxyFactory.getProxy(this.getProxyClassLoader()); }
  方法的执行流程如代码注释。下面我们主要分析将拦截器封装成 Advisor 对象的过程,以及基于 ProxyFactory 创建增强代理对象的过程。
  Spring 定义了非常多的拦截器、增强器,以及增强方法等,这里通过 AbstractAutoProxyCreator#buildAdvisors 方法统一将他们封装成 Advisor 对象,从而简化代理的创建过程。封装的核心步骤由 DefaultAdvisorAdapterRegistry#wrap 方法实现:public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {     // 已经是 Advisor,则无需多做处理     if (adviceObject instanceof Advisor) {         return (Advisor) adviceObject;     }     // 要求必须是 Advice 类型     if (!(adviceObject instanceof Advice)) {         throw new UnknownAdviceTypeException(adviceObject);     }     Advice advice = (Advice) adviceObject;     // 如果是 MethodInterceptor,则直接使用 DefaultPointcutAdvisor 进行包装     if (advice instanceof MethodInterceptor) {         // So well-known it doesn"t even need an adapter.         return new DefaultPointcutAdvisor(advice);     }     // 否则遍历注册的适配器,如果存在关联的适配器则使用 DefaultPointcutAdvisor 进行包装     for (AdvisorAdapter adapter : this.adapters) {         // Check that it is supported.         if (adapter.supportsAdvice(advice)) {             return new DefaultPointcutAdvisor(advice);         }     }     throw new UnknownAdviceTypeException(advice); }
  接下来我们重点分析一下通过代理工厂 ProxyFactory 创建增强代理对象的过程,实现位于 ProxyFactory#getProxy 方法中:public Object getProxy(@Nullable ClassLoader classLoader) {     return this.createAopProxy() // 1. 创建 AOP 代理             .getProxy(classLoader); // 2. 基于 AOP 代理创建目标类的增强代理对象 }
  该方法的执行过程可以拆分成两个步骤:创建 AOP 代理,Spring 默认提供了两种 AOP 代理实现,即 java 原生代理和 CGLib 代理;基于 AOP 代理创建目标类的增强代理对象。
  我们首先来看一下步骤 1 的实现,位于 ProxyCreatorSupport#createAopProxy 方法中:protected final synchronized AopProxy createAopProxy() {     if (!this.active) {         this.activate();     }     return this.getAopProxyFactory().createAopProxy(this); }  // org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {     if (config.isOptimize() // 需要对代理策略进行优化             || config.isProxyTargetClass() // // 指定使用 CGLib 生成代理对象             || this.hasNoUserSuppliedProxyInterfaces(config)) // 当前类没有接口定义,不得不使用 CGLib     {         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 原生代理         if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {             return new JdkDynamicAopProxy(config);         }         // 使用 CGLib 动态代理         return new ObjenesisCglibAopProxy(config);     }     // 使用 JDK 原生动态代理     else {         return new JdkDynamicAopProxy(config);     } }
  这部分代码清晰说明了 Spring 在生成代理对象时如何在 java 原生代理和 CGLib 代理之间进行选择,可以概括如下:如果目标类实现了接口,则 Spring 默认会使用 java 原生代理。如果目标类未实现接口,则 Spring 会使用 CGLib 生成代理。如果目标类实现了接口,但是在配置时指定了 proxy-target-class=true,则使用 CGLib 生成代理。
  下面分别对基于 java 原生代理和 CGLib 代理生成增强代理对象的过程进行分析。基于 java 原生代理创建增强代理对象
  首先来看一下基于 java 原生代理生成增强代理对象的过程,位于 JdkDynamicAopProxy 类中。Java 原生代理要求代理类实现 InvocationHandler 接口,并在 InvocationHandler#invoke 方法中实现代理增强逻辑。JdkDynamicAopProxy 正好实现了该接口,对应的 JdkDynamicAopProxy#invoke 方法实现如下:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {     Object oldProxy = null;     boolean setProxyContext = false;      TargetSource targetSource = this.advised.targetSource;     Object target = null;      try {         // 当前是 equals 方法,但是被代理类接口中未定义 equals 方法         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {             return this.equals(args[0]);         }         // 当前是 hashCode 方法,但是被代理类接口中未定义 hashCode 方法         else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {             return this.hashCode();         }         // 如果是 DecoratingProxy 中定义的方法(即 DecoratingProxy#getDecoratedClass),直接返回目标类对象         else if (method.getDeclaringClass() == DecoratingProxy.class) {             return AopProxyUtils.ultimateTargetClass(this.advised);         } else if (!this.advised.opaque // 允许被转换成 Advised 类型                 && method.getDeclaringClass().isInterface() // 接口类型                 && method.getDeclaringClass().isAssignableFrom(Advised.class)) // 方法所在类是 Advised 类及其父类         {             // 直接反射调用该方法             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);         }          // 结果值         Object retVal;          // 指定内部间调用也需要代理         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);          // 获取当前方法的拦截器链         List 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);         }         // 否则需要创建对应的 MethodInvocation,以链式调用拦截器方法和增强方法         else {             MethodInvocation invocation =                     new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);             // Proceed to the joinpoint through the interceptor chain.             retVal = invocation.proceed();         }          // 处理返回值         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);         }     } }
  由上述方法实现,我们可以概括出整个增强代理的执行过程,如下:特殊处理 Object#equals、Object#hashCode、DecoratingProxy#getDecoratedClass,以及 Advised 类及其父类中定义的方法;如果配置了 expose-proxy 属性,则记录当前代理对象,以备在内部间调用时实施增强;获取当前方法的拦截器链;如果没有拦截器定义,则直接反射调用增强方法,否则先逐一执行拦截器方法,最后再应用增强方法;处理返回值。
  重点来看一下步骤 4 中应用拦截器方法的实现,位于 ReflectiveMethodInvocation#proceed 方法中:public Object proceed() throws Throwable {     // 如果所有的增强都执行完成,则执行增强方法     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {         return this.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 {             return this.proceed();         }     }     // 静态拦截器,直接应用拦截方法     else {         return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);     } }
  拦截器方法的执行流程如上述代码注释,是一个递归调用的过程,并在最后应用增强方法。
  完成了对于 AOP 代理对象 JdkDynamicAopProxy 的创建,最后来看一下获取该对象的过程,实现位于 JdkDynamicAopProxy#getProxy 方法中:public Object getProxy(@Nullable ClassLoader classLoader) {     // 获取需要被代理的接口集合     Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);     // 检测是否在被代理接口中声明了 equals 和 hashCode 方法     this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);     // 基于 java 原生代理生成代理对象     return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
  这里的逻辑也就是 java 原生代理的模板代码,如果对 java 代理比较熟悉的话,应该不难理解。基于 CGLib 代理创建增强代理对象
  基于 CGLib 代理生成增强代理对象的过程位于 ObjenesisCglibAopProxy 类中,该类继承自 CglibAopProxy 类。获取 CGLib 代理类对象的方法定义在 CglibAopProxy 中,即 CglibAopProxy#getProxy 方法。该方法基于 CGLib 的 Enhancer 类创建代理对象,属于 CGLib 的标准使用模式,因为有多个 callback 实现,所以这里使用了 CallbackFilter 模式,依据场景选择并应用对应的 callback 拦截器。
  我们重点关注 callback 的实现,位于 CglibAopProxy#getCallbacks 方法中。受制于 CGLib 在执行时一次只允许应用一个 callback 的约束,所以该方法依据参数配置实现了一组 callback,以覆盖不同的场景。核心的 AOP callback 实现是 DynamicAdvisedInterceptor 类,它实现了 MethodInterceptor 接口,对应的 DynamicAdvisedInterceptor#intercept 方法实现如下:public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {     Object oldProxy = null;     boolean setProxyContext = false;     Object target = null;     TargetSource targetSource = this.advised.getTargetSource();     try {         // 指定内部间调用也需要代理         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);          // 获取当前方法的拦截器链         List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);         // 结果值         Object retVal;         // Check whether we only have one InvokerInterceptor:         // that is, no real advice, but just reflective invocation of the target.         // 拦截器链为空,则直接反射调用增强方法         if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {             // 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 = methodProxy.invoke(target, argsToUse);         }         // 否则需要创建对应的 MethodInvocation,以链式调用拦截器方法和增强方法         else {             retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();         }         // 处理返回值         retVal = processReturnType(proxy, target, method, retVal);         return retVal;     } finally {         if (target != null && !targetSource.isStatic()) {             targetSource.releaseTarget(target);         }         if (setProxyContext) {             // Restore old proxy.             AopContext.setCurrentProxy(oldProxy);         }     } }
  可以看出上述方法在实现流程上与前面介绍的 JdkDynamicAopProxy#invoke 方法是一致的,只是这里是基于 CGLib 实现而已。总结
  最后我们对 Spring AOP 的运行机制进行一个总结。Spring AOP 的实现本质上是一个动态代理的过程,Spring 引入了 java 原生代理和 CGLib 代理,并依据场景选择基于哪种代理机制对目标对象进行增强。由前面对于 Spring IoC 实现的分析可以了解到,Spring 容器在完成对 bean 对象的创建之后会执行初始化操作,而 AOP 初始化的过程就发生在 bean 的后置初始化阶段,整体流程可以概括为:从容器中获取所有的切面定义;筛选适用于当前 bean 的增强器集合;依据增强器集合基于动态代理机制生成相应的增强代理对象。
  当我们在调用一个被增强的方法时,相应的拦截器会依据连接点的方位在适当的位置触发对应的增强定义,从而最终实现 AOP 中定义的各类增强语义。
6s备用机升级15系统桌面布局心动的瞬间6s备用机升级15系统桌面布局昨晚刚给用了很久的小6s从11系统大跃进升到了iOS15以前一直没升级过除了下载的时候很烫以外升级后丝滑升级系统换了新手机壳钢化膜新的壁纸可液晶拼接屏逐渐迈向大众化随着科技的快速发展,高清技术是显示大屏的主要发展方向。在未来的发展,液晶大屏的应用将会越来光广泛,如办公会议商用显示等场合都会转向高清时代,在此人们也在不断提出更高的要求,而高清液SpringBoot项目鉴权的4种方法文章介绍了springboot中实现通用auth的四种方式,包括传统AOP拦截器参数解析器和过滤器,并提供了对应的实例代码,最后简单总结了下他们的执行顺序。前言最近一直被无尽的业务每日一题移动零(双指针)题目给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。请注意,必须在不复制数组的情况下原地对数组进行操作。解题(Java)classSolut老年人怎么购买新智能手机现在越来越多的老年人开始购买新的智能手机了,大部分老年人觉得购买新智能手机要比子女换新后退下了的智能手机要好,可是自己买新智能手机的时候又觉得不能卖的太贵了,新的怎么也比旧的强,买2022年性价比最高的手机推荐一,购机指南1处理器(处理器是手机的核心部位,处理器越好,手机越流畅。)旗舰处理器骁龙8Gen1高通骁龙888Plus麒麟9000苹果A15中高端处理器骁龙865骁龙865骁龙87Lazada将开启预售政策,亚马逊美国站增添新功能2。14跨境早报新闻直通车01hrLazada发布新公告,平台将对本土卖家开启预售政策。目前该政策处于测试阶段,第一批仅限官方孵化群内的卖家参与,不限制站点,后期会逐步向符合要求的卖家开放。2月12。用end结束代码不少编程语言,循环判断代码块需要用end标明结束,这样在一定程度上会使代码逻辑更加清晰。但是其实在Python这种严格缩进的语言里并没有必要这样。如果你想用,也是可以的,具体看下面明起汽车消费可申领补贴啦本报讯(记者李静)2月15日起,我市晋情消费悦享生活汽车消费补贴可申领啦!只要是1月29日起从我市各汽车销售商处购置国六标准及以上乘用车微型或轻型货车的个人消费者,均可通过云闪付A元宇宙新鲜事网易音乐理想汽车申请元宇宙商标被驳回啫喱暂停新用户进入财联社区块链日报14日讯今日元宇宙新鲜事有米哈游宣布创立元宇宙品牌HoYoverse元宇宙社交APP啫喱暂停新用户进入网易音乐元宇宙及理想汽车元宇宙商标申请被驳回。原神开发商米哈游未来行程少不了新能源手机导航除了输入目的地,点击导航,告诉你左转右转这种最基本的用法之外,其实还有一些挺实用的小功能小技巧。就好像说蛋炒饭,除了把鸡蛋和米饭一起倒在锅里,chuachuachua这么炒
沃尔沃XC40RECHARGE汽车正式上市,专为中国消费者打造智能出行携旗下全电动阵容参展,沃尔沃汽车以电动心脏为主题,该品牌首款纯电动汽车城市豪华纯电动SUVXC40RECHARGE汽车也正式亮相车展,这款车将通过新的官方直销模式销售,立即启动盲订光有大屏幕不行,配上小鸟K7才叫家庭影院对于大部分的家庭来说,客厅是一个家的中心,而电视往往又是客厅的中心,因为这是能够聚拢家庭的地方。但只有大屏幕就可以了吗?没有好的音质,大家的乐趣将会大大降低。随着影音技术的升级,很贝莱德基金经理几乎抛光了全部黄金,发生了什么?美元超发则黄金暴涨,这几乎成了自布雷顿体系崩溃以后的必然规律,如果有基金抛光了黄金那么意味着美联储要提高利率,缩表,回收美元先来看看黄金最近一年的走势。以伦敦金为例,去年黄金走势一手机的寿命真的很短么?为什么更新迭代这么快?在2021年的今天,手机的迭代速度远超以前。先说每个手机厂商型号众多每年都要更新,甚至有的厂商的当家期间不足一年便更新一次。在这么快的迭代速度下也许会有人和我在想同样的问题,手机的小小四方屏见证中国企业科技转型脚步来源人民网拍张照片,纪念一下录段视频,分享给好友随着通信技术发展,智能手机功能越来越强大,已成为日常生活中必不可缺的设备。打开手机摄像头拍照录视频,几乎成了大多数人的习惯动作。5GvivoX80Pro持续开火,陶瓷机身100倍变焦5100mAh,全能旗舰总体来说,vivo这个手机品牌在外观设计方面创新力很强,当然了,这也倒不是vivo的硬核配置不够。其实现在vivo已经是一个集研发创新能力和硬核实力配置为一体的品牌了,近年来在vi海信发布卷曲屏激光电视,新的风口正在形成产品与技术创新历来都被企业视为发展的生命线,一方面,可以向外争取市场,另一方面,还能向内争取效益。9月16日,在北京召开的第三届全球激光显示技术与产业发展论坛上,海信重磅发布全球首苹果cpu非常强大,为什么会这样?苹果cpu又是谁设计的?苹果的CPU非常强大,为什么会这样?苹果cpu又是谁设计的?苹果手机的处理器确实很强大,在同一时期内,苹果的处理器性能基本上领先高通半代,领先华为麒麟一代。这是由多方面原因决定的。比特币将在元宇宙中扮演什么角色?进入2021年以来,随着全球经济社会环境的变化,加密市场内外有关元宇宙的讨论愈发热烈。而在加密市场内,让我们对于元宇宙能够产生近距离感受的无疑当属GameFi。不过,不论是近期大火股票该怎么补仓?谢邀请!补仓的本质首先要明确的是,补仓是一种投资的技巧,是工具,不是目的。投资的目的是获得风险含量最小的收益,因此只有当补仓可以帮助投资人达到目的时,它才是有利用价值的,否则就要舍阿里巴巴和华为都自研了ai芯片,谁的比较厉害?阿里的含光800和华为的昇腾910都属于AI芯片,但是应用领域却有较大的区别。含光800是一款更加偏重于专用领域的产品昇腾910相对通用一些,可以用作AI学习训练,两者的应用范围和