手写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接口实现类的相…...

<OpenCV> Mat属性
OpenCV的图像数据类型可参考之前的博客:https://blog.csdn.net/thisiszdy/article/details/120238017 OpenCV-Mat类型的部分属性如下: size:矩阵的大小, s i z e ( c o l s , r o w s ) size(cols,rows) size(cols,rows)…...

LAMP 综合实验
LAMP 综合实验 一.实验目标 实验目标如下: 实现 LAMP 架构 实现数据库主从复制 实现 NFS 服务器存储 wordpress 文件 实现备份服务器实时备份 NFS 服务器文件 实现日志集中存储 实现 loganalyzer 分析展示日志 二.实验准备 2.1 实验环境 实验环境: 虚拟机版本: VM…...

JavaScript发展历程
目录 一、起源(1995-1997) 二、发展(1997-2005) 三、进化——Ajax与Web 2.0(2005-2010年) 四、移动互联网与现代化(2010年至今) 结论 JavaScript是一种广泛使用的网络编程语言&…...

LP(六十九)智能文档助手升级
本文在笔者之前研发的大模型智能文档问答项目中,开发更进一步,支持多种类型文档和URL链接,支持多种大模型接入,且使用更方便、高效。 项目介绍 在文章NLP(六十一)使用Baichuan-13B-Chat模型构建智能文档中…...

VIM统计搜索关键词命令
:%s/./&/gn 统计字符数 :%s/\i\/&/gn 统计单词数 :%s/^//n 统计行数 :%s/keyword/&/g 统计任何地方出现的 "keyword" :%s/keyword/&/gn 统计任何地方出现的 "keyword" :%s/keyword/:这部分是 Vi…...

0017Java程序设计-spr农业过程化管理系统
摘 要目 录系统设计开发环境 摘 要 本农业过程化管理系统就是建立在充分利用现在完善科技技术这个理念基础之上,并使用IT技术进行对农业过程化的管理,从而保证种植户能种植出优质的农作物,可以实现农业过程化的在线管理,这样保证…...

以可视化方式解释 Go 并发 - 通道
在并发编程中,许多编程语言采用共享内存/状态模型。然而,Go 通过实现 通信顺序进程 (CSP) 区别于众多语言。在 CSP 中,一个程序由并行的进程组成,这些进程不共享状态,而是使用通道进行通信和同步它们的操作。因此&…...

kafka学习-生产者
目录 1、消息生产流程 2、生产者常见参数配置 3、序列化器 基本概念 自定义序列化器 4、分区器 默认分区规则 自定义分区器 5、生产者拦截器 作用 自定义拦截器 6、生产者原理解析 1、消息生产流程 2、生产者常见参数配置 3、序列化器 基本概念 在Kafka中保存的数…...

【Python】设计模式
设计模式分为三种类型,共23类。 创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。行为型模式:模版方法模式、命令模…...

C++ 数字
C 数字 通常,当我们需要用到数字时,我们会使用原始的数据类型,如 int、short、long、float 和 double 等等。这些用于数字的数据类型,其可能的值和数值范围,我们已经在 C 数据类型一章中讨论过。 C 定义数字 我们已…...