Spring原理

目录

1.Bean的作用域

[1.1 概念](#1.1 概念)

[1.2 Bean的作用域](#1.2 Bean的作用域)

[1.3 @Resource 和 @Autowired](#1.3 @Resource 和 @Autowired)

[1.3.1 @Autowired](#1.3.1 @Autowired)

[1.3.2 @Resource](#1.3.2 @Resource)

[1.4 观察作用域](#1.4 观察作用域)

2.Bean的生命周期

[2.1 代码演示](#2.1 代码演示)

3.SpringBoot自动配置

[3.1 Spring加载Bean](#3.1 Spring加载Bean)

[3.2 解决方案](#3.2 解决方案)

[3.2.1 @ComponentScan](#3.2.1 @ComponentScan)

[3.2.2 @Import](#3.2.2 @Import)

[3.2.3 第三方依赖提供注解](#3.2.3 第三方依赖提供注解)

4.总结


1.Bean的作用域

1.1 概念

在SpringloC&DI阶段,我们学习了Spring是如何帮助我们管理对象的.

  1. 通过@controller,@Service,@Repository,@Component,@Configuration,@Bean来声明Bean对象。

  2. 通过ApplicationContext或者BeanFactory来获取对象

  3. 通过@AutowiredSetter方法或者构造方法等来为应用程序注入所依赖的Bean对象

1.2 Bean的作用域

在Spring中支持6种作用域,后4种在SpringMVC环境才生效

  1. singleton:单例作用域

  2. prototype:原型作用域(多例作用域)

  3. request:请求作用域

  4. session:会话作用域

  5. Application:全局作用域

  6. websocket:HTTP WebSocket 作用域

|-------------|--------------------------------------------|
| 作用域 | 说明 |
| singleton | 每个 Spring IoC 容器内同名称的 bean 只有一个实例(单例)(默认) |
| prototype | 每次使用该 bean 时会创建新的实例(非单例) |
| request | 每个 HTTP 请求生命周期内,创建新的实例(web 环境中,了解) |
| session | 每个 HTTP Session 生命周期内,创建新的实例(web 环境中,了解) |
| application | 每个 ServletContext 生命周期内,创建新的实例(web 环境中,了解) |
| websocket | 每个 WebSocket 生命周期内,创建新的实例(web 环境中,了解) |

参考文档:https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html

java 复制代码
package com.example.principle.mapper;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class Dog {
    private String name;
}
java 复制代码
package com.example.principle.config;

import com.example.principle.mapper.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)
    public Dog singleDog() {
        Dog dog = new Dog();
        return dog;
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Dog protoDog() {
        Dog dog = new Dog();
        return dog;
    }

    @Bean
    @RequestScope
    public Dog requestDog() {
        Dog dog = new Dog();
        return dog;
    }
    @Bean
    @SessionScope
    public Dog sessionDog() {
        Dog dog = new Dog();
        return dog;
    }

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

@RequestScope等同于@Scope(value=WebApplicationContext.SCOPE_REQUEST,proxyMode=ScopedProxyMode.TARGET_CLASS)

@SessionScope等同于@Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.TARGET_CLASS)

@ApplicationScope等同于@Scope(value=WebApplicationContext.SCOPE_APPLICATION,proxyMode=ScopedProxyMode.TARGET_CLASS)

proxyMode用来为springbean设置代理.proxyMode=ScopedProxyMode.TARGET_CLASS

表示这个Bean基于CGLIB实现动态代理.++Request,session和application作用域的Bean需要设置proxyMode++

测试不同作用域的Bean取到的对象是否一样

java 复制代码
package com.example.principle.controller;

import com.example.principle.mapper.Dog;
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;

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

    @Resource(name = "protoDog")
    private Dog protoDog;

    @Resource(name = "requestDog")
    private Dog requestDog;

    @Resource(name = "sessionDog")
    private Dog sessionDog;

    @Resource(name = "applicationDog")
    private Dog applicationDog;

    @Autowired
    ApplicationContext context;

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

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


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

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

每个请求都获取两次Bean

@Autowired和applicationContext.getBean("singleDog")都是从Spring容器中获取对象.

1.3 @Resource@Autowired

核心区别:@Autowired 是"ByType"起手,不行再"ByName";而 @Resource 是"ByName"起手,不行再"ByType"。

|-------------|---------------------------------|-------------------------------------------|
| 特性 | @Autowired (Spring 专属) | @Resource (JSR-250 标准) |
| 来源 | Spring 框架定义 | Java EE 的 JSR-250 标准提供,Spring 支持 |
| 默认注入方式 | 按类型(ByType) | 按名称(ByName) |
| 名称指定 | 需要与 @Qualifier("beanName") 配合使用 | 自带 name 属性(如 @Resource(name="singleDog")) |
| 是否支持按类型 | 是(核心方式) | 是(当未指定 name 且按名称找不到唯一bean时) |
| 是否支持按名称 | 是(需配合 @Qualifier) | 是(主要方式) |
| required 属性 | 有(@Autowired(required=false)) | 无(如果找不到且未指定name,会报错) |

1.3.1 @Autowired

  • 机制:它首先根据类型在Spring IoC容器中查找匹配的Bean。

    • 如果找到一个,直接注入。

    • 如果找到多个相同类型的Bean(例如有多个UserService的实现类),Spring会报错 NoUniqueBeanDefinitionException

    • 解决多个Bean的冲突:必须使用 @Qualifier("specificBeanName") 注解来额外指定Bean的名称,从而切换到按名称注入。

java 复制代码
// 1. 按类型注入
@Autowired
private SomeService someService;

// 2. 遇到多个同类型Bean,配合@Qualifier按名称注入
@Autowired
@Qualifier("mainDatabaseService") // 指定注入名为'mainDatabaseService'的Bean
private DatabaseService databaseService;

1.3.2 @Resource

  • 机制:它首先根据名称(由 name 属性指定)在容器中查找匹配的Bean。

    • 如果指定了name(如 @Resource(name="singleDog")),它就只按这个名称找。

    • 如果没有指定name,它会将字段名或属性名作为要查找的Bean名称(例如,字段名为 userDao,它就找名为 userDao 的Bean)。

    • 如果按名称找不到,它才会回退到按类型进行查找。

java 复制代码
// 1. 明确指定名称(你例子中的方式)
@Resource(name = "singleDog")
private Person specialPerson;

// 2. 不指定name,默认使用字段名(singleDog)作为Bean名称查找
@Resource
private Person singleDog; // 会查找名为'singleDog'的Bean

// 3. 如果没找到名为'singleDog'的Bean,则会回退到按Person类型查找
// 如果此时容器中只有一个Person类型的Bean,注入也会成功

1.4 观察作用域

  • 单例作用域

多次访问,得到的都是同一个对象,并且@AutowiredapplicationContext.getBean()也是同一个对象.

  • 多例作用域

观察ContextDog,每次获取的对象都不一样(注入的对象在Spring容器启动时,就已经注入了,所以多请求也不会发生变化)

  • 请求作用域

在一次请求中,@AutowiredapplicationContext.getBean()也是同一个对象.但是每次请求都会重新创建对象

  • 会话作用域

在一个session中,多次请求,获取到的对象都是同一个.

换一个浏览器访问,发现会重新创建对象.(另一个Session)

  • Application作用域

在一个应用中,多次访问都是同一个对象

2.Bean的生命周期

生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期

Bean的生命周期分为以下5个部分:

  1. 实例化(为Bean分配内存空间)

  2. 属性赋值(Bean注入和装配,比如@AutoWired)

  3. 初始化

    1. 执行各种通知,如BeanNameAwareBeanFactoryAware,ApplicationContextAware的接口方法.

    2. 执行初始化方法

      1. xml定义init-method

      2. 使用注解的方式@PostConstruct

      3. 执行初始化后置方法(BeanPostProcessor)

  4. 使用Bean

  5. 销毀Bean

    1. 销毁容器的各种方法,如@PreDestroyDisposableBean接口方法,destroy-method.

实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段,

可以在实例化之后,类加载完成之前进行自定义"事件"处理。

比如我们现在需要买一栋房子,那么我们的流程是这样的:

1.先买房(实例化,从无到有)

2.装修(设置属性)

3.买家电,如洗衣机,冰箱,电视,空调等([各种]初始化,可以入住);

4.入住(使用 Bean)

5.卖房(Bean销毁)

2.1 代码演示

java 复制代码
package com.example.principle.component;

import com.example.principle.mapper.Dog;
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;

    //1.创建实例执行构造方法
    public BeanLifeComponent() {
        System.out.println("执行构造函数...");
    }

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

    //4.初始化方法
    @PostConstruct
    public void init(){
        System.out.println("执行init方法...");
    }


    //3.Aware 接口回调,该方法提供了该 Bean 在 Spring 容器中的 ID/名称
    @Override
    public void setBeanName(String name) {
        System.out.println("执行setBeanName, name:"+name);
    }

    //5.使用Bean
    public void use(){
        System.out.println("执行use方法....");
    }

    //销毁Bean
    @PreDestroy
    public void destroy(){
        System.out.println("执行destroy方法...");
    }
}

singleton 作用域的范围是单个 Spring ApplicationContext(IoC 容器),而 application 作用域的范围是整个 Web 应用程序的 ServletContext

3.SpringBoot自动配置

3.1 Spring加载Bean

java 复制代码
package com.example.autoconfig;


import org.springframework.stereotype.Component;

@Component
public class BiteConfig {

    public void use(){
        System.out.println("bite config....");
    }
}
java 复制代码
@SpringBootTest
class SpringPrincipleApplicationTests {
    @Autowired
    private ApplicationContext applicationContext;


    @Test
    void contextLoads() {
    //从容器中查找 "名称为 biteConfig 且类型为 BiteConfig" 的 Bean
        BiteConfig biteConfig = context.getBean(BiteConfig.class, "biteConfig");
        System.out.println(biteConfig);
    }

}

观察日志: No qualifying bean of type 'com.bite.autoconfig.BiteConfig' available

没有com.bite.autoconfig.BiteConfig这个类型的Bean

3.2 解决方案

3.2.1 @ComponentScan

java 复制代码
@SpringBootApplication
@ComponentScan("com.example.autoconfig")
public class SpringPrincipleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }

}

可以指定扫描多个包

@ComponentScan({"com.example.autoconfig","com.example.demo"})

3.2.2 @Import

@Import

导入主要有以下几种形式:

  1. 导入类

    java 复制代码
    @SpringBootApplication
    @Import({BiteConfig.class, BiteConfig2.class})
    public class SpringPrincipleApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringPrincipleApplication.class, args);
        }
    
    }
  2. 导入ImportSelector接口实现类

    java 复制代码
    package com.example.autoconfig;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //需要导入的全限定类名
            return new String[] {"com.example.autoconfig.BiteConfig","com.example.autoconfig.BiteConfig2"};
        }
    }

    启动类

    java 复制代码
    @SpringBootApplication
    @Import(MyImportSelector.class)
    public class SpringPrincipleApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringPrincipleApplication.class, args);
        }
    
    }

3.2.3 第三方依赖提供注解

依赖中有哪些Bean,使用时需要配置哪些bean,第三方依赖最清楚,那能否由第三方依赖来做这件事呢?

  • 比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解

    java 复制代码
    package com.example.autoconfig;
    
    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(MyImportSelector.class)
    public @interface EnableBiteConfig {
    }
java 复制代码
@SpringBootApplication
@EnableBiteConfig
public class SpringPrincipleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }

}

4.总结

SpringBoot自动配置原理的大概流程如下:

当SpringBoot程序启动时,会加载配置文件当中所定义的配置类,通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。

相关推荐
v_cxsj8136 小时前
基于大数据的京东手机销售数据 可视化分析设计与开发03446原创的定制程序,java、PHP、python、C#小程序、文案全套、毕设程序定制、成品等
java·大数据·智能手机
saddhu.6 小时前
C++ 快速复习指南(上半部分)
java·c++·算法
唐天一6 小时前
Rust基础之异常
后端
毕设源码-赖学姐6 小时前
【开题答辩全过程】以 基于Spring Boot的网上家庭烹饪学习系统的设计与实现为例,包含答辩的问题和答案
spring boot·后端·学习
Moonbit6 小时前
MoonBit Pearls Vol.08: MoonBit 与 Python集成指南
后端·python·程序员
大厂码农老A6 小时前
凌晨零点,一个TODO,差点把我们整个部门抬走
java
写代码的大聪明6 小时前
Java Socket 短链接 自定义报文
java·tcp/ip
张三xy6 小时前
Java网络编程基础 Socket通信入门指南
java·开发语言·网络协议
刘祯昊6 小时前
中望CAD二次开发(一)——开发环境配置
后端·c#