Spring实例化源码解析之ComponentScanAnnotationParser(四)

上一章我们分析了ConfigurationClassParser,配置类的解析源码分析。在ComponentScans和ComponentScan注解修饰的候选配置类的解析过程中,我们需要深入的了解一下ComponentScanAnnotationParser的parse执行流程,SpringBoot启动类为什么这么写,为什么可以不写ComponentScan注解也不需要配置扫描路径等,这些问题都将在本章中被一一分析出来。

回顾

ConfigurationClassParser的doProcessConfigurationClass方法中,涉及到对@ComponentScan的解析,之前我说过在这里Spring将会接管我们自定义的这些bean的定义信息。具体怎么接管的我们接下来就开始分析。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)throws IOException {
//省略部分代码// Process any @ComponentScan annotations// 我们的AopConfig候选配置类就会走到下面这个逻辑Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// componentScans不为空,第一个条件满足// sourceClass.getMetadata()不为null;通过Conditional注解来控制bean是否需要注册,控制被@Configuration标注的配置类是否需要被解析 第二个条件false,取反。if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediately// 使用this.componentScanParser ComponentScanAnnotationParser来解析// 这里面将会注册我们自己写的一些将被spring接管的类的BeanDefinition信息Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}

本次源码分析的入口就在上述方法中的this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName())方法。

在这里插入图片描述

componentScan:第一个参数是注解的描述信息

sourceClass.getMetadata().getClassName():第二个参数候选配置类的名称

@ComponentScan

下面是该注解的所有属性,我们一般只是使用到value,或者Springboot写久了,这个注解都没再看过了。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {@AliasFor("basePackages")String[] value() default {};@AliasFor("value")String[] basePackages() default {};Class<?>[] basePackageClasses() default {};Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;boolean useDefaultFilters() default true;Filter[] includeFilters() default {};Filter[] excludeFilters() default {};boolean lazyInit() default false;@Retention(RetentionPolicy.RUNTIME)@Target({})@interface Filter {FilterType type() default FilterType.ANNOTATION;@AliasFor("classes")Class<?>[] value() default {};@AliasFor("value")Class<?>[] classes() default {};String[] pattern() default {};}}
  • 以下是该注解中定义的各个属性的含义:

    1. value()basePackages():
      • 用于指定要扫描的基础包路径,以查找组件类。可以使用这两个属性中的任何一个,它们是互为别名的。
      • 默认为空数组,表示不指定基础包路径。
    2. basePackageClasses():
      • 用于指定一组类,从这些类所在的包路径开始扫描组件。
      • 默认为空数组。
    3. nameGenerator():
      • 指定用于生成Bean名称的类,通常是一个 BeanNameGenerator 接口的实现类。
      • 默认值为 BeanNameGenerator.class,表示使用Spring默认的Bean名称生成器。
    4. scopeResolver():
      • 指定用于解析组件的作用域范围的类,通常是一个 ScopeMetadataResolver 接口的实现类。
      • 默认值为 AnnotationScopeMetadataResolver.class,表示使用Spring默认的作用域解析器。
    5. scopedProxy():
      • 指定Scoped Proxy的模式。可以使用枚举值 ScopedProxyMode 中的选项。
      • 默认值为 ScopedProxyMode.DEFAULT,表示使用默认的Scoped Proxy模式。
    6. resourcePattern():
      • 用于指定用于扫描组件的资源模式。默认是 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN,通常用于指示扫描类路径下的所有类文件。
    7. useDefaultFilters():
      • 一个布尔值,用于确定是否使用默认的过滤器。如果设置为 true,则会应用默认的包含过滤器,通常用于扫描带有特定注解的类。
      • 默认值为 true
    8. includeFilters():
      • 一个数组,用于指定自定义的包含过滤器。这些过滤器可以根据需要自定义以确定要包含的类。
      • 默认为空数组。
    9. excludeFilters():
      • 一个数组,用于指定自定义的排除过滤器。这些过滤器可以根据需要自定义以确定要排除的类。
      • 默认为空数组。
    10. lazyInit():
      • 一个布尔值,用于确定是否懒加载扫描到的组件类。
      • 默认值为 false,表示不懒加载。

    此外,@ComponentScan 注解还支持 @Repeatable(ComponentScans.class),允许多次使用该注解,以指定多个组件扫描配置。

    总之,@ComponentScan 注解用于配置Spring组件扫描的各种属性,以便在应用程序中自动发现和注册组件类。不同的属性允许您以不同的方式自定义扫描的行为。

ComponentScanAnnotationParse

从这个类名就能猜到是处理ComponentScan注解的。进入到parse方法进行源码解析

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {// 创建ClassPathBeanDefinitionScanner对象,通过componentScan参数中的属性构建了一个对象// useDefaultFilters default trueClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);// 设置Bean名称生成器// 从componentScan属性中获取Bean名称生成器的类,并根据需要创建实例。// 如果没有指定Bean名称生成器,将使用默认的Bean名称生成器Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);// 这里利用反射可以生成出来你自定义的,AnnotationBeanNameGenerator,用于生成bean名称的scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :BeanUtils.instantiateClass(generatorClass));// 设置Scoped proxy模式// 从属性中获取Scoped proxy模式,将其设置未扫描器的Scoped proxy模式ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");if (scopedProxyMode != ScopedProxyMode.DEFAULT) {scanner.setScopedProxyMode(scopedProxyMode);}else {// AnnotationScopeMetadataResolverClass<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));}// 设置资源模式,从属性中获取资源模式,并将其设置为扫描器的资源模式// **/**.classscanner.setResourcePattern(componentScan.getString("resourcePattern"));// 添加包含过滤器和排除过滤器:// 遍历componentScan属性中的包含过滤器和排除过滤器,并将它们添加到扫描器中。// 这些过滤器用于确定哪些类会被扫描并注册为Bean定义,哪些类会被排除。// 这里默认就是没有for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,this.resourceLoader, this.registry);for (TypeFilter typeFilter : typeFilters) {scanner.addIncludeFilter(typeFilter);}}// 默认没有for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,this.resourceLoader, this.registry);for (TypeFilter typeFilter : typeFilters) {scanner.addExcludeFilter(typeFilter);}}// 设置Lazy Initialization(懒加载):// 从componentScan属性中获取懒加载的配置,并根据需要将其设置为Bean定义的默认值。boolean lazyInit = componentScan.getBoolean("lazyInit");if (lazyInit) {scanner.getBeanDefinitionDefaults().setLazyInit(true);}// 解析基础包路径://从componentScan属性中获取基础包路径的配置。//如果未指定基础包路径,将使用declaringClass的包路径作为默认值。Set<String> basePackages = new LinkedHashSet<>();String[] basePackagesArray = componentScan.getStringArray("basePackages");for (String pkg : basePackagesArray) {String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);Collections.addAll(basePackages, tokenized);}for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {basePackages.add(ClassUtils.getPackageName(clazz));}// 如果没有写,将使用declaringClass,也就是AopConfig这个类的路径。这也是spring里面不写basePackages的原因,直接约定大于配置。if (basePackages.isEmpty()) {basePackages.add(ClassUtils.getPackageName(declaringClass));}// 添加排除过滤器://添加一个排除过滤器,该过滤器用于排除与declaringClass相同的类,以避免将其注册为Bean定义。scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {@Overrideprotected boolean matchClassName(String className) {return declaringClass.equals(className);}});// 实际做事的地方就是去doScan// 最终,调用scanner.doScan方法来扫描指定的基础包路径,并返回扫描结果作为Set<BeanDefinitionHolder>。return scanner.doScan(StringUtils.toStringArray(basePackages));}

@ComponentScan(value = {“com.qhyu.cloud.**”},这个是我的AopConfig类的注解信息,此类只写了一个注解用于启动扫描。

ClassPathBeanDefinitionScanner

首先创建了一个ClassPathBeanDefinitionScanner对象,通过componentScan参数中的属性构建了一个ClassPathBeanDefinitionScanner对象。这个扫描器用于扫描类路径中的Bean定义,并将它们注册到Spring的Bean注册表中。为了后续的观察方便我在ClassPathBeanDefinitionScanner的doScan方法中加入两个打印。

@ComponentScan中的所有属性都是为这个Bean定义信息扫描器做准备。

这个方法本章不做分析,将在下一章进行详细分析。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();AtomicInteger index = new AtomicInteger();for (String basePackage : basePackages) {Set<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 在这里就把我们需要spring管理的类组装BeanDefinition,放到了(BeanDefinitionRegistry)BeanFactory中System.out.println("当前加载的beanName:"+beanName);index.addAndGet(1);registerBeanDefinition(definitionHolder, this.registry);}}}System.out.println("当前加载的所有beanName的个数为:"+ index);return beanDefinitions;}

useDefaultFilters

useDefaultFilters是个布尔类型,默认是true,这边先直接进行默认启动,打印出我们自定义的beanName。

在这里插入图片描述

然后我开始修改useDefaultFilters的值为false。我们自定义的bean就不会被加载了。

@ComponentScan(value = {“com.qhyu.cloud.**”},useDefaultFilters = false)

在这里插入图片描述

进入ClassPathBeanDefinitionScanner类的构造函数,发现如果useDefaultFilters为true的时候会注册默认的过滤器。registerDefaultFilters(),源码如下

/*** Register the default filter for {@link Component @Component}.* <p>This will implicitly register all annotations that have the* {@link Component @Component} meta-annotation including the* {@link Repository @Repository}, {@link Service @Service}, and* {@link Controller @Controller} stereotype annotations.* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and* JSR-330's {@link javax.inject.Named} annotations, if available.**/
protected void registerDefaultFilters() {// 包含过滤器,Component注解this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();// @ManagedBeantry {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.}// @Namedtry {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.}}

从源码中可以得值,次方法在includeFilters中加了至少一个Component的注解类型过滤器,如果有的话还会添加javax.annotation.ManagedBean和javax.inject.Named注解类型的解析器。

从注释中可以得知,包含过滤器中加入了@Component之后其实隐士的加入了@Repository、@Service、 @Controller,因为这些注解中都包含了@Component。

至于这个包含过滤器在哪里使用,就是在ClassPathBeanDefinitionScanner的doScan方法中的findCandidateComponents(basePackage)中。将在下一章节进行详细分析。

nameGenerator

从componentScan属性中获取Bean名称生成器的类,并根据需要创建实例,如果没有指定Bean名称生成器,将使用默认的Bean名称生成器。这里好像没啥好说的,就是一个名称生成器,使用的是AnnotationBeanNameGenerator。

Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);// 这里利用反射可以生成出来你自定义的,AnnotationBeanNameGenerator,用于生成bean名称的scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :BeanUtils.instantiateClass(generatorClass));

scopedProxy和scopeResolver

设置Scoped proxy模式,从属性中获取Scoped proxy模式,将其设置为扫描器的Scoped proxy模式。在不自定义的情况下,使用的是AnnotationScopeMetadataResolver。

ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");if (scopedProxyMode != ScopedProxyMode.DEFAULT) {scanner.setScopedProxyMode(scopedProxyMode);}else {// AnnotationScopeMetadataResolverClass<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));}

resourcePattern

设置资源模式,从属性中获取资源模式,并将其设置为扫描器的资源模式。默认是**/**.class,通常用于指示扫描类路径下的所有类文件。

scanner.setResourcePattern(componentScan.getString("resourcePattern"));

includeFilters和excludeFilters

默认的时候这两个都为空,但是excludeFilters一定会有一个,在后面手动加上的。这些过滤器主要用于确定哪些类会被扫描并注册为bean定义,哪些类会被排除掉。而后面手动添加了一个排除过滤器用于排除与declaringClass相同的类,以避免将其注册为Bean定义。一般情况下我们都不去新增这些内容,除非你有自定义的一些注解也需要被扫描起来获取一些类需要被排除的时候可以尝试这么做。

// 这里默认就是没有for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,this.resourceLoader, this.registry);for (TypeFilter typeFilter : typeFilters) {scanner.addIncludeFilter(typeFilter);}}// 默认没有for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,this.resourceLoader, this.registry);for (TypeFilter typeFilter : typeFilters) {scanner.addExcludeFilter(typeFilter);}}scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {@Overrideprotected boolean matchClassName(String className) {return declaringClass.equals(className);}});

lazyInit

设置Lazy Initialization(懒加载),从componentScan属性中获取懒加载的配置,并根据需要将其设置为Bean定义的默认值。

boolean lazyInit = componentScan.getBoolean("lazyInit");if (lazyInit) {scanner.getBeanDefinitionDefaults().setLazyInit(true);}

basePackages和basePackageClasses

这段逻辑的主要目的是解析基础包路径,用于配置组件扫描。让我详细解释这段逻辑的步骤:

  1. 首先,创建一个空的 LinkedHashSet 集合 basePackages,用于存储基础包路径。
  2. 获取 componentScan 注解中的 basePackages 属性的值,该值是一个字符串数组,可能包含一个或多个基础包路径。
  3. basePackagesArray 中的每个包路径进行处理:
    • 使用 environment.resolvePlaceholders(pkg) 方法,将包路径中的占位符解析为实际的值。这是为了支持在配置中使用占位符来动态设置基础包路径。
    • 使用 StringUtils.tokenizeToStringArray 方法,将解析后的包路径字符串按照 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS 分隔符进行拆分,得到一个字符串数组 tokenized
    • 最后,使用 Collections.addAll 方法将 tokenized 中的每个拆分后的包路径添加到 basePackages 集合中。
  4. 接下来,处理 componentScan 注解中的 basePackageClasses 属性,该属性是一个类数组,用于指定基础包路径。这通常用于从特定的类所在的包开始扫描组件。
    • 对于每个类 clazz,使用 ClassUtils.getPackageName(clazz) 方法获取其所在包的包名,并将包名添加到 basePackages 集合中。
  5. 最后,如果 basePackages 集合为空,表示没有明确指定要扫描的基础包路径,那么将使用 declaringClass 参数所表示的类的包路径作为默认基础包路径。
    • 这是一种常见的约定大于配置的方式,在Spring中,如果不显式指定基础包路径,通常会默认使用配置类(declaringClass)所在的包路径作为基础包路径。
Set<String> basePackages = new LinkedHashSet<>();String[] basePackagesArray = componentScan.getStringArray("basePackages");for (String pkg : basePackagesArray) {String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);Collections.addAll(basePackages, tokenized);}for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {basePackages.add(ClassUtils.getPackageName(clazz));}// 如果没有写,将使用declaringClass,也就是AopConfig这个类的路径。这也是spring里面不写basePackages的原因,直接约定大于配置。if (basePackages.isEmpty()) {basePackages.add(ClassUtils.getPackageName(declaringClass));}

总结

本章主要对ComponentScanAnnotationParser的parse方法的整体流程进行了分析,这个类是对@ComponentScan注解进行解析,并且将这些属性值设置到扫描器中,主要的使用还是在解析器,也就是ClassPathBeanDefinitionScanner的doScan方法,下一章节将进行详细分析。

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

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

相关文章

WinPcap4.1.3安装失败解决方法,A newer version of winpcap...

WinPcap4.1.3安装失败解决方法&#xff0c;A newer version of winpcap… 如图所示&#xff0c;提示本地安装有更高版本的WinPcap时&#xff0c;按如下操作即可解决。 找到相应文件&#xff0c;扩展名修改成如下&#xff1a; C:\Windows\SysWOW64 的wpcap.dll改成 wpcap.dll.…

【Verilog 教程】6.5 Verilog避免Latch

关键词&#xff1a;触发器&#xff0c;锁存器 Latch 的含义 锁存器&#xff08;Latch&#xff09;&#xff0c;是电平触发的存储单元&#xff0c;数据存储的动作取决于输入时钟&#xff08;或者使能&#xff09;信号的电平值。仅当锁存器处于使能状态时&#xff0c;输出才会随着…

使用 Velocity 模板引擎的 Spring Boot 应用

使用 Velocity 模板引擎的 Spring Boot 应用 模板引擎是构建动态内容的重要工具&#xff0c;特别适用于生成HTML、邮件内容、报告和其他文本文档。Velocity是一个强大的模板引擎&#xff0c;它具有简单易用的语法和灵活性。本文将介绍如何在Spring Boot应用中使用Velocity模板…

C语言入门Day_27 开发环境

前言&#xff1a; 在线编译环境涉及到联网&#xff0c;如果在没有网的情况下&#xff0c;我们就不能写代码了&#xff0c;这一章节&#xff0c;我们将会给大家介绍一下如何搭建一个本地的C语言编译环境。 如果想要设置 C 语言环境&#xff0c;需要确保电脑上有以下两款可用的…

二十二,加上各种贴图

使用pbr的各种贴图&#xff0c;albedo,金属度&#xff0c;ao,法线&#xff0c;粗糙度&#xff0c;可以更好的控制各个片元 1&#xff0c;先加上纹理坐标 texCoords->push_back(osg::Vec2(xSegment, ySegment)); geom->setVertexAttribArray(3, texCoords, osg::Array::BI…

凹凸贴图如何提高物体的真实感

什么是凹凸贴图 凹凸贴图&#xff08;Bump Mapping&#xff09;是一种计算机图形学中的技术&#xff0c;用于在表面上模拟微小的凹凸形状&#xff0c;从而增加了物体的细节和真实感。它可以在不改变物体几何形状的情况下&#xff0c;通过修改光照的反应&#xff0c;使表面看起来…

Java编程技巧:文件上传、下载、预览

目录 1、上传文件1.1、代码1.2、postman测试截图 2、下载resources目录中的模板文件2.1、项目结构2.2、代码2.3、使用场景 3、预览文件3.1、项目结构3.2、代码3.3、使用场景 1、上传文件 1.1、代码 PostMapping("/uploadFile") public String uploadFile(Multipart…

idea Springboot 高校科研资源共享系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 高校科研资源共享系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c; 系统具有完整的源代码和数据…

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型 第八章 驱动设计的思想&#xff1a;面向对象/分层/分离8.1 面向对象8.2 分层8.3 分离8.4 写示例代码8.5 课后作业 第九章 驱动进化之路&#xff1a;总线设备驱动模型9.1 驱动编写的 3种方法9.1.1 传统写法9.1.2 总线设备…

BUUCTF reverse wp 21 - 30

[ACTF新生赛2020]rome 无壳, 直接拖进IDA32 y键把v2改成char[49], n键重命名为iuput int func() {int result; // eaxint v1[4]; // [esp14h] [ebp-44h]char input[49]; // [esp24h] [ebp-34h] BYREFstrcpy(&input[23], "Qsw3sj_lz4_Ujwl");printf("Please…

《Upload-Labs》01. Pass 1~13

Upload-Labs 索引前言Pass-01题解 Pass-02题解总结 Pass-03题解总结 Pass-04题解 Pass-05题解总结 Pass-06题解总结 Pass-07题解总结 Pass-08题解总结 Pass-09题解 Pass-10题解 Pass-11题解 Pass-12题解总结 Pass-13题解 靶场部署在 VMware - Win7。 靶场地址&#xff1a;https…

基于Java的旅游管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

FreeRTOS入门教程(任务优先级,Tick)

文章目录 前言一、什么是任务优先级二、FreeRTOS如何分辨出优先级最高可运行的任务三、FreeRTOS中的时钟节拍Tick四、什么是时间片五、相同优先级任务怎么进行切换六、任务优先级实验七、修改任务优先级总结 前言 本篇文章将带大家学习FreeRTOS中的任务优先级&#xff0c;并且…

GPT-4科研实践:数据可视化、统计分析、编程、机器学习数据挖掘、数据预处理、代码优化、科研方法论

查看原文>>>GPT4科研实践技术与AI绘图 GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。例如在科研编程、绘图领域&#xff1a;1、编程建议和示例代码: 无论你使用的编程语言是Python、R、MATLAB还是其他语言&#x…

【SSL】用Certbot生成免费HTTPS证书

1. 实验背景 服务器&#xff1a;CentOS7.x 示例域名&#xff1a; www.example.com 域名对应的web站点目录&#xff1a; /usr/local/openresty/nginx/html 2. 安装docker # yum -y install yum-utils# yum-config-manager --add-repo https://download.docker.com/linux/ce…

web:[极客大挑战 2019]LoveSQL

题目 打开页面显示如下 查看源代码&#xff0c;查到一个check.php&#xff0c;还是get传参 尝试账号密码输入 题目名为sql&#xff0c;用万能密码 1or 11# 或 admin or 11 给了一段乱码&#xff0c;也不是flag 查看字段数 /check.php?usernameadmin order by 3%23&pass…

Word | 简单可操作的快捷公式编号、右对齐和引用方法

1. 问题描述 在理工科论文的写作中&#xff0c;涉及到大量的公式输入&#xff0c;我们希望能够按照章节为公式进行编号&#xff0c;并且实现公式居中&#xff0c;编号右对齐的效果。网上有各种各样的方法来实现&#xff0c;操作繁琐和简单的混在一起&#xff0c;让没有接触过公…

Redis学习笔记(常用数据类型,发布订阅,事务和锁机制,持久化,集群,雪崩,缓存击穿,分布式锁)

一、NoSQL数据库简介 解决扩展性问题&#xff0c;如果需要对功能进行改变&#xff08;比如增删功能&#xff09;&#xff0c;用框架有一定的规范要求&#xff0c;无形中解决了扩展性问题。 Redis是一种典型的NoSQL数据库。 NoSQL的基础作用&#xff1a; 1. nginx负载均衡反向…

Armv9读取cache内容:Direct access to internal memory

10 访问cache Cortex-A720核心提供一种机制,通过IMPLEMENTATION DEFINED系统寄存器可以读取L1缓存、L2缓存和Translation Lookaside Buffer(TLB)。当缓存数据与系统内存数据之间的一致性异常时,您可以使用此机制来调查任何问题。 只有在EL3中才可以访问内部内存(cache)。…

排序:最佳归并树(优化外部排序中对磁盘的读写次数)

1.归并树的性质 每个初始归并段对应一个叶子结点&#xff0c;把归并段的块数作为叶子的权值归并树的WPL树中所有叶结点的带权路径长度之和归并过程中的磁盘I/O次数归并树的WPL*2 如下图&#xff1a; 每个初始归并段看作一个叶子结点&#xff0c;归并段的长度作为结点权值&a…