java-spring 图灵 04 doscan方法,重点是scanCandidateComponents方法

01.本次的重点依旧是扫描函数,这次是spring中的源码:

02.第一步,构造AnnotationConfigApplicationContext

主方法:

	public static void main(String[] args) {

		// 创建一个Spring容器
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

		UserService userService = (UserService) applicationContext.getBean("userService");


		userService.test();
		}

AnnotationConfigApplicationContext 的构造函数:(这个时候是有参)

	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		// 构造DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner
		this();
		register(componentClasses);
		refresh();
	}

this()是指的AnnotationConfigApplicationContext 的无参构造函数:

StartupStep 这个接口是用来记录 AnnotatedBeanDefinitionReader 这个函数运行的时间的

public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		// 额外会创建StandardEnvironment
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

03.来到了本次的重点类:ClassPathBeanDefinitionScanner类,这里依次执行方法

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		this(registry, true);
	}
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
		this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
	}
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment) {

		this(registry, useDefaultFilters, environment,
				(registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
	}
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		if (useDefaultFilters) {
			registerDefaultFilters();//重点
		}
		setEnvironment(environment);
		setResourceLoader(resourceLoader);
	}

注:
这个BeanDefinitionRegistry 是注册 是一个接口

public interface BeanDefinitionRegistry extends AliasRegistry {
}

他其实有一个很重要的实现类,也是DefaultListableBeanFactory ,在扫描中起作用的其实也是DefaultListableBeanFactory ,但是这里是只需要注册,所以,还是用接口比较准确

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
		
		}

最后一个的registerDefaultFilters:

	protected void registerDefaultFilters() {

		// 注册@Component对应的AnnotationTypeFilter
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));

		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();

		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}

		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
			logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

04.扫描scan:我也不知道怎么就到了这个地方

	public int scan(String... basePackages) {
	    //在容器中计算BeanDefinition的数量
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        //真正扫描的函数
		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}
        //得到本次的扫描的BeanDefinition的数量
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

05.scan中的doScan方法(重点)

//参数传入一组包名
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { 
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		
		//创建一个BeanDefinitionHolder的集合
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); 

        //循环这组包名
		for (String basePackage : basePackages) {
           //扫描之后得到一个包的全部BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            //循环这个集合
			for (BeanDefinition candidate : candidates) {
			    //获取BeanDefinition 中的Scope注解的value值
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
                //获取BeanDefinition 所代表的bean 的名字
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}

				// 检查Spring容器中是否已经存在该beanName
				if (checkCandidate(beanName, candidate)) {
				//用BeanDefinitionHolder 来包装 已经通过检查的BeanDefinition
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					//添加到BeanDefinitionHolder集合中
					beanDefinitions.add(definitionHolder);

					// 注册
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

06.findCandidateComponents方法: 扫描之后得到一个包的全部BeanDefinition

	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
	    //这个和简便扫描的索引文件有关,在MENF-INF的文件中的spring.components
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
		//一般都是这里的函数
			return scanCandidateComponents(basePackage);
		}
	}

07.scanCandidateComponents方法(重点重点重点重点重点):

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 获取basePackage下所有的文件资源  
			//ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX  是 classpath*:
			//在Spring框架中,可以使用org.springframework.core.io.support.ResourcePatternResolver接口的resolveBasePackage方法来将指定的基础包解析为用于包搜索路径的模式规范。
           //例如,如果基础包是com.example.app,则可以使用resolveBasePackage方法将其解析为类路径下的资源路径,如classpath:com/example/app/。
           //this.resourcePattern 是  **/*.class
           //classpath*:com.xxx.xxx//*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
		  //得到所有的class 的file对象
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
					//可读的话,读入元数据
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						// excludeFilters、includeFilters判断
						if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断
							//构造一个BeanDefinition
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							//
							sbd.setSource(resource);
							//判断是不是抽象类,接口
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}


注释:

ResourcePatternResolver接口:默认实现类是PathMatchingResourcePatternResolver类。

public interface ResourcePatternResolver extends ResourceLoader {

	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

	Resource[] getResources(String locationPattern) throws IOException;

}

Resource接口:Resource一般包括这些实现类:UrlResource、ClassPathResource、FileSystemResource、ServletContextResource、InputStreamResource、ByteArrayResource

public interface Resource extends InputStreamSource {
 
  boolean exists();
  
  default boolean isReadable() {
		return true;
	}
  
  default boolean isOpen() {//返回一个布尔值,指示此资源是否具有开放流的句柄
		return false;
	}
  
  default boolean isFile() {
		return false;
	}
  
  URL getURL() throws IOException;//返回一个URL句柄,如果资源不能够被解析为URL,将抛出IOException
  
  URI getURI() throws IOException;
  
  File getFile() throws IOException;//返回某个文件,如果资源不能够被解析称为绝对路径,将会抛出FileNotFoundException
  
  default ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}
  
  long contentLength() throws IOException;
  
  long lastModified() throws IOException;
  
  Resource createRelative(String relativePath) throws IOException;
  
	String getFilename();
  
  String getDescription();
}

这里的语句的结果:resources 数组是一个个的class 的file对象 具体的类是FileSystemResource

Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

在这里插入图片描述

08.isCandidateComponent方法:
通过传入的元数据,判断是不是这个类和容器中的已经扫描存在的excludeFilter是不是匹配,有的话,排除,不是一个bean

在@Component注解中,如果没有显示声明includeFilter,那么就会这个类的Component的includeFilter是被修饰类名

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}

		// 符合includeFilters的会进行条件匹配,通过了才是Bean,也就是先看有没有@Component,再看是否符合@Conditional
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

09.isConditionMatch 进一步 查看是不是有@Conditional注解

	private boolean isConditionMatch(MetadataReader metadataReader) {
		if (this.conditionEvaluator == null) {
			this.conditionEvaluator =
					new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
		}
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}

10.shouldSkip方法:表示是不是要跳过,如果有@Conditional ,不要跳过

	public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
		return shouldSkip(metadata, null);
	}

shouldSkip

	public boolean  shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
	//判断有无Conditional  没有就跳过
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
			return false;
		}
//有的话,这块是编译原理的phase  
		if (phase == null) {
			if (metadata instanceof AnnotationMetadata &&
					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
			}
			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
		}
  }
//后面还有,但不是重点,会去执行那个实现接口的类,看一下条件是什么,成立的话,@Conditional修饰的类就会是一个bean

注释:@Conditional,参数是一个类
怎么使用@Conditional注解了?
01.先创建一个实现类,实现Condition接口:
这里写的是条件

public class User implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		try {
			context.getClassLoader().loadClass("com.zhouyu.service.OrderService");
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}
		return false;
	}
}

02.使用@Conditional注解

@Component
@Conditional(User.class)
public class UserService  {



}

11.回到scanCandidateComponents方法:

第7到10步,此时已经扫描完成了包,完成了判断通过,符合构造一个BeanDefinition 的条件,不匹配容器中excludeFilter,在includeFilter上有符合的,也完成了关于@Conditional判断

构造一个BeanDefinition :
ScannedGenericBeanDefinition 方法:

	public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
		Assert.notNull(metadataReader, "MetadataReader must not be null");

        //获取元数据
		this.metadata = metadataReader.getAnnotationMetadata();
		// 这里只是把className设置到BeanDefinition中
		
		setBeanClassName(this.metadata.getClassName());
		setResource(metadataReader.getResource());
	}
@Override
	public void setBeanClassName(@Nullable String beanClassName) {
		this.beanClass = beanClassName;
	}
@Override
	public void setResource(@Nullable Resource resource) {
		this.resource = resource;
	}

12.创建完成了,要判断isCandidateComponent方法,判断01.这个类是不是内部类,因为内部类也有class文件,内部类不能成为bean,02.是不是接口或者抽象类 ,这些都不能成为bean ,03.是抽象类,但是类中有一个注解@Lookup,可以成为bean

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

成功的话,把这个BeanDefinition加入数组中,结束了这个scanCandidateComponents方法

if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}

13.回到了doscan方法,这里获取的是Scope注解的值和beanname的值

for (BeanDefinition candidate : candidates) {
				//获取Scope注解的值
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
//获取beanname的值,在注解@Component中
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

14.其中的this.beanNameGenerator:

	private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;

AnnotationBeanNameGenerator类:

	public static final AnnotationBeanNameGenerator INSTANCE = new AnnotationBeanNameGenerator();

用到的方法:
AnnotationBeanNameGenerator的generateBeanName方法:

	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
	// 获取注解@Component的注解所指定的beanName
		if (definition instanceof AnnotatedBeanDefinition) {
			
			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
			if (StringUtils.hasText(beanName)) {
				// 返回一个
				return beanName;
			}
		}
		// 返回一个默认的名字
		return buildDefaultBeanName(definition, registry);
	}

determineBeanNameFromAnnotation方法:返回注解中的指定的名字,如果没有返回一个null

@Nullable
	protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
	    //获得元数据
		AnnotationMetadata amd = annotatedDef.getMetadata();
		//获取元数据中的注解信息
		Set<String> types = amd.getAnnotationTypes();
		String beanName = null;
		for (String type : types) {
			AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
			if (attributes != null) {
				Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
					Set<String> result = amd.getMetaAnnotationTypes(key);
					return (result.isEmpty() ? Collections.emptySet() : result);
				});
				if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
				//获取value值
					Object value = attributes.get("value");
					if (value instanceof String) {
						String strVal = (String) value;
						if (StringUtils.hasLength(strVal)) {
							if (beanName != null && !strVal.equals(beanName)) {
								throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
										"component names: '" + beanName + "' versus '" + strVal + "'");
							}
							beanName = strVal;
						}
					}
				}
			}
		}
		return beanName;
	}

isStereotypeWithNameValue方法:
对注解@Component判断是不是有指定的value,有的话返回value


用到的常量
	private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";


	protected boolean isStereotypeWithNameValue(String annotationType,
			Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {

		boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
				metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
				annotationType.equals("javax.annotation.ManagedBean") ||
				annotationType.equals("javax.inject.Named");

		return (isStereotype && attributes != null && attributes.containsKey("value"));
	}

15.回到generateBeanName方法:
buildDefaultBeanName:如果没有指定value,那么就生成一个默认的名字

	protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		return buildDefaultBeanName(definition);
	}
	protected String buildDefaultBeanName(BeanDefinition definition) {
		String beanClassName = definition.getBeanClassName();
		Assert.state(beanClassName != null, "No bean class name set");
		String shortClassName = ClassUtils.getShortName(beanClassName);
		return Introspector.decapitalize(shortClassName);
	}

这句话回生成一个默认的名字:

Introspector.decapitalize(shortClassName);
  public static String decapitalize(String name) {
        if (name == null || name.length() == 0) {
            return name;
        }
        if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
                        Character.isUpperCase(name.charAt(0))){
            return name;
        }
        char[] chars = name.toCharArray();
        chars[0] = Character.toLowerCase(chars[0]);
        return new String(chars);
    }

16.再回到doscan方法:
这一步设置一些默认的值

if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
		// 设置BeanDefinition的默认值
		beanDefinition.applyDefaults(this.beanDefinitionDefaults);

		// AutowireCandidate表示某个Bean能否被用来做依赖注入
		if (this.autowireCandidatePatterns != null) {
			beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
		}
	}

具体设置那些

public void applyDefaults(BeanDefinitionDefaults defaults) {
		Boolean lazyInit = defaults.getLazyInit();
		if (lazyInit != null) {
			setLazyInit(lazyInit);
		}
		setAutowireMode(defaults.getAutowireMode());
		setDependencyCheck(defaults.getDependencyCheck());
		setInitMethodName(defaults.getInitMethodName());
		setEnforceInitMethod(false);
		setDestroyMethodName(defaults.getDestroyMethodName());
		setEnforceDestroyMethod(false);
	}

17.一些其他的注解

if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}

	public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
		processCommonDefinitionAnnotations(abd, abd.getMetadata());
	}
	static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
		else if (abd.getMetadata() != metadata) {
			lazy = attributesFor(abd.getMetadata(), Lazy.class);
			if (lazy != null) {
				abd.setLazyInit(lazy.getBoolean("value"));
			}
		}

		if (metadata.isAnnotated(Primary.class.getName())) {
			abd.setPrimary(true);
		}
		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
		if (dependsOn != null) {
			abd.setDependsOn(dependsOn.getStringArray("value"));
		}

		AnnotationAttributes role = attributesFor(metadata, Role.class);
		if (role != null) {
			abd.setRole(role.getNumber("value").intValue());
		}
		AnnotationAttributes description = attributesFor(metadata, Description.class);
		if (description != null) {
			abd.setDescription(description.getString("value"));
		}
	}

18.再回到doscan方法:
最后一步,判断是不是已经存在bean

// 检查Spring容器中是否已经存在该beanName
				if (checkCandidate(beanName, candidate)) {
				//没有的话
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);

					// 注册
					registerBeanDefinition(definitionHolder, this.registry);
				}
	protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
		if (!this.registry.containsBeanDefinition(beanName)) {
		//如果没有的话,就直接返回ture,容器中没有相同的beanName
			return true;
		}
		BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
		BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
		if (originatingDef != null) {
			existingDef = originatingDef;
		}
		// 是否兼容,如果兼容返回false表示不会重新注册到Spring容器中,如果不冲突则会抛异常。
		if (isCompatible(beanDefinition, existingDef)) {
			return false;
		}
		throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
				"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
				"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
	}

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

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

相关文章

我们一起看看《看漫画学C++》中如何讲解对象的动态创建与销毁

《看漫画学C》这本书中会用图文的方式生动地解释对象的动态创建与销毁。在C中&#xff0c;动态创建对象是通过new运算符来实现的&#xff0c;而销毁对象则是通过delete运算符来完成的。这种方式可以让程序在需要时分配内存给对象&#xff0c;并在对象不再需要时释放内存&#x…

MambaDFuse:一种基于mamba的多模态图像融合双相位模型

MambaDFuse:一种基于mamba的多模态图像融合双相位模型 摘要IntroductionRelated WorksMethodComparison with SOTA methodsAblation StudyDownstream IVF applications Conclusion 摘要 多模态图像融合&#xff08;MMIF&#xff09;旨在将来自不同模态的互补信息整合到单一的融…

(四)相关性分析 学习简要笔记 #统计学 #CDA学习打卡

目录 一. 相关性分析简介 二. 相关性分析方法 1&#xff09;连续型变量vs连续型变量&#xff1a;Pearson/Spearman &#xff08;a&#xff09;Pearson &#xff08;b&#xff09;Spearman等级相关系数 2&#xff09;二分类变量&#xff08;自然&#xff09;vs连续型变量&…

【C++干货基地】面向对象核心概念 const成员函数 | 初始化列表 | explicit关键字 | 取地址重载

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引入 哈喽各位铁汁们好啊&#xff0c;我是博主鸽芷咕《C干货基地》是由我的襄阳家乡零食基地有感而发&#xff0c;不知道各位的…

前端从零到一搭建脚手架并发布到npm

这里写自定义目录标题 一、为什么需要脚手架&#xff1f;二、前置-第三方工具的使用1. 创建demo并运行-4步新建文件夹 zyfcli&#xff0c;并初始化npm init -y配置入口文件 2.commander-命令行指令3. chalk-命令行美化工具4. inquirer-命令行交互工具5. figlet-艺术字6. ora-lo…

Oracle数据库的简单使用

Oracle简单使用 一、数据库的介绍二、Oracle介绍账号管理Oracle的安装Oracle服务的作用OracleRemExecService服务创建数据库 常用命令 三、SQL语言SQL分类实用的数据表添加注释数据操纵语言&#xff08;DML&#xff09;查询语句&#xff08;SELECT&#xff09;wherelikedistinc…

ShardingSphere:强大的分布式数据库中间件【图文】

ShardingSphere的诞生 ShardingSphere的结构 Sharding-JDBC :它提供了一个轻量级的 Java 框架&#xff0c;在 Java 的 JDBC 层提供额外的服务。使用客户端直连数据库&#xff0c;以 jar 包形式提供服务&#xff0c;无需额外部署和依赖&#xff0c;可理解为增强版的 JDBC 驱动&…

如何使用 Cloudflare 和 Mailgun 设置自定义电子邮件

作为一名软件工程师&#xff0c;您可能考虑拥有一个专业的电子邮件账户&#xff0c;以及自己的网站&#xff0c;比如 “infoexample.com”. 但这可能会花费一定金额&#xff0c;您可能不愿意支付。 但您知道您可以免费做到吗&#xff1f;事实上&#xff0c;有一种方法可以做到…

error解决expression before ‘static‘

问题现象 报警如下 跳转到提示第125行&#xff0c;但是这行明显是没有问题的。 问题分析 经过排查可以看到&#xff0c;是120行的末尾\在S32DS编译器里面被认为是“接下一行”的意思&#xff0c;120行注释掉之后&#xff0c;后面的121行、122行、123行均被注释掉&#xff0c;…

得物sign参数逆向分析与Python算法还原

文章目录 1. 写在前面2. 接口分析3. 断点分析4. Python算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚…

bp神经网络拟合函数未知参数【源码+视频教程】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《复杂函数拟合案例分享》本专栏旨在提供 1.以案例的形式讲解各类复杂函数拟合的程序实现方法&#xff0c;并提供所有案例完整源码&#xff1b;2.…

Linux 操作系统非缓冲区的文件操作、时间编程

1、文件操作 1.1 基于缓冲区的文件操作 基于缓冲区的文件操作---高级Io 以f开头的是基于缓冲区的文件操作 printf是一个基于缓冲区的函数 输出条件&#xff1a; 1.程序正常运行 2.遇到换行\n也能输出 3.缓存区内存已满 1024大小 4.遇到fflush&#xff08;stdout&a…

Ansible-变量-迭代-jinja2模版

变量&#xff1a; 在ansible中&#xff0c;变量是用来存储和传递数据的容器。 这些变量可以包含各种类型的数据&#xff0c;列如数字、字符串、列表、字典。 可以增加ansible playbook的灵活性和重用性变量的使用&#xff1a; 声明&#xff1a;变量名值 引用&#xff1a;{{ 变量…

详细分析Java中的AuthRequest类(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 公共接口&#xff0c;定义了对第三方平台进行授权、登录、撤销授权和刷新 token 的操作 1. 基本知识 先看源码基本API接口&#xff1a; import me.zhyd.oauth.enums.AuthResponseStatus; import me.zhyd.oauth.exception.AuthExce…

提升测试效率,专业方案揭秘

提升测试效率是软件开发中一个永恒的主题&#xff0c;它不仅关乎项目能否按期完成&#xff0c;更影响着软件产品的质量与用户体验。随着敏捷开发、持续集成等方法论的普及&#xff0c;如何在有限的时间内进行高效、全面的测试成为了开发者和测试人员面临的挑战。 在传统模式中&…

Windows 平台上面管理服务器程式的高级 QoS 策略

在 Windows 平台上面&#xff0c;目前有两个办法来调整应用程式的 QoS 策略设置&#xff0c;一种是通过程式设置&#xff0c;一种是通过 “Windows 组策略控制”。 在阅读本文之前&#xff0c;您需要先查阅本人以下的几篇文献&#xff0c;作为前情提示&#xff1a; VC Windows…

数据质量与策略:解锁生成式AI潜力的关键步骤

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

每日一题(L2-011):玩转二叉树--建树+层序遍历

与L2-006近乎相同&#xff0c;先建树&#xff0c;然后遍历 #include<bits/stdc.h> using namespace std; int in[35]; int pre[35]; typedef struct Tree{int num;Tree* left;Tree* right; }T;T * build(int in1,int in2,int pre1,int pre2){T * tnew T;t->numpre[pr…

战姬物语部署

一.准备环境 #关闭seliunx和防火墙 setenforce 0 systemctl stop firewalld systemctl disable firewalld #配置源&#xff0c;并安装常用工 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo curl -o /etc/yum.repos.d/epel.repo …

Leetcode 86. 分隔链表

题目链接&#xff1a; 86. 分隔链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/partition-list/description/ 题目&#xff1a; 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出…
最新文章