Spring AOP(2)原理(代理模式和源码解析)

目录

一、代理模式

二、静态代理

三、动态代理

1、JDK动态代理

(1)JDK动态代理实现步骤

(2)定义JDK动态代理类

(3)代码简单讲解

2、CGLIB动态代理

(1)CGLIB 动态代理类实现步骤

(2)添加依赖

(3)自定义MethodInterceptor(方法拦截器)

(4)创建代理类,并使用

(5)代码简单讲解

四、Spring AOP 源码剖析(了解)

五、常见面试题

1、什么是 AOP?

2、Spring AOP的实现方式有哪些?

3、Spring AOP 的实现原理?

4、Spring 使用的是哪种代理方式?

5、JDK 和 CGLIB 的区别?

六、总结


        上篇文章学习了 Spring AOP 的应用,接下来我们来学习 Spring AOP 的原理,也就是 Spring 是如何实现 AOP 的。

        Spring AOP 是基于动态代理来实现 AOP 的,咱门学习内容主要分以下两部分:

1、代理模式

2、Spring AOP 源码 剖析


一、代理模式

        代理模式,也叫 委托模式。

定义为其他对象提供一种代理以控制这个对象的访问它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用

       某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。

        使用代理前:

        使用代理后:

        

        生活中的代理:

艺人经纪人:广告商找艺人拍广告,需要经过经纪人,由经纪人来和艺人沟通。

房屋中介:房屋进行租赁时,卖方会把房屋授权给中介,由中介来代理看房,房屋咨询等服务。

经销商:厂商不直接对外销售产品,由经销商负责代理销售。

秘书/助理:合作伙伴找老板谈合作,需要先经过秘书/助理预约。

        代理模式的主要角色:

1、Subject业务接口类。可以是抽象类或者接口(不一定有)

2、RealSubject业务实现类。具体的业务执行,也就是被代理对象

3、Proxy代理类。RealSubject的代理

比如 房屋出租:

        Subject:就是提前定义了房东做的事情,交给中介代理,也是中介要做的事情。

        RealSubject:房东。

        Proxy:中介。

        UML类图如下:

        

        代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强根据代理的创建时期,代理模式分为静态代理和动态代理

        静态代理由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了

        动态代理在程序运行时,运用反射机制动态创建而成


二、静态代理

        静态代理:在程序运行前,代理类的 .class文件 就已经存在了。(在出租房子之前,中介已经做好了相关的工作,就等租户来租房子了)。

        我们通过代码来加深理解。以房租租赁为例:

1、定义接口(定义房东要做的事情,也是中介需要做的事情)

public interface HouseSubject {void rentHouse();
}

2、实现接口(房东出租房子)

public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我是房东, 我出租房子");}
}

3、代理(中介,帮房东出租房子)

public class HouseProxy implements HouseSubject{private HouseSubject target;public HouseProxy(HouseSubject target) {this.target = target;}@Overridepublic void rentHouse() {//代理前System.out.println("我是中介, 开始代理");//出租房子target.rentHouse();;//代理后System.out.println("我是中介, 结束代理");}
}

4、使用

public class Main {public static void main(String[] args) {HouseSubject subject = new RealHouseSubject();//创建代理类HouseProxy houseProxy = new HouseProxy(subject);//通过代理类访问⽬标⽅法houseProxy.rentHouse();}
}

        运行结果:

        上面这个代理实现方式就是静态代理(仿佛啥也没干)。从上述程序可以看出,虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对目标对象的每个方法的增强都是手动完成的,非常不灵活。所以日常开发几乎看不到静态代理的场景。

        接下来新增需求:中介又新增了其他业务:代理房屋出售。我们就需要对上述代码进行修改。

1、接口定义修改:

public interface HouseSubject {void rentHouse();void saleHouse();
}

2、接口实现修改

public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我是房东, 我出租房子");}@Overridepublic void saleHouse() {System.out.println("我是房东, 我出售房子");}
}

3、代理类修改

public class HouseProxy implements HouseSubject{private HouseSubject target;public HouseProxy(HouseSubject target) {this.target = target;}@Overridepublic void rentHouse() {//代理前System.out.println("我是中介, 开始代理");//出租房子target.rentHouse();;//代理后System.out.println("我是中介, 结束代理");}@Overridepublic void saleHouse() {//代理前System.out.println("我是中介, 开始代理");//出租房子target.rentHouse();;//代理后System.out.println("我是中介, 结束代理");}
}

4、使用

public class Main {public static void main(String[] args) {HouseSubject subject = new RealHouseSubject();//创建代理类HouseProxy houseProxy = new HouseProxy(subject);//通过代理类访问⽬标⽅法houseProxy.rentHouse();System.out.println("=========");houseProxy.saleHouse();}
}

        运行结果:

        从上述代码可以看出,我们修改接口(Subject)和业务实现类(RealSubject),还需要修改代理类(Proxy)

        同样的,如果新增接口(Subject)和业务实现类(RealSubject),也需要对每一个业务实现类新增代理类(Proxy)

        既然代理的流程是一样的,有没有一种办法,让他们通过一个代理类来实现呢?这就需要用到动态代理技术了。


三、动态代理

        相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由JVM来实现也就是说动态代理在程序运行时,根据需要动态创建生成

        比如房屋中介,我不需要提前预测都有哪些业务,而是业务来了我再根据情况创建。

        先看代码再来理解。Java也对动态代理进行了实现,并给我们提供一些API,常见的实现方式有两种:

        1、JDK动态代理

        2、CGLIB动态代理

动态代理在我们日常开发中使用的相对较少,但是在框架中几乎是必用的一门技术。学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。

1、JDK动态代理

(1)JDK动态代理实现步骤

1、定义一个接口及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject)

2、自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法(被代理类的方法),并自定义一些处理逻辑

3、通过 Proxy.newProxyInstance(ClassLoader, Class<?>[ ]  Interfaces, InvocationHandler  h)方法创建代理对象

(2)定义JDK动态代理类

        创建 JDKInvocationHandler类 实现 InvocationHandler 接口:

public class JDKInvocationHandler implements InvocationHandler {//目标对象,即被代理的对象private Object target;public JDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//代理增强内容System.out.println("我是中介,开始代理");//通过反射调用被代理类的方法Object result = method.invoke(target, args);//代理增强内容System.out.println("我是中介,结束代理");return result;}
}

        创建一个代理对象并使用:

public class Main {public static void main(String[] args) {/*** JDK动态代理*///创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建RealHouseSubject target = new RealHouseSubject();//目标对象/*** newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)* loader:加载我们的被代理类的ClassLoad* interfaces:要实现的接口* h:代理要做的事情,需要实现 InvocationHandler 这个接口*/HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();System.out.println("==============");proxy.saleHouse();}
}

        运行程序,结果如下:

        假设代理的是类,而不是对象,代码如下:

public class Main {public static void main(String[] args) {RealHouseSubject target = new RealHouseSubject();RealHouseSubject proxy = (RealHouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{RealHouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();System.out.println("==============");proxy.saleHouse();}
}

        运行程序,结果如下:(报错了)

        报错原因RealHouseSubject is not an interface(RealHouseSubject 类不是接口)说明JDK 动态代理只能代理接口,不能代理类,不然会报错。

(3)代码简单讲解

        主要是学习API的使用,我们按照 Java API 的规范来使用即可。

1、InvocationHandler:

        InvocationHandler 接口是 Java 动态代理的关键接口之一,它定义了一个单一方法 invoke(),用于处理被代理对象的方法调用。

    public interface InvocationHandler {/*** 参数说明* proxy:被代理对象* method:被代理对象需要实现的⽅法,即其中需要重写的⽅法* args:method所对应⽅法的参数*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;}

        通过实现 InvocationHandler 接口,可以对被代理对象的方法进行功能增强。

2、Proxy:

        Proxy 类中使用频率最高的方法:newProxyInstance(),这个方法主要用来生成一个代理对象。

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{//...代码省略}

        这个方法一共有 3 个参数:

loader类加载器,用于加载被代理对象

interface被代理类实现的一些接口(这个参数的定义,也决定了JDK动态代理只能代理实现了接口的一些类)

h代理要做的事情,实现 InvocationHandler 接口的对象

2、CGLIB动态代理

        JDK动态代理有一个最致命的问题是只能代理实现了接口的类

        有些场景下,我们的业务码是直接实现的,并没有接口定义。为了解决这个问题,我们可以用 CGLIB 动态代理机制来解决。

        CGLIB(Code Generation Library)是一个基于 ASM 的字节码生产库,它允许我们在运行时对字节码进行修改和动态生成。

        CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了 CGLIB。例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。(其中 Spring 是基于动态代理实现的,动态代理是基于反射实现的

(1)CGLIB 动态代理类实现步骤

1、定义一个类(被代理类)

2、自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似

3、通过 Enhancer 类的 create() 创建代理类

        接下来看实现:

(2)添加依赖

        和 JDK 动态代理不同,CGLIB(Code Generation Library)实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖

        <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>

(3)自定义MethodInterceptor(方法拦截器)

        实现 MethodInterceptor 接口:

import org.springframework.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Method;public class CGLibInterceptor implements MethodInterceptor {private Object target;public CGLibInterceptor(Object target) {this.target = target;}/*** 调用代理对象的方法*/@Overridepublic Object intercept(Object obj, Method method, Object[] args, org.springframework.cglib.proxy.MethodProxy proxy) throws Throwable {//代理增强内容System.out.println("我是中介,开始代理");Object result = method.invoke(target, args);//代理增强内容System.out.println("我是中介,结束代理");return result;}
}

(4)创建代理类,并使用

        代理接口:

public class Main {public static void main(String[] args) {//目标对象HouseSubject target = new RealHouseSubject();HouseSubject proxy  = (HouseSubject) Enhancer.create(target.getClass(), new CGLibInterceptor(target));proxy.rentHouse();System.out.println("=============");proxy.saleHouse();}
}

        运行程序,执行结果如下:

        代理类:

public class Main {public static void main(String[] args) {//目标对象HouseSubject target = new RealHouseSubject();RealHouseSubject proxy  = (RealHouseSubject) Enhancer.create(target.getClass(), new CGLibInterceptor(target));proxy.rentHouse();System.out.println("=============");proxy.saleHouse();}
}

        运行程序,执行结果如下:

(5)代码简单讲解

1、MethodInterceptor

        MethodInterceptor 和 JDK动态代理中的 InvocationHandler 类似,它只定义了一个方法 intercept(),用于增强目标方法。

    public interface MethodInterceptor extends Callback {/*** 参数说明:* o: 被代理的对象* method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法)* objects: ⽅法⼊参* methodProxy: ⽤于调⽤原始⽅法*/Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;}

2、Enhancer.create()

    public static Object create(Class type, Callback callback) {//...代码省略}

        type被代理类的类型(类或接口)

        callback自定义方法拦截器 MethodInterceptor


四、Spring AOP 源码剖析(了解)

        Spring AOP 主要基于两种方式实现的:JDK 及 CGLIB 的方式。

        Spring 源码过于复杂,我们只摘出一些主要内容,以了解为主

        Spring 对于 AOP 的实现,基本都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成 生成代理对象的逻辑在父类 AbstractAutoProxyCreator 中。

protected Object createProxy(Class<?> beanClass,@Nullable String beanName,
@Nullable Object[]specificInterceptors,TargetSource targetSource){if(this.beanFactory instanceof ConfigurableListableBeanFactory){AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory,beanName,beanClass);}//创建代理⼯⼚ProxyFactory proxyFactory=new ProxyFactory();proxyFactory.copyFrom(this);/*** 检查proxyTargetClass属性值,spring默认为false* proxyTargetClass 检查接⼝是否对类代理, ⽽不是对接⼝代理* 如果代理对象为类, 设置为true, 使⽤cglib代理*/if(!proxyFactory.isProxyTargetClass()){//是否有设置cglib代理if(shouldProxyTargetClass(beanClass,beanName)){//设置proxyTargetClass为true,使⽤cglib代理proxyFactory.setProxyTargetClass(true);}else{/*** 如果beanClass实现了接⼝,且接⼝⾄少有⼀个⾃定义⽅法,则使⽤JDK代理* 否则CGLIB代理(设置ProxyTargetClass为true )* 即使我们配置了proxyTargetClass=false, 经过这⾥的⼀些判断还是可能会将其设为true*/evaluateProxyInterfaces(beanClass,proxyFactory);}}Advisor[]advisors=buildAdvisors(beanName,specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if(advisorsPreFiltered()){proxyFactory.setPreFiltered(true);}// Use original ClassLoader if bean class not locally loaded in overriding class loaderClassLoader classLoader =getProxyClassLoader();if(classLoader instanceof SmartClassLoader&&classLoader != beanClass.getClassLoader()){classLoader=((SmartClassLoader)classLoader).getOriginalClassLoader();}
//从代理⼯⼚中获取代理return proxyFactory.getProxy(classLoader);}

        代理工厂有一个重要的属性:proxyTargetClass,默认值为false。也可以通过程序设置

proxyTargetClass⽬标对象代理⽅式
false实现了接口jdk代理
false未实现接口(只有实现类)cglib代理
true实现了接口cglib代理
true未实现接口(只有实现类)cglib代理

        可以通过 @EnableAspectJAutoProxy(proxyTargetClass = true) 来设置。

        注意:

Spring 默认 proxyTargetClass:false,会分为两种情况:

        实现了接口使用 JDK 代理

        普通类:       使用 CGLIB 代理

Spring Boot 2.X 开始,默认使用 proxyTargetClass:true

        默认使用 CGLIB 代理

        SpringBoot设置 @EnableAspectJAutoProxy 无效,因为 Spring Boot 默认使用 AopAutoConfiguration 进行装配。

        可以通过配置项 spring.aop.proxy-target-class=false 来进行修改,设置为 jdk 代理。

        使用 context.getBean() 需要添加注解,使 HouseProxy,RealHouseSubject 被 Spring 管理。测试 AOP 代理,需要把这些类交给 AOP 管理(自定义注解或使用 @Aspect)

        我们现在从源码中点进去看看代理工厂的代码:

public class ProxyFactory extends ProxyCreatorSupport {//...代码省略//获取代理public Object getProxy(@Nullable ClassLoader classLoader) {//分两步 先createAopProxy,后getProxyreturn createAopProxy().getProxy(classLoader);}protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this);}//...代码省略
}

        createAopProxy 的实现在 DefaultAopProxyFactory 中

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {//...代码省略@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throwsAopConfigException {/*** 根据proxyTargetClass判断* 如果⽬标类是接⼝, 使⽤JDK动态代理* 否则使⽤cglib动态代理*/if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() ||hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determinetarget class:" +"Either an interface or a target is required for proxycreation. ");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) ||ClassUtils.isLambdaClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);} else {return new JdkDynamicAopProxy(config);}}//...代码省略
}

        接下来就是创建代理了

JDK动态代理:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {//...代码省略@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {if (logger.isTraceEnabled()) {logger.trace("Creating JDK dynamic proxy: " +this.advised.getTargetSource());}return Proxy.newProxyInstance(determineClassLoader(classLoader),this.proxiedInterfaces, this);}//...代码省略
}

CGLIB动态代理:

class CglibAopProxy implements AopProxy, Serializable {//...代码省略@Overridepublic Object getProxy(@Nullable ClassLoader classLoader) {//...代码省略// Configure CGLIB Enhancer...Enhancer enhancer = createEnhancer();// Generate the proxy class and create a proxy instance.return createProxyClassAndInstance(enhancer, callbacks);}//...代码省略
}

五、常见面试题

1、什么是 AOP?

        AOP是 面向切面编程,也是一种思想,切面指的是某一类特定问题,所以 AOP 也可以理解为 面向切面编程。

2、Spring AOP的实现方式有哪些?

(1)基于注解(@Aspect 或 自定义注解)

(2)基于 xml

(3)基于代理

3、Spring AOP 的实现原理?

        基于动态代理实现的,其中的动态代理有两种形式:(1)JDK   (2)CGLIB

4、Spring 使用的是哪种代理方式?

        Spring 的 proxyTargetClass 默认为:false,其中:实现了接口,使用 JDK 代理;普通类:使用CGLIB代理。

        Spring Boot 从 2.X 之后,proxyTargetClass 默认为:true,默认使用 CGLIB 代理

         现在我们测试一下:当这个值为 true 时,则使用的是动态代理

        

@SpringBootApplication
public class SpringAopApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);//代理类TestController bean = context.getBean(TestController.class);System.out.println(bean);}
}

        要打断电才能观察到结果,如图:

        我们获取Spring管理的对象,打断点,观察对象名称,如图:

        可以看到,是 CGLIB代理。

当值设为 false 时;就要看代理对象是不是接口了,是接口用的就是JDK代理,代理对象是类就是CGLIB代理。

        因为要代理接口,所以现在重新创建一个接口,内容如下:
 

public interface IFace {void test();
}

        再创建一个成 Controller 类,实现上面这个类

@RequestMapping("/test2")
@RestController
public class TestController2 implements IFace{@MyAspect@RequestMapping("t1")@Overridepublic void test() {System.out.println("测试测试");}
}

        main方法如下:

@SpringBootApplication
public class SpringAopApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringAopApplication.class, args);//代理类TestController bean = context.getBean(TestController.class);System.out.println(bean);//代理接口IFace iFace = (IFace) context.getBean("testController2");System.out.println(iFace);}
}

        现在看看Spring对象,任然要使用断点才能看到,如图:

        看看bean对象,如图:

5、JDK 和 CGLIB 的区别?

        使用JDK 动态代理只能代理接口。

        使用 CGLIB 动态代理 既可以代理接口,也可以代理类。


六、总结

        1、AOP 是一种思想是对某一类事情的集中处理。Spring 框架实现了AOP,称之为 Spring AOP。

        2、Spring AOP 场景的实现方式有两种:(1)基于注解@Aspect来实现(2)基于自定义注解来实现还有一些更原始的方式,比如基于代理、基于 xml 配置的方式,但目标比较少见

        3、Spring AOP 是基于动态代理实现的,有两种方式:(1)基于 JDK 动态代理实现(2)基于 CGLIB 动态代理实现。运行时使用哪种方式与项目配置的代理对象有关

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

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

相关文章

k8s中的重启策略

一、k8s的pod&#xff0c;kill进程和上节点停止容器&#xff0c;容器是否被重启&#xff08;重启策略为OnFailure&#xff09;&#xff1a; 解释&#xff1a;docker ps -a看到容器退出码为&#xff1a; kill进程&#xff0c;其容器退出码为&#xff1a;exit(137)stop 容器&am…

【数据结构】稀疏数组

问题引导 在编写五子棋程序的时候&#xff0c;有“存盘退出”和“续上盘”的功能。现在我们要把一个棋盘保存起来&#xff0c;容易想到用二维数组的方式把棋盘表示出来&#xff0c;但是由于在数组中很多数值取默认值0&#xff0c;因此记录了很多没有意义的数据。此时我们使用稀…

Apache压测工具ab(Apache Bench)工具的下载安装和使用示例

场景 Jmeter进行http接口压力测试&#xff1a; Jmeter进行http接口压力测试_接口压测两万量-CSDN博客 上面讲压测工具Jmeter的使用&#xff0c;下面介绍另外一个ab(Apache Bench)压测工具的使用。 apache bench apache bench是apache自带的压力测试工具。 ab不仅可以对ap…

构件组装不兼容-系统架构师(三十三)

1、&#xff08;系统工程与信息系统基础->信息系统战略规划&#xff09;企业信息化程度是国家信息化建设的基础和关键&#xff0c;企业信息化方法不包括&#xff08;&#xff09;。 A业务流程重组 B组织机构变革 C供应链管理 D人力资本投入 解析&#xff1a; 企业信息化…

CSS 创建:从入门到精通

CSS 创建:从入门到精通 CSS(层叠样式表)是网页设计中不可或缺的一部分,它用于控制网页的布局和样式。本文将详细介绍CSS的创建过程,包括基本概念、语法结构、选择器、样式属性以及如何将CSS应用到HTML中。无论您是初学者还是有经验的开发者,本文都将为您提供宝贵的信息。…

awk的模式

在awk 中&#xff0c;匹配模式处于非常重要的地位&#xff0c;它决定着匹配模式后面的操作会影响到哪些文本行。 awk 中 的匹配模式主要包括关系表达式、正则表达式、混合模式&#xff0c; BEGIN 模式以及 END 模式等。 &#xff08; 1 &#xff09;关系表达式 awk 提供了许…

localSorage,sessionStorage,cookie三者的区别和特点

LocalStorage、SessionStorage、Cookie三者的区别和特点? 什么是Cookie HTTP协议本身是无状态的。什么是无状态呢&#xff0c;即服务器无法判断用户身份。 Cookie实际上是一小段的文本信息&#xff08;key-value格式&#xff09;。客户端向服务器发起请求&#xff0c;如果服务…

培训第十三天(DNS逆向解析与主从服务、ntp时间服务器)

上午 编号主机名ip地址说明修改1web服务器10.0.0.10发布部署web服务发布了一个nginx web服务2dns服务器10.0.0.11用于解析域名和ip地址1、安装bind 2、配置一个conf&#xff0c;zones&#xff0c;zone 3、检查了3个文件&#xff0c;启动3cli主机10.0.0.12用于模拟客户机修改了…

基于联盟链Fabric 2.X 的中药饮片代煎配送服务与监管平台

业务背景 近年来&#xff0c;随着公众对中医药青睐有加&#xff0c;中药代煎服务作为中医药现代化的重要一环&#xff0c;在全国各地蓬勃兴起。鉴于传统煎煮方式的繁琐耗时&#xff0c;医疗机构纷纷转向与第三方中药饮片企业合作&#xff0c;采用集中代煎模式。这些第三方煎药中…

没有51基础,能不能学好STM32?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「STM32的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 我们通常准备攻读一本大部…

【HTML】iframe

<iframe> 标签是 HTML 中的⼀个元素&#xff0c;⽤于在当前 HTML ⽂档中嵌⼊另⼀个 HTML ⽂档。它可以在⼀个 HTML ⻚⾯中嵌⼊另⼀个 HTML ⻚⾯或其他类型的⽂档&#xff0c;⽐如 PDF ⽂件或视频⽂件。 <iframe> 标签的优点包括&#xff1a; 可以让⻚⾯嵌⼊其他⽹…

浅谈监听器之断言结果

浅谈监听器之断言结果 在进行测试过程中&#xff0c;断言是一种关键组件&#xff0c;用于验证采样器&#xff08;如HTTP请求&#xff09;的响应数据是否符合预期。而“断言结果”监听器则是展示这些断言执行情况的重要工具&#xff0c;它帮助用户快速识别哪些断言通过或未通过…

JavaScript轮播图

HTML部分 <div class"box" onmouseover"over()" onmouseout"noover()"><img src"./img/zuo.png" alt"" class"left_arrow" onclick"left_last()"><img src"./img/yy.png" al…

【JAVA多线程】线程的状态控制

目录 1.JDK中的线程状态 2.基础操作 2.1关闭 2.2中断 2.3.等待、唤醒 2.4.阻塞、唤醒 1.JDK中的线程状态 在JDK的线程体系中线程一共6种状态&#xff1a; NEW&#xff08;新建&#xff09;: 当线程对象创建后&#xff0c;但尚未启动时&#xff0c;线程处于新建状态。RUN…

python毕业设计选题校园食堂菜谱推荐系统

✌网站介绍&#xff1a;✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围&#xff1a;Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容&#xff1a;免费功能设计、免费提供开题答辩P…

stack(leetcode练习题,牛客)

文章目录 STL用法总结32 最长有效括号思路代码 496 下一个最大元素思路代码 856 括号的分数思路 最优屏障思路代码 STL用法总结 关于stack的知识&#xff0c;可以看点击查看上面的博客&#xff0c;以下题目前三个全在leetcode&#xff0c;最后一个在牛客 32 最长有效括号 思路…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十章 Linux设备树

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

C++类别(建构函数、初值串列、EXPLICIT指示词)

类别- 类别(class) 是物件(object) 的模板&#xff0c;物件的所有功能都由类别设置。 典型的类别设计如下 class class_name { access_label_1:// memberaccess_label_2:// member }; 利用关键字(keyword) class宣告类别名称&#xff0c;其后的大括弧为类别的宣告内容&…

代理IP在数据采集中具体有哪些作用

在当今信息化高速发展的时代&#xff0c;从市场趋势分析到消费者行为预测&#xff0c;从产品优化到服务改进&#xff0c;大数据都在其中扮演着不可或缺的角色。但数据的采集、整理和分析并非易事&#xff0c;特别是在面对海量的网络数据时&#xff0c;我们往往需要借助一些技术…

python3.12 String类方法大全

字符串相关链接 类型细分与链接注释 字面值 转义字符字符串字符串基础方法拼接, 索引, 路径, 长字符串分行等等内置类型str 字符串的方法 见下表 printf 风格的字符串格式化 format % values 标准库string 字符串常量字符串模块中定义的常量class string.Formatter 自定义字符…