一文吃透 Spring 中的 AOP 编程

news2025/1/16 2:35:45

在这里插入图片描述

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:SSM 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
🥭本文内容:一文吃透 Spring 中的 AOP 编程

文章目录

    • AOP 概述
    • AOP 实现分类
    • AOP 术语
    • 基于 Aspectj 实现 AOP 操作
      • 第一版:基于xml(aop:config)配置文件
      • 第二版:基于xml(aop:aspect)配置文件
      • 第三版:基于注解实现通知

在这里插入图片描述

AOP 概述

AOPAspect Oriented Programming 的缩写,是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOPOOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型

AOP 可以分离业务代码和关注点代码(重复代码),在执行业务代码时,动态的注入关注点代码。切面就是关注点代码形成的类。Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理。JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口

在这里插入图片描述

AOP 实现分类

AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能,按照 AOP 框架修改源代码的时机,可以将其分为两类:

  • 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
  • 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP

AOP 术语

  • 连接点(JointPoint)与切入点匹配的执行点,在程序整个执行流程中,可以织入切面的位置,方法的执行前后,异常抛出的位置

  • 切点(PointCut)在程序执行流程中,真正织入切面的方法。

  • 切面(ASPECT)切点+通知就是切面

  • 通知(Advice)切面必须要完成的工作,也叫增强。即,它是类中的一个方法,方法中编写织入的代码。
    前置通知 后置通知
    环绕通知 异常通知
    最终通知

  • 目标对象(Target)被织入通知的对象

  • 代理对象(Proxy)目标对象被织入通知之后创建的新对象

通知的类型

Spring 方面可以使用下面提到的五种通知工作:

通知描述
前置通知在一个方法执行之前,执行通知。
最终通知在一个方法执行之后,不考虑其结果,执行通知。
后置通知在一个方法执行之后,只有在方法成功完成时,才能执行通知。
异常通知在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。
环绕通知在一个方法调用之前和之后,执行通知。

基于 Aspectj 实现 AOP 操作

基于 Aspectj 实现 AOP 操作,经历了下面三个版本的变化,注解版是我们最常用的。

切入点表达式

作用:声明对哪个类中的哪个方法进行增强

语法:

execution([访问权限修饰符] 返回值 [ 类的全路径名 ] 方法名 (参数列表)[异常])

  • 访问权限修饰符:

    可选项,不写就是四个权限都包含

    写public就表示只包括公开的方法

  • 返回值类型

    必填项 * 标识返回值任意

  • 全限定类名

    可选项,两个点 … 表示当前包以及子包下的所有类,省略表示所有类

  • 方法名

    必填项 * 表示所有的方法 set*表示所有的set方法

  • 形参列表

    必填项

    ()表示没有参数的方法

    (…)参数类型和参数个数随意的方法

    (*)只有一个参数的方法

    (*,String) 第一个参数类型随意,第二个参数String类型

  • 异常信息

    可选项 省略时标识任何异常信息

在这里插入图片描述

第一版:基于xml(aop:config)配置文件

使用 Spring AOP 接口方式实现 AOP, 可以通过自定义通知来供 Spring AOP 识别对应实现的接口是:

  1. 前置通知:MethodBeforeAdvice
  2. 返回通知:AfterReturningAdvice
  3. 异常通知:ThrowsAdvice
  4. 环绕通知:MethodInterceptor

实现步骤:

1、定义业务接口

/**
 * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
 */

package cn.kgc.spring05.entity;

public interface Teacher {
    String teachOnLine(String course);
    String teachOffLine(Integer course);
}

2、定义实现类

package cn.kgc.spring05.entity;

public class TeacherA implements Teacher{
    @Override
    public String teachOnLine(String course) {
        System.out.println("TeacherA开始"+course+"课程线上教学");
        if(course.equals("java")){
            throw new RuntimeException("入门到放弃!");
        }
        return course+"课程线上教学";
    }

    @Override
    public String teachOffLine(Integer course) {
        System.out.println("TeacherA开始"+course+"课程线下教学");
        return course+"课程线下教学";
    }
}

3、实现接口定义通知类

前置通知类

package cn.kgc.spring05.advice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

//前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("------------spring aop 前置通知------------");
    }
}

后置通知类

package cn.kgc.spring05.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterReturnAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("------------spring aop 后置通知------------");
    }
}

4、XML 配置方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--托管通知-->
    <bean id="after" class="cn.kgc.spring05.advice.MyAfterReturnAdvice"></bean>
    <bean id="before" class="cn.kgc.spring05.advice.MyMethodBeforeAdvice"></bean>
    <bean id="teacherA" class="cn.kgc.spring05.entity.TeacherA"></bean>
    <!--AOP的配置-->
    <aop:config>
        <!--切点表达式-->
        <aop:pointcut id="pt" expression="execution(* *(..))"/>
        <aop:advisor advice-ref="before" pointcut-ref="pt"></aop:advisor>
        <aop:advisor advice-ref="after" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>

5、测试

package cn.kgc.spring05;

import cn.kgc.spring05.entity.Teacher;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Unit test for simple App.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-config.xml")
public class AppTest
{
    @Autowired
    Teacher teacher;

    @Test
    public void teachOnLine() {
        System.out.println(teacher.getClass());
        String s = teacher.teachOnLine("java");
        System.out.println("s = " + s);
    }
}

6、运行结果

在这里插入图片描述

第二版:基于xml(aop:aspect)配置文件

基于 xml(aop:config) 配置文件的方式,增加几个通知,就会创建几个通知类,那我们能否将这些通知类写在一个类中呢?下面就让我来带你们找到解决之法!

配置 AspectJ 标签解读表

在这里插入图片描述

实现步骤:

1、定义业务接口

/**
 * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
 */

package cn.kgc.spring05.entity;

public interface Teacher {
    String teachOnLine(String course);
    String teachOffLine(Integer course);
}

2、定义实现类

package cn.kgc.spring05.entity;

public class TeacherA implements Teacher{
    @Override
    public String teachOnLine(String course) {
        System.out.println("TeacherA开始"+course+"课程线上教学");
        if(course.equals("java")){
            throw new RuntimeException("入门到放弃!");
        }
        return course+"课程线上教学";
    }

    @Override
    public String teachOffLine(Integer course) {
        System.out.println("TeacherA开始"+course+"课程线下教学");
        return course+"课程线下教学";
    }
}

3、实现接口定义通知类

package cn.kgc.spring05.advice;

public class AllAdvice {
    public void before(){System.out.println("------------前置通知--------------");}

    public void afterReturning(){System.out.println("------------后置通知--------------");}

    public void afterThrowing(){System.out.println("------------异常通知--------------");}

    public void after(){System.out.println("------------最终通知--------------");}

    public void around(){System.out.println("------------环绕通知--------------");}
}

4、XML 配置方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--托管通知-->
    <bean id="all" class="cn.kgc.spring05.advice.AllAdvice"></bean>
    <bean id="teacherA" class="cn.kgc.spring05.entity.TeacherA"></bean>
    <!--AOP的配置-->
    <aop:config>
        <!--切点表达式-->
        <aop:pointcut id="pt" expression="execution(* *(String))"/>
        <aop:aspect ref="all">
            <aop:before method="before" pointcut-ref="pt"></aop:before>
            <aop:after-returning method="afterReturning" pointcut-ref="pt"></aop:after-returning>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt"></aop:after-throwing>
            <aop:after method="after" pointcut-ref="pt"></aop:after>
<!--            <aop:around method="around" pointcut-ref="pt"></aop:around>-->
        </aop:aspect>
    </aop:config>
</beans>

5、测试

package cn.kgc.spring05.advice;

import cn.kgc.spring05.entity.Teacher;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-config2.xml")
public class AllAdviceTest{

    @Autowired
    Teacher teacher;

    @Test
    public void test01() {
        System.out.println(teacher.getClass());
        String s = teacher.teachOnLine("java");
        System.out.println("s = " + s);
    }
}

6、运行结果

在这里插入图片描述

第三版:基于注解实现通知

  • 常用 “通知” 注解如下:

    @Aspect 注解将此类定义为切面。
    @Before 注解用于将目标方法配置为前置增强(前置通知)。
    @AfterReturning 注解用于将目标方法配置为后置增强(后置通知)。
    @Around 定义环绕增强(环绕通知)
    @AfterThrowing 配置异常通知
    @After 也是后置通知,与 @AfterReturning 很相似,区别在于 @AfterReturning 在方法执行完毕后进行返回,可以有返回值。@After 没有返回值。

实现步骤:

1、定义业务接口

/**
 * 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
 */

package cn.kgc.spring05.entity;

public interface Teacher {
    String teachOnLine(String course);
    String teachOffLine(Integer course);
}

2、定义注解

package cn.kgc.spring05.advice;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoAdvice {
}

3、定义实现类

package cn.kgc.spring05.entity;

import cn.kgc.spring05.advice.AnnoAdvice;
import org.springframework.stereotype.Component;

@Component
public class TeacherA implements Teacher{
    @Override
    @AnnoAdvice
    public String teachOnLine(String course) {
        System.out.println("TeacherA开始"+course+"课程线上教学");
        if(course.equals("java")){
            throw new RuntimeException("入门到放弃!");
        }
        return course+"课程线上教学";
    }

    @Override
    @AnnoAdvice
    public String teachOffLine(Integer course) {
        System.out.println("TeacherA开始"+course+"课程线下教学");
        return course+"课程线下教学";
    }
}

4、实现接口定义切面类

首先在类上面添加 @Aspect 注解,将该类转化为切面类,再在类中的各个方法上面使用各自的 “通知” 注解即可实现。

package cn.kgc.spring05.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AllAdvice {

    @Pointcut("@annotation(AnnoAdvice)")
    public void point(){}

    @Before("point()")
    public void before(){System.out.println("------------前置通知--------------");}

    @AfterReturning("point()")
    public void afterReturning(){System.out.println("------------后置通知--------------");}

    @AfterThrowing("point()")
    public void afterThrowing(){System.out.println("------------异常通知--------------");}

    @After("point()")
    public void after(){System.out.println("------------最终通知--------------");}

    @Around("point()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint){

        Object proceed = null;
        try {
            System.out.println("----------spring aop 环绕 前通知-----------");
            proceed = joinPoint.proceed();
            System.out.println("----------spring aop 环绕 后通知-----------");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("----------spring aop 环绕 异常通知-----------");
        }finally {
            System.out.println("----------spring aop 环绕 最终通知-----------");
        }
        return proceed;
    }
}

5、XML 配置方式

开启包扫描和aspectj自动代理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启包扫描-->
    <context:component-scan base-package="cn.kgc.spring05"></context:component-scan>
    <!--开启aspectj自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

6、测试

package cn.kgc.spring05.advice;

import cn.kgc.spring05.entity.Teacher;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-config3.xml")
public class AllAdviceTest{

    @Autowired
    Teacher teacher;

    @Test
    public void test01() {
        System.out.println(teacher.getClass());
        String s = teacher.teachOnLine("html");
        System.out.println("s = " + s);
    }
}

7、运行效果

在这里插入图片描述


  码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/379110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ STL:容器 Container

文章目录1、序列容器1.1、容器共性1.2、vectorvector 结构* vector 扩容原理* vector 迭代器失效1.3、dequedeque 结构deque 迭代器deque 模拟连续空间1.4、listlist 特殊操作list 结构list 迭代器2、关联式容器2.1、容器共性2.2、容器特性3、无序关联式容器3.1、容器共性3.2、…

【剑指offer】JZ5 替换空格、JZ6 从尾到头打印链表

目录 JZ5 替换空格 思路&#xff1a; 解题步骤&#xff1a; JZ6 从尾到头打印链表 思路&#xff1a; 解题步骤 JZ5 替换空格 描述&#xff1a; 请实现一个函数&#xff0c;将一个字符串s中的每个空格替换成“%20”。 例如&#xff0c;当字符串为We Are Happy.则经过替换之…

LibAFL的安装及基本使用

本教程安装LibAFL使用的是Ubuntu 22.04 操作系统 1. 安装 1.1 Rust 安装 Rust的安装&#xff0c;参照Rust官网&#xff1a;https://www.rust-lang.org/tools/install curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh1.2 LLVM安装 直接apt安装&#xff0c;安…

Day903.自增主键不能保证连续递增 -MySQL实战

自增主键不能保证连续递增 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于自增主键不能保证连续递增的内容。 MySql保证了主键是自增&#xff0c;但不相对连续&#xff1b;帮助开发人员快速识别每个行的唯一性&#xff0c;并提高查询效率。 自增主键可以让主键索引…

2023上半年北京/上海/广州/深圳NPDP产品经理认证报名

产品经理国际资格认证NPDP是国际公认的唯一的新产品开发专业认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年…

【GUI】Robo 3T(Studio 3T Free) for Mongodb安装与使用教程

下载 robo 3T现已更名为studio 3T free&#xff0c;官网即可下载 studio 3T free下载地址 安装 mac电脑下载的是dmg安装包&#xff0c;直接正常安装即可&#xff0c;windows电脑也是一样的&#xff0c;不需要配置环境&#xff0c;安装即可使用。&#xff08;前提是你已经安装…

【C++】二叉搜索树的模拟实现

一、概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左右子树也分别…

Python---time模块

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;Python在学&#xff0c;希望能够得到各位的支持&#xff01;&#xff01;&#xff01; time模块前言时间戳time.time()将时间戳转换成字符串time.ctime()将时间戳转换为元组time.localtime(时间戳)将元…

大四、非计算机专业,Python该怎么学?

我是非计算机专业&#xff0c;大四时开始学Python&#xff0c;用了大概一个半月时间&#xff0c;现在从事数据挖掘工作&#xff0c;算是有点发言权。虽然之前学了点C&#xff0c;但仅仅是皮毛&#xff0c;为了应付考试&#xff0c;所以我基本是零基础开始学Python的。 总结学习…

Stress压力工具的部署及使用

Stress压力工具的部署及使用 下载地址&#xff1a;wget https://fossies.org/linux/privat/old/stress-1.0.5.tar.gz 1.部署 进入目录执行./autogen.sh [rootiZ2ze1pj93eyq389c2ppi5Z stress-1.0.5]# ./autogen.sh ps&#xff1a;如果执行过程中缺包&#xff0c;安装对应的…

运维自动化——Ansible

一&#xff1a;ansible命令执行过程 1. 加载自己的配置文件 默认/etc/ansible/ansible.cfg 2. 加载自己对应的模块文件&#xff0c;如command 3. 通过ansible将模块或命令生成对应的临时py文件&#xff0c; 并将该文件传输至远程服务器的对应执行用户 $HOME…

leetcode 51~60 学习经历

leetcode 51~60 学习经历51. N 皇后52. N 皇后 II53. 最大子数组和54. 螺旋矩阵55. 跳跃游戏56. 合并区间57. 插入区间58. 最后一个单词的长度59. 螺旋矩阵 II60. 排列序列小结51. N 皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子…

【MobileNet】MobileNet V1

MobileNet V11、简介2、Depthwise Separable Convolution1&#xff09;Depthwise Separable Convolution 的优点2&#xff09;Depthwise Separable Convolution 网络结构3&#xff09;pytorch 函数 实现 depth-wise convolution2、Mobile 网络结构pytorch实现 Mobile 网络结构&…

hiveSQL开窗函数详解

hive开窗函数 文章目录hive开窗函数1. 开窗函数概述1.1 窗口函数分类1.2 窗口函数和普通聚合函数的区别2. 窗口函数的基本用法2.1 基本用法2.2 设置窗口的方法2.2.1 window_name2.2.2 partition by2.2.3 order by 子句2.2.4 rows指定窗口大小窗口框架2.3 开窗函数中加 order by…

Linux下使用Makefile实现条件编译

在Linux系统下Makefile和C/C语言都有提供条件选择编译的语法&#xff0c;就是在编译源码的时候&#xff0c;可以选择性地编译指定的代码。这种条件选择编译的使用场合有好多&#xff0c;例如我们开发一个兼容标准版本与定制版本兼容的项目&#xff0c;那么&#xff0c;一些与需…

[数据结构]:07-二叉树(无头结点)(C语言实现)

目录 前言 已完成内容 二叉树实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-QueueFunction.cpp 04-TreeFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容&#xff0c;除其中使用到C引用外&#xff0c;全为C语言代码。使用C引用主要是为了…

金三银四,助力你的大厂梦,2023年软件测试经典面试真题(3)(共3篇)

前言 金三银四即将到来&#xff0c;相信很多小伙伴要面临面试&#xff0c;一直想着说分享一些软件测试的面试题&#xff0c;这段时间做了一些收集和整理&#xff0c;下面共有三篇经典面试题&#xff0c;大家可以试着做一下&#xff0c;答案附在后面&#xff0c;希望能帮助到大…

【软件测试】从0到1的突破,appium自动化测试你真的会吗?自动化测试思路总结......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 总结一下APP UI自动…

超店有数分享:tiktok数据分析工具推荐,助你成功出海!

现阶段的跨境电商人都纷纷入局tiktok&#xff0c;这是风口也是发展趋势。Tiktok的下载量已经超过了35亿&#xff0c;每月都有10亿用户活跃&#xff0c;在154国家/地区使用。Tiktok用户每天在平均花1小时左右进行浏览&#xff0c;打开率也很高。如今&#xff0c;tiktok也越来越成…

酷雷曼VR丨十大“高含金量”荣誉,一起见证!

VR全景领域 十大“高含金量”荣誉 高光时刻 一同见证 01、双高新技术企业 同时获得国家高新技术企业、中关村高新技术企业双认证&#xff0c;是对酷雷曼企业研究开发组织管理水平、科技成果转化能力、自主知识产权数量、销售与总资产成长性等多维度实力的综合体现。 双高…