认识Spring 中的BeanPostProcessor

关于BeanPostProcessor和BeanFactoryPostProcessors,将分2篇文章来写,这篇文章是对Spring 中BeanPostProcessor进行了总结

先看下大模型对这个类的介绍,随后再看下这两个类的示例,最后看下这两个类的实现。
这两个类从名字看都很类似,见名知意:BeanPostProcessor=Bean后置处理器,BeanFactoryPostProcessors=BeanFactory后置处理器

BeanPostProcessor

在Spring 5.3中,BeanPostProcessor是一个强大的接口,它允许开发者在Spring容器中的bean实例化、配置和初始化前后添加自定义的逻辑处理。以下是关于BeanPostProcessor的详细解释:

1. 作用阶段
BeanPostProcessor在Spring容器完成bean的实例化、配置和其他的初始化前后工作。它提供了两个方法,一个在初始化方法之前执行,另一个在初始化方法之后执行。
2. 主要方法
postProcessBeforeInitialization(Object bean, String beanName):
在bean的初始化方法(如@PostConstruct标记的方法或实现InitializingBean接口中的afterPropertiesSet方法)被调用之前执行。
允许在bean的属性被设置之后,但在初始化之前,对bean进行额外的处理。
postProcessAfterInitialization(Object bean, String beanName):
在bean的初始化方法被调用之后执行。
允许在bean完成初始化后,对其进行修改或返回一个新的对象(尽管这通常不是推荐的做法,因为可能导致意外的行为)。
3. 返回值
这两个方法都不能返回null。如果返回null,那么在后续的操作中可能会遇到空指针异常,或者通过getBean()方法无法获取到bean的实例。
4. 使用方式
要使用BeanPostProcessor,你需要:
实现BeanPostProcessor接口。
实现postProcessBeforeInitialization和postProcessAfterInitialization方法。
将你的BeanPostProcessor实现注册为Spring容器中的一个bean,这样Spring就能够识别并应用它。
5. 排序和优先级
如果在Spring配置中定义了多个BeanPostProcessor,那么默认情况下,Spring容器会根据它们的定义顺序来依次调用。
但是,你可以通过实现Ordered接口或使用@Order、@Priority注解来指定优先级。
6. 应用场景
BeanPostProcessor常被用于AOP(面向切面编程)的自动代理创建、日志记录、安全检查、性能监控等场景。
7. 注意事项
由于BeanPostProcessor能够影响所有bean的初始化过程,因此在使用时需要特别小心,避免引入不必要的复杂性或错误。
如果在postProcessBeforeInitialization或postProcessAfterInitialization方法中修改了bean的状态,需要确保这些修改是预期的,并且不会对其他bean或整个应用程序产生负面影响。
8. 总结
BeanPostProcessor是Spring框架中一个非常强大的工具,它允许开发者在bean的生命周期中的关键阶段插入自定义逻辑。然而,由于其强大的能力,使用时需要谨慎,并确保理解其工作原理和潜在影响。

现在写几个示例来看下是怎么执行的。
我这里定义A、B、C、D 、E、F类,分别如下:
A类

package org.springframework.example.BPP;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * 注解方式
 */
@Component
public class A implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		System.out.println("A.postProcessBeforeInitialization");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		System.out.println("A.postProcessAfterInitialization");
		return bean;
	}
}

B类

package org.springframework.example.BPP;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 注解方式,加了顺序
 */
@Component
@Order(0)
public class B implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		System.out.println("B.postProcessBeforeInitialization, order: 0");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		System.out.println("B.postProcessAfterInitialization, order: 0");
		return bean;
	}
}

C类

package org.springframework.example.BPP;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * 注解方式,实现Order
 */
@Component
public class C implements BeanPostProcessor, Ordered {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		System.out.println("C.postProcessBeforeInitialization, order: 1");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		System.out.println("C.postProcessAfterInitialization, order: 1");
		return bean;
	}

	@Override
	public int getOrder() {
		return 1;
	}
}

D类

package org.springframework.example.BPP;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

/**
 * 注解方式,实现PriorityOrdered
 */
@Component
public class D implements BeanPostProcessor, PriorityOrdered {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		System.out.println("D.postProcessBeforeInitialization, PriorityOrdered: 2");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		System.out.println("D.postProcessAfterInitialization, PriorityOrdered: 2");
		return bean;
	}

	@Override
	public int getOrder() {
		return 2;
	}
}

E类

package org.springframework.example.BPP;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

/**
 * 注解方式,实现PriorityOrdered
 */
@Component
public class E implements BeanPostProcessor, PriorityOrdered {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		System.out.println("E.postProcessBeforeInitialization, PriorityOrdered: 3");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		System.out.println("E.postProcessAfterInitialization, PriorityOrdered: 3");
		return bean;
	}

	@Override
	public int getOrder() {
		return 3;
	}
}

F类

package org.springframework.example.BPP;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * 注解方式,实现Order
 */
@Component
public class F implements BeanPostProcessor, Ordered {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		System.out.println("F.postProcessBeforeInitialization, order: 4");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		System.out.println("F.postProcessAfterInitialization, order: 4");
		return bean;
	}

	@Override
	public int getOrder() {
		return 4;
	}
}

BeanTest 测试类

package org.springframework.example.BPP;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class BeanTest {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext =
				new AnnotationConfigApplicationContext("org.springframework.example.BPP");
	}
}

看下打印结果

D.postProcessBeforeInitialization, PriorityOrdered: 2
E.postProcessBeforeInitialization, PriorityOrdered: 3
D.postProcessAfterInitialization, PriorityOrdered: 2
E.postProcessAfterInitialization, PriorityOrdered: 3
D.postProcessBeforeInitialization, PriorityOrdered: 2
E.postProcessBeforeInitialization, PriorityOrdered: 3
D.postProcessAfterInitialization, PriorityOrdered: 2
E.postProcessAfterInitialization, PriorityOrdered: 3
D.postProcessBeforeInitialization, PriorityOrdered: 2
E.postProcessBeforeInitialization, PriorityOrdered: 3
C.postProcessBeforeInitialization, order: 1
F.postProcessBeforeInitialization, order: 4
D.postProcessAfterInitialization, PriorityOrdered: 2
E.postProcessAfterInitialization, PriorityOrdered: 3
C.postProcessAfterInitialization, order: 1
F.postProcessAfterInitialization, order: 4
D.postProcessBeforeInitialization, PriorityOrdered: 2
E.postProcessBeforeInitialization, PriorityOrdered: 3
C.postProcessBeforeInitialization, order: 1
F.postProcessBeforeInitialization, order: 4
D.postProcessAfterInitialization, PriorityOrdered: 2
E.postProcessAfterInitialization, PriorityOrdered: 3
C.postProcessAfterInitialization, order: 1
F.postProcessAfterInitialization, order: 4

初步分析结果,后面再看原因:
1、打印的结果怎么是这样
2、AB为什么没有打印
3、PriorityOrdered 优先级高于Ordered
4、B类的@Order(0)好像没用

带着问题,来分析一下源码:
首先我们的BeanTest类里面的AnnotationConfigApplicationContext,new 的时候内部自带了refresh(),在refresh()里面找到registerBeanPostProcessors(beanFactory),最后在PostProcessorRegistrationDelegate类的registerBeanPostProcessors中打个断点,后面调试用。

在这里插入图片描述
先整体分析一下这段代码:

public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		// WARNING: Although it may appear that the body of this method can be easily
		// refactored to avoid the use of multiple loops and multiple lists, the use
		// of multiple lists and multiple passes over the names of processors is
		// intentional. We must ensure that we honor the contracts for PriorityOrdered
		// and Ordered processors. Specifically, we must NOT cause processors to be
		// instantiated (via getBean() invocations) or registered in the ApplicationContext
		// in the wrong order.
		//
		// Before submitting a pull request (PR) to change this method, please review the
		// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
		// to ensure that your proposal does not result in a breaking change:
		// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
		//上面的注释看,作者意思是这样写很多循环都是有意的,不需要重构
		//获取BeanPostProcessor的实现类的Bean名称集合
		//抛开Spring自带的BeanPostProcessor,这里将会获取到我们自定义的ABCDEF6个类,Spring 自带的BeanPostProcessor,后面再说
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		//这里为什么要+1,我个人估计是因为BeanPostProcessorChecker本身也是一个BeanPostProcessor
		//BeanPostProcessorChecker 用来检测不符合BeanPostProcessors规则的Bean
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		//分别收集PriorityOrdered、Ordered和没有实现排序的BeanPostProcessor和MergedBeanDefinitionPostProcessor类型的Bean
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			//查询指定名称的Bean 是否实现PriorityOrdered 接口,判断的代码也挺复杂,暂且不关心
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				//获取BeanPostProcessor的实例
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				//如果是MergedBeanDefinitionPostProcessor类型,该处理器后面再说
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			//如果实现了Ordered接口,则添加到orderedPostProcessorNames集合中
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			//如果没有实现排序接口,则添加到nonOrderedPostProcessorNames集合中
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		//先排序priorityOrderedPostProcessors
		//默认排序规则使用的OrderComparator,优先排序PriorityOrdered的实现类,顺序从小到大排序
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		//注册,就是往beanFactory中的一个List增加BeanPostProcessor
		//这里面又判断了beanFactory 是否是AbstractBeanFactory,关于什么事AbstractBeanFactory,也留着后面讲
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		//注册实现了Ordered的BeanPostProcessors
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			//同样判断了MergedBeanDefinitionPostProcessor
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		//同理,排序
		sortPostProcessors(orderedPostProcessors, beanFactory);
		//同理,注册
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		//处理没有实现排序的BeanPostProcessor
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		//同理,注册
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		//最后,处理MergedBeanDefinitionPostProcessor
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		//添加ApplicationListenerDetector,其用来注册ApplicationListener类型的bean到上下文中,后面来测试一下
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

下面来调试一下
解释第一个问题,打印结果解密,首先要知道在哪里调用postProcessBeforeInitialization和postProcessAfterInitialization。这里是在通过beanFactory.getBean获取Bean的时候,没有Bean就会创建一个Bean,此时就会调用到,跟进去在AbstractAutowireCapableBeanFactory–>initializeBean 内部进行调用,此处打个断点调式。

在这里插入图片描述
由于几个内置BeanPostProcessor的存在,运行时会多次进入断点,所以我们都跳过,进入applyBeanPostProcessorsBeforeInitialization,查看**getBeanPostProcessors()**的值,此时为几个内置BeanPostProcessor。
在这里插入图片描述
内置Bean自然没有我们的东西,跳过,后面的进入断点次数,不算内置BeanPostProcessor!!!

第二次进入断点,此时beanName 是 e
在这里插入图片描述
此时的getBeanPostProcessors() 还是原来的内置BeanPostProcessor,跳过。

第三次进入断点,此时beanName是“c”
在这里插入图片描述
这时getBeanPostProcessors() 多了D、E,这时继续执行,将打印D、E的postProcessBeforeInitialization日志
在这里插入图片描述
继续往下执行到applyBeanPostProcessorsAfterInitialization,打印

在这里插入图片描述
到这里,Bean C已经处理完了,直接跳过,到下个断点

第四次进入断点是F
在这里插入图片描述
这里的getBeanPostProcessors() 还是DE,没有C,为什么?因为CF都是实现的Ordered,他们都是在一块处理,处理完后才添加到beanPostProcessors中。由前面的代码分析可知。
所以在这里还是打印的DE的日志
在这里插入图片描述

第五次进入断点,beanName是a
在这里插入图片描述
这里开始打印DECF的日志
在这里插入图片描述
第六次进入断点,beanName是b
在这里插入图片描述
到这里所有的类都执行完了

解释一下第二个问题,AB为什么没有被打印。
从上面可知,AB 两个bean的创建在最后,后续没有其他Bean创建,因此没有执行AB 两个BeanPostProcessor的机会。要想打印,单独创建一个普通Bean,来测试一下。
G类

package org.springframework.example.BPP;

import org.springframework.stereotype.Component;

@Component
public class G {
}

在这里插入图片描述
这里发现一个问题,B类的order 是0,怎么会在最后?实际是因为B类被认为是没有实现排序的BeanPostProcessor,也就是 Order(0) 注解在这里没用!!!这也就回答了第四个问题。
在这里插入图片描述

解释第三个问题,PriorityOrdered 优先级高于Ordered,因为代码是先处理的PriorityOrdered 的实现
在这里插入图片描述

最后对于大模型对BeanPostProcessor的介绍中的第五点

5. 排序和优先级
如果在Spring配置中定义了多个BeanPostProcessor,那么默认情况下,Spring容器会根据它们的定义顺序来依次调用。
但是,你可以通过实现Ordered接口或使用@Order、@Priority注解来指定优先级。

是错误的!!!目前只能通过实现Ordered或者PriorityOrdered接口

通过上面的实例测试@Order并没有起作用,@Priority 注解也没法在类上使用,查了一下该注解在javax.annotation-api中。
在这里插入图片描述

作者其他文章推荐:
基于Spring Boot 3.1.0 系列文章

  1. Spring Boot 源码阅读初始化环境搭建
  2. Spring Boot 框架整体启动流程详解
  3. Spring Boot 系统初始化器详解
  4. Spring Boot 监听器详解
  5. Spring Boot banner详解
  6. Spring Boot 属性配置解析
  7. Spring Boot 属性加载原理解析
  8. Spring Boot 异常报告器解析
  9. Spring Boot 3.x 自动配置详解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三丰云免费服务器

云网址&#xff1a; https://www.sanfengyun.com 可申请免费云服务器&#xff0c;1核/1G内存/5M宽带/有公网IP/10G SSD硬盘/免备案。 收费云服务器&#xff0c;买2年送1年&#xff0c;有很多优惠

MAVEN架构项目管理工具(下)

1、maven工程约定目录结构 每一个maven在磁盘中都是一个文件夹&#xff08;即项目&#xff0c;以hello项目为例&#xff09; Hello/---/src------/main #放置主程序Java代码和配置文件-----------/java #程序的包和包中的文件-----------/resource #java程…

敏捷风险管理:识别和应对项目威胁与机会

项目风险是一种不确定的事件或条件&#xff0c;一旦发生&#xff0c;就会对项目目标造成积极或消极的影响。 现实中&#xff0c;风险可能是微妙而复杂的&#xff0c;缺少经验的人很难对其进行识别和管理。 敏捷风险管理是敏捷项目治理的基础。在敏捷环境下&#xff0c;敏捷风险…

Spring Boot 项目启动时在 prepareContext 阶段做了哪些事?

概览 如果你对Spring Boot 启动流程还不甚了解&#xff0c;可阅读《Spring Boot 启动流程详解》这篇文章。如果你已了解&#xff0c;那就让我们直接看看prepareContext() 源码。 private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironme…

Knife4j 全局鉴权需求 (在OpenAPI3规范中添加Authorization鉴权请求Header)

文章目录 引言I Knife4j 全局鉴权需求1.1 利用springdoc项目提供的customizer接口解决1.2 常见问题II 添加自定义Header参数(签名字段)see also引言 OpenAPI3规范对于Security的定义说明,主要分为两部分: 在compoents组件下定义Security的鉴权方案类型在接口级别的Operati…

工业机器人远程运维,增强智慧工厂运营管理

1、需求背景 随着工业自动化技术的普及和工业机器人应用的增加&#xff0c;制造业对于生产线稳定性和效率的要求不断提高。然而&#xff0c;传统的现场监控方式存在着地理位置限制、实时监控难度大以及诊断能力有限等问题&#xff0c;迫切需要一种更具灵活性和效率的监控方式。…

这可能是最清晰易懂的 G1 GC 资料

滑动验证页面 概述 G1 (Garbage-First) 于JDK 6u14版本发布&#xff0c;JDK 7u4版本发行时被正式推出&#xff0c;在JDK9时已经成了默认的垃圾回收器&#xff0c;算是CMS回收器的替代 方案&#xff08;CMS在JDK9以后已经废弃&#xff09; G1是一款分代的 (generational)&a…

AI漫画赛道,10分钟快速赚钱秘诀!

AI百宝箱-Chatgpt4.0、Midjourney绘画、人工智能绘画、AI换脸、AI图片放大、AI图片分析、AI图片融合https://h5.cxyhub.com/?invitationhmeEo7 先使用ChatGPT写小说 ComicAI 漫画小说生成网站 1. 创建小说漫画 2. 故事模板 3. 生成角色形…

基于spring boot+MySQL 小区物业管理系统-计算机毕设 附源码37236

spring boot 小区物业管理系统 摘 要 在网络信息的时代&#xff0c;众多的软件被开发出来&#xff0c;给用户带来了很大的选择余地&#xff0c;而且人们越来越追求更个性的需求。在这种时代背景下&#xff0c;小区物业只能以客户为导向&#xff0c;以产品的持续创新作为小区物…

音视频集式分布式拉流管理

一直以来&#xff0c;由于srs zlm等开源软件采用传统直播协议&#xff0c;即使后面实现了webrtc转发&#xff0c;由于信令交互较弱&#xff0c;使得传统的安防监控方案需要在公网云平台上部署大型流媒体服务器&#xff0c;而且节点资源不能统一管理调度&#xff0c;缺乏灵活性和…

问题:小石明知道上课玩手机会影响学习,但就是管不住自己。这说明小石缺乏()教育。 #职场发展#职场发展#经验分享

问题&#xff1a;小石明知道上课玩手机会影响学习&#xff0c;但就是管不住自己。这说明小石缺乏&#xff08;&#xff09;教育。 A.道德情感 B.道德意志 C.道德行为 D.道德认识 参考答案如图所示

B-6 Web应用程序文件包含-Server2233(环境+解析)

B-6 Web应用程序文件包含 任务环境说明:服务器场景名称:Server2233(关闭链接) 通过扫

C++ | Leetcode C++题解之第143题重排链表

题目&#xff1a; 题解&#xff1a; class Solution { public:void reorderList(ListNode* head) {if (head nullptr) {return;}ListNode* mid middleNode(head);ListNode* l1 head;ListNode* l2 mid->next;mid->next nullptr;l2 reverseList(l2);mergeList(l1, l…

Apple WWDC24的18条总结:从GPT-4o开始集成ChatGPT

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,所以创建了“AI信息Gap”这个公众号,专注于分享AI全维度知识,包括但不限于AI科普,AI工具测评,AI效率提升,AI行业洞察。关注我,AI之…

通过 CartPole 游戏详细说明 PPO 优化过程

CartPole 介绍 在一个光滑的轨道上有个推车&#xff0c;杆子垂直微置在推车上&#xff0c;随时有倒的风险。系统每次对推车施加向左或者向右的力&#xff0c;但我们的目标是让杆子保持直立。杆子保持直立的每个时间单位都会获得 1 的奖励。但是当杆子与垂直方向成 15 度以上的…

以太网基础知识(二)——PAM4NRZ

概念&#xff1a;PAM4&#xff08;Pulse Amplitude Modulation 4&#xff09;是用于将数字数据转换成模拟信号。在400G以太网中PAM4被用于将数字数据转换成光信号以便在光纤中传输。PAM4技术通过改变光信号的振幅和相位来表示4个不同的数字状态&#xff0c;每个状态代表2个比特…

46-4 等级保护 - 网络安全等级保护概述

一、网络安全等级保护概述 原文:没有网络安全就没有国家安全 二、网络安全法 - 安全立法 中华人民共和国主席令 第五十三号 《中华人民共和国网络安全法》已于2016年11月7日由中华人民共和国第十二届全国人民代表大会常务委员会第二十四次会议通过,并自2017年6月1日起正式…

问题:高炉风口前的燃烧带的边界是以()含量降为1%为界限标志的。 #媒体#媒体#笔记

问题&#xff1a;高炉风口前的燃烧带的边界是以()含量降为1%为界限标志的。 参考答案如图所示

RESTful API最佳实践:Python构建指南

目录 一、引言 二、RESTful API设计原则 三、Python构建RESTful API的技术栈 四、Flask构建RESTful API实践 安装Flask 定义路由和资源 处理HTTP方法 错误处理 数据验证和序列化 使用Flask扩展 五、最佳实践案例 七、结论 一、引言 在当今的软件开发领域&#…

C51学习归纳10 --- 单总线通信协议、DS18B20

通信协议是最重要的&#xff0c;我们之前学习了I2C通信协议&#xff0c;这一节我们学习一下新的通信协议&#xff0c;单总线通信。 一、开发板原理图 可以看出直接由P3_7口控制&#xff0c;但是遵循单总线协议。 单总线的电路要求 现在介绍单总线的通信协议细节&#xff1a; 1…