Java反射自定义注解底层设计原理

文章目录

          • 一、反射
            • 1. 反射概念
            • 2. 反射机制的优缺点
            • 3. 反射的用途
            • 4. 反射技术的使用
            • 5. 反射常用的Api
            • 6. 反射执行构造函数
            • 7. 反射执行给属性赋值
            • 8. 反射执行调用方法
          • 二、注解
            • 2.1. 注解概念
            • 2.2. 常用注解
            • 2.3. 元注解
            • 2.4. 常用注解
            • 2.5. 注解的Target
            • 2.6. 获取注解信息
            • 2.7. 注解如何生效
            • 2.8. 注解实现案例
            • 2.09. 封装自定义注解限流框架
            • 2.10. 整合Aop实现接口限流
            • 2.11. 案例

1.什么是反射、反射优缺点
2.反射的用途/反射应用场景
3.反射调用方法/给属性赋值
4.反射如何越过泛型检查
5.什么是注解/注解生效的原理
6.自定义注解实现API接口限流框架

一、反射
1. 反射概念

使用反射机制可以动态获取当前class的信息 比如方法的信息、注解信息、方法的参数、属性等。
.java 源代码 编译.class 类加载器 jvm 字节码

2. 反射机制的优缺点

优点:提供开发者能够更好封装框架实现扩展功能。
缺点:
(1)反射会消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

3. 反射的用途

反编译:.class–>.java
1.通过反射机制访问java对象的属性,方法,构造方法等
2. JDBC加载驱动连接 class.forname
Class.forName(“com.mysql.jdbc.Driver”); // 动态加载mysql驱动
3. Spring容器框架IOC实例化对象

 <bean id="mayikt" class="com.mayikt.UserEntity" />

4.自定义注解生效(反射+Aop)
5.第三方核心的框架 mybatis orm

4. 反射技术的使用

Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法
1.getField、getMethod和getCostructor方法可以获得指定名字的域、方法和构造器。
2.getFields、getMethods和getCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员。
3.getDeclatedFields、getDeclatedMethods和getDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

5. 反射常用的Api

(1)Object–>getClass
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
(3)通过class类的静态方法:forName(String className)(最常用)
Class<?> aClass = Class.forName(“com.mayikt.entity.UserEntity”);

    /*** 反射机制使用的三种方式* <p>* 第1种:获取class UserEntity.class* 第2种:获取class Class.forName("类的全路径");* 第3种:new UserEntity().getClass()*/@Testpublic void objCreateTest() throws InstantiationException, IllegalAccessException, ClassNotFoundException {Class<UserEntity> userClass1 = UserEntity.class;//默认执行无参构造函数UserEntity userEntity1 = userClass1.newInstance();System.out.println(userEntity1);//2.类的的完成路径 报名+类名Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");System.out.println(userClass1 == userClass2);//3.new UserEntity().getClass()UserEntity userEntity2 = new UserEntity();Class userClass3 = userEntity2.getClass();System.out.println(userClass1 == userClass3);//trueSystem.out.println(userEntity1 == userEntity2);//false}

运行期间,一个类,只有一个Class对象产生

6. 反射执行构造函数

执行无参数构造函数和执行有参数构造函数

   /*** 使用反射机制初始化对象*/@Testpublic void constructorTest() throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {// //2.类的的完成路径 报名+类名Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");// //默认执行无参构造函数UserEntity userEntity = (UserEntity) userClass2.newInstance();System.out.println(userEntity);//执行有参构造函数Constructor<?> declaredConstructor = userClass2.getDeclaredConstructor(String.class, Integer.class);UserEntity userEntity2 = (UserEntity) declaredConstructor.newInstance("mayikt", 22);System.out.println(userEntity2);}
7. 反射执行给属性赋值

反射执行给公有属性赋值和反射执行给私有属性赋值

/*** 反射如何给属性赋值*/@Testpublic void evaluationTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");UserEntity userEntity2 = (UserEntity) userClass2.newInstance();//给公有属性赋值Field publicName = userClass2.getDeclaredField("publicName");publicName.set(userEntity2, "mayikt");System.out.println(userEntity2.getPublicName());//给私有属性赋值Field userName = userClass2.getDeclaredField("userName");//设置访问私有属性权限userName.setAccessible(true);userName.set(userEntity2, "mayikt2");System.out.println(userEntity2.getUserName());}
注意:
xception in thread "main" java.lang.IllegalAccessException: Class com.mayikt.test.Test03 can not access a member of class com.mayikt.entity.UserEntity with modifiers "private"at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)at java.lang.reflect.Field.set(Field.java:761)at com.mayikt.test.Test03.main(Test03.java:28)
解决办法:
// 设置允许访问私有属性
userName.setAccessible(true);
8. 反射执行调用方法

反射调用公有方法

   //使用反射机制调用公用无参方法@Testpublic void methodNoparamTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");//创建类实例Object o = userClass2.newInstance();//获取公有和私有方法Method method = userClass2.getDeclaredMethod("mayikt");//设置访问私有方法权限method.setAccessible(true);method.invoke(o);}

反射调用私有方法和反射调用方法传递参数

//使用反射机制调用私有有参方法@Testpublic void methodCarryParamTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {Class<?> userClass2 = Class.forName("com.gblfy.elk.entity.UserEntity");//创建类实例Object o = userClass2.newInstance();//获取公有和私有方法Method method = userClass2.getDeclaredMethod("sum", Integer.class, Integer.class);//设置访问私有方法权限method.setAccessible(true);Integer result = (Integer) method.invoke(o, 1, 5);System.out.println(result);}
二、注解
2.1. 注解概念

什么是注解
注解用来给类声明附加额外信息,可以标注在类、字段、方法等上面,编译器、JVM以及开发人员等都可以通过反射拿到注解信息,进而做一些相关处理

SpringBoot 全部都是采用注解化

2.2. 常用注解
@Override     只能标注在子类覆盖父类的方法上面,有提示的作用
@Deprecated    标注在过时的方法或类上面,有提示的作用
@SuppressWarnings("unchecked")   标注在编译器认为有问题的类、
方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unused、all
2.3. 元注解
元注解用来在声明新注解时指定新注解的一些特性
@Target 指定新注解标注的位置,比如类、字段、方法等,取值有ElementType.Method等
@Retention 指定新注解的信息保留到什么时候,取值有RetentionPolicy.RUNTIME等
@Inherited  指定新注解标注在父类上时可被子类继承
2.4. 常用注解

@Target(ElementType.METHOD) // 指定新注解可以标注在方法上
@Retention(RetentionPolicy.RUNTIME) // 指定新注解保留到程序运行时期
@Inherited // 指定新注解标注在父类上时可被子类继承
public @interface MayiktName {public String name();
}
自定义注解 运行 :反射+aop
2.5. 注解的Target
TYPE:类、接口(包括注解类型)和枚举的声明
FIELD:字段声明(包括枚举常量)
METHOD:方法声明
PARAMETER:参数声明
CONSTRUCTOR:构造函数声明
LOCAL_VARIABLE:本地变量声明
ANNOTATION_TYPE:注解类型声明
PACKAGE:包声明
TYPE_PARAMETER:类型参数声明,JavaSE8引进,可以应用于类的泛型声明之处
TYPE_USE:JavaSE8引进,此类型包括类型声明和类型参数声明
2.6. 获取注解信息
package com.gblfy.elk.annotate;import java.lang.annotation.*;/*** ElementType.TYPE 注解在类上生效* ElementType.METHOD 注解在方法上生效* ElementType.FIELD 注解在属性上生效* Retention 加此注解,反射才可以获取* Inherited 子类可以继承*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MayiktName {
}
package com.gblfy.elk.entity;import com.gblfy.elk.annotate.MayiktName;@MayiktName
public class UserEntity {private String userName;private Integer userAge;@MayiktNamepublic String publicName;public UserEntity() {System.out.println("执行无参构造函数");}public UserEntity(String userName, Integer userAge) {System.out.println("执行有参构造函数");this.userName = userName;this.userAge = userAge;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public Integer getUserAge() {return userAge;}public void setUserAge(Integer userAge) {this.userAge = userAge;}public String getPublicName() {return publicName;}public void setPublicName(String publicName) {this.publicName = publicName;}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("UserEntity{");sb.append("userName='").append(userName).append('\'');sb.append(", userAge=").append(userAge);sb.append('}');return sb.toString();}@MayiktNameprivate void mayikt() {System.out.println("mayikt");}@MayiktNameprivate Integer sum(Integer a, Integer b) {return a + b;}
}
/*** 注解联练习** @author gblfy* @date 2022-03-13*/
public class AnnotateCase {//判断某类上是否加上@MayiktName注解@Testpublic void classAnnotateTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {//加载类Class<?> userClass = Class.forName("com.gblfy.elk.entity.UserEntity");// 1.获取当前类上的注解MayiktName declaredAnnotation = userClass.getDeclaredAnnotation(MayiktName.class);System.out.println(declaredAnnotation);}//判断指定方法上是否加上@MayiktName注解@Testpublic void methodAnnotateTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {//加载类Class<?> userClass = Class.forName("com.gblfy.elk.entity.UserEntity");//创建类实例Object o = userClass.newInstance();//获取指定mayikt方法Method method = userClass.getDeclaredMethod("mayikt");//获取该方法上的注解,有则返回,无则返回nullMayiktName mayiktName = method.getDeclaredAnnotation(MayiktName.class);System.out.println(mayiktName);}//判断某属性上是否加上@MayiktName注解@Testpublic void fieldAnnotateTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {//加载类Class<?> userClass = Class.forName("com.gblfy.elk.entity.UserEntity");// 1.获取属性上的注解Field publicName = userClass.getDeclaredField("publicName");MayiktName mayiktName = publicName.getDeclaredAnnotation(MayiktName.class);System.out.println(mayiktName);}
}
2.7. 注解如何生效

实际项目中 注解想生效通过反射+aop机制

2.8. 注解实现案例

自定义限流注解

对我们接口实现 限流 比如 每s 只能访问1次 或者每s 访问两次。
Maven

      <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.79</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>22.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

使用谷歌的guava例子

package com.gblfy.elk.controller;import com.gblfy.elk.annotate.GblfyStreamLimit;
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class SreamLimitController {/*** 每秒生成1.0个令牌* 滑动窗口、令牌桶、漏桶算法实现*/private RateLimiter rateLimiter = RateLimiter.create(1.0);@GetMapping("/get")public String get() {System.out.println("-----------------执行目标方法-----------------");boolean result = rateLimiter.tryAcquire();if (!result) {return "当前访问人数过多,请稍后重试!";}return "my is get";}@GetMapping("/add")public String add() {return "my is add";}
}
2.09. 封装自定义注解限流框架

整合自定义注解

package com.gblfy.elk.annotate;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义请求限流注解** @author gblfy* @date 2022-03-13*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GblfyStreamLimit {/*** 限流名称** @return*/String name() default "";/*** 限流次数,默认限流频次 1秒/20次** @return*/double limitNum() default 20.0;
}
2.10. 整合Aop实现接口限流
package com.gblfy.elk.aop;import com.gblfy.elk.annotate.GblfyStreamLimit;
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.util.concurrent.ConcurrentHashMap;@Aspect
@Component
public class StreamLimitAop {//并发map储存private ConcurrentHashMap<String, RateLimiter> rateLimiterStrategy = new ConcurrentHashMap();/*** 只要在方法上添加该自定义限流注解,就会被AOP环绕通知拦截** @param joinPoint* @return*/@Around(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)")public Object around(ProceedingJoinPoint joinPoint) {try {//获取拦截的方法名Signature sig = joinPoint.getSignature();//获取拦截的方法名MethodSignature methodSignature = (MethodSignature) sig;// 判断方法上是否有加上该注解,如果有加上注解则限流GblfyStreamLimit gblfyStreamLimit =methodSignature.getMethod().getDeclaredAnnotation(GblfyStreamLimit.class);if (gblfyStreamLimit == null) {// 执行目标方法return joinPoint.proceed();}// 1.获取注解上的限流名称(name)String name = gblfyStreamLimit.name();// 2.获取注解上的limitNum(限流次数),实现对不同的方法限流策略不一样的效果double limitNum = gblfyStreamLimit.limitNum();RateLimiter rateLimiter = rateLimiterStrategy.get(name);if (rateLimiter == null) {//3.动态匹配并创建不同的限流策略rateLimiter = RateLimiter.create(limitNum);rateLimiterStrategy.put(name, rateLimiter);}// 开始限流boolean result = rateLimiter.tryAcquire();if (!result) {return "当前访问人数过多,请稍后重试!";}return joinPoint.proceed();} catch (Throwable throwable) {return "系统出现了错误!";}}/*** 前置通知*/@Before(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)")public void before() {System.out.println("----------------------前置通知----------------------");}/*** 后置通知*/@AfterReturning(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)")public void AfterReturning() {System.out.println("----------------------后置通知----------------------");}/*** 异常通知** @param point*/@AfterThrowing(value = "@annotation(com.gblfy.elk.annotate.GblfyStreamLimit)", throwing = "e")public void serviceAspect(JoinPoint point, Exception e) {System.out.println("----------------------异常通知----------------------");}
}
2.11. 案例
package com.gblfy.elk.controller;import com.gblfy.elk.annotate.GblfyStreamLimit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class SreamLimitController {@GetMapping("/get2")@GblfyStreamLimit(name = "get2", limitNum = 1.0)public String get2() {System.out.println("-----------------执行目标方法-----------------");return "my is get";}@GetMapping("/add")public String add() {return "my is add";}
}

http://127.0.0.1:8080/get
http://127.0.0.1:8080/my

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

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

相关文章

DSW:面向AI研发的集成开发平台

发布会传送门 产品详情 云原生技术&#xff0c;注重用户体验&#xff0c;提升研发效率 环境搭建是算法研发过程中的重要一环&#xff0c;这里除了硬件选型外&#xff0c;软件环境的安装配置&#xff0c;后续升级往往会耗费不少时间。DSW借助阿里云ECS&#xff0c;Docker和Ku…

@Builder(toBuilder=true) 链式初始化对象、修改对象

引入lombok&#xff1a; <!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>实体创建&#xff1a; Data Builder(toBuildertrue)…

程序员应如何理解高并发中的协程

来源 | 码农的荒岛求生责编 | 晋兆雨头图 | 付费下载于视觉中国作为程序员&#xff0c;想必你多多少少听过协程这个词&#xff0c;这项技术近年来越来越多的出现在程序员的视野当中&#xff0c;尤其高性能高并发领域。当你的同学、同事提到协程时如果你的大脑一片空白&#xff…

5G边缘计算行业通识:阿里云ENS技术演进之路

近日&#xff0c;阿里云杨敬宇在CSDN阿里云核心技术竞争力在线峰会上进行了《5G基础设施-阿里云边缘计算的技术演进之路》主题演讲&#xff0c;针对5G时代下&#xff0c;行业和技术的趋势、边缘计算产业通识以及阿里云边缘计算从过去到未来的技术演进之路进行分享。 5GAI需求推…

精讲23种设计模式-策略模式~聚合短信服务和聚合支付服务

文章目录一、设计模式1. 为什么需要使用设计模式2. 设计模式的分类3. 什么是策略模式4. 为什么叫做策略模式5. 策略模式优缺点6. 策略模式应用场景7. Spring框架中使用的策略模式二、策略模式~聚合短信服务2.1. 依赖引入2.2. 抽象公共行为接口2.3. 具体策略接口实现类2.4. 策略…

引领开源新风潮,阿里巴巴编程之夏第二期重磅来袭!

“唯有热爱&#xff0c;可抵岁月漫长”。 2020 年 5 月 25 日&#xff0c;阿里巴巴编程之夏&#xff08;Alibaba Summer of Code&#xff0c;以下简称 ASoC &#xff09;第二期正式上线&#xff0c;项目规模再度升级&#xff0c;来自开源社区的 Apache Dubbo、Apache RocketMQ…

powerdesigner逆向工程(sql转pdm)

第一步&#xff1a; File -> Reverse Engineer -> Database 第二步 &#xff1a; Using Script Files -> Add Files

安谋中国发布“玲珑”i3i5 ISP处理器,剑指何方?

随着图像视频处理技术的发展和 5G 时代的来临&#xff0c;除了社交等平台外&#xff0c;以图像、视频为载体的内容渗透到各领域&#xff0c;特别是智能安防、AIoT、智能汽车等新兴领域应用。与此同时&#xff0c;人们对其清晰度、图像分辨率有新的扩展需求&#xff0c;对摄像头…

凯度信息之美奖揭晓,数据可视化后有多“性感”?

前言&#xff1a;更多关于数智化转型、数据中台内容可扫码加群一起探讨 阿里云数据中台官网 https://dp.alibaba.com/index &#xff08;作者&#xff1a;常成&#xff09; 2019年的“凯度信息之美奖”揭晓了&#xff0c;有很多很有意思的信息可视化作品。很多作品看到的时候…

精讲23种设计模式-基于责任链模式~构建企业级风控系统

文章目录一、责任链1. 责任链基本概念2. 定义3. 关键要点4. 责任链模式优缺点5. 责任链模式类结构图6. 网关权限控制责任链模式二、构建企业级风控系统2.1. 定义公共抽象任务2.2. (失信名单)校验处理类2.3. (信用卡)逾期处理类2.4. (蚂蚁信用积分)处理类2.5. 责任链工厂(第一种…

mybatis报错:Could not find result map java.lang.Integer

异常原因&#xff1a; 返回值为Integer的语句&#xff0c;本应该是&#xff1a; resultType"java.lang.Integer"//正确写的是&#xff1a; resultMap"java.lang.Integer"//错误最坑的是别人的mapper写错&#xff0c;会影响全局哦&#xff0c;不一定是你…

说一说阿里云弹性公网IP那些事

弹性公网IP是独立的公网IP资源&#xff0c;可以绑定到阿里云专有网络VPC类型的ECS、NAT网关、私网负载均衡SLB上&#xff0c;并可以动态解绑&#xff0c;实现公网IP和ECS、NAT网关、SLB的解耦&#xff0c;满足灵活管理的要求。 弹性公网IP作为一种网络基础能力&#xff0c;也是…

抓取了《大秦赋》所有数据,我发现了这些秘密

本文由黄勇老师特约供稿学习人数超13万人的<Python入门到实战一卡通>作者网易、360、华为特约Python讲师前言最近大火的电视剧《大秦赋》&#xff0c;给朋友圈的小伙伴都拉回到那个风云激荡的春秋战国时期&#xff0c;大家都在热情的讨论着大秦一统&#xff0c;秦始皇嬴政…

org.apache.ibatis.reflection.ReflectionException: There is no getter for property named XXX

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named XXX 根本原因&#xff1a; mybatis 的xml中有的属性&#xff0c;在mapper传入的参数中找不到。 可能引起原因&…

精讲23种设计模式-基于观察者模式~设计异步多渠道群发框架

文章目录一、观察者模式1. 观察者模式基本概念2. 观察者模式的应用场景3. 观察者模式的类图二、设计异步多渠道群发框架2.1. 定义消息观察者抽象接口2.2. 创建观察者2.3. 主题通知所有观察者2.4. 观察者注册2.5. 自定义线程池2.6. 签单通知入口2.6. 异步通知接口测试2.7. 依赖三…

君子动手不动口,阿里云喊你做云上体验官啦!

想要免费搭建云上博客&#xff1f;想要玩转全云端开发&#xff1f;想要挑战AI经典命题&#xff1f;想要7天进阶成为云计算专家&#xff1f;想要初始化你的云原生工程&#xff1f;快来阿里云 Hands-on Labs&#xff01; Hands-on Labs 是阿里云全新推出的云上动手实验室&#x…

我是Redis,MySQL大哥被我害惨了!

来源 | 编程技术宇宙责编 | 晋兆雨头图 | 付费下载于视觉中国我是Redis你好&#xff0c;我是Redis&#xff0c;一个叫Antirez的男人把我带到了这个世界上。说起我的诞生&#xff0c;跟关系数据库MySQL还挺有渊源的。在我还没来到这个世界上的时候&#xff0c;MySQL过的很辛苦&a…

MySQLSyntaxErrorException: Every derived table must have its own alias

每个派生出来的表都必须有一个自己的别名 因为&#xff0c;进行嵌套查询的时候子查询出来的的结果是作为一个派生表来进行上一级的查询的&#xff0c;所以子查询的结果必须要有一个别名 把MySQL语句改成&#xff1a;select count(*) from (select * from ……) as total; 问…

联手友盟+打造云上数据增长“样板间”, 好兔视频成功逆势突围

前言&#xff1a;更多关于数智化转型、数据中台内容可扫码加群一起探讨 阿里云数据中台官网 https://dp.alibaba.com/index &#xff08;作者&#xff1a;友盟&#xff09; “消费升级”是近年来的中国消费市场热门词汇&#xff0c;消费升级的同时也驱动了内容消费升级。在这样…

SpringBoot 使用 Caffeine 本地缓存

文章目录一、本地缓存介绍二、缓存组件 Caffeine 介绍2.1. Caffeine 性能2.2. Caffeine 配置说明2.3. 软引用与弱引用三、SpringBoot 集成 Caffeine 方式一3.1. Maven 引入相关依赖3.2. 配置缓存配置类3.3. 定义实体对象3.4. 定义服务接口类3.5. 定义服务接口实现类3.6. Caffei…