【Spring】深入解析 Spring 原理:Bean 的多方面剖析(源码阅读)

 🔥个人主页: 中草药

🔥专栏:【Java】登神长阶 史诗般的Java成神之路


一、Bean的作用域

        在 Java Spring 框架中,Bean 的作用域是一个关键概念,它决定了 Bean 的生命周期和实例化方式,对应用的性能、资源利用以及数据一致性等方面有着深远影响。本文将详细探讨 Spring Bean 的各种作用域。

举例 设置Bean的作用域

package com.bite.principle.config;


import com.bite.principle.model.Dog;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;

@Configuration
public class DogConfig {
    @Bean
    public Dog dog(){
        Dog dog = new Dog();
        dog.setName("旺旺");
        return dog;
    }

    @Bean
//    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    @Scope("singleton")
    public Dog singleDog(){
        return new Dog();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//    @Scope("prototype")
    public Dog prototypeDog(){
        return new Dog();
    }

    @Bean
    @RequestScope
    public Dog requestDog(){
        return new Dog();
    }

    @Bean
    @SessionScope
    public Dog sessionDog(){
        return new Dog();
    }

    @Bean
    @ApplicationScope
    public Dog applicationDog(){
        return new Dog();
    }

}

 1、singleton 单例作用域

        Singleton 是 Spring 默认的作用域。当一个 Bean 被定义为 Singleton 时,在整个 Spring 容器的生命周期内,只会创建一个该 Bean 的实例

        例如,在一个处理数据库操作的应用中,数据库连接池通常被设置为 Singleton 作用域。因为创建数据库连接是一个资源密集型操作,多个组件共享同一个连接池实例可以避免重复创建连接带来的开销,提高资源利用率。

        Spring 实现 Singleton 作用域的原理是通过容器级别的缓存。容器在启动时创建 Bean 实例并将其存储在缓存中,后续对该 Bean 的请求直接从缓存中获取,从而保证始终返回同一个实例。

可用以下代码测试

package org.example.principle;

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/scope")
@RestController
public class ScopeController {
    @Resource(name = "singleDog")
    private Dog singleDog;

    @RequestMapping("/single")
    public String single(){
        //从context中获取对象
        Dog dog = (Dog)context.getBean("singleDog");
        return "注入对象:"+ singleDog.toString() + ",context Dog:"+ dog.toString();
    }

}

测试结果:

即使进行多次刷新,显示的结果还是同一个,这就是 单例模式

 2、prototype 原型作用域

        与 Singleton 不同,Prototype 作用域的 Bean 在每次被使用时都会创建一个新的实例。

        考虑一个用户订单处理服务,每个订单可能具有不同的处理逻辑和状态,将订单处理服务设置为 Prototype 作用域,就能确保每个订单处理过程都使用独立的服务实例,避免不同订单之间的状态干扰。

        然而,由于每次请求都会创建新实例,开发人员需要谨慎使用 Prototype 作用域,避免因频繁创建复杂对象而导致性能问题。

可用以下代码测试:

package org.example.principle;

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/scope")
@RestController
public class ScopeController {
    @Resource(name = "prototypeDog")
    private Dog prototypeDog;

    @RequestMapping("/prototype")
    public String prototype(){
        //从context中获取对象
        Dog dog = (Dog)context.getBean("prototypeDog");
        return "注入对象:"+ prototypeDog.toString() + ",context Dog:"+ dog.toString();
    }

}

测试结果:

如测试结果,注入的对象不发生变化,但是提取出来的Bean会变化,每一次的刷新即使用都会变化,创建新的实例

3、Request 请求作用域

        Request 作用域适用于 Web 应用场景。在一次 HTTP 请求的生命周期内,同一个 Request 作用域的 Bean 是共享的,不同请求会创建不同的实例。

        比如在一个基于 Spring MVC 的应用中,用于存储请求参数的 Bean 可以设置为 Request 作用域。从请求开始,到各个控制器和服务层方法处理请求参数,都使用同一个参数 Bean 实例,保证了请求数据在处理过程中的一致性,当请求结束时,该 Bean 实例被销毁。

        Spring 借助与 Web 容器的集成,在请求开始时创建 Bean 实例并绑定到请求上下文,请求结束时自动销毁实例来实现 Request 作用域。

package org.example.principle;

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/scope")
@RestController
public class ScopeController {
    @Resource(name = "requestDog")
    private Dog requestDog;

    @RequestMapping("/request")
    public String request(){
        //从context中获取对象
        Dog dog = (Dog)context.getBean("requestDog");
        return "注入对象:"+ requestDog.toString() + ",context Dog:"+ dog.toString();
    }

}

结果:

 

 如上测试结果,每一次刷新都会创建新的实例,但是注入对象,和使用Bean保持统一,并不会随着Bean的使用而创建新的实例

4、Session 会话作用域

        Session 作用域的 Bean 在一个用户会话期间是同一个实例。从用户首次访问应用建立会话,到会话结束(如用户注销或会话超时),对该 Bean 的引用都指向同一个对象。

        在电子商务应用中,购物车 Bean 常被设置为 Session 作用域。用户在一次购物会话中,无论浏览多少商品页面,添加或修改购物车内容,操作的都是同一个购物车实例,方便用户购物体验的同时,也保证了购物车数据在会话内的一致性。

        Spring 通过利用 Web 容器的会话管理机制,在会话创建时实例化 Bean 并绑定到会话上下文,会话结束时销毁实例来实现 Session 作用域。

测试代码:

package org.example.principle;

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/scope")
@RestController
public class ScopeController {
   @Resource(name = "sessionDog")
    private Dog sessionDog;

    @RequestMapping("/session")
    public String session(){
        //从context中获取对象
        Dog dog = (Dog)context.getBean("sessionDog");
        return "注入对象:"+ sessionDog.toString() + ",context Dog:"+ dog.toString();
    }
   
}

测试结果:

在 浏览器1 反复刷新下

 在 浏览器2 反复刷新下

这就相当于在不同的session下,才会去创建新的实例 

5、Application 全局作用域

        Application 作用域的 Bean 在整个 Web 应用范围内只有一个实例,类似于 Singleton,但更侧重于 Web 应用级别。

        例如,应用的全局配置信息 Bean,如应用名称、版本号等配置信息,整个 Web 应用只需一个实例即可,各个组件都可以共享这个配置信息,方便统一管理和维护。

        总之,正确理解和运用 Spring Bean 的作用域对于构建高效、稳定且可维护的 Java 应用至关重要。开发人员需要根据不同的业务需求和场景,合理选择 Bean 的作用域,以优化应用的性能和功能。

6、websocket  HTTP WebSocket 作用域

        WebSocket 的 “作用域” 更倾向于长期的、持续的连接场景。一旦建立连接,这个连接就可以在整个通信过程中持续存在,直到连接被关闭。

        在一个 WebSocket 连接的范围内,服务器和客户端可以进行多次数据交互,这些交互都基于同一个连接,并且可以根据业务需求灵活地处理消息,如在一个在线游戏中,玩家的操作和游戏状态的更新都可以通过这个连接来完成。

 二、Bean的生命周期

Bean 的生命周期是一个核心概念,它涵盖了从 Bean 的创建到销毁的整个过程。

我们可以大致将他划分为5个阶段,分别是实例化阶段,属性赋值阶段,初始化阶段,使用阶段,销毁阶段

我们可以用此代码去形象理解这个生命周期的五个过程,代码如下


package org.example.principle;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class BeanLifeComponent implements BeanNameAware {

    private Dog dog;

    //相当于实例化阶段
    public BeanLifeComponent() {
        System.out.println("执行构造函数...");
    }

    //属性装配
    @Autowired
    public void setDog(Dog dog) {
        this.dog = dog;
        System.out.println("使用setter方法, 完成属性装配...");
    }

    //初始化
    @PostConstruct
    public void init(){
        System.out.println("执行init方法...");
    }
    @Override
    public void setBeanName(String name) {
        System.out.println("执行setBeanName, name:"+name);
    }
    
    //使用
    public void use(){
        System.out.println("执行use方法....");
    }
    
    //利用此注解去表示销毁阶段
    @PreDestroy
    public void destroy(){
        System.out.println("执行destroy方法...");
    }
}

测试代码:

package org.example.principle;

import org.example.autoconfig.TestConfig1;
import org.example.autoconfig.TestConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

@SpringBootApplication
public class SpringPrincipleApplication {
    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(SpringPrincipleApplication.class, args);

        BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
        beanLifeComponent.use();
    }
     
}

 执行结果:

由此日志信息我们 可以大致来观察Bean的生命周期

1、实例化阶段

        当 Spring 容器启动时,首先会根据配置信息(如 XML 配置、Java 配置或基于注解的配置)确定需要创建的 Bean。容器会使用反射机制调用 Bean 的构造函数来创建一个实例。例如,如果有一个简单的 UserService Bean,Spring 会在这个阶段创建 UserService 的对象实例,就像执行 new UserService() 操作一样。

2、属性赋值阶段

        在实例创建之后,Spring 会根据配置将相应的值赋给 Bean 的属性。这可能包括从配置文件中读取属性值、注入其他依赖 Bean 等。比如,若 UserService 依赖于一个  UserRepository Bean,Spring 会在此时将对应的 UserRepository 实例注入到 UserService 中,使得 UserService 能够使用 UserRepository 的功能。

3、初始化阶段

        Bean 的初始化是一个关键步骤。在这个阶段,Bean 可以执行一些自定义的初始化逻辑。Spring 提供了两种主要的方式来定义初始化方法:一是在 XML 配置中使用 init-method 属性指定一个方法,二是在 Bean 类中使用 @PostConstruct 注解标注一个方法

实例化和初始化的区别

        实例化是指创建一个类的对象的过程。在 Java 中,通过使用new关键字或者通过反射等机制,为对象分配内存空间,使得这个对象从类的定义变成一个可以使用的实体。这就好比是盖房子,实例化是按照蓝图(类的定义)搭建出房子的框架(对象),但此时房子还没有进行内部装修等后续工作。

        初始化是在对象已经实例化之后,对这个对象进行的一些准备工作,使得它能够正常使用。这包括给对象的属性赋值(如果还没有赋值)、建立必要的资源连接(如数据库连接、网络连接等)、加载配置信息等操作。继续用盖房子的比喻,初始化就像是对房子进行内部装修,安装水电、家具等,让房子具备居住的条件。

4、使用阶段

        经过前面的步骤,Bedan 已经完全准备好,可以被应用程序使用了。在整个应用运行期间,Spring 容器会管理这些 Bean 实例,根据需要将它们注入到其他组件中,以提供各种服务。例如,在一个 Web 应用中,UserService 可能会被控制器调用,以处理用户相关的业务逻辑,如用户注册、登录、信息查询等操作。

5、销毁阶段

        当应用程序关闭或 Spring 容器被销毁时,Bean 也会被销毁。与初始化类似,Spring 提供了两种定义销毁方法的方式:在 XML 配置中使用 destroy-method 属性,或者在 Bean 类中使用 @PreDestroy 注解。在销毁方法中,可以进行资源的释放操作,如关闭数据库连接、释放文件句柄等。

三、Spring Boot的自动装配

        Spring Boot的自动配置就是当Spring容器启动后,一些配置类,bean对象就自动存入到了IoC容器之中,不需要我们手动声明。从而简化了开发,省去了繁琐的配置操作

1、Spring 加载 Bean

我们可以模拟去理解第三方jar包的使用

autoconfig 下创建 TestConfig 作为第三方资源

如上,启动类与第三方资源不在同一包下,因此,通过启动类

package org.example.principle;

import org.example.autoconfig.TestConfig1;
import org.example.autoconfig.TestConfig2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;

@SpringBootApplication
public class SpringPrincipleApplication {
    public static void main(String[] args) {

        ApplicationContext context = SpringApplication.run(SpringPrincipleApplication.class, args);

        TestConfig1 bean = context.getBean(TestConfig1.class);
        bean.use();

        TestConfig2 bean2 = context.getBean(TestConfig2.class);
        bean2.use();

//        BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
//        beanLifeComponent.use();
    }
     
}

会发生报错

No qualifying bean of type 'org.example.autoconfig.TestConfig1' available

没有类型为“org.example.autoconfig”的合格bean

原因分析

        Spring 通过五大注解和 @Bean 注解可以帮助我们把Bean加载到SpringIoC容器中,但前提是,这些注解类与SpringBoot启动类在同一个目录之下。

        而此时,启动类即@SpringBootApplication在principle的包之下,他只会扫描此路径下的所有包,并不包括autoconfig之下的第三方资源

解决方案:

1、@ComponentScan

通过该注解去指定Spring的扫描路径

@ComponentScan({"org.example.autoconfig","org.example.principle"})//当表现多个路径时,要用集合{}

Spring 并没有采取这种方式 (当我们引入第三方框架时,并没有额外添加扫描路径,比如mybatis)

如果采取这种方式,当我们引入大量第三方依赖,比如Mybatis,jackson等时,就需要在启动类上配置不同的依赖需要扫描的包,这种方式会非常繁琐

2、@Import

使用该注解导入该类

@Import({TestConfig1.class,TestConfig2.class})

Spring 同样没有采取这种方式,理由同上,过于繁琐

3、导入ImportSelector接口实现类

先写一个接口实现类

package org.example.autoconfig;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;

public class MyImport implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"org.example.autoconfig.TestConfig1","org.example.autoconfig.TestConfig2"};
    }
}

再通过@Import去引入这个接口实现类

@Import(MyImport.class)

这些都存在一个明显问题,需要使用者需要知道第三方依赖中需要那些配置和Bean对象,如果我们漏掉了一个类,会使项目出现巨大问题

4、给第三方依赖提供一个注解

package org.example.autoconfig;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImport.class)
public @interface EnableTestConfig {
}

注解中封装@Import这个注解,并且导入MyImport.class这个类

再启动类上加入这个注解

@EnableTestConfig

Spring采取的方式便是这种

以上所有方式最后都能顺利使用TestConfig

2、自动装配的源码阅读

        pring Boot 应用通常以一个带有 @SpringBootApplication(实现自动装配的核心) 注解的主类作为入口。这个注解实际上是一个组合注解,它包含了 @Configuration@EnableAutoConfiguration 和 @ComponentScan。其中,@EnableAutoConfiguration 是实现自动装配的关键。

        当 Spring Boot 应用启动时,@EnableAutoConfiguration 注解会触发自动装配机制。它会在类路径下查找 META-INF/spring.factories 文件,并加载其中配置的自动装配类。

阅读源码:

 1、元注解

JDK中提供了4个标准的用来来对注解类型进⾏注解的注解类, 我们称之为 meta-annotation(元注

解), 他们分别是:
@Target 描述注解的使用范围(即被修饰的注解可以用在什么地方)
@Retention 描述注解保留的时间范围
@Documented 描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息
@Inherited 使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则
其子类将自动具有该注解)
2、 @SpringBootConfiguration

点开该注解,只是对@Configuration进行了一层封装

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Indexed;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

3、@EnableAutoConfiguration (开启自动配置)

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

该注解首先使用@Import注解,导入了实现 ImportSelector 接口的实现类

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
   
    //省略代码

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
        //获取自动配置的配置类信息
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
selectImports() 方 法底层调用  getAutoConfigurationEntry() 方 法, 获取可自动配置的配置类信息集合。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //获取在配置⽂件中配置的所有⾃动配置类的集合
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }
getAutoConfigurationEntry() 方 法通过调用 getCandidateConfigurations(annotationMetadata, attributes) 方法获取在配置文件中配置的所有自动配置类的集合
//获取所有基于
//METAINF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports⽂件
//META-INF/spring.factories⽂件中配置类的集合
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, this.getBeanClassLoader());
        List<String> configurations = importCandidates.getCandidates();
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
getCandidateConfigurations 方 法的功能: 获取所有基于 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件, META-INF/spring.factories 文 件中配置类的集合.
在引入的起步依赖中, 通常都有包含以上两个文件
在加载自动配置类的时候,并不是将所有的配置全部加载进来,而是通过@Conditional等注解的判断进行动态加载
@Conditional是Spring 的底层注解,意思就是根据不同的条件,进行自己不同的条件判断 ,如果满足指定的条件,那么配置类里面的配置才会生效
此外还有 @AutoConfigurationPackage
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}
这个注解主要是导入⼀个配置文件 AutoConfigurationPackages.Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
    }
@AutoConfigurationPackage 就是将启动类所在的包下面所有的组件都扫描注册到 spring 容器中
因此,SpringBoot 自动配置原理大致如下

 SpringBoot的自动配置原理源码口是 @SpringBootApplication 注解,这个注解封装了3个注解@SpringBootConfiguration 标志当前类为配置类
@ComponentScan 进行包扫描(默认扫描的是启动类所在的当前包及其子包)
@EnableAutoConfiguration

  • @Import 注解:读取当前项目下所有依赖jar包中 META-INF/spring.factories,META-INF/spring/org.springframework.boot.autoconfigure.Autoconfiguration.imports 两个文件里面定义的配置类(配置类中定义了 @Bean 注解标识的方法)
  • @AutoconfigurationPackage:把启动类所在的包下面所有的组件都注入到Spring容器中 

“读书不是为了雄辩和驳斥,也不是为了轻信和盲从,而是为了思考和权衡。”—— 培根

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸

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

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

相关文章

基于高德地图js api实现掩膜效果 中间矢量 周围卫星图

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>管网服务</title><style>html,body,#ma…

Vue.js组件(6):echarts组件

1 前言 本章主要对常用的echars图表展示进行基本的组件封装。使用该组件前需要在项目中引入echarts。官网&#xff1a;Apache ECharts npm install echarts --save 2 图表组件 2.1 折线图组件 组件属性&#xff1a;chartId&#xff0c;指定图表挂载div的id&#xff0c;注意不…

RCE常见姿势

文章目录 常见漏洞执行函数&#xff1a;1.系统命令执行函数2.代码执行函数 命令拼接符读取文件命令绕过&#xff1a;空格过滤绕过关键字绕过长度过滤绕过无参数命令执行绕过无字母数字绕过利用%0A截断利用回溯绕过利用create_function()代码注入无回显RCE1.反弹shell2.dnslog外…

selenium执行js

JS知识 获取元素 document.getElement 移除属性&#xff1a;removeAttribute("xx") 窗口移动&#xff1a;window.scrollTo(0, document.body.scrollHeight)方法 drivier.execute_script(js)场景&#xff1a; 日期选择框&#xff0c;不能输入&#xff0c;只能设置…

三维场景重建与3D高斯点渲染技术探讨

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月25日10点11分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 文章源地址(有视频)&#xff1a;链接h…

springboot启动不了 因一个spring-boot-starter-web底下的tomcat-embed-core依赖丢失

这个包丢失了 启动不了 起因是pom中加入了 <tomcat.version></tomcat.version>版本指定&#xff0c;然后idea自动编译后&#xff0c;包丢了&#xff0c;删除这个配置后再也找不回来&#xff0c; 这个包正常在 <dependency><groupId>org.springframe…

Java日志框架:log4j、log4j2、logback

文章目录 配置文件相关1. properties测试 2. XMl使用Dom4j解析XML Log4j与Log4j2日志门面 一、Log4j1.1 Logges1.2 Appenders1.3 Layouts1.4 使用1.5 配置文件详解1.5.1 配置根目录1.5.2 配置日志信息输出目的地Appender1.5.3 输出格式设置 二、Log4j22.1 XML配置文件解析2.2 使…

基于LSTM长短期记忆神经网络的多分类预测【MATLAB】

在深度学习中&#xff0c;长短期记忆网络&#xff08;LSTM, Long Short-Term Memory&#xff09;是一种强大的循环神经网络&#xff08;RNN&#xff09;变体&#xff0c;专门为解决序列数据中的长距离依赖问题而设计。LSTM因其强大的记忆能力&#xff0c;广泛应用于自然语言处理…

机器学习基础 衡量模型性能指标

目录 1 前言 ​编辑1.1 错误率(Error rate)&精度(Accuracy)&误差(Error)&#xff1a; 1.2 过拟合(overfitting): 训练误差小&#xff0c;测试误差大 1.3 欠拟合(underfitting)&#xff1a;训练误差大&#xff0c;测试误差大 1.4 MSE: 1.5 RMSE: 1.6 MAE: 1.7 R-S…

TCP的流量控制的实现

滑动窗口的介绍 滑动窗口是tcp协议中的一个重要概念&#xff0c;滑动窗口是字节为单位&#xff0c;而tcp头部的序列化和确认号也是以字节为单位的&#xff0c;滑动窗口里是含有可以传输的字节的数量&#xff08;可以传输不是已经传输&#xff09;&#xff0c;而滑动窗口的大小是…

【0x001D】HCI_Read_Remote_Version_Information命令详解

目录 一、命令概述 二、命令格式及参数说明 2.12. HCI_Read_Remote_Version_Information 命令格式 2.2. Connection_Handle 三、生成事件 3.1. HCI_Command_Status 事件 3.2. HCI_Read_Remote_Version_Information_Complete 事件 四、命令执行流程 4.1. 命令发起阶段(…

一篇文章学会HTML

目录 页面结构 网页基本标签 图像标签 超链接标签 文本链接 图像链接 锚链接 功能链接 列表 有序列表 无序列表 自定义列表 表格 跨列/跨行 表头 媒体元素 视频 音频 网站的嵌套 表单 表单元素 文本框 单选框 多选框 按钮 下拉框 文本域和文件域 表…

C语言项目 天天酷跑(上篇)

前言 这里讲述这个天天酷跑是怎么实现的&#xff0c;我会在天天酷跑的下篇添加源代码&#xff0c;这里会讲述天天酷跑这个项目是如何实现的每一个思路&#xff0c;都是作者自己学习于别人的代码而创作的项目和思路&#xff0c;这个代码和网上有些许不一样&#xff0c;因为掺杂了…

如何完全剔除对Eureka的依赖,报错Cannot execute request on any known server

【现象】 程序运行报错如下&#xff1a; com.netflix.discovery.shared.transport.TransportException报错Cannot execute request on any known server 【解决方案】 &#xff08;1&#xff09;在Maven工程中的pom去掉Eureka相关的引用&#xff08;注释以下部分&#xff0…

华为云国内版与国际版的主要区别解析

华为云作为全球领先的云计算服务提供商&#xff0c;提供了国内版和国际版两种服务。虽然它们都旨在为用户提供高效、可靠的云计算解决方案&#xff0c;但在功能、服务、合规性等方面存在一些显著的区别。我们九河云通过本文将详细分析华为云国内版与国际版的主要区别&#xff0…

基于北斗短报文+4G的森林草原火险因子综合监测方案

近年来&#xff0c;全球气候变暖的趋势日益严重&#xff0c;气温升高导致森林火灾的发生频率和严重程度逐年增加&#xff0c;对人类社会和自然生态环境造成了严重的危害。森林火灾的发生受到植被类型、气象条件、扑救方式和监管方式等多种因素的影响。 因此&#xff0c;林业建…

小程序app封装公用顶部筛选区uv-drop-down

参考ui:DropDown 下拉筛选 | 我的资料管理-uv-ui 是全面兼容vue32、nvue、app、h5、小程序等多端的uni-app生态框架 样式示例&#xff1a; 封装公用文件代码 dropDownTemplete <template><!-- 顶部下拉筛选区封装公用组件 --><view><uv-drop-down ref&…

3 JDK 常见的包和BIO,NIO,AIO

JDK常见的包 java.lang:系统基础类 java.io:文件操作相关类&#xff0c;比如文件操作 java.nio:为了完善io包中的功能&#xff0c;提高io性能而写的一个新包 java.net:网络相关的包 java.util:java辅助类&#xff0c;特别是集合类 java.sql:数据库操作类 IO流 按照流的流向分…

Uniapp 手机基座调试App 打包成Apk文件,并上传到应用商店

1.Uniapp手机基座调试App。 1.1 以下是我另一篇文章 讲解 uniapp连接手机基座调试App、 Hbuildx使用SUB运行到手机基座测试_hbuilder基座-CSDN博客 2.打包本地的uniapp项目为apk文件。 打包的方式有很多种&#xff0c;我们可以选择本地打包和远程云端打包两种方式。 我们在打包…

vue调试工具 Vue.jsDevtools

文件下载 Vue.js Devtools 通过网盘分享的文件&#xff1a;ddebf336f8a44293bd4db9d0f287bc1c.crx 链接: https://pan.baidu.com/s/1uS3a49CwW-B000p5GwUQmQ 提取码: ko89 下载完了 &#xff0c;拖入chrome里&#xff0c;打开详情配置. 打开红框中的开关 重启浏览器&#xff…