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,一经查实,立即删除!

相关文章

【数据结构】稀疏数组

问题引导 在编写五子棋程序的时候&#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…

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

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

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

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

浅谈监听器之断言结果

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

【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…

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

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

前端 SSE 长连接

使用 const options {withCredentials: true, // 默认 false}const eventSource new EventSource(/api, options);eventSource.addEventListener(open, () > {});eventSource.onmessage (event) > {}; // 或addEventListener(message,callback)eventSource.addEvent…

程序员拔火罐技能的分享

一.背景 之前为了考“中医康复理疗师”的证书&#xff0c;自学了拔火罐。自学主要是在自己大腿上练习&#xff0c;然后拿家人做小白鼠。后来考试没有那么严格也就顺利通过了。操作过程中&#xff0c;积累的一些小知识&#xff0c;分享给大家&#xff0c;有空在家里给家人服务体…

CTF ssrf 基础入门 (一)

0x01 引言 我发现我其实并不是很明白这个东西&#xff0c;有些微妙&#xff0c;而且记忆中也就记得Gopherus这个工具了&#xff0c;所以重新学习了一下&#xff0c;顺便记录一下吧 0x02 辨别 我们拿到一个题目&#xff0c;他的名字可能就是题目类型&#xff0c;但是也有可能…

FastAPI(七十三)实战开发《在线课程学习系统》接口开发-- 回复留言

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 之前文章分享FastAPI&#xff08;七十二&#xff09;实战开发《在线课程学习系统》接口开发-- 留言列表开发&#xff0c;这次我们分享如何回复留言 按…

Jenkins+Gitlab持续集成综合实战

一、持续集成应用背景&#xff1a; DevOps&#xff1a;&#xff08;英文Development&#xff08;开发&#xff09;和Operations&#xff08;技术运营&#xff09;的组合&#xff09;是一组过程、方法与系统的统称&#xff0c;用于促进开发&#xff08;应用程序/软件工程&#…

NCRE3 2-1 网络总体设计基本方法

这部分记忆的比较多 概览 设计网络建设总体目标确定网络系统方案设计原则网络系统总体设计设计网络拓扑结构进行网络设备选型网络系统安全设计 设计网络建设总体目标 这部分视频没说到 确定网络系统方案设计原则 这部分视频没说到 网络系统总体设计 核心层网络结构设计 …

嵌入式Linux八股(四)——MCURTOS

嵌入式Linux八股完整文章目录 嵌入式Linux八股&#xff08;一&#xff09;——语言篇_嵌入式软件八股-CSDN博客 嵌入式Linux八股&#xff08;二&#xff09;——Linux_linux嵌入式的八股文有哪些-CSDN博客 嵌入式Linux八股&#xff08;三&#xff09;——计算机基础_嵌入式哪…

【Vue实战教程】之Vue工程化项目详解

Vue工程化项目 随着多年的发展&#xff0c;前端越来越模块化、组件化、工程化&#xff0c;这是前端发展的大趋势。webpack是目前用于构建前端工程化项目的主流工具之一&#xff0c;也正变得越来越重要。本章节我们来详细讲解一下如何使用webpack搭建Vue工程化项目。 1 使用we…

基于Python的哔哩哔哩国产动画排行数据分析系统

需要本项目的可以私信博主&#xff0c;提供完整的部署、讲解、文档、代码服务 随着经济社会的快速发展&#xff0c;中国影视产业迎来了蓬勃发展的契机&#xff0c;其中动漫产业发展尤为突出。中国拥有古老而又璀璨的文明&#xff0c;仅仅从中提取一部分就足以催生出大量精彩的…

源码拆解SpringBoot的自动配置机制

SpringBoot相比于Spring系列的前作&#xff0c;很大的一个亮点就是将配置进行了简化&#xff0c;引入了自动化配置&#xff0c;仅靠几个注解和yml文件就取代了之前XML的繁琐配置机制&#xff0c;这也是SpringBoot的独有特点&#xff0c;下面我们从源码角度&#xff0c;一点点拆…