Clion开发STM32之W5500系列(DNS服务封装)

概述

  1. 在w5500基础库中进行封装,通过域名的方式获取实际的ip地址
  2. 用于动态获取ntp的ip地址

DNS封装

头文件

/*******************************************************************************Copyright (c) [scl]。保留所有权利。******************************************************************************/
#ifndef F1XX_TEMPLATE_W5500_DNS_H
#define F1XX_TEMPLATE_W5500_DNS_H#include "socket.h"#define TYPE_A 1      /* Host address */
#define TYPE_NS 2      /* Name server */
#define TYPE_MD 3      /* Mail destination (obsolete) */
#define TYPE_MF 4      /* Mail forwarder (obsolete) */
#define TYPE_CNAME 5  /* Canonical name */
#define TYPE_SOA 6      /* Start of Authority */
#define TYPE_MB 7      /* Mailbox name (experimental) */
#define TYPE_MG 8      /* Mail group member (experimental) */
#define TYPE_MR 9      /* Mail rename name (experimental) */
#define TYPE_NULL 10  /* Null (experimental) */
#define TYPE_WKS 11      /* Well-known sockets */
#define TYPE_PTR 12      /* Pointer record */
#define TYPE_HINFO 13 /* Host information */
#define TYPE_MINFO 14 /* Mailbox information (experimental)*/
#define TYPE_MX 15      /* Mail exchanger */
#define TYPE_TXT 16      /* Text strings */
#define TYPE_ANY 255  /* Matches any type */
#define MAX_DNS_BUF_SIZE 256 /* maximum size of DNS buffer. *//*** @memberof delay_ms_cb 毫秒延迟回调* @memberof dns_server_ip: dns 服务ip* @memberof dns_port: dns 端口,默认53*/
struct dns_conf {void (*delay_ms_cb)(uint32_t ms);uint8_t dns_server_ip[4];uint16_t dns_port;
};void dns_config_set(struct dns_conf *cnf);
/**** @param s socket num* @param domain_name 服务域名* @param dst_ip [out] 保存解析的数据* @param wait_ms [in] 发送请求后等待时间* @return*/
bool do_dns(SOCKET s, uint8_t *domain_name, uint8_t *dst_ip, uint32_t wait_ms);#endif //F1XX_TEMPLATE_W5500_DNS_H

源文件

/*******************************************************************************Copyright (c) [scl]。保留所有权利。******************************************************************************/
#include "w5500_dns.h"#define DBG_ENABLE
#define DBG_SECTION_NAME "dns"
#define DBG_LEVEL DBG_INFO // DBG_LOG DBG_INFO DBG_WARNING DBG_ERROR#include "sys_dbg.h"/* Header for all domain messages */
struct dhdr {uint16_t id; /* Identification */uint8_t qr;  /* Query/Response */uint8_t opcode;uint8_t aa;     /* Authoratative answer */uint8_t tc;     /* Truncation */uint8_t rd;     /* Recursion desired */uint8_t ra;     /* Recursion available */uint8_t rcode; /* Response code */uint16_t qdcount; /* Question count */uint16_t ancount; /* Answer count */uint16_t nscount; /* Authority (name server) count */uint16_t arcount; /* Additional record count */
};static uint8_t DNS_BUF[MAX_DNS_BUF_SIZE] = {0};
static struct dhdr dhp = {0}; /*定义一个结构体用来包含报文头信息*/
static uint16_t MSG_ID = 0x1122;
struct dns_conf *cnf_ptr = NULL;static int dns_packet_build(uint16_t op, uint8_t *name, uint8_t *buf, uint16_t len);static bool parse_DNS_Response(struct dhdr *pdhdr, uint8_t *pbuf, uint8_t *dst_result_ip);static uint8_t *dns_question(uint8_t *msg, uint8_t *cp);static uint8_t *dns_answer(uint8_t *msg, uint8_t *cp, uint8_t *dst_result_ip);static int16_t parse_name(uint8_t *msg, uint8_t *compressed, uint16_t len);void dns_config_set(struct dns_conf *cnf) {cnf_ptr = cnf;
}/*** dns 解析* @param s* @param domain_name 域名* @return*/
bool do_dns(SOCKET s, uint8_t *domain_name, uint8_t *dst_ip, uint32_t wait_ms) {if (cnf_ptr == NULL) {LOG_E("dns_config_set not set");goto _exit_false;}if (udp_client_init(s, cnf_ptr->dns_port)) {uint16_t len = dns_packet_build(0, domain_name, DNS_BUF, MAX_DNS_BUF_SIZE);sendto(s, DNS_BUF, len, cnf_ptr->dns_server_ip, cnf_ptr->dns_port); /*发送DNS请求报文给DNS服务器*/for (int i = 0; i < wait_ms / 2; ++i) {len = w5500_socket_rx_size_read(s);if (len > 0) break;cnf_ptr->delay_ms_cb(2);}if (len > 0) {if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE;recvfrom_simple(s, DNS_BUF, len);if (parse_DNS_Response(&dhp, DNS_BUF, dst_ip)) {close(s);/*关闭socket*/return true;/*返回DNS解析成功域名信息*/} else {LOG_W("parse_DNS_Response err");}} else {LOG_D("w5500_socket_rx_size_read not rec data");}} else {LOG_W("udp_client_init error");}_exit_false:return false;
}static int dns_packet_build(uint16_t op, uint8_t *name, uint8_t *buf, uint16_t len) {uint8_t *cp = buf;uint8_t *cp1;uint8_t *dname;uint16_t p;uint16_t dlen;MSG_ID++;*(uint16_t *) &cp[0] = htons(MSG_ID);p = (op << 11) | 0x0100;*(uint16_t *) &cp[2] = htons(p);*(uint16_t *) &cp[4] = htons(1);*(uint16_t *) &cp[6] = htons(0);*(uint16_t *) &cp[8] = htons(0);*(uint16_t *) &cp[10] = htons(0);cp += sizeof(uint16_t) * 6;dname = name;dlen = strlen((char *) dname);for (;;) {/* Look for next dot */cp1 = (unsigned char *) strchr((char *) dname, '.');if (cp1) {/* More to come */len = cp1 - dname;} else {len = dlen; /* Last component */}*cp++ = len;   /* Write length of component */if (len == 0) break;strncpy((char *) cp, (char *) dname, len);/* Copy component up to (but not including) dot */cp += len;if (!cp1) {*cp++ = 0;                                                                                                                    /* Last one; write null and finish */break;}dname += len + 1;dlen -= len + 1;}*(uint16_t *) &cp[0] = htons(0x0001);    /* type */*(uint16_t *) &cp[2] = htons(0x0001);    /* class */cp += sizeof(uint16_t) * 2;return ((int) ((uint32_t) (cp) - (uint32_t) (buf)));}static bool parse_DNS_Response(struct dhdr *pdhdr, uint8_t *pbuf, uint8_t *dst_result_ip) {uint16_t tmp = 0, i = 0;uint8_t *msg = NULL, *cp = NULL;msg = pbuf;memset(pdhdr, 0, sizeof(struct dhdr));pdhdr->id = ntohs(*((uint16_t *) &msg[0]));tmp = ntohs(*((uint16_t *) &msg[2]));if (tmp & 0x8000)pdhdr->qr = 1;pdhdr->opcode = (tmp >> 11) & 0xf;if (tmp & 0x0400)pdhdr->aa = 1;if (tmp & 0x0200)pdhdr->tc = 1;if (tmp & 0x0100)pdhdr->rd = 1;if (tmp & 0x0080)pdhdr->ra = 1;pdhdr->rcode = tmp & 0xf;pdhdr->qdcount = ntohs(*((uint16_t *) &msg[4]));pdhdr->ancount = ntohs(*((uint16_t *) &msg[6]));pdhdr->nscount = ntohs(*((uint16_t *) &msg[8]));pdhdr->arcount = ntohs(*((uint16_t *) &msg[10]));/* Now parse the variable length sections */cp = &msg[12];/* Question section */for (i = 0; i < pdhdr->qdcount; i++) {cp = dns_question(msg, cp);}/* Answer section */for (i = 0; i < pdhdr->ancount; i++) {cp = dns_answer(msg, cp, dst_result_ip);}/* Name server (authority) section */for (i = 0; i < pdhdr->nscount; i++) { ;}/* Additional section */for (i = 0; i < pdhdr->arcount; i++) { ;}if (pdhdr->rcode == 0)return 1;elsereturn 0;
}/**
*@brief		 解析回复消息的问询记录
*@param		 msg - 指向回复信息的指针cp  - 指向问询记录的指针
*@return	 返回下一个记录指针
*/
uint8_t *dns_question(uint8_t *msg, uint8_t *cp) {int16_t len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);if (len == -1)return 0;cp += len;cp += 2; /* type */cp += 2; /* class */return cp;
}static uint8_t *dns_answer(uint8_t *msg, uint8_t *cp, uint8_t *dst_result_ip) {int16_t len, type;len = parse_name(msg, cp, /*name,*/ MAX_DNS_BUF_SIZE);if (len == -1)return 0;cp += len;type = ntohs(*((uint16_t *) &cp[0]));cp += 2; /* type */cp += 2; /* class */cp += 4; /* ttl */cp += 2; /* len */switch (type) {case TYPE_A:dst_result_ip[0] = *cp++;dst_result_ip[1] = *cp++;dst_result_ip[2] = *cp++;dst_result_ip[3] = *cp++;break;case TYPE_CNAME:case TYPE_MB:case TYPE_MG:case TYPE_MR:case TYPE_NS:case TYPE_PTR:/* These types all consist of a single domain name *//* convert it to ascii format */len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);if (len == -1)return 0;cp += len;break;case TYPE_HINFO:len = *cp++;cp += len;len = *cp++;cp += len;break;case TYPE_MX:cp += 2;/* Get domain name of exchanger */len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);if (len == -1)return 0;cp += len;break;case TYPE_SOA:/* Get domain name of name server */len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);if (len == -1)return 0;cp += len;/* Get domain name of responsible person */len = parse_name(msg, cp, MAX_DNS_BUF_SIZE);if (len == -1)return 0;cp += len;cp += 4;cp += 4;cp += 4;cp += 4;cp += 4;break;case TYPE_TXT:/* Just stash */break;default:/* Ignore */break;}return cp;
}static int16_t parse_name(uint8_t *msg, uint8_t *compressed, uint16_t len) {static int8_t name[MAX_DNS_BUF_SIZE];uint16_t slen = 0; /* Length of current segment */uint8_t *cp = NULL;int16_t clen = 0;     /* Total length of compressed name */int16_t indirect = 0; /* Set if indirection encountered */int16_t nseg = 0;     /* Total number of segments in name */int8_t *buf = NULL;buf = name;cp = compressed;for (;;) {slen = *cp++; /* Length of this segment */if (!indirect)clen++;if ((slen & 0xc0) == 0xc0) {if (!indirect)clen++;indirect = 1;/* Follow indirection */cp = &msg[((slen & 0x3f) << 8) + *cp];slen = *cp++;}if (slen == 0) /* zero length == all done */break;len -= slen + 1;if (len <= 0)return -1;if (!indirect)clen += slen;while (slen-- != 0)*buf++ = (int8_t) *cp++;*buf++ = '.';nseg++;}if (nseg == 0) /* Root name; represent as single dot */{*buf++ = '.';len--;}*buf++ = '\0';len--;return clen; /* Length of compressed message */
}

测试

配置文件

/********************************************************************************  Copyright (c) [scl]。保留所有权利。*     本文仅供个人学习和研究使用,禁止用于商业用途。******************************************************************************/#include "app_conf.h"
#include "w5500_config.h"#if APP_CONFIG_W5500
#define DBG_ENABLE
#define DBG_SECTION_NAME "w5500"
#define DBG_LEVEL W5500_DBG_LEVEL#include "sys_dbg.h"
#include "w5500_dns.h"
#include "w5500_ntp.h"#define W5500_CS stm_port_define(B,12)
#define W5500_RST stm_port_define(C,7)extern struct net_date_time gb_app_time;static SPI_HandleTypeDef *w5500_spi = NULL;static void send_and_rec_bytes(uint8_t *in_dat, uint8_t *out_data, uint16_t len) {while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);HAL_SPI_TransmitReceive(w5500_spi, in_dat, out_data, len, 1000);while (HAL_SPI_GetState(w5500_spi) != HAL_SPI_STATE_READY);
}static void send_only(uint8_t *in_data, uint16_t len) {HAL_SPI_Transmit(w5500_spi, in_data, len, 1000);
}static void W5500_RST_HIGH(void) { stm_pin_high(W5500_RST); }static void W5500_RST_LOW(void) { stm_pin_low(W5500_RST); }static void W5500_CS_LOW(void) { stm_pin_low(W5500_CS); }static void W5500_CS_HIGH(void) { stm_pin_high(W5500_CS); }static void W5500_Driver_MspInit(void) {stm32_pin_mode(W5500_CS, pin_mode_output);  /*CS*/stm32_pin_mode(W5500_RST, pin_mode_output); /*RST*/stm_pin_low(W5500_RST);stm_pin_low(W5500_CS);/*初始化SPI外设*//*W5500 支持 SPI 模式 0 及模式 3..MOSI 和 MISO 信号无论是接收或发送,均遵从从最高标志位(MSB)到最低标志位(LSB)的传输序列。*/bsp_SpiHandleInit(w5500_spi, SPI_BAUDRATEPRESCALER_2, spi_mode_3);
}module_w5500_t w5500_conf = {.base_conf={.socket_num = 4,.rx_size={4, 4, 4, 4},.tx_size={4, 4, 4, 4},},.net_conf={.ip={192, 168, 199, 12},.gw={192, 168, 199, 1},.sub={255, 255, 255, 0},},.driver={.cs_high = W5500_CS_HIGH,.cs_low = W5500_CS_LOW,.rst_high= W5500_RST_HIGH,.rst_low=W5500_RST_LOW,.delay = HAL_Delay,.send_and_rec_bytes = send_and_rec_bytes,.send_only =send_only},.api = {.msp_init=W5500_Driver_MspInit,}
};
uint8_t ntp_domain[] = {"ntp.ntsc.ac.cn"}; /*ntp域名*/
static struct dns_conf net_dns_cnf = { /*dns服务配置*/.dns_server_ip={114, 114, 114, 114},.dns_port = 53,.delay_ms_cb = HAL_Delay
};
static struct ntp_conf net_ntp_conf = {
//        .ntp_server={114, 118, 7, 163},.ntp_port = 123,.delay_ms_cb = HAL_Delay
};static void w5500_pre_init(void) {/*一般做数据加载,此时系统时钟使用的是内部时钟,如需要使用系统时钟的外设不在此进行初始化*/w5500_spi = conv_spi_handle_ptr(handle_get_by_id(spi2_id));/*初始化资源*/module_w5500_init(&w5500_conf);uint32_t uid0 = HAL_GetUIDw0();uint32_t uid1 = HAL_GetUIDw1();uint32_t uid2 = HAL_GetUIDw2();uint8_t mac[6] = {0, uid0 >> 8, uid1, uid1 >> 8, uid2, uid2 >> 8};memcpy(w5500_conf.net_conf.mac, mac, sizeof(mac));}static void w5500_init(void) {w5500_conf.api.msp_init();/*初始化*/w5500_conf.net_conf_init();uint8_t ip[4];w5500_reg_ip_read(ip);LOG_D("w5500_reg_ip_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);w5500_reg_gw_read(ip);LOG_D("w5500_reg_gw_read:%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}static void w5500_after_init(void) {uint8_t try_cnt = 3;// 尝试次数SOCKET sn = 1;/*使用的socket*/
#if APP_CONFIG_W5500_DNS // 启用dnsfor (int i = 0; i < try_cnt; ++i) {dns_config_set(&net_dns_cnf);if (do_dns(sn, ntp_domain, net_ntp_conf.ntp_server, 500)) {LOG_D("dns parse result ip:%d.%d.%d.%d",net_ntp_conf.ntp_server[0], net_ntp_conf.ntp_server[1],net_ntp_conf.ntp_server[2], net_ntp_conf.ntp_server[3]);goto next_step; /*解析成功跳转下一步*/}}LOG_D("do dns try cnt over");return;next_step:#endif#if APP_CONFIG_W5500_NTP // 启用ntpntp_config_set(&net_ntp_conf);for (int i = 0; i < try_cnt; ++i) {if (ntp_date_time_get(sn, 500, &gb_app_time)) {HAL_TIM_Base_Start_IT(handle_get_by_id(tim6_id));goto exit_ok;}}LOG_W("ntp_date_time_get time out");return;exit_ok:LOG_D("NTP TIME:%d-%02d-%02d %02d:%02d:%02d",gb_app_time.year, gb_app_time.month, gb_app_time.day,gb_app_time.hour, gb_app_time.min, gb_app_time.sec);
#elsereturn;
#endif
}app_init_export(w5500_net_conf, w5500_pre_init, w5500_init, w5500_after_init);
#endif

结果

在这里插入图片描述

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

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

相关文章

7.26作业

用fread和fwrite实现文件拷贝 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<head.h> int main(int argc, const char *argv[]) {FILE* fpfopen("./11.txt","w");FILE* fp1fopen("./12.txt",&quo…

idea的Plugins中搜索不到插件

1、ctrlalts 打开设置 ; 2、搜索框输入plugins; 3、点击plugins; 4、点齿轮按钮&#xff0c;选择HTTP Proxy settings; 如下操作&#xff1a; 5、刷新DNS&#xff0c;ipconfig /flushdns 6、重新打开idea 的plugins 插件列表出来了

无涯教程-jQuery - jQuery.ajax( options )方法函数

jQuery.ajax(options)方法使用HTTP请求加载远程页面。 $.ajax()返回它创建的XMLHttpRequest。在大多数情况下&#xff0c;您不需要该对象直接进行操作&#xff0c;但是如果您需要手动中止请求&#xff0c;则可以使用该对象。 jQuery.ajax( options ) - 语法 $.ajax( options…

51单片机--LCD1602

LCD1602的介绍 LCD1602是一种字符型液晶显示模块&#xff0c;通常用于嵌入式系统、单片机等领域。它由LCD&#xff08;液晶显示屏&#xff09;、HD44780控制驱动主电路及其扩展驱动电路、少量电阻、电容元件等组成。 LCD1602具有以下特点和功能&#xff1a; 显示能力&#xf…

面试总结-Redis篇章(八)——Redis分布式锁

JAVA 面试总结-Redis分布式锁 模拟抢券场景 通过下面方法添加Synchronized锁来防止上述情况&#xff0c;如果上面是单体服务没有问题&#xff0c;但是如果项目是集群部署&#xff0c;会出现下面的问题&#xff0c;因为Synchronized是属于本地的锁端口8080和8081同时访问&#x…

Qt 第二讲 登录框完善,登录成功后,进入新的界面;新建工程文件,默认提供的代码注释信息;前两讲思维导图

一&#xff0c;代码完善 头文件 #ifndef ZUOYE_H #define ZUOYE_H#include <QWidget> #include <QDebug> #include <QIcon> #include <QPushButton> #include <QLineEdit> #include <QLabel> //#include <QTextToSpeech>QT_BEGIN_…

⛳ 面向对象面试题

面向对象面试题目录 ⛳ 面向对象面试题&#x1f69c; 一&#xff0c;成员变量&#xff0c;局部变量&#xff0c;类变量存储在内存的什么地方&#xff1f;&#x1f43e; 1.1&#xff0c;类变量&#xff08;静态成员变量&#xff09;&#x1f4dd; 1.2&#xff0c;成员变量⭐ 1.3…

网络安全进阶学习第七课——文件包含漏洞

文章目录 一、文件包含概念二、文件包含漏洞原理三、文件包含分类文件包含分为两种&#xff1a; 四、与文件包含相关的配置文件&#xff1a;&#xff08;php.ini文件&#xff09;五、与文件包含有关的函数1、include()2、include_once()3、require()4、require_once() 六、文件…

iTOP-STM32MP157开发板Linux Misc驱动-编译驱动程序

这里我们以 stm32mp157 开发板为例&#xff0c;将杂项设备驱动编译成模块&#xff0c;请参考本手册第三十七章 Linux 内核模块。我们将 misc.c 文件拷贝到 Ubuntu 的/home/nfs/03 目录下。将上次编译 helloworld 的 Makefile 文 件拷贝到 misc.c 同级目录下&#xff0c;修改 …

解决Unable to load authentication plugin ‘caching_sha2_password‘

连接Mysql8时报错&#xff1a; java.sql.SQLException: Unable to load authentication plugin caching_sha2_password.原因 不同版本的认证插件不同造成的 8版本前是&#xff1a;default_authentication_pluginmysql_native_password 8版本后是&#xff1a;default_authenti…

Postman抓包教程

目录 什么是抓包&#xff1f; 如何使用 Postman 进行抓包 查看历史抓包数据 使用抓包数据进行接口测试和开发 抓包技巧和注意事项 什么是抓包&#xff1f; 在计算机网络中&#xff0c;抓包是指捕获网络流量的过程。抓包工具可以截获进出计算机网络的数据流&#xff0c;并将…

Python numpy库的应用、matplotlib绘图、opencv的应用

numpy import numpy as npl1 [1, 2, 3, 4, 5]# array():将列表同构成一个numpy的数组 l2 np.array(l1) print(type(l2)) print(l2) # ndim : 返回数组的轴数&#xff08;维度数&#xff09; # shape&#xff1a;返回数组的形状&#xff0c;用元组表示&#xff1b;元组的元素…

SpringCloudAlibaba微服务实战系列(五)Sentinel1.8.5+Nacos持久化

Sentinel数据持久化 前面介绍Sentinel的流控、熔断降级等功能&#xff0c;同时Sentinel应用也在面临着一个问题&#xff1a;我们在Sentinel后台管理界面中配置了一堆流控、降级规则&#xff0c;但是Sentinel一重启&#xff0c;这些规则全部消失了。那么我们就要考虑Sentinel的持…

1、传统锁回顾(Jvm本地锁,MySQL悲观锁、乐观锁)

目录 1.1 从减库存聊起1.2 环境准备1.3 简单实现减库存1.4 演示超卖现象1.5 jvm锁1.6 三种情况导致Jvm本地锁失效1、多例模式下&#xff0c;Jvm本地锁失效2、Spring的事务导致Jvm本地锁失效3、集群部署导致Jvm本地锁失效 1.7 mysql锁演示1.7.1、一个sql1.7.2、悲观锁1.7.3、乐观…

深度学习:常用优化器Optimizer简介

深度学习&#xff1a;常用优化器Optimizer简介 随机梯度下降SGD带动量的随机梯度下降SGD-MomentumSGDWAdamAdamW 随机梯度下降SGD 梯度下降算法是使权重参数沿着整个训练集的梯度方向下降&#xff0c;但往往深度学习的训练集规模很大&#xff0c;计算整个训练集的梯度需要很大…

集睿致远推出CS5466多功能拓展坞方案:支持DP1.4、HDMI2.1视频8K输出

ASL新推出的 CS5466是一款Type-C/DP1.4转HDMI2.1的显示协议转换芯片,&#xff0c;它通过类型C/显示端口链路接收视频和音 频流&#xff0c;并转换为支持TMDS或FRL输出信令。DP接收器支持81.Gbp s链路速率。HDMI输出端口可以作为TMDS或FRL发射机工作。FRL发射机符合HDMI 2.1规范…

OpenCvSharp (C# OpenCV) 二维码畸变矫正--基于透视变换(附源码)

导读 本文主要介绍如何使用OpenCvSharp中的透视变换来实现二维码的畸变矫正。 由于CSDN文章中贴二维码会导致显示失败,大家可以直接点下面链接查看图片: C# OpenCV实现二维码畸变矫正--基于透视变换 (详细步骤 + 代码) 实现步骤 讲解实现步骤之前先看下效果(左边是原图,右边…

菜鸡shader:L13 渐隐渐显的UV动画

文章目录 SD部分Shader代码部分 呃呃&#xff0c;这是目前我学习庄懂AP01课程的最后一节了&#xff0c;我看了一下21集之后的内容是关于LightingMap&#xff0c;目前感觉还用不到&#xff0c;加上之前参与过一个项目里面也有用到LightingMap&#xff0c;也算了解过&#xff0c;…

拦截Bean使用之前各个时机的Spring组件

拦截Bean使用之前各个时机的Spring组件 之前使用过的BeanPostProcessor就是在Bean实例化之后&#xff0c;注入属性值之前的时机。 Spring Bean的生命周期本次演示的是在Bean实例化之前的时机&#xff0c;使用BeanFactoryPostProcessor进行验证&#xff0c;以及在加载Bean之前进…

NISP含金量?NISP真的有必要考么?NISP好考吗?NISP二级为什么那么贵?

NISP证书简述 NISP证书三个级别&#xff0c;分别是&#xff1a;一级、二级、三级&#xff08;专项&#xff09; 证书。其每一项资格证书都有不同的优点&#xff0c;但是优点各有 相同&#xff0c;而且拥有NISP二级证书可以免考更换CISP资格证书&#xff0c;那么证书含金量如何下…