SpringAOP复习

SpringAOP

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程。他是一种可以在不修改原来核心代码的情况侠给程序动态统一进行增强的一种技术

SpringAOP:批量对Spring容器中的bean方法做增强,并且这种增强不会与原来方法中的代码耦合

1.快速入门

1.1需求

要求让08_SpringAOP模块中service包下所有类的所有方法在调用前都输出:方法被调用了

1.2准备工作

第一步:引入依赖

  <!--SpringIOC相关依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency><!--AOP相关依赖--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></dependency>

第二步:开启组件扫描

<context:component-scan base-package="com.sangeng"></context:component-scan>

第三步:相关bean注入容器中

@Service
public class PhoneService {public void deleteAll(){System.out.println("PhoneService中deleteAll的核心代码");}
}
@Service
public class UserService {public void deleteAll(){System.out.println("UserService中deleteAll的核心代码");}
}

1.3实现AOP

①开启AOP注解支持
<?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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--开启组件扫描--><context:component-scan base-package="com.sangeng"></context:component-scan><!--开启aop注解支持--><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>
②创建切面类

创建一个类,在类上加上@Component和@Aspect

使用@Pointcut注解来指定要被增强的方法

使用@Before注解来给我们的增强代码所在的方法进行标识,并且指定了增强代码是在被增强方法执行之前执行的

package com.sangeng.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Component
@Aspect
public class MyAspect {//写一个pt方法,作用是用来承载PointCut//PointCut注解来声明要对哪些方法进行增强@Pointcut("execution(* com.sangeng.service.*.*(..))")public void pt(){}//用@Before注解来指定该方法中是增强的代码,并且指定被哪个切点表达式指定的方法前执行前执行的//@Before的属性写上加了@Pointcut注解的方法: 方法名()@Before("pt()")public void methodBefore(){System.out.println("方法被调用了");}}
③测试
package com.sangeng;import com.sangeng.service.PhoneService;
import com.sangeng.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Demo {public static void main(String[] args) {ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService = app.getBean(UserService.class);PhoneService phoneService = app.getBean(PhoneService.class);userService.deleteAll();phoneService.deleteAll();}
}

2.AOP核心概念

  • Jointpoint(连接点):所谓连接点是指那些可以被增强的点。在spring中,这些点指的的方法,因为spring只支持方法类型的连接点

  • Pointcut(切入点):所谓切入点是指被增强的连接点(方法)

  • Advice(通知/ 增强):所谓通知是指具体增强的代码

  • Target(目标对象):被增强的对象就是目标对象

  • Aspect(切面):是切入点和通知(引介)的结合

  • Proxy (代理):一个类被 AOP 增强后,就产生一个结果代理类

3.切点确定

3.1切点表达式

可以使用切点表达式来表示要对哪些方法进行增强

写法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  • 访问修饰符可以省略,大部分情况下省略
  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
  • 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表
execution(* com.sangeng.service.*.*(..))   表示com.sangeng.service包下任意类,方法名任意,参数列表任意,返回值类型任意execution(* com.sangeng.service..*.*(..))   表示com.sangeng.service包及其子包下任意类,方法名任意,参数列表任意,返回值类型任意execution(* com.sangeng.service.*.*())     表示com.sangeng.service包下任意类,方法名任意,要求方法不能有参数,返回值类型任意execution(* com.sangeng.service.*.delete*(..))     表示com.sangeng.service包下任意类,参数列表任意,返回值类型任意,方法名要求已delete开头

3.2切点函数@annotation

我们也可以在要增强的方法上加上注解。然后使用@annotation来表示对加了什么注解的方法进行增强

写法:@annotation(注解的全类名)

第一步:自定义注解如下

package com.sangeng.aspect;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 KekeAnnotation {
}

切面类中使用@annotation来确定要增强的方法

package com.sangeng.aspect;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Component
@Aspect
public class MyAspect {@Pointcut("@annotation(com.sangeng.aspect.KekeAnnotation)")public void pt(){}//用@Before注解来指定该方法中是增强的代码,并且指定被哪个切点表达式指定的方法前执行前执行的//@Before的属性写上加了@Pointcut注解的方法: 方法名()@Before("pt()")public void methodBefore(){System.out.println("方法被调用了");}}

给需要增强的方法增加注解

package com.sangeng.service;import com.sangeng.aspect.KekeAnnotation;
import org.springframework.stereotype.Service;@Service
public class PhoneService {@KekeAnnotationpublic void deleteAll(){System.out.println("PhoneService中deleteAll的核心代码");}
}

测试

image-20231218104941170

4.通知分类

  • @Before:前置通知,在目标方法执行前执行

  • @AfterReturning: 返回后通知,在目标方法执行后执行,如果出现异常不会执行

  • @After:后置通知,在目标方法之后执行,无论是否出现异常都会执行

  • @AfterThrowing:异常通知,在目标方法抛出异常后执行

  • @Around:环绕通知,围绕着目标方法执行

环绕通知非常特殊,它可以对目标方法进行全方位的增强,我们重点学习@Around环绕通知

package com.sangeng.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Component
@Aspect
public class MyAspect {@Pointcut("@annotation(com.sangeng.aspect.KekeAnnotation)")public void pt(){}@Around("pt()")public void around(ProceedingJoinPoint pjp){System.out.println("目标方法执行前");try {pjp.proceed(); //目标方法执行System.out.println("目标方法执行后");} catch (Throwable e) {e.printStackTrace();System.out.println("目标方法出现异常");} finally {System.out.println("finally代码块");}}}

5.获取被增强方法相关信息

我们实际对方法进行增强时往往还需要获取到被增强代码的相关信息,比如方法名,参数,返回值,异常对象等

我们可以在除了环绕通知外的所有通知方法中增加一个JoinPoint类型的参数。这个参数封装了被增强方法的相关信息。**我们可以通过这个参数获取到除了异常对象和返回值之外的所有信息

@Before("pt()")public void methodbefore(JoinPoint jp){Object[] args = jp.getArgs();//方法调用时传入的参数Object target = jp.getTarget();//被代理对象MethodSignature signature = (MethodSignature) jp.getSignature();//获取被被增强方法签名封装的对象System.out.println("Before方法被调用了");}

案例:

需求:要求让所有service包下类的所有方法被调用前都输出全类名,方法名,以及调用时传入的参数

package com.sangeng.aspect;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Arrays;@Component
@Aspect
public class MyAspect {@Pointcut("execution(* com.sangeng.service..*.*(..))")public void pt(){}@Before("pt()")public void beforeMethod(JoinPoint jp){MethodSignature signature = (MethodSignature) jp.getSignature();//获取参数Object[] args = jp.getArgs();Method method = signature.getMethod();//获取方法名String methodName = method.getName();//获取全类名String declaringTypeName = signature.getDeclaringTypeName();System.out.println("全类名:" + declaringTypeName);System.out.println("方法名:" + methodName);System.out.println("参数:" + Arrays.toString(args));}
}

如果需要获取被增强方法中的异常对象或者返回值则需要在方法参数上增加一个对应类型的参数,并且使用注解的属性进行配置。这样Spring会把你想获取的数据赋值给对应的方法参数

但是上述的方法比较麻烦,直接在环绕通知方法中增加一个ProceedingJoinPoint类型的参数。这个参数封装了被增强方法的相关信息

该参数的proceed()方法被调用相当于被增强方法被执行,调用后的返回值就相当于被增强方法的返回值

@Around(value = "pt()")public Object around(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();//方法调用时传入的参数Object target = pjp.getTarget();//被代理对象MethodSignature signature = (MethodSignature) pjp.getSignature();//获取被被增强方法签名封装的对象Object ret = null;try {ret = pjp.proceed();//ret就是目标方法执行后的返回值} catch (Throwable throwable) {throwable.printStackTrace();//throwable就是出现异常时的异常对象}return ret;}

6.多切面顺序问题

在实际项目中我们可能会存在配置了多个切面的情况。这种情况下我们很可能需要控制切面的顺序

我们在默认情况下Spring有它自己的排序规则。(按照类名排序)

默认排序规则往往不符合我们的要求,我们需要进行特殊控制

如果是注解方式配置的AOP可以在切面类上加**@Order注解来控制顺序。@Order中的属性越小优先级越高**

如果是XML方式配置的AOP,可以通过调整配置顺序来控制

例如:

@Component
@Aspect
@Order(2)
public class APrintLogAspect {//省略无关代码
}
@Component
@Aspect
@Order(1)
public class CryptAspect {//省略无关代码
}

7.动态代理

实际上Spring的AOP其实底层就是使用动态代理来完成的。并且使用了两种动态代理分别是JDK的动态代理和Cglib动态代理。所以我们接下去来学习下这两种动态代理,理解下它们的不同点

7.1JDK动态代理

JDK的动态代理使用的java.lang.reflect.Proxy这个类来进行实现的。要求被代理(被增强)的类需要实现了接口。并且JDK动态代理也只能对接口中的方法进行增强

public static void main(String[] args) {AIControllerImpl aiController = new AIControllerImpl();//使用动态代理增强getAnswer方法//1.JDK动态代理//获取类加载器ClassLoader cl = Demo.class.getClassLoader();//被代理类所实现接口的字节码对象数组Class<?>[] interfaces = AIControllerImpl.class.getInterfaces();AIController proxy = (AIController) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {//使用代理对象的方法时 会调用到invokepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//proxy   是代理对象//method 是当前被调用的方法封装的Method对象//args   是调用方法时传入的参数//调用被代理对象的对应方法//判断 当前调用的是否是getAnswer方法if(method.getName().equals("getAnswer")){System.out.println("增强");}Object ret = method.invoke(aiController, args);return ret;}});String answer = proxy.getAnswer("三连了吗?");System.out.println(answer);}

7.2Cglib动态代理

使用的是org.springframework.cglib.proxy.Enhancer类进行实现的

public class CglibDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();//设置父类的字节码对象enhancer.setSuperclass(AIControllerImpl.class);enhancer.setCallback(new MethodInterceptor() {//使用代理对象执行方法是都会调用到intercept方法@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//判断当前调用的方法是不是getAnswer方法 如果是进行增强if ("getAnswer".equals(method.getName())){System.out.println("被增强了");}//调用父类中对应的方法Object ret = methodProxy.invokeSuper(o, objects);return ret;}});//生成代理对象AIControllerImpl proxy = (AIControllerImpl) enhancer.create();
//        System.out.println(proxy.getAnswer("你好吗?"));System.out.println(proxy.fortuneTelling("你好吗?"));}
}

7.3两种动态代理方案总结

​ JDK动态代理要求被代理(被增强)的类必须要实现接口,生成的代理对象相当于是被代理对象的兄弟

​ Cglib的动态代理不要求被代理(被增强)的类要实现接口,生成的代理对象相当于被代理对象的子类对象

Spring的AOP默认情况下优先使用的是JDK的动态代理,如果使用不了JDK的动态代理才会使用Cglib的动态代理

7.4切换默认动态代理方式

如果我们是采用注解方式配置AOP的话:

设置aop:aspectj-autoproxy标签的proxy-target-class属性为true,代理方式就会修改成Cglib

<aop:aspectj-autoproxy proxy-target-class="true"/>

8.Spring声明式事务

8.1配置事务管理器和事务注解驱动

在spring的配置文件中添加如下配置:

    <!--把事务管理器注入Spring容器,需要配置一个连接池--><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--开启事务注解驱动,配置使用的事务管理器--><tx:annotation-driven transaction-manager="txManager"/>

8.2添加注解

在需要进行事务控制的方法或者类上添加@Transactional注解就可以实现事务控制。

    @Transactionalpublic void transfer(Integer outId, Integer inId, Double money) {//增加accoutDao.updateMoney(inId,money);
//        System.out.println(1/0);//减少accoutDao.updateMoney(outId,-money);}

注意:如果加在类上,这个类的所有方法都会受事务控制,如果加在方法上,就是那一个方法受事务控制。

注意,因为声明式事务底层是通过AOP实现的,所以最好把AOP相关依赖都加上。

       <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency>

9.属性配置

9.1事务传播行为propagation

测试案例:

@Service
public class TestServiceImpl {@AutowiredAccountService accountService;@Transactionalpublic void test(){accountService.transfer(1,2,10D);accountService.log();}
}
public class AccountServiceImpl implements AccountService {//...省略其他不相关代码@Transactionalpublic void log() {System.out.println("打印日志");int i = 1/0;}}

当事务方法嵌套调用时,需要控制是否开启新事务,可以使用事务传播行为来控制

属性值行为
REQUIRED(必须要有,默认值)外层方法有事务,内层方法就加入。外层没有,内层就新建
REQUIRES_NEW(必须要有新事务,其实就是创建了一个新的连接)外层方法有事务,内层方法新建。外层没有,内层也新建
SUPPORTS(支持有)外层方法有事务,内层方法就加入。外层没有,内层就也没有
NOT_SUPPORTED(支持没有)外层方法有事务,内层方法没有。外层没有,内层也没有
MANDATORY(强制要求外层有)外层方法有事务,内层方法加入。外层没有。内层就报错
NEVER(绝不允许有)外层方法有事务,内层方法就报错。外层没有。内层就也没有

上述案例中,test()方法里面有两个业务1.转账操作 2.打印日志,并且该方法加了声明式事务注解。这两个业务也都加了事务注解。那么测试的时候转账是成功的,但是打印r日志的时候出现了除0异常,事务就会回滚,注意这里是回滚所有的业务,因为@Transactional注解的默认传播行为,只建立一个连接c1

但我们并不希望这样,因为转账业务是成功的,仅仅由于日志打印出现异常就导致转账业务回滚,显然得不偿失,日志记录可以少记录一天,但是转账无需回滚

如何解决这一问题?可以修改转账业务的事务传播行为@Transactional(propagation = Propagation.REQUIRES_NEW),即创建了一个新的连接c2,开启了一个新的事务,当执行到转账业务的时候,转账业务成功,这个连接就会提交事务,即使日志打印的业务出现异常回滚,也是只回滚c1,转账业务并不会回滚

代码修改如下:

@Transactional(propagation = Propagation.REQUIRES_NEW)public void transfer(Integer outId, Integer inId, Double money) {//增加accoutDao.updateMoney(inId,money);//减少accoutDao.updateMoney(outId,-money);}

9.2隔离级别isolation

Isolation.DEFAULT 使用数据库默认隔离级别

Isolation.READ_UNCOMMITTED

Isolation.READ_COMMITTED

Isolation.REPEATABLE_READ

Isolation.SERIALIZABLE

@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED)public void transfer(Integer outId, Integer inId, Double money) {//增加accoutDao.updateMoney(inId,money);//减少accoutDao.updateMoney(outId,-money);}

9.3只读readOnly

如果事务中的操作都是读操作,没涉及到对数据的写操作可以设置readOnly为true。这样可以提高效率

    @Transactional(readOnly = true)public void log() {System.out.println("打印日志");int i = 1/0;}

Isolation.READ_UNCOMMITTED

Isolation.READ_COMMITTED

Isolation.REPEATABLE_READ

Isolation.SERIALIZABLE

@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.READ_COMMITTED)public void transfer(Integer outId, Integer inId, Double money) {//增加accoutDao.updateMoney(inId,money);//减少accoutDao.updateMoney(outId,-money);}

9.3只读readOnly

如果事务中的操作都是读操作,没涉及到对数据的写操作可以设置readOnly为true。这样可以提高效率

    @Transactional(readOnly = true)public void log() {System.out.println("打印日志");int i = 1/0;}

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

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

相关文章

Odoo16 实用功能之在Form视图的各个部位加入按钮

目录 1、 如何在form视图中的头部加上按钮 2、如何在form视图中的身体加上按钮 3、如何在notebook标签中加入按钮 1、 如何在form视图中的头部加上按钮 以CRM中的渠道form视图为例子介绍&#xff08;实现红框中的效果&#xff09; 直接在<header>标签里加入按钮即可 …

树莓派4B搭建开源NAS系统openmediavault

目录 搭建过程使用镜像准备硬件准备软件账号信息制作系统盘首次启动配置获取树莓派IP地址 ssh登录到树莓派上登录到openmediavualt连接到wifi 搭建过程 搭建过程参考链接两篇文章,已经搭建完毕.期间遇到一些坑,为了方便大家,我把搭建好的镜像和使用到的工具放在百度网盘共享了…

HashSet 和HashMap的区别、优缺点、使用场景

HashSet和HashMap是Java集合框架中的两个常用类&#xff0c;它们都用于存储和管理数据&#xff0c;但在使用方式、功能和性能上有很大的区别。 HashSet和HashMap的区别 区别一&#xff1a;用途不同 HashSet&#xff1a;HashSet是一个基于哈希表的集合&#xff0c;用于存储不…

常用两种Linux命令生成器

在Linux中&#xff0c;可以使用多种命令来生成随机密码。以下是其中两种常用的命令&#xff1a; 1.pwgen&#xff1a;这个命令可以生成随机、无意义的但容易发音的密码。生成的密码可以只包含小写字母、大小写混合或数字。大写字母和数字会以一种便于记忆的方式放置&#xff0…

Java_Stream流

一、JDK8新特性&#xff08;Stream流&#xff09; 接下来学习一个全新的知识&#xff0c;叫做Stream流&#xff08;也叫Stream API&#xff09;。它是从JDK8以后才有的一个新特性&#xff0c;是专业用于对集合或者数组进行便捷操作的。有多方便呢&#xff1f;我们用一个案例体…

【OAuth2】授权框架的四种授权方式详解

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《OAuth 2》。&#x1f3af;&#x1f3af; &#x1…

TwIST算法MALTLAB主程序详解

TwIST算法MALTLAB主程序详解 关于TwIST算法的具体原理可以参考&#xff1a; 链接: https://ieeexplore.ieee.org/abstract/document/4358846 链接: https://blog.csdn.net/jbb0523/article/details/52193209 该算法的MATLAB源代码&#xff1a; 链接: http://www.lx.it.pt/~bi…

Node.js(二)-模块化

1. 模块化的基本概念 1.1 什么是模块化 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层将系统拆分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合、分解和更换的单元。 1.2 编程领域中的模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&…

48V转12V 300mA降压芯片,60V耐压、0.6A稳压芯片带ECO模式-AH590L

AH590L是一种48V转12V 300mA降压芯片&#xff0c;具有60V耐压、0.6A稳压电流的特点&#xff0c;并且还带有ECO模式&#xff0c;是一种理想的开关电源解决方案。 AH590L是PWM模式 DC/DC降压转换器。TEL&#xff1a;l86*4884*3702*宽输入电压范围4至60V适用于工业领域的广泛应用…

AI安全综述

1、引言 AI安全这个话题&#xff0c;通常会引伸出来图像识别领域的对抗样本攻击。下面这张把“熊猫”变“猴子”的攻击样例应该都不陌生&#xff0c;包括很多照片/视频过人脸的演示也很多。 对抗样本的研究领域已经具备了一定的成熟性&#xff0c;有一系列的理论来论述对抗样本…

one wire(单总线)FPGA代码篇

一.引言 单总线&#xff08;OneWire&#xff09;是一种串行通信协议&#xff0c;它允许多个设备通过一个单一的数据线进行通信。这个协议通常用于低速、短距离的数字通信&#xff0c;特别适用于嵌入式系统和传感器网络。 二.one wire通信优点缺点 优点&#xff1a; 单一数据线…

Unity程序向Web服务器发送数据

Unity程序向Web服务器发送数据 一、介绍二、HTTP协议三、新建Unity工程&#xff0c;创建脚本1.新建Unity工程&#xff0c;创建脚本WebManager.cs&#xff0c;将其指定给场景中的任意游戏体。2.在WebManager.cs中添加一个m_info属性和OnGUI函数显示UI&#xff1a; 四、GET请求在…

一个利用摸鱼时间背单词的软件

大家好&#xff0c;我是 Java陈序员。 最近进入了考试季&#xff0c;各种考试&#xff0c;英语四六级、考研、期末考等。不知道大家的英语四六级成绩怎么样呢&#xff1f; 记得大学时&#xff0c;英语四级都是靠高中学习积累的老本才勉强过关。 而六级则是考了多次&#xff…

20231224解决outcommit_id.xml1 parser error Document is empty的问题

20231224解决outcommit_id.xml1 parser error Document is empty的问题 2023/12/24 18:13 在开发RK3399的Android10的时候&#xff0c;出现&#xff1a;rootrootrootroot-X99-Turbo:~/3TB/Rockchip_Android10.0_SDK_Release$ make installclean PLATFORM_VERSION_CODENAMEREL…

静态HTTP的常见问题和解决方法

大家好&#xff0c;欢迎来到今天的“静态HTTP大讲堂”&#xff01;今天我们要聊聊静态HTTP的常见问题和解决方法。别小看这些问题哦&#xff0c;它们就像是那些顽皮的小妖精&#xff0c;时不时地给你的网站搞点恶作剧。不过别担心&#xff0c;有我在&#xff0c;这些小妖精都得…

推荐一款好用的免费图片转换工具bmp转png工具bmp2png

推荐一款好用的免费图片转换工具bmp转png工具bmp2png 写这个工具是因为要使用传奇的部分素材在COCOS2DX使用&#xff0c;但是COCOS2DX不支持BMP如果直接将BMP转换到PNG的话&#xff0c;网上找到的工具都不支持透明色转换。难道要用PS一个一个抠图吗&#xff1f;要累死所以写了…

HBase 超大表迁移、备份、还原、同步演练手册:全量快照 + 实时同步(Snapshot + Replication)不停机迁移方案

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维…

免 费 搭 建 小程序商城,打造多商家入驻的b2b2c、o2o、直播带货商城

在数字化时代&#xff0c;电商行业正经历着前所未有的变革。鸿鹄云商的saas云平台以其独特的架构和先进的理念&#xff0c;为电商行业带来了全新的商业模式和营销策略。该平台涉及多个平台端&#xff0c;包括平台管理、商家端、买家平台、微服务平台等&#xff0c;涵盖了pc端、…

Python 数据分析 Matplotlib篇 plt.rcParams 字典(第5讲)

Python 数据分析 Matplotlib篇 plt.rcParams字典(第5讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

17.仿简道云公式函数实战-数学函数-ABS

1. ABS函数 ABS 函数可用于返回数字的绝对值 2. 函数用法 ABS(number) 3. 函数示例 如&#xff0c;ABS(-12)和ABS(12)的返回结果均为 12。 4. 代码实战 首先我们在function包下创建math包&#xff0c;在math包下创建AbsFunction类&#xff0c;代码如下&#xff1a; pac…