Spring | Srping AOP (AOP简介、动态代理、基于“代理类”的AOP实现)
目录:
- 1.Spring AOP简介
- 1.1 AOP简介
- 1.2 AOP术语
- 2.动态代理
- 2.1 JDK动态代理
- 2.2 CGLIB代理
- 3.基于“代理类”的AOP实现
- 3.1 Spring的通知类型
- 3.2 ProxyFactoryBean ( 可通知.xml配置文件完成aop功能 )
1.Spring AOP简介
1.1 AOP简介
Spring的AOP模块,是Spring框架体系结构中十分重要的内容,该模块中提供了面向切面编程实现。
AOP的全称是 Aspect-Oriented Programming,即面向切面编程( 也称面向方面编程 )它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。
在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。(要实现AOP功能,核心关键之一是 : “代理”,通过“代理”可以获得 “增强功能后的目标对象” ,从而实现AOP功能)
为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码 提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的O0P思想显然是无法办到的,因为00P只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是O0P的替代品,它只是OOP的延伸和补充。
在AOP思想中,类与切面的关系图 如下所示 :
从上图 (类与切面关系图)可以看出,通过Aspect (切面) 分别在Class1和Class2的方法中加入了事务、日志、权限和异常等功能。
AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。
目前最流行的AOP框架有两个,分别为Spring AOP和 AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。
AspectJ是一个基于Java语言的AOP框架,从Spring 2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
1.2 AOP术语
AOP术语包括 Aspect、Joinpoint、Pointcut、 Advice、 Target Object、Proxy 和 Weaving。具体讲解如下。
一、Aspect ( 切面 ) :
在实际应用中,切面通常是指封装的用于横向插入系统功能 (如事务、日志等) 的类。如 “类与切面关系图” 的 Aspect。该 类要被Spring容器识别为切面,需要在配置文件<bean>元素指定 (哪个类将作为 切面)。
(切面 可以 理解为 “切面类”)二、Joinpoint ( 连接点 ) :
在程序执行过程中的某个阶段点,它实际上是对象的一个操作。
例子如:方法的调用或异常的抛出。在Spring AOP中,连接点就是指方法的调用 ( 非切面类的那个类的方法的调用)。三、Pointcut( 切入点 ) :
是指切面与程序流程的 交叉点,即那些 需要处理的连接点 ( 即待处理的“ 方法的调用 ” )。
通常在程序中,切入点指的是类或者方法名。
例子如 :某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。“切面、连接点 和切入点” 关系图如下图所示” :
四、Advice( 通知 / 增强处理 ) :
AOP框架在特定的 切入点 ( 类/方法名 )执行的增强处理,即在定义好的切入点处所 要执行的程序代码。
可以将 ( Advice ) 理解为 切面中的方法,Advice 是 切面的具体实现。五、Target Object ( 目标对象 ) :
是指所有 被通知的对象,也称为被 增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。六、Weaving ( 织入 ) :
将 切面类中代码插入到目标对象上,从而生成代理对象的过程。七、Proxy( 代理 ) :
将 通知应用到目标对象之后,被动态创建的对象 ,该对象称为“代理对象” / 增强功能后的目标对象 ,
该对象可直接当目标对象使用。(Proxy (代理) 可理解为一个 过程,一个将目标对象 进行 增强,变为代理对象 / “增强功能后的目标对象的过程。) ( 代理就是AOP框架动态生成的一个对象 )八、 Proxy Class (代理类) :
实现“Proxy ( 代理 )功能”的类。该类 (代理类) 中包含了 Proxy(代理)”所需的功能代码。九、Proxy Object (代理对象) / 增强功能后的目标对象:
Proxy Object (代理对象) 称为 / 可理解为 “增强功能后的目标对象” 。该对象可直接当 “目标对象” 使用。代理对象通常包含了目标对象的功能,同时在 原目标对象的基础上 添加一些额外的功能或逻辑,例如日志记录、事务管理等,因此可以说,代理对象可以被理解为"增强功能后的目标对象“。
2.动态代理
AOP中的==代理就是由AOP框架动态生成的一个对象==,该对象可直接当目标对象使用。
(此处的代理 : 就是上面提到过的 生成 “增强功能后的目标对象” 的代理)Spring 中的AOP代理,有 ①JDK动态代理 ②CGLIB代理。(两者都是 “动态代理”)。
ps :① JDK动态代理 和 CGlib代理都属于 “动态代理”。
② JDK动态代理的目标对象要实现一个或多个接口,如果要对没有实现接口的目标类 / 目标对象 进行代理时,可用CGLIB代理。
2.1 JDK动态代理
JDK动态代理 是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的 newProxyInstance() 方法来创建代理对象。对于使用业务接口的类,Sping 默认会使用JDK代理来实现AOP。
(JDK动态代理要实现接口,CGLIB代理不用实现接口。)
Spring 中JDK动态代理的例子如 :
第一步、在DIEA中创建一个Java项目,添加web功能模块。
第二步、在项目WEB-INF目录中创建lib文件夹,存放Spring框架最基本核心的jar包,让jar包生效。
获取spring框架基本核心jar包
第三步、创建UserDao接口,在该接口中编写添加 和 删除的方法 。
创建UserDao的实现类 UserDaoImpl,分别实现接口中的方法,并在每个方法中添加一条输出语句。
UserDao.javapublic interface UserDao{public void addUser();public void deleteUser(); }
UserDaoImpl.java
public class UserDaoImpl implements UserDao { // ”目标类“,该类创建的对象为”目标对象“@Override public void addUser() { //这方法将要被“增强处理”System.out.println("添加用户"); }@Override public void deleteUser() { //这方法将要被“增强处理”System.out.println("删除用户"); } }
实现类UserDaoImpl作为 目标类 ,对其中的方法进行 增强处理。
第四步、创建切面类 : MyAspect 。
在该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知。public class MyAspect { //切面类 : 以下为两个通知,将用于为“目标对象”实现增强功能。//通知 : 要执行的切面类”的方法” //在特定的 “切入点” 进行增强处理 : 在“切入点”的前或后,执行切面类中的方法public void check_Permissions() { //该方法即为“通知” : 切面类中的方法System.out.println("模拟检查权限..."); }public void log() { //通知System.out.println("模拟记录日志..."); }/*对某个方法(切入点)进制“增强处理” : 即在程序执行这个方法的“前或后”的时机执行"切面方法"(添加通知)*/ }
第五步、创建 代理类 JdkProxy ,该类实现 InvocationHandler 接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理。
/*** JDK代理类 (JdkProxy) : 将通知应用到目标对象之后,被动态创建的对象。* 代理对象 : 经过“代理”这一过程,生成的类即为“代理对象”,也称为“增强功能后的目标对象”* (可以理解为: “代理” / "代理类"的目的之一 : 生成“代理对象”) */ public class JdkProxy implements InvocationHandler {//声明目标类接口 private UserDao userDao;//创建代理方法 public Object createProxy(UserDao userDao) {this.userDao = userDao;//1.类加载器 (JdkProxy的类加载器) ---获得JdkProxy对应的“类加载器”ClassLoader classLoader = JdkProxy.class.getClassLoader();//2.被代理对象实现的所有接口 (此处为: UserDao 的接口内容等。)Class[] clazz = userDao.getClass().getInterfaces();//3.使用代理类,进行增强,返回的是 ”代理后的对象“ / “增强功能后的目标对象”return Proxy.newProxyInstance(classLoader, clazz, this); }/*** @param proxy 被代理后的对象* @param method 将要执行的方法信息 (反射)* @param args 执行方法时需要的参数* @throws Throwable*/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/**声明切面类 (其中有要用在特定“切入点”的“通知”)切入点 : 要进行“增强处理”/被“通知” 的 方法名通知(也称“增强处理”): 切面类中的方法对切入点进行“增强处理” : 在某个方法被执行的前/后的时机执行切面中的“方法”*///创建切面类MyAspect myAspect = new MyAspect();/*** 目标方法执行的前后,会分别执行切面类中的 check_Permissions()方法 和 log()方法* 前增强 : 执行UserDao接口的方法之前先执行 切面类中的 check_Permissions()这个方法* 后增强 : 执行完接口的方法后执行log()这个方法*///前增强myAspect.check_Permissions();//在目标类上调用方法,并传入参数Object obj = method.invoke(userDao, args); //执行UserDao接口中的方法//后增强myAspect.log();return obj; } }
上述例子中,JdkProxy类 (代理类) 实现了 InvocationHandler 接口,并实现了接口中的 invoke方法,所有动态代理类所调用的方法都会交由该 ( invoke) 方法处理。在创建的代理方法 createProxy()中使用了 Proxy 类的 newProxyInstance()方法来创建 代理对象。newProxyInstance()方法中包含3个参数,其中第1个参数是当前类的类加载器,第2个参数表示的是被代理对象实现的所有接口,第3个参数this代表的就是代理类JdkProxy本身。在invoke()方法中,目标类方法执行的前后会分别执行切面类中的check _Permissions() 方法和 log() 方法。
第六步、创建测试类 JdkTest,在该类中的main()方法中创建代理对象和目标对象,然后从代理对象中获得对目标对象userDao增强功能后的对象,最后调用该对象的添加和删除方法。
public class JdkTest { public static void main(String[] args) {//实例化JdkProxy类(JDK代理类) : 该类能产生“代理对象”(增强功能的目标对象)JdkProxy jdkProxy = new JdkProxy();//创建目标对象UserDao userDao = new UserDaoImpl();/*调用JdkProxy中的createProxy()方法获得“代理对象”*/UserDao userDao1 = (UserDao)jdkProxy.createProxy(userDao);//指向UserDao中的两个方法,该UserDao已被进行AOP操作,其中方法执行的前后都会执行切面类中的方法userDao1.addUser();userDao1.deleteUser(); } }
测试类运行结果 :
UserDao实例的添加用户 和 删除用户的方法被成功调用,并且在调用前后分别添加了“权限检查”和“记录日志”的功能,这种实现了接口的代理方式,就是Spring中的JDK动态代理。
2.2 CGLIB代理
JDK动态代理的使用简单,但它还有一定的局限性,使用 JDK动态代理的对象必须实现一个或多个接口。
如果要对没有实现接口的类进行代理,那么可以使用 CGLIB代理。即目标类 / 目标对象,没有实现任何接口时,可用CGLIB代理。
ps:
JDK动态代理的目标对象要实现一个或多个接口,如果要对没有实现接口的目标类 / 目标对象 进行代理时,可用CGLIB代理。CGLIB ( Code Generation Library )是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类, 并对子类进行增强。在Spring的核心包中已经集成了CGLIB所需要的包,所以开发中不需要另外导入JAR包。
(JDK动态代理 和 CGLIB代理 用核心包就行,不需要额外的JAR包)Spring 中JDK动态代理的例子如 :
第一步、在DIEA中创建一个Java项目,添加web功能模块。第二步、在项目WEB-INF目录中创建lib文件夹,存放Spring框架最基本核心的jar包,让jar包生效。
获取spring框架基本核心jar包
第三步、创建目标类 :UserDao,UserDao不需要实现任何接口,只需定义一个添加用户的方法 和 一个删除用户的方法。
public class UserDao { //目标类,没实现任何接口,可用CGLIB代理public void addUser() {System.out.println("添加用户");}public void deleteUser() {System.out.println("删除用户");} }
第四步、创建切面类 : MyAspect 。
在该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知。public class MyAspect { //切面类 : 以下为两个通知,将用于为“目标对象”实现增强功能。//通知 : 要执行的切面类”的方法” //在特定的 “切入点” 进行增强处理 : 在“切入点”的前或后,执行切面类中的方法public void check_Permissions() { //该方法即为“通知” : 切面类中的方法System.out.println("模拟检查权限..."); }public void log() { //通知System.out.println("模拟记录日志..."); }/*对某个方法(切入点)进制“增强处理” : 即在程序执行这个方法的“前或后”的时机执行"切面方法"(添加通知)*/ }
第五步、创建代理类 : CglibProxy,该代理类需要实现 MethodInterceptor 接口,并实现接口中的 intercept( ) 方法。
import com.myh.cglib.aspect.MyAspect; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method;//代理类 public class CglibProxy implements MethodInterceptor { //CGLIB代理 : 目标类不需要实现接口//代理方法public Object createProxy(Object target) { //创建“代理对象/增强后的目标对象”的方法//创建一个动态类对象 (Enhancer : 增强器)Enhancer enhancer = new Enhancer();//确定要增强的类,为Enhancer设置“父类”enhancer.setSuperclass(target.getClass());//添加回调函数enhancer.setCallback(this);//返回创建的代理类return enhancer.create();}/*** @param proxy CGlib指定“父类”生成的“代理对象”* @param method 拦截的方法* @param args 拦截方法的参数数组* @param methodProxy 方法的代理对象,用于执行父类的方法* @throws Throwable*/@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//创建切面类对象MyAspect myAspect = new MyAspect();//前增强myAspect.check_Permissions();//目标方法的执行Object obj = methodProxy.invokeSuper(proxy, args); //执行"代理对象"的 “父类的方法”//后增强myAspect.log();return obj;} }
在 CglibProxy类的代理方法中,首先创建了一个动态类对象Enhancer,它是CGLIB的核心类;然后调用了Enhancer类的 setSuperclass()方法来确定目标对象 (为动态类对象指定父类 );接下来调用了setCallback()方法添加回调函数,其中的this代表的就是代理类CglibProxy本身;最后通过return语句将创建的代理类对象返回。 intercept()方法会在程序执行目标方法时被调用,方法运行时将会执行切面类中的增强方法。
第六步、
创建测试类 CglibTest 在该类的main()方法中首先创建代理对象和目标对象,然后从代理对象中获得增强后的目标对象,最后调用对象的添加和删除方法。public class CglibTest {public static void main(String[] args) {//创建代理对象CglibProxy cglibProxy = new CglibProxy();//创建目标对象UserDao userDao = new UserDao();UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);//执行方法userDao1.addUser();userDao1.deleteUser();} }
测试类运行结果 :
从测试类结果可以看出,目标类UserDao中的方法被成功调用且增强了,这种 (目标类) 没有实现接口的代理方式,就是CGLIB代理。
3.基于“代理类”的AOP实现
- 动态代理中的 JDK动态代理 和 CGLIB代理都是基于“代理类”的AOP实现。
- Spring中的 AOP代理默认使用 的是 JDK动态代理。
- 在Spring中使用 ProxyFactoryBean 是创建 AOP代理 的最基本的方式。
3.1 Spring的通知类型
Spring 中的 通知 按照在 目标类方法 的连接点位置,可以分为以下5种类型 :
- org.aopalliance.intercept.MethodInterceptor (环绕通知) :
在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。- org.springframework. aop.MethodBeforeAdvice (前置通知) :
在目标方法执行前实施增强,可以应用于权限管理等功能。- org.springframework. aop.AfterReturningAdvice (后置通知) :
在目标方法执行后实施增强,可以应用于关闭流、上传文件、 删除临时文件等功能- org.springframework.aop.ThrowsAdvice (异常通知) :
在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。- org.springframework.aop.IntroductionInterceptor (引介通知) :
在目标类中添加一些新的方法和属性,可以应用于修改老版本程序(增强类)。
3.2 ProxyFactoryBean ( 可通知.xml配置文件完成aop功能 )
ProxyFactoryBean是 FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,
而 ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean 是 创建AOP代理的基本方式。ProxyFactoryBean类中的常用可配置属性如下表所示 :
属性名称 描述 target 代理的目标对象 proxyInterfaces 代理要实现的接口,如果是多个接口,可以使用**以下格式赋值
<list>
<value></value>
…
</list>proxyTargetClass 是否对类代理而不是接口,设置为true时,使用CGLIB代理.
(设置为true表示是对类代理,自然是用 CGLIB代理)interceptorNames 需要织入目标的Advice。 singleton 返回的代理是否为单实例,默认为true (即返回单实例) optimize 当设置为true时,强制使用CGLIB 对ProxyFactoryBean类有了初步的了解后,接下来通过一个典型的环绕通知案例,来演示Spring使用ProxyFactoryBean创建AOP代理的过程,例子如下 :
第一步、
在核心JAR包的基础上,项目的lib目录中导入AOP的两个JAR包,①spring-aop.jar 和 ②aopalliance.jar ;
spring-aop.jar : 是Spring为实现AOP功能提供的JAR包。
aopalliance.jar : 是AOP联盟提供的规范包,是实现AOP功能所需JAR包。Spring核心jar + aop有关的jar
第二步、创建UserDao接口,在该接口中编写添加 和 删除的方法 。
创建UserDao的实现类 UserDaoImpl,分别实现接口中的方法,并在每个方法中添加一条输出语句。
UserDao.javapublic interface UserDao{public void addUser();public void deleteUser(); }
UserDaoImpl.java
public class UserDaoImpl implements UserDao { // ”目标类“,该类创建的对象为”目标对象“@Override public void addUser() { //这方法将要被“增强处理”System.out.println("添加用户"); }@Override public void deleteUser() { //这方法将要被“增强处理”System.out.println("删除用户"); } }
实现类UserDaoImpl作为 目标类 ,对其中的方法进行 增强处理。
第三步、
创建切面类MyAspect。由于实现环绕通知需要实现 org. aopalliance.intercept.MethodInterceptor接口,所以MyAspect类需要实现该接口,并实现接口中的 invoke() 方法,来执行目标方法。
MyAspect.javaimport org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; //切面类 public class MyAspect implements MethodInterceptor { //切面类,该切面类实现了aopalliance包下的 MethodInterceptor(环绕通知)接口public void check_Permission() { //切面方法 (通知)System.out.println("模拟权限检查...");}public void log() { //切面方法 (通知)System.out.println("模拟记录日志...");}@Overridepublic Object invoke(MethodInvocation mi) throws Throwable {/*在目标方法执行的前后,分別执行了“检查权限” 和 “记录日志”的方法,这两个方法也就是“增强的方法”,也就是通知。*/ check_Permission();//执行目标方法Object obj = mi.proceed();log();return obj;} }
第四步、
创建配置文件applicationContext.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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 1.目标类 (把对象放入IOC容器中,作为Bean)--><bean id="userDao" class="com.myh.factorybean.UserDaoImpl"/><!-- 2.切面类 --><bean id="myAspect" class="com.myh.factorybean.MyAspect"/><!-- 3.使用Spring代理工厂定义一个名称为: userDaoProxy 的代理对象 --><bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定“代理实现的接口” --><property name="proxyInterfaces" value="com.myh.factorybean.UserDao"/><!-- 指定“目标对象” --><property name="target" ref="userDao"/><!-- 指定“切面”,植入环绕通知 --><property name="interceptorNames" value="myAspect"/><!-- 指定代理方式,true : 使用cglib, false(默认) : 使用jdk动态代理 --><property name="proxyTargetClass" value="true"/></bean> </beans>
上述配置文件代码中,首先通过 <bean>元素定义了目标类和切面,然后使用 ProxyFactoryBean类定义了代理对象。在定义的代理对象中,分别通过 <property>子元素指定了①代理实现的接口切代理、②目标对象、③需要织入目标类的通知以及④代理方式。
第五步、
创建ProxyFactoryBeanTest 测试类,在类中通过Spring容器获取代理对象的实例,并执行目标方法。
package com.myh.factorybean;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class ProxyFactoryBeanTest { //测试类public static void main(String[] args) {String xmlPath = "com/myh/factorybean/applicationContext.xml";//创建ApplicationContext (从其中获取IOC容器中的Bean)ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);//从IOC容器中获得“代理对象”UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy"); //获得“增强功能后的目标对象”//执行目标方法userDao.addUser();userDao.deleteUser();} }
测试类运行结果如下 :
相关文章:

Spring | Srping AOP (AOP简介、动态代理、基于“代理类”的AOP实现)
目录: 1.Spring AOP简介1.1 AOP简介1.2 AOP术语 2.动态代理2.1 JDK动态代理2.2 CGLIB代理 3.基于“代理类”的AOP实现3.1 Spring的通知类型3.2 ProxyFactoryBean ( 可通知.xml配置文件完成aop功能 ) 1.Spring AOP简介 1.1 AOP简介 Spring的AOP模块,是Spring框架体系…...

StarRocks 生成列:百倍提速半结构化数据分析
半结构化分析主要是指对 MAP,STRUCT,JSON,ARRAY 等复杂数据类型的查询分析。这些数据类型表达能力强,因此被广泛应用到 OLAP 分析的各种场景中,但由于其实现的复杂性,对这些复杂类型分析将会比一般简单类型…...
数据结构---数组
一、基本概念 1. 存放一组相同数据类型的集合 2.在内存中,分配连续的空间,数组创建时要指定大小 3. 定义 数据类型 [] 数组名 // 1.定义一个数组,里面的元素包含10, 20, 24, 17, 35, 58, 45, 74 int arr[] {10, 20, 24, 17, 35, 58, 45, 74}; 4. 获取数组的长度 int lengt…...
知识笔记(八十四)———链式语句中fetchSql和force和bind用法
fetchSql: fetchSql用于直接返回SQL而不是执行查询,适用于任何的CURD操作方法。 例如: $result Db::table(think_user)->fetchSql(true)->find(1);输出result结果为: SELECT * FROM think_user where id 1 force&#…...
为什么要用B+树
B树的优势 支持范围查询:B树在进行范围查询时,只需要从根节点一直遍历到叶子节点,因为数据都存储在叶子节点上,而且叶子节点之间有指针连接,可以很方便的进行范围查询 支持排序:B树的叶子节点按照关键字顺…...

Android 通过adb命令查看应用流量
一. 获取应用pid号 通过adb shell ps -A | grep 包名 来获取app的 pid号 二. 查看应用流量情况 使用adb shell cat /proc/#pid#/net/dev 命令 来获取流量数据 备注: Recevice: 表示收包 Transmit: 表示发包 bytes: 表示收发的字节数 packets: 表示收发正确的…...

超全的测试类型详解,再也不怕面试答不出来了!
在软件测试工作过程中或者在面试过程中经常会被问到一些看起来简单但是总是有些回答不上的问题,比如你说说“黑盒测试和白盒测试的区别?”,“你们公司做灰度测试么?", ”α测试和β测试有什么不一样?“࿰…...

【Linux】
Linux零基础入门 列出文件/文件夹新建/切换路径查看当前路径重命名或者移动文件夹拷贝文件/文件夹删除文件夹设置环境变量编辑文本文件压缩和解压查看cpu的信息查看/杀死进程查看进程的CPU和内存占用重定向日志场景一场景二场景三场景四 列出文件/文件夹 命令:Ls(L…...

「 网络安全常用术语解读 」网络攻击者的战术、技术和常识知识库ATTCK详解
引言:随着网络攻击手段的不断升级和多样化,网络安全领域面临着越来越严峻的挑战。为了帮助网络安全专业人员更好地识别和防御网络攻击,MITRE公司创建了ATT&CK框架,以提供一个统一且结构化的方法来描述网络攻击者的行为和技巧。…...
Java.lang.Integer类详解
Java.lang.Integer类详解 大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!在今天的文章中,我们将深度解析Java中的一个重要类——java.lang.Integer&…...

GitFlow工作流
基于 Git 这一版本控制系统,通过定义不同的分支,探索合适的工作流程来完成开发、测试、修改等方面的需求。 例如:在开发阶段,创建 feature 分支,完成需求后,将此分支合并到 develop 分支上;在发…...
GitHub Copilot 与 OpenAI ChatGPT 的区别及应用领域比较
GitHub Copilot 和 OpenAI ChatGPT 都是近年来颇受关注的人工智能项目,它们在不同领域中的应用继续引发热议。本文旨在分析和比较这两个项目的区别,从技术原理、应用场景、能力和限制、输出结果、能力与限制和发展前景等方面进行综合评估,帮助…...

【C++】类和对象(上篇)
文章目录 🛟一、面向过程和面向对象初步认识🛟二、类的引入🛟三、类的定义📝1、类的两种定义方式📝2、成员变量命名规则的建议 🛟四、类的访问限定符及封装🍩1、访问限定符🍩2、封装…...

甜蜜而简洁——深入了解Pytest插件pytest-sugar
在日常的软件开发中,测试是确保代码质量的关键步骤之一。然而,对于测试报告的生成和测试结果的可读性,一直以来都是开发者关注的焦点。Pytest插件 pytest-sugar 以其清晰而美观的输出,为我们提供了一种愉悦的测试体验。本文将深入介绍 pytest-sugar 插件的基本用法和实际案…...

SpringBoot3整合OpenAPI3(Swagger3)
文章目录 一、引入依赖二、使用1. OpenAPIDefinition Info2. Tag3. Operation4. Parameter5. Schema6. ApiResponse swagger2更新到3后,再使用方法上发生了很大的变化,名称也变为OpenAPI3。 官方文档 一、引入依赖 <dependency><groupId>…...
2023美赛各题分析,2024美赛数学建模思路解析2.2日第一时间更新
目录 2024美赛数学建模各题思路模型代码:开赛后第一时间更新,更新见文末 一、2023题目重述 拟解决的问题 我们的工作: 二、模型和计算 1.数据预处理 2.报告数量区间预测模型 3.猜词结果分布预测模型 2024美赛数学建模交流࿰…...

分享一个学习git的网站
Learn Git Branching...
用户拉新的4大关键策略,照着做就对了!
今天给大家分享用户拉新的4个关键策略,掌握了这些策略,不仅有助于增加用户数量,还能让对方成为你忠实的粉丝。 1、制定明确的目标:在开始拉新之前,你需要明确自己的目标。你想要吸引什么样的用户?你希望他…...

如何用“VMware安装Ubuntu”win11系统?
一、 下载Ubuntu 企业开源和 Linux |Ubuntu的 二、 安装 三、 启动虚拟机 选中Try or Install Ubuntu Server,按回车...
ZJOI2009 对称的正方形
P2601 [ZJOI2009] 对称的正方形 题目大意 给定一个 n m n\times m nm的矩阵,求这个矩阵中满足上下对称且左右对称的正方形子矩阵的个数。 1 ≤ n , m ≤ 1000 1\leq n,m\leq 1000 1≤n,m≤1000 题解 首先,我们对原矩阵、左右翻转后的矩阵、上下翻转后…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...