Spring面向切面编程AOP使用介绍

news2025/1/17 23:20:06

文章目录

  • AOP
    • AOP核心概念
    • AOP快速入门
    • AOP工作流程
    • AOP切入点表达式
    • AOP通知类型
    • AOP通知获取数据
    • AOP总结

AOP

AOP核心概念

AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构

OOP(Object Oriented Programming)面向对象编程

作用:在不惊动原始设计的基础上为其进行功能增强

Spring理念:无入侵式/无侵入式

我们来看下面这样一段程序:

某个类中, 有如下四个方法, save方法会打印一句话, 并循环一万次计算出所消耗的时间; 而其他三个方法中只打印一句话, 我们想要update和delete也有save相同的功能, 而select方法就只单纯打印一句话

public void save() {
  	//记录程序当前执行执行(开始时间)
  	Long startTime = System.currentTimeMillis();
  	//业务执行百万次
  	for (int i = 0;i<10000;i++) {
    System.out.println(“book dao save ...);
    }
  	//记录程序当前执行时间(结束时间)
  	Long endTime = System.currentTimeMillis();
  	//计算时间差
  	Long totalTime = endTime-startTime;
  	//输出信息
  	System.out.println("万次耗时:" + totalTime + "ms");
}

public void update(){
  	System.out.println("book dao update ...");
}

public void delete(){
  	System.out.println("book dao delete ...");
}

public void select(){
  	System.out.println("book dao select ...");
}

我们可以使用AOP思想, 将方法的共性抽取出来, 在不改变方法的原代码上, 追加上循环一万次并计算时间的功能

在这里插入图片描述

连接点(JoinPoint):程序执行过程中的任意位置,可以为执行方法、抛出异常、设置变量等

在SpringAOP中,理解为方法的执行

切入点(Pointcut):匹配连接点的式子

在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法

  • 一个具体方法:com.chenyq.dao包下的BookDao接口中的无形参无返回值的save方法
  • 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法

通知(Advice):在切入点处执行的操作,也就是共性功能

在SpringAOP中,功能最终以方法的形式呈现, 执行共性操作的方法

通知类:定义通知的类

切面(Aspect):描述通知与切入点的对应关系


简单总结:

连接点: 代表所有的方法

切入点: 代表要追加功能的方法

通知: 代表共性功能的方法

通知类: 代表定义通知(共性功能方法)的类

切面: 代表切入点与通知之间的关系

AOP快速入门

案例需求:要求方法在之前之前打印出当前系统时间

开发模式:注解开发

AOP入门案例思路分析:

  1. 导入坐标到pom.xml文件
  2. 制作连接点方法
  3. 制作共性功能, 通知类和通知
  4. 定义切入点
  5. 绑定切入点与通知关系

实现步骤如下:

导入aop和aspect相关坐标, 我们在导入spring坐标时会默认导入aop的依赖坐标, 所以我们只需导入aspect坐标即可

<dependencies>
  <!--spring依赖-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
  </dependency>

  <!--aspect依赖-->
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
  </dependency>
</dependencies>

定义dao接口和实现类

BookDaoImpl实现类类中有下面几个方法, 我们希望update方法在不改变源程序的基础上, 添加执行之前打印当前系统时间的功能, 而add方法不变

public interface BookDao {
    void save();
    void update();
    void add();
}
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save...");
    }

    public void update() {
        System.out.println("book dao update...");
    }

    public void add() {
        System.out.println("book dao add...");
    }
}

将共性功能抽取到一个通知类下的通知中, 通知类中需要使用@Component注解, 让Spring识别该类为bean; 在通过@Aspect注解告知Spring当前类为切面类

@Component
@Aspect
public class MyAdvice {
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}

@Pointcut定义切入点, 切入点要求依托在一个不具有实际意义的方法, 即无参数, 无返回值, 方法体无实际逻辑

@Component
@Aspect
public class MyAdvice {
    // 表示包下无返回值无参数的update方法
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {}
    
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}

绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置, 由于我们希望在方法之前执行, 所以使用@Before注解

@Component
@Aspect
public class MyAdvice {
    // 表示包下无返回值无参数的update方法
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {}

    // 描述切入点与通知之间的关系
    @Before("pt()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}

Spring核心配置文件中, 通过@EnableAspectJAutoProxy开启Spring对AOP注解驱动支持

@Configuration
@ComponentScan("com.chenyq")
@EnableAspectJAutoProxy // 告知Spring有用注解开发的AOP
public class SpringConfig {
}

此时运行update方法也会打印当前的系统时间了, 我们就做到了在不改变源代码的基础上添加了新的功能

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }
}

AOP工作流程

  1. Spring容器启动
  2. 读取所有切面配置中的切入点

注意: 是所有切面配置的切入点, 并不是所有配置的切入点, 例如下面代码, 我们有两个切入点, 但是只配置使用了一个切入点pt, ptx是没有配置使用的那么不会被读取的

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.chenyq.dao.BookDao.add())")
    public void ptx() {}
    
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {}

    // 描述切入点与通知之间的关系
    @Before("pt()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}
  1. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

匹配失败,创建对象

匹配成功,创建原始对象(目标对象)的代理对象

  1. 获取bean执行方法

获取bean,调用方法并执行,完成操作

获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

目标对象(Target):

原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的

代理(Proxy):

目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

AOP切入点表达式

切入点:要进行增强的方法

切入点表达式:要进行增强的方法的描述方式

对于任何一个方法, 它的表达形式是多种多样的:

例如我们有一个接口BookDao

package com.chenyq.dao;
public interface BookDao {
    void update();
}

BookDao接口的实现类BookDaoImpl

@Repository
public class BookDaoImpl implements BookDao {
    public void update() {
        System.out.println("book dao update...");
    }
}

描述上面的update方法, 我们就有两种方法进行描述:

描述方式一: 按照接口描述

// 执行com.chenyq.dao包下的BookDao接口中的无参数, 无返回值的update方法
execution(void com.itheima.dao.BookDao.update())

描述方式二: 按照类描述

// 执行com.chenyq.dao.impl包下的BookDaoImpl类中的无参数, 无返回值的update方法
execution(void com.itheima.dao.impl.BookDaoImpl.update())

切入点表达式标准格式如下

动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution(public User com.chenyq.service.UserService.findById(int))

动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点

访问修饰符:public,private等,可以省略

返回值: 方法返回值的类型

包名: 方法所在哪个包下

类/接口名: 方法所在的类或者接口

方法名

参数

异常名:方法定义中抛出指定异常,可以省略

在程序中, 我们可以使用通配符描述切入点,快速描述

*: 单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

// 匹配chenyq包下的任意包中的UserService类或接口下的所有以find开头且带有一个任意参数,任意返回值的方法
execution(public * com.chenyq.*.UserService.find*(*))

..:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

// 匹配com包下任意包中的UserService类或接口的findById方法, 且参数任意个(0个或多个)
execution(public User com..UserService.findById(..))

+:专用于匹配子类类型

// 匹配任意返回值 任意包下以Service结尾的类或接口的子类的任意方法
execution(* *..*Service+.*(..))

切入点表达式书写技巧:

所有代码按照标准规范开发, 也就是命名要标准,否则以下技巧全部失效

描述切入点通常描述接口,而不描述实现类, 因为描述到实现类耦合过高

访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)

返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述

包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配

接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名

方法名书写以动词进行精准匹配,名词采用匹配,例如getById书写成getBy,selectAll书写成selectAll

参数规则较为复杂,根据业务方法灵活调整

通常不使用异常作为匹配规则

AOP通知类型

AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

AOP通知共分为5种类型:

前置通知

后置通知

环绕通知(重点)

返回后通知(了解)

抛出异常后通知(了解)

前置通知

名称:@Before

类型:方法注解

位置:通知方法定义上方

作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行前运行

相关属性:value(默认):切入点方法名,格式为类名.方法名()

示例:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {}

    // 描述切入点与通知之间的关系
    @Before("pt()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}

后置通知

名称:@After

类型:方法注解

位置:通知方法定义上方

作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行后运行

相关属性:value(默认):切入点方法名,格式为类名.方法名()

示例:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {}

    @After("pt()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}

环绕通知

名称:@Around(重点,常用)

类型:方法注解

位置:通知方法定义上方

作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法执行的前后运行

相关属性:value(默认):切入点方法名,格式为类名.方法名()

注意: 环绕操作, 我们需要接受一个ProceedingJoinPoint类型的参数, 该参数的proceed方法代表着对原始方法调用的操作; 简单来说就是proceed方法代表着什么执行原始方法

示例:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.chenyq.dao.BookDao.update())")
    private void pt() {}

    @Around("pt()")
    public void method(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(System.currentTimeMillis());
        System.out.println("方法执行前的操作...");
        pjp.proceed(); // 代表着原始方法发调用
        System.out.println("方法执行后的操作...");
    }
}

环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知

通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行

对原始方法的调用可以不接收返回值,通知方法设置成void即可; 如果接收返回值,必须设定为Object类型, 并将原始方法的返回值返回出去

原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object

由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(int com.chenyq.dao.BookDao.update())")
    private void pt() {}

    @Around("pt()")
    public Object method(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("方法执行前的操作...");
        Object res = pjp.proceed();// 接收原始方法的返回值
        System.out.println("方法执行后的操作...");
        // 将原始方法的返回值返回
        return res;
    }
}

返回后通知

名称:@AfterReturning(了解)

类型:方法注解

位置:通知方法定义上方

作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行

示例:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(int com.chenyq.dao.BookDao.update())")
    private void pt() {}
  
    @AfterReturning("pt()")
    public void method() {
        System.out.println("afterReturning...");
    }
}

抛出异常后通知

名称:@AfterThrowing(了解)

类型:方法注解

位置:通知方法定义上方

作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行, 有异常才会执行, 无异常不执行

示例:

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(int com.chenyq.dao.BookDao.update())")
    private void pt() {}

    @AfterThrowing("pt()")
    public void method() {
        System.out.println("AfterThrowing...");
    }
}

五种通知类型中, 最常用的是环绕通知@Around

AOP通知获取数据

通知获取数据分三种:

获取切入点方法的参数

获取切入点方法返回值

获取切入点方法运行异常信息(了解)

AOP通知获取参数数据:

JoinPoint:适用于前置、后置、返回后、抛出异常后通知

  • JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {}

    @Before("pt()")
    public void methods(JoinPoint jp) {
        // 通过JoinPoint获取参数
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
    }
}

ProceedJointPoint:适用于环绕通知

@Aspect
public class MyAdvice {
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {}

    @Around("pt()")
    public void methods(ProceedingJoinPoint pjp) throws Throwable {
        // 环绕通知通过ProceedingJoinPoint获取参数
        Object[] args = pjp.getArgs();
        pjp.proceed();
        System.out.println(Arrays.toString(args));
    }
}

获取切入点方法返回值, 只有返回后通知和环绕通知可以获取

返回后通知获取返回值

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {}
  
    // 设置通过ret变量获取返回值
    @AfterReturning(value = "pt()", returning = "ret")
    public void methods(Object ret) {
        System.out.println(ret);
    }
}

环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(String com.chenyq.dao.BookDao.findName(*))")
    private void pt() {}

    @Around("pt()")
    public void methods(ProceedingJoinPoint pjp) throws Throwable {
        // 环绕通知通过ProceedingJoinPoint获取参数
        Object res = pjp.proceed();
        System.out.println(res);
    }
}

获取切入点方法运行异常信息只有抛出异后常通知和环绕通知可以获取(了解)

抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象

@AfterThrowing(value = "pt()", throwing = "err")
public void afterThrowing(Throwable err) {
    System.out.println(err);
}

环绕通知可以通过try…catch获取

@Around("pt()")
public void methods(ProceedingJoinPoint pjp) {
    // 环绕通知通过ProceedingJoinPoint获取参数
    Object res = null;
    try {
        res = pjp.proceed();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println(res);
}

AOP总结

概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式

作用:在不惊动原始设计的基础上为方法进行功能增强

核心概念:

代理(Proxy):SpringAOP的核心本质是采用代理模式实现的

连接点(JoinPoint): 在SpringAOP中,理解为任意方法的执行

切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述

通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法

切面(Aspect):描述通知与切入点的对应关系

目标对象(Target):被代理的原始对象成为目标对象

切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)

execution(* com.itheima.service.*Service.*(..))

切入点表达式描述通配符

作用:用于快速描述,范围描述

* :匹配任意符号(常用)

..:匹配多个连续的任意符号(常用)

+:匹配子类类型

切入点表达式书写技巧:

按标准规范开发

查询操作的返回值建议使用*匹配

减少使用…的形式描述包

对接口进行描述,使用表示模块名,例如UserService的匹配描述为Service

方法名书写保留动词,例如get,使用表示名词,例如getById匹配描述为getBy

参数根据实际情况灵活调整

通知类型:

前置通知

后置通知

环绕通知(重点)

  • 环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用
  • 环绕通知可以隔离原始方法的调用执行
  • 环绕通知返回值设置为Object类型
  • 环绕通知中可以对原始方法调用过程中出现的异常进行处理

返回后通知

抛出异常后通知

获取切入点方法的参数

JoinPoint:适用于前置、后置、返回后、抛出异常后通知,设置为方法的第一个形参

ProceedJointPoint:适用于环绕通知

获取切入点方法返回值

返回后通知

环绕通知

获取切入点方法运行异常信息

抛出异常后通知

环绕通知

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

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

相关文章

Web自动化测试的详细流程和步骤,一篇足矣

Web自动化测试是软件测试中非常重要的一种测试方法&#xff0c;它通过编写脚本来模拟人工操作网页&#xff0c;从而实现对Web应用程序进行自动化测试的过程。为了保证测试质量和效率&#xff0c;我们需要遵循一定的流程和步骤来完成Web自动化测试。 一、测试环境准备 在进行W…

webp怎么转换成png,4个方法教你快速处理

webp怎么转换成png&#xff1f;目前在一些比较大的图片素材网站下载的图片都是webp格式的。我们都知道webp格式图片&#xff0c;它在正常的图片浏览器中是无法打开的。 所以说我们要把webp图片转变成png格式&#xff0c;正常来说我们常用的图片处理软件也能进行格式转换&#x…

都说互联网不行了,真的是这样吗?

最近在马士兵教育的课堂上经常会听到学员说这样的话&#xff1a; 现在公司都不招人&#xff0c;程序员根本找不到工作 早知道IT行业这么难&#xff0c;当初就不学编程了 简历石沉大海、面试机会也没有&#xff0c;互联网是不是不行了 互联网行情到底如何&#xff1f; 作为…

从此告别写SQL!DataLeap帮你零门槛完成“数据探查”

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 在日常数据处理工作中&#xff0c;产品、运营、研发或数据分析师经常会面临数据量大且混乱、质量参差不齐的问题&#xff0c;需要花费大量时间和精力校验表数据是否…

车企外卷:一个关于智能手机的“围城故事”

从2016年达到顶峰开始&#xff0c;全球智能手机出货量逐年下行&#xff0c;手机市场进入红海竞争逐渐成为了各界的共识。此后全球疫情与经济疲软的影响也进一步在手机市场施压&#xff0c;很多媒体认为手机产业距离“至暗时刻”已经不远。而在去年&#xff0c;新增变数&#xf…

Velocity tools进阶(下篇)

最近自己所做的项目使用到这个Velocity模板引擎&#xff0c;分享一下在互联网找的学习资料&#xff0c;仅供学习使用&#xff0c;不参与任何商业活动。 一. VelocityTools介绍 1.1 VelocityTools简介 Velocity Tools 是 Velocity模板引擎的一个子项目&#xff0c;用于将 Velo…

Excel玩转自然语言查询

ChatGPT火出圈&#xff0c;人类被人工智能替代又成为热门话题。有人欢喜&#xff0c;有人忧&#xff0c;也有人不以为意&#xff0c;觉得离自己工作远着呢&#xff0c;比如现在是用Excel做报表&#xff0c;有本事你动动嘴就直接把Excel里面的数据查询出来啊。 你可别说&#xf…

metersphere逻辑整理

整体架构 Frontend: MeterSphere 的前端工程, 基于 Vue.js 进行开发。 Backend: MeterSphere 的后端工程, 基于 Spring Boot 进行开发, 为 MeterSphere 的功能主体。 Chrome Plugin: 浏览器插件, 录制 Web 访问请求生成 JMeter 脚本并导入到 MeterSphere 中用于接口测试及性能…

阿里云产品试用更新,产品组合试用装更划算,快来免费上云吧

最近阿里云产品又上新了&#xff0c;尤其是推出了最新的产品组合试用装&#xff0c;个人觉得阿里云关于云产品的更新迭代是非常重视的&#xff0c;而且每次推出的产品不仅会惊艳到用户&#xff0c;而且产品功能也是随着一次迭代而更加完善、强大。前段时间也写了一篇关于为什么…

Nginx服务配置及相关模块

目录一、Nginx简介1、Nginx简介2、I/O模型相关概念3、Nginx事件驱动模型4、Nginx和Apache的区别二、编译安装Nginx服务1.关闭防火墙&#xff0c;将安装的Nginx软件包安装到opt目录下2、编译安装Nginx3、创建用户、组&#xff0c;以便于更好的管理4、创建软连接并启动5、停止Ngi…

FinClip 开发者工具重构升级!性能飞跃,体验有礼

一直以来不少开发者朋友在社区反馈&#xff0c;在使用 FIDE 工具的过程中&#xff0c;时常会遇到诸如加载不及时、代码预览/渲染性能不如意的情况&#xff0c;十分影响开发体验。 作为技术团队&#xff0c;我们深知一件趁手的开发工具对开发者的重要性&#xff0c;因此&#x…

企业AD域(域控服务器)的安装和配置详细教程

一、环境以及工具准备 软件&#xff1a;VMWare Workstation 2016 &#xff08; 下载链接&#xff1a;https://pan.baidu.com/s/1iX1VRilerYPGbGvX4pvaKw 提取码&#xff1a;75R6 &#xff09; 镜像&#xff1a;Windows Server 2016 &#xff08; 下载地址&#xff…

[CVPR 2020] Regularizing Class-Wise Predictions via Self-Knowledge Distillation

ContentsIntroductionClass-wise self-knowledge distillation (CS-KD)Class-wise regularizationEffects of class-wise regularizationExperimentsClassification accuracyReferencesIntroduction 为了缓解模型过拟合&#xff0c;作者提出 Class-wise self-knowledge distil…

SpringDataRedis客户端详解

SpringData是Spring中数据操作的模块&#xff0c;包含对各种数据库的集成&#xff0c;其中对Redis的集成模块就叫做SpringDataRedis&#xff0c;官网地址&#xff1a;https://spring.io/projects/spring-data-redis 提供了对不同Redis客户端的整合&#xff08;Lettuce和Jedis&…

乡村振兴吹响品牌号角——首届中国乡村振兴品牌大会成功举办

“品牌是力量&#xff0c;是变量&#xff0c;也是流量。”这是浙江省衢州市人大常委会副主任、常山县委书记潘晓辉在首届中国乡村振兴品牌大会上分享的乡村振兴实践体会&#xff0c;引发与会代表的广泛共鸣。 首届中国乡村振兴品牌大会主会场 4月10日至11日&#xff0c;以“加…

计组2.2——数据在计算机中的存储

计组2.2定点数和浮点数定点数无符号数有符号数的定点表示——定点整数和定点小数移位运算1.算术移位1.原码算数移位2.反码算数移位3.补码算术移位4.计算机硬件的乘法是基于算数移位和加法完成的2.逻辑移位3.循环移位加减运算溢出判断硬件判断溢出法一&#xff1a;单一符号位法二…

51单片机语音提示盲人拐杖整点报时夜间警示超声波测距

实践制作DIY- GC0128-语音提示盲人拐杖 一、功能说明&#xff1a; 基于51单片机设计-语音提示盲人拐杖 二、功能介绍&#xff1a; 硬件组成&#xff1a; STC89C52单片机最小系统LCD1602显示器红色LED灯ADC0832光敏电阻采集光强DY-SV17F语音播报模块HSR04超声波测距多个按键…

如何在facebook上打造成功的个人品牌?马克·扎克伯格告诉你!

在现代商业社会中&#xff0c;一个企业成功的关键之一是打造出良好的企业形象&#xff0c;即所谓的“人设”。一个好的企业形象可以让消费者产生信任感和归属感&#xff0c;从而吸引更多的客户和业务机会。而如今&#xff0c;社交媒体成为了一个打造企业形象的重要工具&#xf…

JS操作文本域获取光标/指定位置插入

学习链接 js 如何获取与设置光标在input框的位置 在输入框/文本域中光标位置插入字符串 Web 中的“选区”和“光标” 可参考另外一篇&#xff1a;vue.js支持表情输入&#xff0c;这个比操作 div简单多了。 效果图 代码 <style scoped> textarea {outline: none;res…

微信小程序开发 | API应用案例(下)

API应用案例&#xff08;下&#xff09;6.1【案例5】模拟时钟6.1.1 案例分析6.1.2 前导知识6.1.3 钟表页面布局6.1.4 钟表页面绘制6.2【案例6】罗盘动画6.2.1 案例分析6.2.2 前导知识6.2.3 设计罗盘页面布局6.2.4 手指触摸旋转罗盘6.2.5 单击按钮操作罗盘6.3【案例7】文件上传与…