手写Spring:第14章-自动扫描Bean对象注册
文章目录
- 一、目标:自动扫描Bean对象注册
- 二、设计:自动扫描Bean对象注册
- 三、实现:自动扫描Bean对象注册
- 3.0 引入依赖
- 3.1 工程结构
- 3.2 Bean生命周期中自动加载包扫描注册Bean对象和设置占位符属性类图
- 3.3 主力占位符配置
- 3.4 定义拦截注解
- 3.4.1 定义拦截注解
- 3.4.2 定义注册注解
- 3.5 处理对象扫描装配
- 3.5.1 类路径扫描装配提供者类
- 3.5.2 类路径扫描装配实现类
- 3.6 解析xml中调用扫描
- 四、测试:自动扫描Bean对象注册
- 4.1 添加测试配置
- 4.1.1 用户服务层实现类
- 4.1.2 属性配置文件
- 4.1.3 Spring属性配置文件
- 4.1.4 Spring扫描配置文件
- 4.2 单元测试
- 4.2.1 占位符测试
- 4.2.2 包扫描测试
- 五、总结:自动扫描Bean对象注册
一、目标:自动扫描Bean对象注册
💡 怎么完成自动化扫描Bean对象,自动注册填充?
- 早期的 Spring 版本,需要一个一个在 spring.xml 中进行配置。
- 现在的 Spring 版本,在核心功能逻辑上建设的更少的配置下,做到更简化的使用。包括:包的扫描注册、注解配置的使用、占位符属性的填充等。
- 目标:在目前的核心逻辑上填充一些自动化的功能。
二、设计:自动扫描Bean对象注册
💡 怎么简化 Bean 对象的配置?怎么让整个 Bean 对象的注册都是自动扫描的?
- 需要的元素包括:扫描路径入口、XML 解析扫描信息、给需要扫描的 Bean 对象做注解标记、扫描 Class 对象摘取 Bean 注册的基本信息、组装注册信息、注册成 Bean 对象。
- 在这些条件元素的支撑下,就可以实现出自定义注解和配置扫描路径的情况下,完成 Bean 对象的注册。
- 除此之外再可以解决一个配置中占位符属性的知识点,
- 比如:
${token}
给 Bean 对象注入进去属性信息,那么就需要用到BeanFactoryPostProcessor
。 - 因为它可以处理 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制。
- 而实现这部分内容是为了后续把此类内容结合到自动化配置处理中。
- 比如:
- 结合 Bean 的生命周期,包扫描只不过是扫描特定注解的类,提取类的相关信息组装成
BeanDefinition
注册到容器中。 - 在
XmlBeanDefinitionReader
中解析<context component-scan />
标签,扫描类组装BeanDefinition
然后注册到容器中的操作在ClassPathBeanDefinitionScanner#doScan
中实现。- 自动扫描注册:主要是扫描添加了自定义注解的类,在 xml 加载过程中提取类的信息,组装 BeanDefinition 注册到 Spring 容器中。
- 需要用到
<context component-scan />
:配置包路径并在XmlBeanDefinitionReader
解析并做相应的处理(对类的扫描、获取注解信息等)。 - 最后包括了
BeanFactoryPostProcessor
的使用。因为我们需要完成对占位符配置信息的加载,所以需要使用到BeanFactoryPostProcessor
在所有的BeanDefinition
加载完成后,实例化 Bean 对象之前,修改BeanDefinition
的属性信息。
三、实现:自动扫描Bean对象注册
3.0 引入依赖
pom.xml
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version>
</dependency>
3.1 工程结构
spring-step-12
|-src|-main| |-java| |-com.lino.springframework| |-aop| | |-aspectj| | | |-AspectJExpressionPointcut.java| | | |-AspectJExpressionPointcutAdvisor.java| | |-framework| | | |-adapter| | | | |-MethodBeforeAdviceInterceptor.java| | | |-autoproxy| | | | |-DefaultAdvisorAutoProxyCreator.java| | | |-AopProxy.java| | | |-Cglib2AopProxy.java| | | |-JdkDynamicAopProxy.java| | | |-ProxyFactory.java| | | |-ReflectiveMethodInvocation.java| | |-AdvisedSupport.java| | |-Advisor.java| | |-BeforeAdvice.java| | |-ClassFilter.java| | |-MethodBeforeAdvice.java| | |-MethodMatcher.java| | |-Pointcut.java| | |-PointcutAdvisor.java| | |-TargetSource.java| |-beans| | |-factory| | | |-config| | | | |-AutowireCapableBeanFactory.java| | | | |-BeanDefinition.java| | | | |-BeanFactoryPostProcessor.java| | | | |-BeanPostProcessor.java| | | | |-BeanReference.java| | | | |-ConfigurableBeanFactory.java| | | | |-InstantiationAwareBeanPostProcessor.java| | | | |-SingletonBeanRegistry.java| | | |-support| | | | |-AbstractAutowireCapableBeanFactory.java| | | | |-AbstractBeabDefinitionReader.java| | | | |-AbstractBeabFactory.java| | | | |-BeabDefinitionReader.java| | | | |-BeanDefinitionRegistry.java| | | | |-CglibSubclassingInstantiationStrategy.java| | | | |-DefaultListableBeanFactory.java| | | | |-DefaultSingletonBeanRegistry.java| | | | |-DisposableBeanAdapter.java| | | | |-FactoryBeanRegistrySupport.java| | | | |-InstantiationStrategy.java| | | | |-SimpleInstantiationStrategy.java| | | |-xml| | | | |-XMLBeanDefinitionReader.java| | | |-Aware.java| | | |-BeanClassLoaderAware.java| | | |-BeanFactory.java| | | |-BeanFactoryAware.java| | | |-BeanNameAware.java| | | |-ConfigurableListableBeanFactory.java| | | |-DisposableBean.java| | | |-FactoryBean.java| | | |-HierarcgicalBeanFactory.java| | | |-InitializingBean.java| | | |-ListableBeanFactory.java| | | |-PropertyPlaceholderConfigurer.java| | |-BeansException.java| | |-PropertyValue.java| | |-PropertyValues.java| |-context| | |-annotation| | | |-ClassPathBeanDefinitionScanner.java| | | |-ClassPathScanningCandidateComponentProvider.java| | | |-Scope.java| | |-event| | | |-AbstractApplicationEventMulticaster.java| | | |-ApplicationContextEvent.java| | | |-ApplicationEventMulticaster.java| | | |-ContextclosedEvent.java| | | |-ContextRefreshedEvent.java| | | |-SimpleApplicationEventMulticaster.java| | |-support| | | |-AbstractApplicationContext.java| | | |-AbstractRefreshableApplicationContext.java| | | |-AbstractXmlApplicationContext.java| | | |-ApplicationContextAwareProcessor.java| | | |-ClassPathXmlApplicationContext.java| | |-ApplicationContext.java| | |-ApplicationContextAware.java| | |-ApplicationEvent.java| | |-ApplicationEventPublisher.java| | |-ApplicationListener.java| | |-ConfigurableApplicationContext.java| |-core.io| | |-ClassPathResource.java| | |-DefaultResourceLoader.java| | |-FileSystemResource.java| | |-Resource.java| | |-ResourceLoader.java| | |-UrlResource.java| |-stereotype| | |-Component.java| |-util| | |-ClassUtils.java|-test|-java|-com.lino.springframework.test|-bean| |-IUserService.java| |-UserService.java|-ApiTest.java|-resources|-spring-property.xml|-spring-scan.xml|-token.properties
3.2 Bean生命周期中自动加载包扫描注册Bean对象和设置占位符属性类图
- 整个类图看,主要包括的就是 xml 解析类
XmlBeanDefinitionReader
对ClassPathBeanDefinitionScanner#doScan
的使用。 - 在
doScan
方法中处理所有指定路径下添加了注解的类,拆解出类的信息:名称、作用范围等,进行创建BeanDefinition
好用于 Bean 对象的注册操作。 PropertyPlaceholderConfigurer
后续会与自动加载 Bean 对象进行整合,也就是可以在注解上使用占位符配置一些在配置文件里的属性信息。
3.3 主力占位符配置
PropertyPlaceholderConfigurer.java
package com.lino.springframework.beans.factory;import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.PropertyValues;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.lino.springframework.core.io.DefaultResourceLoader;
import com.lino.springframework.core.io.Resource;
import java.io.IOException;
import java.util.Properties;/*** @description: 处理占位符配置类*/
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {/*** 占位符前缀*/public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";/*** 占位符后缀*/public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";private String location;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 加载属性文件try {DefaultResourceLoader resourceLoader = new DefaultResourceLoader();Resource resource = resourceLoader.getResource(location);Properties properties = new Properties();properties.load(resource.getInputStream());String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanDefinitionNames) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);PropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {Object value = propertyValue.getValue();if (!(value instanceof String)) {continue;}String strVal = (String) value;StringBuilder buffer = new StringBuilder(strVal);int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {String propKey = strVal.substring(startIdx + 2, stopIdx);String propVal = properties.getProperty(propKey);buffer.replace(startIdx, stopIdx + 1, propVal);propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buffer.toString()));}}}} catch (IOException e) {throw new BeansException("Could not load properties", e);}}public void setLocation(String location) {this.location = location;}
}
- 依赖于
BeanFactoryPostProcessor
在 Bean 生命周期的属性,可以在 Bean 对象实例化之前,改变属性信息。- 所以这里通过实现
BeanFactoryPostProcessor
接口,完成对配置文件的加载以及摘取占位符在属性文件中的配置。
- 所以这里通过实现
- 这样可以把提取到的配置信息放置到属性信息中:
buffer.replace(startIdx, stopIdx + 1, propVal)
、propertyValues.addPropertyValue
3.4 定义拦截注解
3.4.1 定义拦截注解
Scope.java
package com.lino.springframework.context.annotation;import java.lang.annotation.*;/*** @description: 拦截注解*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {String value() default "singleton";
}
- 用于配置作用域的自定义注解,方便通过配置 Bean 对象注解的时候,拿到 Bean 对象的作用域。
3.4.2 定义注册注解
Component.java
package com.lino.springframework.stereotype;import java.lang.annotation.*;/*** @description: 注册注解*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {String value() default "";
}
- Component 自定义注解,用于配置到 Class 类上。
3.5 处理对象扫描装配
3.5.1 类路径扫描装配提供者类
ClassPathScanningCandidateComponentProvider.java
package com.lino.springframework.context.annotation;import cn.hutool.core.util.ClassUtil;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.stereotype.Component;
import java.util.LinkedHashSet;
import java.util.Set;/*** @description: 类路径扫描装配提供者*/
public class ClassPathScanningCandidateComponentProvider {public Set<BeanDefinition> findCandidateComponents(String basePackages) {Set<BeanDefinition> candidates = new LinkedHashSet<>();Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackages, Component.class);for (Class<?> clazz : classes) {candidates.add(new BeanDefinition(clazz));}return candidates;}
}
- 这里先要提供一个可以通过配置路径
beanPackage=com.lino.springframework.test.bean
,解析出classes
信息的工具方法findCandidateComponents
,通过这个方法就可以扫描到所有@Component
注解的 Bean 对象。
3.5.2 类路径扫描装配实现类
ClassPathBeanDefinitionScanner.java
package com.lino.springframework.context.annotation;import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.stereotype.Component;
import java.util.Set;/*** @description: 类路径扫描装配实现类*/
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {private BeanDefinitionRegistry registry;public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {this.registry = registry;}public void doScan(String... basePackages) {for (String basePackage : basePackages) {Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : candidates) {// 解析 bean 的作用域 singleton、prototypeString beanScope = resolveBeanScope(beanDefinition);if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);}}}private String resolveBeanScope(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();Scope scope = beanClass.getAnnotation(Scope.class);if (null != scope) {return scope.value();}return StrUtil.EMPTY;}private String determineBeanName(BeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();Component component = beanClass.getAnnotation(Component.class);String value = component.value();if (StrUtil.isEmpty(value)) {value = StrUtil.lowerFirst(beanClass.getSimpleName());}return value;}
}
ClassPathBeanDefinitionScanner
是继承自ClassPathScanningCandidateComponentProvider
的具体扫描包处理的类,在doScan
中获取到扫描的类信息后,还需要获取 Bean 的作用域和类名,如果不配置类名基本都是把首字母缩写。
3.6 解析xml中调用扫描
XmlBeanDefinitionReader.java
package com.lino.springframework.beans.factory.xml;import cn.hutool.core.util.StrUtil;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.PropertyValue;
import com.lino.springframework.beans.factory.config.BeanDefinition;
import com.lino.springframework.beans.factory.config.BeanReference;
import com.lino.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.lino.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.lino.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import com.lino.springframework.core.io.Resource;
import com.lino.springframework.core.io.ResourceLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;/*** @description: XML处理Bean注册*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {...@Overridepublic void loadBeanDefinitions(Resource resource) throws BeansException {try {try (InputStream inputStream = resource.getInputStream()) {doLoadBeanDefinitions(inputStream);}} catch (IOException | ClassNotFoundException | DocumentException e) {throw new BeansException("IOException parsing XML document from " + resource, e);}}...protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException {SAXReader reader = new SAXReader();Document document = reader.read(inputStream);Element root = document.getRootElement();// 解析 context:component-scan标签,扫描包中的类并提取相关信息,用于组装 BeanDefinitionElement componentScan = root.element("component-scan");if (null != componentScan) {String scanPath = componentScan.attributeValue("base-package");if (StrUtil.isEmpty(scanPath)) {throw new BeansException("The value of base-package attribute can not be empty or null");}scanPackage(scanPath);}List<Element> beanList = root.elements("bean");for (Element bean : beanList) {String id = bean.attributeValue("id");String name = bean.attributeValue("name");String className = bean.attributeValue("class");String initMethod = bean.attributeValue("init-method");String destroyMethodName = bean.attributeValue("destroy-method");String beanScope = bean.attributeValue("scope");// 获取 Class, 方便获取类中的名称Class<?> clazz = Class.forName(className);// 优先级 id > nameString beanName = StrUtil.isNotEmpty(id) ? id : name;if (StrUtil.isEmpty(beanName)) {beanName = StrUtil.lowerFirst(clazz.getSimpleName());}// 定义beanBeanDefinition beanDefinition = new BeanDefinition(clazz);beanDefinition.setInitMethodName(initMethod);beanDefinition.setDestroyMethodName(destroyMethodName);if (StrUtil.isNotEmpty(beanScope)) {beanDefinition.setScope(beanScope);}List<Element> propertyList = bean.elements("property");// 读取属性并填充for (Element property : propertyList) {// 解析标签:propertyString attrName = property.attributeValue("name");String attrValue = property.attributeValue("value");String attrRef = property.attributeValue("ref");// 获取属性值:引入对象、值对象Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;// 创建属性信息PropertyValue propertyValue = new PropertyValue(attrName, value);beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}if (getRegistry().containsBeanDefinition(beanName)) {throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");}// 注册 BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition);}}private void scanPackage(String scanPath) {String[] basePackages = StrUtil.splitToArray(scanPath, ',');ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());scanner.doScan(basePackages);}
}
XmlBeanDefinitionReader
主要是在加载配置文件后,处理新增的自定义配置属性component-scan
,解析后调用scanPackage
方法。- 其实就是
ClassPathBeanDefinitionScanner#doScan
方法。
- 其实就是
- 为了方便加载和解析 xml,
XmlBeanDefinitionReader
替换为dom4j
进行解析处理。
四、测试:自动扫描Bean对象注册
4.1 添加测试配置
4.1.1 用户服务层实现类
UserService.java
package com.lino.springframework.test.bean;import com.lino.springframework.stereotype.Component;import java.util.Random;/*** @description: 用户接口实现类*/
@Component("userService")
public class UserService implements IUserService {private String token;@Overridepublic String queryUserInfo() {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "张三,100001,杭州";}@Overridepublic String register(String userName) {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "注册用户:" + userName + " success!";}@Overridepublic String toString() {return "UserService#token = {" + token + "}";}public String getToken() {return token;}public void setToken(String token) {this.token = token;}
}
- 给
UserService
添加了一个自定义注解@Component("userService")
和一个属性信息String token
。
4.1.2 属性配置文件
token.properties
token=RejDlI78hu223Opo983Ds
- 这里配置一个
token
属性信息,通过占位符的方式进行获取。
4.1.3 Spring属性配置文件
spring-property.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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.lino.springframework.beans.factory.PropertyPlaceholderConfigurer"><property name="location" value="classpath:token.properties"/></bean><bean id="userService" class="com.lino.springframework.test.bean.UserService"><property name="token" value="${token}"/></bean></beans>
- 加载
classpath:token.properties
设置占位符属性值${token}
4.1.4 Spring扫描配置文件
spring-scan.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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context"><context:component-scan base-package="com.lino.springframework.test.bean"/></beans>
- 添加
component-scan
属性,设置包扫描根路径。
4.2 单元测试
4.2.1 占位符测试
ApiTest.java
@Test
public void test_property() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-property.xml");IUserService userService = applicationContext.getBean("userService", IUserService.class);System.out.println("测试结果:" + userService);
}
测试结果
测试结果:UserService#token = {RejDlI78hu223Opo983Ds}
- 通过测试结果看,
UserService
的token
属性已经通过占位符的方式设置进去配置文件里的token.properties
的属性值了。
4.2.2 包扫描测试
ApiTest.java
@Test
public void test_scan() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");IUserService userService = applicationContext.getBean("userService", IUserService.class);System.out.println("测试结果:" + userService.queryUserInfo());
}
测试结果
测试结果:张三,100001,杭州
- 测试结果看,现在使用注解的方式就可以让 Class 注册完成 Bean 对象了。
五、总结:自动扫描Bean对象注册
- 通过整篇的内容实现来看,目前的功能添加其实不是很复杂,都是在
IOC
、AOP
核心的基础上来补全功能。这些补全的功能也是在完善 Bean 的生命周期,让整个功能使用越来越容易。
相关文章:

手写Spring:第14章-自动扫描Bean对象注册
文章目录 一、目标:自动扫描Bean对象注册二、设计:自动扫描Bean对象注册三、实现:自动扫描Bean对象注册3.0 引入依赖3.1 工程结构3.2 Bean生命周期中自动加载包扫描注册Bean对象和设置占位符属性类图3.3 主力占位符配置3.4 定义拦截注解3.4.1…...
redux中间件的简单讲解
redux中间件 中间件的作用: 就是在 源数据 到 目标数据 中间做各种处理,有利于程序的可拓展性,通常情况下,一个中间件就是一个函数,且一个中间件最好只做一件事情 数据源 --------> 中间件 --------> 中间件 -…...

嵌入式开发-绪论
目录 一.什么是嵌入式 1.1硬件系统 1.2软件系统 二.嵌入式应用场景 2.1消费电子 2.1.1智能家居 2.1.2影音 2.1.3家用电器 2.1.4玩具游戏机 2.2通信领域 2.2.1对讲机 2.2.2手机 2.2.3卫星 2.2.4雷达 2.3控制领域 2.3.1机器人 2.3.2采集器PLC 2.4金融 2.4.1POS…...
大数据知识合集之预处理方法
数据预处理方法主要有: 数据清洗、数据集成、数据规约和数据变换。 1、数据清洗 数据清洗(data cleaning) :是通过填补缺失值、光滑噪声数据,平滑或删除离群点,纠正数据的不一致来达到清洗的目的。 缺失值处理 实际开发获取信…...

mysql(九)mysql主从复制
目录 前言概述提出问题主从复制的用途工作流程 主从复制的配置创建复制账号配置主库和从库启动主从复制从另一个服务器开始主从复制主从复制时推荐的配置sync_binloginnodb_flush_logs_at_trx_commitinnodb_support_xa1innodb_safe_binlog 主从复制的原理基于语句复制优点&…...

nodejs采集淘宝、天猫网商品详情数据以及解决_m_h5_tk令牌及sign签名验证(2023-09-09)
一、淘宝、天猫sign加密算法 淘宝、天猫对于h5的访问采用了和APP客户端不同的方式,由于在h5的js代码中保存appsercret具有较高的风险,mtop采用了随机分配令牌的方式,为每个访问端分配一个token,保存在用户的cookie中,通…...

虚拟机上部署K8S集群
虚拟机上部署K8S集群 安装VM Ware安装Docker安装K8S集群安装kubeadm使用kubeadm引导集群 安装VM Ware 参考:http://www.taodudu.cc/news/show-2034573.html?actiononClick 安装Docker 参考:https://www.yuque.com/leifengyang/oncloud/mbvigg#2ASxH …...

设计模式 - 责任链
一、前言 相信大家平时或多或少都间接接触过责任链设计模式,只是可能有些同学自己不知道此处用的是该设计模式,比如说 Java Web 中的 Filter 过滤器,就是非常经典的责任链设计模式的例子。 那么什么是责任链设计模式呢? …...

【小沐学Unity3d】3ds Max 骨骼动画制作(CAT、Character Studio、Biped、骨骼对象)
文章目录 1、简介2、 CAT2.1 加载 CATRig 预设库2.2 从头开始创建 CATRig 3、character studio3.1 基本描述3.2 Biped3.3 Physique 4、骨骼系统4.1 创建方法4.2 简单示例 结语 1、简介 官网地址: https://help.autodesk.com/view/3DSMAX/2018/CHS https://help.aut…...

CUDA说明和安装[window]
文章目录 1、查看版本信息查看GPU查看cuda版本其他方法 2区分 了解cudaCUDA ToolkitNVCCcuDNN 3/ 安装过程4/版本的问题CUDA Toolkit和 显卡驱动 的版本对应CUDA / CUDA Toolkit和cuDNN的版本对应 5/关于CUDA和Cudnn**5.1 CUDA的命名规则****5.2 如何查看自己所安装的CUDA的版本…...

sqlserver2012性能优化配置:设置性能相关的服务器参数
前言 sqlserver2012 长时间运行的话会将服务器的内存占满 解决办法 通过界面设置 下图中设置最大服务器内存 通过执行脚本设置 需要先开发开启高级选项配置才能设置成功 设置完成之后将高级选择配置关闭,还原成跟之前一样 --可以配置高级选项 EXEC sp_conf…...

介绍 dubbo-go 并在Mac上安装,完成一次自己定义的接口RPC调用
目录 RPC 远程调用的说明作用:像调用本地方法一样调用远程方法和直接HTTP调用的区别:调用模型图示: Dubbo 框架说明Dubbo Go 介绍应用 Dubbo Go环境安装(Mac 系统)安装 Go语言环境安装 序列化工具protoc安装 dubbogo-c…...
目标检测数据集:摄像头成像吸烟检测数据集(自己标注)
1.专栏介绍 ✨✨✨✨✨✨目标检测数据集✨✨✨✨✨✨ 本专栏提供各种场景的数据集,主要聚焦:工业缺陷检测数据集、小目标数据集、遥感数据集、红外小目标数据集,该专栏的数据集会在多个专栏进行验证,在多个数据集进行验证mAP涨点明显,尤其是小目标、遮挡物精度提升明显的…...

Unity的UI管理器
1、代码 public class UIManager {private static UIManager instance new UIManager();public static UIManager Instance > instance;//存储显示着的面板脚本(不是面板Gameobject),每显示一个面板就存入字典//隐藏的时候获取字典中对…...
Mp4文件提取详细H.264和MP3文件
文章目录 Mp4文件提取为H.264和MP3文件**提取视频为H.264:****提取音频为MP3:** 点赞收藏加关注,追求技术不迷路!!!欢迎评论区互动。 Mp4文件提取为H.264和MP3文件 要将视频分开为H.264(视频编…...

Qt应用程序连接达梦数据库-飞腾PC麒麟V10
目录 前言1 安装ODBC1.1 下载unixODBC源码1.2 编译安装1.4 测试 2 编译QODBC2.1 修改 qsqldriverbase.pri 文件2.2 修改 odbc.pro 文件2.3 编译并安装QODBC 3 Qt应用程序连接达梦数据库测试4 优化ODBC配置,方便程序部署4.1 修改pro文件,增加DESTDIR 变量…...

2023-09-03 LeetCode每日一题(消灭怪物的最大数量)
2023-09-03每日一题 一、题目编号 1921. 消灭怪物的最大数量二、题目链接 点击跳转到题目位置 三、题目描述 你正在玩一款电子游戏,在游戏中你需要保护城市免受怪物侵袭。给你一个 下标从 0 开始 且长度为 n 的整数数组 dist ,其中 dist[i] 是第 i …...
绘图 | MATLAB
目的语法注意事项图片中出现网格grid on放在plot后面在同一图片中绘制多个图例hold on在图形中添加图例legend LineSpec 线性 线型描述线型描述" - "实线" : "点线" - - "虚线" -. "点划线 标记 标记描述标记描述“o”圆圈“squa…...

2023年下半年高项考试学习计划
之前总结 2023年上半年的考试,对于我自己,就是虎头蛇尾,也谈不上太过自信,好好学习了一段时间之后,也就是不再发博文,截止到2022年11月的时候,自己就算是放弃了,没有再主动学习。 结…...
SpringBoot中CommandLineRunner的使用
开发中,你有没有遇到这样的场景,项目启动后,立即需要进行一些操作。比如:加载一些初始化数据、执行一段逻辑代码。你可以使用SpringBoot中CommandLineRunner。它可以在项目启动后,执行CommandLineRunner接口实现类的相…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...