Spring-AOP(面向切面)

Spring-AOP(面向切面)

场景模拟(计算器)

功能接口

public interface Calculator {int add(int i, int j);int minus(int i, int j);int multiply(int i, int j);int div(int i, int j);
}

实现类

public class CalculateLogImpl implements Calculator {@Overridepublic int add(int i, int j) {System.out.println("[日志]add方法开始,参数是"+ i+" " + j);int result = i + j;System.out.println("方法内部:resultAdd = " + result);System.out.println("[日志]add方法结束,结果是:" + result);return result;}@Overridepublic int minus(int i, int j) {System.out.println("[日志]minus 方法开始,参数是"+ i+" " + j);int result = i - j;System.out.println("方法内部:resultMinus = " + result);System.out.println("[日志]minus 方法结束,结果是:" + result);return result;}@Overridepublic int multiply(int i, int j) {System.out.println("[日志]multiply 方法开始,参数是"+ i+" " + j);int result = i * j;System.out.println("方法内部:resultMultiply = " + result);System.out.println("[日志]multiply 方法结束,结果是:" + result);return result;}@Overridepublic int div(int i, int j) {System.out.println("[日志]div 方法开始,参数是"+ i+" " + j);int result = i / j;System.out.println("方法内部:resultDiv = " + result);System.out.println("[日志]div 方法结束,结果是:" + result);return result;}
}

在含有日志输出的实现类中可以了解到:与核心业务功能没有关系的日志输出加杂在模块中,对核心业务功能有干扰。

思路:解耦将附加功能从业务功能模块中抽取出来

代理模式

概念

二十三种设计模式中的一种,属于结构型模式,它的作用就是通过提供一个代理类,让我们在调用目标方法时,不再直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来(解耦)。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中起来便于管理。

静态代理

目标对象

接口
public interface Calculator {int add(int i, int j);int minus(int i, int j);int multiply(int i, int j);int div(int i, int j);
}
实现类
public class CalculatorImpl implements Calculator {@Overridepublic int add(int i, int j) {int result = i + j;System.out.println("方法内部:resultAdd = " + result);return result;}@Overridepublic int minus(int i, int j) {int result = i - j;System.out.println("方法内部:resultMinus = " + result);return result;}@Overridepublic int multiply(int i, int j) {int result = i * j;System.out.println("方法内部:resultMultiply = " + result);return result;}@Overridepublic int div(int i, int j) {int result = i / j;System.out.println("方法内部:resultDiv = " + result);return result;}
}

代理类

public class CalculatorStaticProxy implements Calculator {//被代理的目标对象传递过来private Calculator calculator;public CalculatorStaticProxy(Calculator calculator) {this.calculator = calculator;}@Overridepublic int add(int i, int j) {System.out.println("[日志]add方法开始,参数是"+ i+" " + j);//调用目标对象的方法实现核心业务int result = calculator.add(i, j);System.out.println("[日志]add方法结束,结果是:" + result);return result;}@Overridepublic int minus(int i, int j) {System.out.println("[日志]minus 方法开始,参数是"+ i+" " + j);int result = calculator.minus(i, j);System.out.println("[日志]minus 方法结束,结果是:" + result);return result;}@Overridepublic int multiply(int i, int j) {System.out.println("[日志]multiply 方法开始,参数是"+ i+" " + j);int result = calculator.multiply(i,j);System.out.println("[日志]multiply 方法结束,结果是:" + result);return result;}@Overridepublic int div(int i, int j) {System.out.println("[日志]div 方法开始,参数是"+ i+" " + j);int result = calculator.div(i, j);System.out.println("[日志]div 方法结束,结果是:" + result);return result;}
}

测试

@Test
public void testStaticProxy(){Calculator calculator = new CalculatorStaticProxy(new CalculatorImpl());calculator.add(10, 5);System.out.println("-------------------");calculator.minus(10, 5);System.out.println("-------------------");calculator.multiply(10, 5);System.out.println("-------------------");calculator.div(10, 5);
}/*
*   [日志]add方法开始,参数是10 5方法内部:resultAdd = 15[日志]add方法结束,结果是:15-------------------[日志]minus 方法开始,参数是10 5方法内部:resultMinus = 5[日志]minus 方法结束,结果是:5-------------------[日志]multiply 方法开始,参数是10 5方法内部:resultMultiply = 50[日志]multiply 方法结束,结果是:50-------------------[日志]div 方法开始,参数是10 5方法内部:resultDiv = 2[日志]div 方法结束,结果是:2
* */

静态代理类实现了解耦,但是由于代理类的代码都是写死的,就不具备复用的功能,在其他类需要相同功能的类需要进行代理时,也需要重新写代理类,增加了代码冗余,解决这一问题需要使用到动态代理方法。

动态代理

在这里插入图片描述

Proxy工具类

public class ProxyUtil {//目标对象private Object target;public ProxyUtil(Object target) {this.target = target;}//反回代理对象public Object getProxy(){//使用Proxy中的newProxyInstance()/*** Proxy.newProxyInstance()* ClassLoader:加载动态生成代理类的类加载器* Class<?>[] interfaces:目标对象实现的所有接口的class类型数组* InvocationHandler:设置代理对象实现目标对象方法的过程*///ClassLoader:加载动态生成代理类的类加载器ClassLoader classLoader = target.getClass().getClassLoader();//Class<?>[] interfaces:目标对象实现的所有接口的class类型数组Class<?>[] interfaces = target.getClass().getInterfaces();//InvocationHandler:设置代理对象实现目标对象方法的过程InvocationHandler invocationHandler = new InvocationHandler(){/**** @param proxy :代理对象* @param method :需要重写目标对象的方法* @param args:method方法中的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy,Method method,Object[] args) throws Throwable {System.out.println("[动态代理][日志]" + method.getName() + ", 参数:" + Arrays.toString(args));//调用目标的方法Object result = method.invoke(target, args);System.out.println("[动态代理][日志]" + method.getName() + ", 结果:" + result);return result;}};return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);}
}

测试类

@Test
public void testProxy(){//动态创建代理对象ProxyUtil proxyUtil = new ProxyUtil(new CalculatorImpl());Calculator proxy = (Calculator) proxyUtil.getProxy();proxy.add(1, 2);System.out.println("---------");proxy.minus(1, 2);System.out.println("---------");proxy.multiply(1, 2);System.out.println("---------");proxy.div(1, 2);
}/*
*   [动态代理][日志]add, 参数:[1, 2]方法内部:resultAdd = 3[动态代理][日志]add, 结果:3---------[动态代理][日志]minus, 参数:[1, 2]方法内部:resultMinus = -1[动态代理][日志]minus, 结果:-1---------[动态代理][日志]multiply, 参数:[1, 2]方法内部:resultMultiply = 2[动态代理][日志]multiply, 结果:2---------[动态代理][日志]div, 参数:[1, 2]方法内部:resultDiv = 0[动态代理][日志]div, 结果:0
* */

AOP概念及相关术语

简介

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译的方式和运行期动态代理方式实现,在不修改源码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,提高开发效率。

相关术语

横切关注点

分散在各个模块中解决同一问题,如用户验证、日志管理、事务处理、事务缓存都属于横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个方面的增强。这个概念不是语法层面的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

通知(增强)

增强,就是想要增强的功能,比如:安全、事务、日志等。每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

前置通知:在被代理的目标方法前执行
返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
异常通知:在被代理的目标方法异常结束后执行(死于非命)
后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置。

切面

封装通知方法的类。

目标

被代理的目标对象。

代理

向目标对象应用通知之后创建的代理对象

连接点

这也是一个逻辑概念,不是语法定义的。把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点。也就是spring允许使用通知的地方。

切入点

定位连接点的方式,每个类的方法中都包含多个连接点,如果把连接点看作数据库中的记录,那么切入点就是查询记录的SQL语句。Spring 的AOP技术可以通过切入点定位到特定的连接点,即可以使用增强方法的位置。

切入点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。

作用

简化代码:把方法中固定位置的重复代码抽取出来,让抽取的方法更专注于自己的核心功能,提高内聚性。
代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了


基于注解的AOP

在这里插入图片描述

AspectJ:是AOP思想的一种实现。本质上是静态代理,将代理逻辑"植入"被代理的目标,编译得到的字节码文件,所以最终效果是动态的。weaver就是植入器。Spring只是借用了AspectJ中的注解。

动态代理分类

JDK动态代理:有接口,生成接口实现类代理对象,代理对象和目标对象都实现同样的接口。
cglib动态代理:没有接口,继承目标类,生成子类代理对象

准备工作

引入相关的依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.0.9</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>6.0.9</version>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.9</version>
</dependency>
创建目标资源

接口

public interface Calculator {int add(int i, int j);int minus(int i, int j);int multiply(int i, int j);int div(int i, int j);
}

实现类

public class CalculatorImpl implements Calculator {@Overridepublic int add(int i, int j) {int result = i + j;System.out.println("方法内部:resultAdd = " + result);return result;}@Overridepublic int minus(int i, int j) {int result = i - j;System.out.println("方法内部:resultMinus = " + result);return result;}@Overridepublic int multiply(int i, int j) {int result = i * j;System.out.println("方法内部:resultMultiply = " + result);return result;}@Overridepublic int div(int i, int j) {int result = i / j;System.out.println("方法内部:resultDiv = " + result);return result;}
}
配置spring配置文件
<?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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
<!--开启组件扫描--><context:component-scan base-package="com.louis.annotation_aop"></context:component-scan>
<!--开启aspectJ自动代理,为目标对象生成代理--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
创建切面类
切入点表达式

语法细节

用*号代替"权限修饰符"和"返回值部分"表示"权限修饰符"和"返回值"不限

包名的部分

①一个"*“号只能代表包的层次结构中的一层,表示这一层是任意的。如:*.com匹配hello.com但不匹配hello.louis.com
②使用”*…"表示任意包名、包的层次深度任意

类名的部分

①类名整体使用*代替,表示类名任意
②可以使用*代替类名的一部分。如:*xxx表示匹配所有名称以xxx结尾的类或接口

方法名的部分

①可以使用*代替,表示方法名任意
②可以使用*代替方法名的一部分。如:*xxx表示匹配所有名称以xxx结尾的方法

方法参数列表部分

①可以使用(…)表示任意参数列表
②可以使用(int,…)表示参数列表以一个int类型的参数开头
③基本数据类型和对应的包装类型是不一样的(切入点中使用int和实际方法中使用Integer是不匹配的)

方法返回值部分

如果想要明确指定一个返回值类型,那么必须同时写明权限修饰符。如:excution(public int…Service.(…,int))

切面类(通知类型)
@Aspect
@Component //表示在spring的ioc容器中进行管理
public class LogAspect {//设置切入点和通知类型//通知类型:// 前置:@Before(value="通过切入点表达式配置切入点")//切入点表达式:execution(访问修饰符 增强方法返回类型 方法所在类的全路径.方法名(参数列表))
//    @Before("execution(* com.louis.annotation_aop.impl.LogAspect.*(..))")@Before("execution(public int com.louis.annotation_aop.impl.CalculatorImpl.*(..))")public void beforeMethod(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("前置通知,增强的方法名称" + name + " , 参数" + Arrays.toString(args));}// 返回:@AfterReturning,返回通知与后置通知有很大的区别,后置通知在返回通知之后执行,返回通知能够得到目标方法的返回值,使用属性returning//它的参数可以随便命名,但是切面方法的参数必须和上面的一致@AfterReturning(value = "execution(* com.louis.annotation_aop.impl.CalculatorImpl.*(..))", returning = "result")public void afterReturningMethod(JoinPoint joinPoint, Object result){String name = joinPoint.getSignature().getName();System.out.println("返回通知, 增强方法名称" + name + " , 返回结果" + result);}// 异常:@AfterThrowing:目标方法出现异常,这个通知会执行,并且能够获得目标方法的异常信息@AfterThrowing(value = "execution(* com.louis.annotation_aop.impl.CalculatorImpl.*(..))", throwing = "Exception")public void afterThrowingMethod(JoinPoint joinPoint, Throwable Exception){String name = joinPoint.getSignature().getName();System.out.println("异常通知, 增强方法名称" + name + " , 返回结果" + Exception);}// 后置:@After()@After("execution(public int com.louis.annotation_aop.impl.CalculatorImpl.*(..))")public void afterMethod(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();System.out.println("后置通知, 增强方法名称" + name);}// 环绕:@Around()@Around("execution(public int com.louis.annotation_aop.impl.CalculatorImpl.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint){String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();String argsString = Arrays.toString(args);Object result = null;try {System.out.println("环绕通知, 目标方法之前执行");//调用目标方法result = joinPoint.proceed();System.out.println("环绕通知, 目标方法返回值之后执行");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("环绕通知, 目标方法出现异常执行");} finally {System.out.println("环绕通知, 目标方法执行完毕");}return result;}
}
测试
@Test
public void testAOPAdd(){ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");Calculator calculator = context.getBean(Calculator.class);calculator.add(1,0);
}/*
环绕通知, 目标方法之前执行
前置通知,增强的方法名称add , 参数[1, 0]
方法内部:resultAdd = 1
返回通知, 增强方法名称add , 返回结果1
后置通知, 增强方法名称add
环绕通知, 目标方法返回值之后执行
环绕通知, 目标方法执行完毕
* */@Test
public void testDiv(){ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");Calculator calculator = context.getBean(Calculator.class);calculator.div(1,0);
}/*
环绕通知, 目标方法之前执行
前置通知,增强的方法名称div , 参数[1, 0]
异常通知, 增强方法名称div , 返回结果java.lang.ArithmeticException: / by zero
后置通知, 增强方法名称div
环绕通知, 目标方法出现异常执行
环绕通知, 目标方法执行完毕
* */

基于注解的AOP

重用切入点和切面优先级

重用切入点
//重用切入点表达式,定义一个方法,之后在需要切入点的时候直接调用这个方法
/*
* 在同一个类下,可以直接使用:@Before("pointcut()")
* 在不同的类中,需要加入类的路径:  @Before("com.louis.annotation_aop.impl.LogAspect.pointcut()")
* */
@Pointcut(value = "execution(public int com.louis.annotation_aop.impl.CalculatorImpl.*(..))")
public void pointcut(){}
切面优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序
优先级高的切面在外面,优先级低的切面在里面。
使用@Order注解可以控制切面的优先级
@Order(较小的数):优先级高
@Order(较大的数):优先级低

切面类

@Component //表示在spring的ioc容器中进行管理
public class LogAspect {//前置通知public void beforeMethod(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("前置通知,增强的方法名称" + name + " , 参数" + Arrays.toString(args));}// 返回通知public void afterReturningMethod(JoinPoint joinPoint, Object result){String name = joinPoint.getSignature().getName();System.out.println("返回通知, 增强方法名称" + name + " , 返回结果" + result);}// 异常通知public void afterThrowingMethod(JoinPoint joinPoint, Throwable Exception){String name = joinPoint.getSignature().getName();System.out.println("异常通知, 增强方法名称" + name + " , 返回结果" + Exception);}// 后置通知public void afterMethod(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();System.out.println("后置通知, 增强方法名称" + name);}// 环绕通知public Object aroundMethod(ProceedingJoinPoint joinPoint){String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();String argsString = Arrays.toString(args);Object result = null;try {System.out.println("环绕通知, 目标方法之前执行");//调用目标方法result = joinPoint.proceed();System.out.println("环绕通知, 目标方法返回值之后执行");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("环绕通知, 目标方法出现异常执行");} finally {System.out.println("环绕通知, 目标方法执行完毕");}return result;}//重用切入点表达式,定义一个方法,之后在需要切入点的时候直接调用这个方法/** 在同一个类下,可以直接使用:@Before("pointcut()")* 在不同的类中,需要加入类的路径:  @Before("com.louis.annotation_aop.impl.LogAspect.pointcut()")* */@Pointcut(value = "execution(public int com.louis.xml_aop.impl.CalculatorImpl.*(..))")public void pointcut(){}
}

配置文件beanaop.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:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--开启组件扫描--><context:component-scan base-package="com.louis.xml_aop"></context:component-scan><!--配置aop五种通知类型--><aop:config><!--配置切面类--><aop:aspect ref="logAspect"><!--配置切入点--><aop:pointcut id="pointcut" expression="execution(* com.louis.xml_aop.impl.CalculatorImpl.*(..))"/><!--配置五种通知类型--><!--前置通知--><aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before><!--后置通知--><aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after><!--返回通知--><aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="pointcut"></aop:after-returning><!--异常通知--><aop:after-throwing method="afterThrowingMethod" throwing="Exception" pointcut-ref="pointcut"></aop:after-throwing><!--环绕通知--><aop:around method="aroundMethod" pointcut-ref="pointcut"></aop:around></aop:aspect></aop:config>
</beans>

测试

@Test
public void testAOPXMLAdd(){ApplicationContext context = new ClassPathXmlApplicationContext("beanaop.xml");Calculator calculator = context.getBean(Calculator.class);calculator.add(1,0);
}
/*
前置通知,增强的方法名称add , 参数[1, 0]
环绕通知, 目标方法之前执行
方法内部:resultAdd = 1
环绕通知, 目标方法返回值之后执行
环绕通知, 目标方法执行完毕
返回通知, 增强方法名称add , 返回结果1
后置通知, 增强方法名称add
* */

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

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

相关文章

PALO ALTO NETWORKS 的新一代防火墙如何保护企业安全

轻松采用创新技术、阻止网络攻击得逞并专注更重要的工作 IT 的快速发展已改变网络边界的面貌。数据无处不在&#xff0c;用户可随时随地从各类设备访问这些数据。同时&#xff0c;IT 团队正在采用云、分析和自动化来加速新应用的交付以及推动业务发展。这些根本性的转变带来了…

【Linux】- 任务调度和定时任务

任务调度和定时任务 1 crond 任务调度2 at 定时任务 1 crond 任务调度 crontab 进行 定时任务的设置 任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序。 任务调度分类&#xff1a;1.系统工作&#xff1a;有些重要的工作必须周而复始地执行。如病毒扫描等 个别…

ChatGPT 最佳实践指南之:系统地测试变化

Test changes systematically 系统地测试变化 Improving performance is easier if you can measure it. In some cases a modification to a prompt will achieve better performance on a few isolated examples but lead to worse overall performance on a more representa…

【Docker】Docker基本概念

Docker基本概念 1.Docker概述1.1 Docker是什么&#xff1f;1.2 Docker的宗旨1.3 容器的优点1.4 Docker与虚拟机的区别1.5 容器在内核中支持的两种技术1.6 namespace的六大类型 2.Docker核心概念2.1 镜像2.2 容器2.3 仓库 3. 知识点总结3.1 Docker是什么&#xff1f;3.2 容器和虚…

智能分析网关V2有抓拍告警但无法推送到EasyCVR,是什么原因?

我们在此前的文章中也介绍了关于智能分析网关V2接入EasyCVR平台的操作步骤&#xff0c;感兴趣的用户可以查看这篇文章&#xff1a;在EasyCVR新版本v3.3中&#xff0c;如何正确接入智能分析网关V2&#xff1f; 智能分析网关V2是基于边缘AI计算技术&#xff0c;可对前端摄像头采…

常见Redis使用问题

一 lettuce使用问题 1 问题描述 Redis Cluster集群&#xff0c;当master宕机&#xff0c;主从切换&#xff0c;客户端报错 timed out 2 原因 SpringBoot2.X版本开始Redis默认的连接池都是采用的Lettuce。当节点发生改变后&#xff0c;Letture默认是不会刷新节点拓扑的。 3…

每日一题2023.7.19|ACM模式

文章目录 C的输入方式介绍cin>>cin.get(字符变量名)cin.get(数组名,接收字符数目)cin.get()cin.getline() getline()gets()getchar() AB问题|AB问题||AB问题|||ABⅣAB问题ⅤAB问题Ⅵ C的输入方式介绍 参考博客 cin>> 最基本&#xff0c;最常用的字符或者数字的输…

rabbitmq部署(docker方式)

前言&#xff1a;rabbitmq一旦有漏洞&#xff0c;版本升级麻烦&#xff0c;于是改为docker部署 环境&#xff1a;centos7 #停掉之前的服务 systemctl stop rabbitmq-server systemctl disable rabbitmq-server 查了官网&#xff0c;当前3.11.x 最高版本是3.11.19, 虽然3.12…

jupyter定制数学函数

from math import * #导入绘图模块 import numpy as np #导入数值计算模块 import matplotlib.pyplot as plt #导入绘图模块 plt.rcParams[font.sans-serif][SimHei] #绘图中文 plt.rcParams[axes.unicode_minus]False #绘图负号 import mpl_toolkits.axisartist as axisartist…

安卓通过adb pull和adb push 手机与电脑之间传输文件

1.可以参考这篇文章 https://www.cnblogs.com/hhddcpp/p/4247923.html2.根据上面的文章&#xff0c;我做了如下修改 //设置/system为可读写&#xff1a; adb remount //复制手机中的文件到电脑中。需要在电脑中新建一个文件夹&#xff0c;我新建的文件夹为ce文件夹 adb pull …

如何在无人机支持下完成自然灾害风险评估的原理和方法

对灾害的损失进行估算与测算是制定防灾、抗灾、救灾 及灾后重建方案的重要依据。 自然灾害评估按灾害客观地发展过程可分三种&#xff1a;一是灾前预评估&#xff0c;二是灾期跟踪或监测性评估&#xff0c;三是灾后实测评估。 灾前预评估要考虑三个因素&#xff0c;第一是未来…

基于ssm的社区生活超市的设计与实现

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战。专注于计算机毕设开发、定制、文档编写指导等&#xff0c;对软件开发具有浓厚的兴趣&#xff0c;工作之余喜欢钻研技术&#xff0c;关注IT技术的发展趋势&#xff0c;感谢大家的关注与支持。 技术交流和部署相关看文章…

JVM系列(5)——类加载过程

一、类的生命周期 加载&#xff08;Loading&#xff09;、验证&#xff08;Verification&#xff09;、准备&#xff08;Preparation&#xff09;、解析&#xff08;Resolution&#xff09;、初始化&#xff08;Initialization&#xff09;、使用&#xff08;Using&#xff09…

Ceph集群

目录 一、存储概述 1.单机存储设备 1.1 DAS 1.2 NAS 1.3 SAN 2. 单机存储的问题 3. 商业存储解决方案 4.分布式存储&#xff08;软件定义的存储 SDS&#xff09; 4.1 分布式存储的类型 二、Ceph简介 1.Ceph 优势 2. Ceph 架构 2.1 RADOS 基础存储系统 2.2 LIBRADOS…

十大网络安全上市公司分析,让我们重点聊聊F5

网络安全上市厂商业务广泛分布于网络安全硬件、软件&#xff0c;网络安全服务等板块&#xff0c;总体来看&#xff0c;十大网络安全上市公司的竞争可谓是如火如荼。今天让我们把目光集中在F5&#xff0c;这个能为我们所有人创造更安全的数字世界的企业&#xff0c;在应用及API交…

从零搭建vue+electron桌面应用

从零搭建vueelectron桌面应用 一、准备工作1.全局下载electron2.全局下载vue脚手架3.创建vue项目&#xff08;这里用的是vue2版本&#xff09;4.安装打包插件5.安装electron-builder&#xff0c;安装后可以直接生成主进程的配置文件6.在vue.config.js中添加以下配置 二、运行项…

left join 和except方法区别和联系

目录 相同点&#xff1a; left join except 不同点 假设有两个表&#xff1a;A客户表 和 B客户表&#xff0c;客户uid是唯一主键 相同点&#xff1a; 查询在A中的客户 但不在B中&#xff0c;也就是图中的阴影部分&#xff0c;left join 和except方法都可以实现 left join …

使用IDEA社区版创建SpringBoot项目

文章目录 1.关于IDEA社区版的版本2.下载Spring Boot Helper3.创建项目4.配置Maven国内源4.1找不到settings.xml的情况4.2找得到settings.xml的情况 4.3删除repository目录下的所有文件和目录5.加载项目6.解决org.springframework.boot:spring-boot-starter-parent:pom:2.7.13.R…

laravel 的SQL使用正则匹配

案例场景 精准正则匹配 查询结果 代码如下 $regexp ^ . $new_str . [^0-9];$info Test::query()->where(is_del, 0)->whereRaw("name REGEXP $regexp")->pluck(name, id)->toArray();字符 “^” 匹配以特定字符或者字符串开头的文本 name 字段值包含…

php做网页版剪刀石头布的功能

实例讲述了php实现的网页版剪刀石头布攻略在玩游网上的设计。分享给大家供大家参考&#xff0c;具体如下&#xff1a; <?php /* * Created on 2016-11-25 * */ if (isset($_POST[sub])) { $what $_POST[what]; //需要输入的数组 $my_array array("剪刀","…