spring IOC 反射 bean生命周期

news2025/4/5 19:34:34

目录

反射

反射三种方式

获取反射中的Class对象

通过反射创建类对象

通过反射获取类属性、方法、构造器

IOC

概念

原理

实现方式

基于 XML 配置

基于注解配置

IOC优点

IOC的初始化过程

1. 资源定位

3. Bean 定义注册

4. BeanFactory 后置处理

5. Bean 后置处理

6. Bean 实例化与初始化

7. 容器初始化完成

2. Bean 定义加载

图解 

​编辑

Spring 创建对象的过程

1. Bean 定义阶段

2. Bean 实例化阶段

3. Bean 属性填充阶段

4. Bean 初始化阶段

5. Bean 使用阶段

6. Bean 销毁阶段

Spring中Bean的作用域

1. 单例(Singleton)

2. 原型(Prototype)

3. 请求(Request)

4. 会话(Session)

5. 全局会话(Global Session)

6. 应用(Application)

在 Bean 注入中涉及多种设计模式。

单例 bean 的线程安全

1. 问题根源

2. 解决方案

方案一:设计无状态 Bean

方案二:使用线程安全的类

方案三:同步方法或代码块

方案四:使用 ThreadLocal

3. 最佳实践

4. 总结

Spring创建对象的过程中,如何解决循环依赖问题

循环依赖的类型

三级缓存机制

解决循环依赖的过程

1. 创建 A Bean

2. 创建 B Bean

3. 从缓存中获取 A

4. 完成 B Bean 的创建

5. 继续完成 A Bean 的创建

代码示例

总结

Bean的初始化和销毁阶段有哪些扩展点?

初始化阶段扩展点

1. 实现 InitializingBean 接口

2. 使用 @PostConstruct 注解

3. 自定义初始化方法

4. BeanPostProcessor 接口

销毁阶段扩展点

1. 实现 DisposableBean 接口

2. 使用 @PreDestroy 注解

3. 自定义销毁方法

执行顺序总结

@Component 和 @Bean 的区别是什么?

1. 使用场景

2. 使用方式

3. 作用目标

4. 灵活性

5. 生命周期管理

@Resource VS AutoWired bean的注入

来源

注入方式

属性

依赖

使用建议

Bean生命周期

BeanFactory

基本概念

主要实现类

工作原理

使用场景

与 ApplicationContext 的比较

BeanFactory和ApplicationContext的区别

BeanFactory 与 FactoryBean的区别

概念与定位

接口定义与使用方式

应用场景

总结


    反射

    Java反射是一种强大的机制,允许程序在运行时动态地获取类的内部信息,并直接操作类的属性和方法。通过反射,开发者可以在运行时检查类、接口、字段和方法,并调用这些方法或访问这些字段,而无需在编译时知道它们的名称。反射主要通过java.lang.reflect包实现,提供了一系列类和接口,用于获取和操作类及其成员。反射在许多框架和库中被广泛使用,例如Spring框架的依赖注入。

    反射在 Java 中主要通过 java.lang.reflect 包实现,这个包提供了一系列类和接口,用于在运行时获取和操作类及其成员。

    以下是 Java 反射的一些主要功能和用法:

    1. 获取类的信息:
      1. 使用 Class.forName(String className) 动态加载类,并返回对应的 Class 对象。
      2. 使用 Object.getClass() 获取对象的 Class 对象。
      3. 使用 Class<?> clazz = MyClass.class; 获取类的 Class 对象(静态方式)。
    2. 获取类的成员信息:
      1. 使用 Class.getMethods() 获取类的所有公共方法。
      2. 使用 Class.getDeclaredMethods() 获取类的所有方法(包括私有方法)。
      3. 使用 Class.getFields() 获取类的所有公共字段。
      4. 使用 Class.getDeclaredFields() 获取类的所有字段(包括私有字段)。
    3. 创建对象实例:
      1. 使用 Class.newInstance() 创建类的实例(需要无参构造函数)。
      2. 使用 Constructor<T>.newInstance(...) 创建类的实例(可以使用带参数的构造函数)。
    4. 调用方法:
      1. 使用 Method.invoke(Object obj, Object... args) 调用对象的方法。
    5. 访问和修改字段:
      1. 使用 Field.get(Object obj) 获取对象的字段值。
      2. 使用 Field.set(Object obj, Object value) 设置对象的字段值。

    参考:
                            Java的反射是什么?超详细+举例子+通俗易懂版!-CSDN博客

    反射三种方式

    获取反射中的Class对象

    在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。

    在 Java API 中,获取 Class 类对象有三种方法:

    第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

    Class clz = Class.forName("java.lang.String");
    

    第二种,使用 .class 方法。

    这种方法只适合在编译前就知道操作的 Class。

    Class clz = String.class;
    

    第三种,使用类对象的 getClass() 方法。

    String str = new String("Hello");
    Class clz = str.getClass();

    通过反射创建类对象

    通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

    第一种:通过 Class 对象的 newInstance() 方法。

    Class clz = Apple.class;
    Apple apple = (Apple)clz.newInstance();
    

    第二种:通过 Constructor 对象的 newInstance() 方法

    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor();
    Apple apple = (Apple)constructor.newInstance();
    

    通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor(String.class, int.class);
    Apple apple = (Apple)constructor.newInstance("红富士", 15);

    通过反射获取类属性、方法、构造器

    我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

    Class clz = Apple.class;
    Field[] fields = clz.getFields();
    for (Field field : fields) {
        System.out.println(field.getName());
    }
    

    输出结果是:

    price
    

    而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

    Class clz = Apple.class;
    Field[] fields = clz.getDeclaredFields();
    for (Field field : fields) {
        System.out.println(field.getName());
    }
    

    输出结果是:

    name
    price
    

    与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

    参考:大白话说Java反射:入门、使用、原理 - 陈树义 - 博客园

    IOC

    Spring IOC(Inversion of Control,控制反转)是 Spring 框架的核心特性之一,它极大地简化了 Java 应用程序的开发过程。

    概念

    在传统的 Java 开发中,对象的创建和依赖关系的管理通常由开发者手动控制。例如,一个类需要使用另一个类的实例时,会在该类内部使用 new 关键字来创建所需对象。而在 Spring IOC 中,对象的创建、初始化、销毁等生命周期管理工作不再由开发者负责,而是交给 Spring 容器来完成。这种将对象控制权从代码中转移到容器的思想就是控制反转。

    原理

    Spring IOC 的核心原理是基于反射机制和工厂模式。

    • 反射机制:Spring 容器利用 Java 的反射机制,在运行时动态地创建对象、调用方法和访问属性,而不需要在编译时就确定具体的类。
    • 工厂模式:Spring 容器充当一个工厂,它根据配置信息(如 XML 配置文件、Java 注解等)来创建和管理对象。开发者只需要告诉 Spring 容器需要哪些对象以及对象之间的依赖关系,容器就会负责创建和组装这些对象。

    实现方式

    Spring IOC 有两种主要的实现方式:基于 XML 配置和基于注解配置。

    基于 XML 配置

    在这种方式下,需要创建一个 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/beans
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 定义一个名为 userService 的 Bean -->
        <bean id="userService" class="com.example.service.UserService">
            <!-- 注入依赖的 userDao -->
            <property name="userDao" ref="userDao"/>
        </bean>
    
        <!-- 定义一个名为 userDao 的 Bean -->
        <bean id="userDao" class="com.example.dao.UserDaoImpl"/>
    
    </beans>
    

    在 Java 代码中加载 XML 配置文件并获取对象:

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
        public static void main(String[] args) {
            // 加载 XML 配置文件
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 从容器中获取 userService 对象
            UserService userService = (UserService) context.getBean("userService");
            userService.doSomething();
        }
    }
    

    基于注解配置

    使用注解可以更简洁地实现 IOC。

    常用的注解有 @Component@Service@Repository@Controller@Autowired 等。

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    // 使用 @Service 注解将该类标记为 Spring 管理的 Bean
    @Service
    public class UserService {
    
        private UserDao userDao;
    
        // 使用 @Autowired 注解进行依赖注入
        @Autowired
        public UserService(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void doSomething() {
            userDao.saveUser();
        }
    }
    
    import org.springframework.stereotype.Repository;
    
    // 使用 @Repository 注解将该类标记为 Spring 管理的 Bean
    @Repository
    public class UserDaoImpl implements UserDao {
        @Override
        public void saveUser() {
            System.out.println("Save user to database.");
        }
    }
    

    在 Java 代码中启用注解扫描并获取对象:

    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    // 启用组件扫描
    @ComponentScan(basePackages = "com.example")
    public class AppConfig {
        public static void main(String[] args) {
            // 创建注解配置的应用上下文
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
            // 从容器中获取 userService 对象
            UserService userService = context.getBean(UserService.class);
            userService.doSomething();
            // 关闭上下文
            context.close();
        }
    }
    

    IOC优点

    • 解耦:IOC 使得对象之间的依赖关系由容器来管理,降低了代码的耦合度。当需要更换依赖对象时,只需要修改配置文件或注解,而不需要修改大量的业务代码。
    • 可维护性:由于对象的创建和管理集中在容器中,代码的结构更加清晰,便于维护和扩展。
    • 可测试性:在进行单元测试时,可以方便地模拟对象的依赖关系,提高测试的效率和准确性。
    • 灵活性:可以根据不同的环境和需求,动态地配置对象的创建和依赖关系。
    1. Spring的IOC容器降低了业务对象之间的复杂性,让组件之间互相解耦。
    2. Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理,

    从而提高了更好的复用性

    1. Spring的高度开放性,并不强制应用完全依赖于Spring,

    开发者可自由选用Spring框架的部分或全部

    1. Spring的高度扩展性,让开发者可以轻易的让自己的框架在Spring上进行集成
    2. Spring的生态极其完整。

    IOC的初始化过程

    Spring IOC(控制反转)容器的初始化过程是一个复杂且关键的流程,它主要包含资源定位、Bean 定义加载、注册以及后续的处理等步骤,

    下面为你详细阐述其初始化过程:

    1. 资源定位

    这是初始化的起始步骤,Spring 容器需要明确从哪里获取 Bean 的定义信息,这些定义信息可以存在于多种形式的资源中,例如 XML 文件、Java 注解或者 Groovy 脚本等。

    3. Bean 定义注册

    将加载得到的 BeanDefinition 对象注册到 BeanDefinitionRegistry 中,BeanDefinitionRegistry 本质上是一个存储 BeanDefinition 的注册表,Spring 可以通过 Bean 的名称从这个注册表中获取对应的 BeanDefinition

    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
    import org.springframework.core.io.ClassPathResource;
    
    public class BeanDefinitionRegistrationExample {
        public static void main(String[] args) {
            // 创建 BeanFactory 作为 BeanDefinitionRegistry
            DefaultListableBeanFactory registry = new DefaultListableBeanFactory();
            // 创建 XML Bean 定义读取器
            XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
            // 从类路径下加载 XML 资源并注册 Bean 定义
            reader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
        }
    }
    

    4. BeanFactory 后置处理

    在 Bean 定义注册完成后,Spring 会调用 BeanFactoryPostProcessor 接口的实现类。这些后置处理器可以在 Bean 实例化之前对 BeanFactory 进行修改,例如修改 BeanDefinition 的属性。

    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            // 可以在这里修改 BeanDefinition
        }
    }
    

    5. Bean 后置处理

    在 Bean 实例化和属性注入之后,Spring 会调用 BeanPostProcessor 接口的实现类。BeanPostProcessor 可以在 Bean 初始化前后进行额外的处理,例如对 Bean 进行代理增强。

    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            // 在 Bean 初始化之前进行处理
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            // 在 Bean 初始化之后进行处理
            return bean;
        }
    }

    6. Bean 实例化与初始化

    • 实例化:根据 BeanDefinition 中的信息,使用反射机制创建 Bean 的实例。
    • 属性注入:将 Bean 所依赖的其他 Bean 或属性值注入到 Bean 中。可以通过构造函数注入、Setter 方法注入等方式实现。
    • 初始化:调用 Bean 的初始化方法,例如实现 InitializingBean 接口的 afterPropertiesSet 方法,或者使用 @PostConstruct 注解标注的方法。

    7. 容器初始化完成

    经过上述步骤,Spring IOC 容器完成初始化,此时可以从容器中获取已经创建好的 Bean 并使用。

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class ContainerInitializationCompletedExample {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 从容器中获取 Bean
            Object bean = context.getBean("exampleBean");
        }
    }
    

    综上所述,Spring IOC 容器的初始化是一个多步骤的过程,通过资源定位、加载、注册、后置处理、实例化和初始化等操作,最终创建并管理应用程序中的所有 Bean。

    常见配置bean有 XML 配置文件的形式或者注解形式等还有一些其他的方式。

    不管哪种方式,spring考虑到扩展性问题,会通过BeanDefinitionReader

    来加载bean的配置信息,

    然后生成一个BeanDefinition(bean的定义信息,用来存储 bean 的所有属性方法定义)

    BeanDefinitionReader 只是接口约束一些定义信息,常见的实现类

          XmlBeanDefinitionReader(xml形式),

    PropertiesBeanDefinitionReader(Properties配置文件),

    AbstractBeanDefinitionReader (相关一些环境信息)等

    BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,其实就是在bean的实例化之前,可以获取bean的定义信息,以及修改相关信息。

    比如说我们现在常见的注解方式来加载bean信息,里面其实就是也是用的BeanFactoryPostProcessor的子类实现的。

    常见的 @Service、@Controller、@Repository等注解其实都是组合注解,里面里面都是包含Component注解实现的

    因此当我们有自己的业务逻辑实现的时候也只需要实现BeanFactoryPostProcessor就可以了,然后加上@Component注解就可以了。

    • 基于 XML 配置:当使用 XML 配置文件时,Spring 会依据配置文件的路径去定位资源。比如,若使用 ClassPathXmlApplicationContext,它会从类路径下查找指定的 XML 文件。
    • import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class XmlBasedExample {
          public static void main(String[] args) {
              // 从类路径下定位 applicationContext.xml 文件
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
          }
      }
      

    • 基于注解配置:若采用注解配置,Spring 会扫描指定包及其子包下带有特定注解(如 @Component@Service 等)的类。使用 AnnotationConfigApplicationContext 时,需要指定配置类。
    • import org.springframework.context.ApplicationContext;
      import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      import org.springframework.context.annotation.ComponentScan;
      
      @ComponentScan(basePackages = "com.example")
      class AppConfig {}
      
      public class AnnotationBasedExample {
          public static void main(String[] args) {
              // 指定配置类,Spring 会扫描该类所在包及其子包
              ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
          }
      }
      

      2. Bean 定义加载

      在完成资源定位后,Spring 会解析这些资源,将其中定义的 Bean 信息加载到内存中,并转化为 BeanDefinition 对象。BeanDefinition 包含了 Bean 的各种元数据,如类名、作用域、依赖关系等。

    • XML 解析:对于 XML 配置文件,Spring 会使用 XmlBeanDefinitionReader 来解析 XML 文件,提取其中的 <bean> 标签信息,并创建对应的 BeanDefinition 对象。
    • 注解解析:对于注解配置,Spring 会使用 AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 来扫描带有注解的类,根据注解信息创建 BeanDefinition 对象。
    • 实例化:根据 BeanDefinition 中的信息,使用反射机制创建 Bean 的实例。
    • 属性注入:将 Bean 所依赖的其他 Bean 或属性值注入到 Bean 中。可以通过构造函数注入、Setter 方法注入等方式实现。
    • 初始化:调用 Bean 的初始化方法,例如实现 InitializingBean 接口的 afterPropertiesSet 方法,或者使用 @PostConstruct 注解标注的方法。

    图解 

    Spring 创建对象的过程

    Spring 创建对象的过程是一个复杂且有序的流程,主要涉及到 Bean 的定义、实例化、属性填充、初始化和销毁等阶段。下面为你详细介绍各个阶段:

    1. Bean 定义阶段

    在这个阶段,Spring 会读取配置信息(如 XML 文件、Java 注解等),将 Bean 的定义信息加载到内存中,并封装成 BeanDefinition 对象。BeanDefinition 包含了 Bean 的各种元数据,如类名、作用域、依赖关系等。

    • 基于 XML 配置:Spring 使用 XmlBeanDefinitionReader 解析 XML 文件,提取 <bean> 标签信息创建 BeanDefinition
    <bean id="userService" class="com.example.service.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="userDao" class="com.example.dao.UserDaoImpl"/>
    
    • 基于注解配置:Spring 使用 AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 扫描带有特定注解(如 @Component@Service 等)的类,根据注解信息创建 BeanDefinition
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
        // 类的具体实现
    }
    

    2. Bean 实例化阶段

    根据 BeanDefinition 中的信息,使用反射机制创建 Bean 的实例。Spring 提供了多种实例化方式:

    • 构造函数实例化:默认情况下,Spring 使用无参构造函数创建 Bean 实例。如果 Bean 类中没有无参构造函数,Spring 会尝试使用有参构造函数进行实例化,并自动注入所需的依赖。
    public class UserService {
        private UserDao userDao;
    
        public UserService(UserDao userDao) {
            this.userDao = userDao;
        }
        // 其他方法
    }
    
    • 静态工厂方法实例化:通过调用 Bean 类中的静态工厂方法来创建实例。
    public class UserServiceFactory {
        public static UserService createUserService() {
            return new UserService();
        }
    }
    

    在 XML 配置中指定静态工厂方法:

    <bean id="userService" class="com.example.factory.UserServiceFactory" factory-method="createUserService"/>
    
    • 实例工厂方法实例化:通过调用某个工厂 Bean 的实例方法来创建实例。
    public class UserServiceFactory {
        public UserService createUserService() {
            return new UserService();
        }
    }
    

    在 XML 配置中指定实例工厂方法:

    <bean id="userServiceFactory" class="com.example.factory.UserServiceFactory"/>
    <bean id="userService" factory-bean="userServiceFactory" factory-method="createUserService"/>
    

    3. Bean 属性填充阶段

    在 Bean 实例化完成后,Spring 会将 Bean 所依赖的其他 Bean 或属性值注入到 Bean 中。可以通过构造函数注入、Setter 方法注入等方式实现。

    • 构造函数注入:在创建 Bean 实例时,通过构造函数将依赖的 Bean 注入。
    public class UserService {
        private UserDao userDao;
    
        public UserService(UserDao userDao) {
            this.userDao = userDao;
        }
        // 其他方法
    }
    
    • Setter 方法注入:通过调用 Bean 的 Setter 方法将依赖的 Bean 注入。
    public class UserService {
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
        // 其他方法
    }
    

    在 XML 配置中进行属性注入:

    <bean id="userService" class="com.example.service.UserService">
        <property name="userDao" ref="userDao"/>
    </bean>
    

    4. Bean 初始化阶段

    在 Bean 的属性填充完成后,Spring 会进行一系列的初始化操作,包括调用初始化方法、执行 BeanPostProcessor 等。

    • 实现 InitializingBean 接口:实现 InitializingBean 接口的 afterPropertiesSet 方法,该方法会在 Bean 的属性设置完成后被调用。
    import org.springframework.beans.factory.InitializingBean;
    
    public class UserService implements InitializingBean {
        @Override
        public void afterPropertiesSet() throws Exception {
            // 初始化逻辑
        }
        // 其他方法
    }
    
    • 使用 @PostConstruct 注解:在方法上使用 @PostConstruct 注解,该方法会在 Bean 初始化时被调用。
    import javax.annotation.PostConstruct;
    
    public class UserService {
        @PostConstruct
        public void init() {
            // 初始化逻辑
        }
        // 其他方法
    }
    
    • 执行 BeanPostProcessorBeanPostProcessor 是 Spring 提供的一个扩展接口,允许在 Bean 初始化前后进行额外的处理。实现 BeanPostProcessor 接口的类会在所有 Bean 初始化前后被调用。
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            // 在 Bean 初始化之前进行处理
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            // 在 Bean 初始化之后进行处理
            return bean;
        }
    }
    

    5. Bean 使用阶段

    经过上述步骤,Bean 已经创建并初始化完成,可以被应用程序使用。可以通过 ApplicationContext 或 BeanFactory 的 getBean 方法获取 Bean 实例。

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Main {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.doSomething();
        }
    }
    

    6. Bean 销毁阶段

    当 Spring 容器关闭时,会对单例 Bean 进行销毁操作。可以通过实现 DisposableBean 接口的 destroy 方法或使用 @PreDestroy 注解来定义销毁逻辑。

    • 实现 DisposableBean 接口
    import org.springframework.beans.factory.DisposableBean;
    
    public class UserService implements DisposableBean {
        @Override
        public void destroy() throws Exception {
            // 销毁逻辑
        }
        // 其他方法
    }
    
    • 使用 @PreDestroy 注解
    import javax.annotation.PreDestroy;
    
    public class UserService {
        @PreDestroy
        public void cleanup() {
            // 销毁逻辑
        }
        // 其他方法
    }
    

    综上所述,Spring 创建对象的过程涉及多个阶段,通过这些阶段的有序执行,确保 Bean 能够正确地创建、初始化和销毁。

    使用 Spring 到底省略了我们什么工作?

    答:new 的过程。把 new 的过程交给第三方来创建、管理,这就是「解耦」。

    Spring 也是用的 set() 方法,它只不过提供了一套更加完善的实现机制而已。

    而说到底,底层的原理并没有很复杂,只是为了提高扩展性、兼容性,

    Spring 提供了丰富的支持,所以才觉得源码比较难。

    一分钟带你玩转 Spring IoC

    Spring中Bean的作用域

    1. singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。@Scope("singleton")
    2. prototype : 每次请求都会创建一个新的 bean 实例。原型      @Scope("prototype")
    3. request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 @RequestScope
    4. session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。 @SessionScope

    5. 全局会话(Global Session)  

    6.   应用(Application) @ApplicationScope

    @Bean
    @Scope("singleton") //@Scope声明 Spring Bean 的作用域
    public Person personSingleton() {
        return new Person();
    }

      在 Spring 框架里,Bean 的作用域定义了 Bean 实例在 Spring 容器中的生命周期和可见范围。Spring 提供了多种 Bean 作用域,以满足不同的应用场景需求。下面为你详细介绍 Spring 中常见的几种 Bean 作用域:

      1. 单例(Singleton)

      • 作用域描述:单例是 Spring 中默认的 Bean 作用域。当一个 Bean 的作用域为单例时,在整个 Spring 容器的生命周期里,只会创建该 Bean 的一个实例。所有对这个 Bean 的请求都会返回同一个实例。
      • 使用场景:适用于无状态的 Bean,像服务层、数据访问层的 Bean 通常可设为单例,因为它们不保存特定于某个请求的状态信息。
      • 示例代码
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      // 定义一个简单的 Bean 类
      class MySingletonBean {
          public MySingletonBean() {
              System.out.println("MySingletonBean 实例已创建");
          }
      }
      
      public class SingletonScopeExample {
          public static void main(String[] args) {
              // 加载 Spring 配置文件
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              // 从容器中获取 Bean 实例
              MySingletonBean bean1 = context.getBean(MySingletonBean.class);
              MySingletonBean bean2 = context.getBean(MySingletonBean.class);
              // 比较两个 Bean 实例是否相同
              System.out.println("bean1 和 bean2 是否为同一个实例: " + (bean1 == bean2));
          }
      }
      

      在 XML 配置文件中配置 Bean:

      xml

      <bean id="mySingletonBean" class="com.example.MySingletonBean" scope="singleton"/>
      

      或者使用注解配置:

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class AppConfig {
          @Bean
          public MySingletonBean mySingletonBean() {
              return new MySingletonBean();
          }
      }
      

      2. 原型(Prototype)

      • 作用域描述:当一个 Bean 的作用域为原型时,每次从 Spring 容器中请求该 Bean 时,容器都会创建一个新的实例。也就是说,对该 Bean 的每个请求都会返回一个不同的实例。
      • 使用场景:适用于有状态的 Bean,例如保存用户会话信息的 Bean,因为每个用户的会话信息是不同的,需要为每个用户创建一个新的实例。
      • 示例代码
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      // 定义一个简单的 Bean 类
      class MyPrototypeBean {
          public MyPrototypeBean() {
              System.out.println("MyPrototypeBean 实例已创建");
          }
      }
      
      public class PrototypeScopeExample {
          public static void main(String[] args) {
              // 加载 Spring 配置文件
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              // 从容器中获取 Bean 实例
              MyPrototypeBean bean1 = context.getBean(MyPrototypeBean.class);
              MyPrototypeBean bean2 = context.getBean(MyPrototypeBean.class);
              // 比较两个 Bean 实例是否相同
              System.out.println("bean1 和 bean2 是否为同一个实例: " + (bean1 == bean2));
          }
      }
      

      在 XML 配置文件中配置 Bean:

      <bean id="myPrototypeBean" class="com.example.MyPrototypeBean" scope="prototype"/>
      

      或者使用注解配置:

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Scope;
      
      @Configuration
      public class AppConfig {
          @Bean
          @Scope("prototype")
          public MyPrototypeBean myPrototypeBean() {
              return new MyPrototypeBean();
          }
      }
      

      3. 请求(Request)

      • 作用域描述:该作用域仅适用于 Web 应用的 Spring 环境。当一个 Bean 的作用域为请求时,在一次 HTTP 请求的生命周期内,只会创建该 Bean 的一个实例。不同的 HTTP 请求会创建不同的实例。
      • 使用场景:适用于需要在一次请求中保存状态信息的 Bean,例如处理用户请求参数的 Bean。
      • 示例代码
      import org.springframework.stereotype.Component;
      import org.springframework.web.context.annotation.RequestScope;
      
      // 定义一个请求作用域的 Bean
      @Component
      @RequestScope
      public class MyRequestBean {
          // Bean 的具体实现
      }
      

      在控制器中使用该 Bean:

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class MyController {
          @Autowired
          private MyRequestBean myRequestBean;
      
          @GetMapping("/test")
          public String test() {
              // 使用 MyRequestBean 处理请求
              return "Request processed";
          }
      }
      

      4. 会话(Session)

      • 作用域描述:同样仅适用于 Web 应用的 Spring 环境。当一个 Bean 的作用域为会话时,在一个用户的会话生命周期内,只会创建该 Bean 的一个实例。不同用户的会话会创建不同的实例。
      • 使用场景:适用于需要在用户会话期间保存状态信息的 Bean,例如保存用户登录信息的 Bean。
      • 示例代码
      import org.springframework.stereotype.Component;
      import org.springframework.web.context.annotation.SessionScope;
      
      // 定义一个会话作用域的 Bean
      @Component
      @SessionScope
      public class MySessionBean {
          // Bean 的具体实现
      }
      

      在控制器中使用该 Bean:

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      public class MySessionController {
          @Autowired
          private MySessionBean mySessionBean;
      
          @GetMapping("/session-test")
          public String sessionTest() {
              // 使用 MySessionBean 处理会话信息
              return "Session processed";
          }
      }
      

      5. 全局会话(Global Session)

      • 作用域描述:该作用域主要用于 Portlet 应用环境(Portlet 是一种可插拔的 Web 组件)。在全局会话中,整个 Portlet 应用的生命周期内,只会创建该 Bean 的一个实例。
      • 使用场景:适用于 Portlet 应用中需要在全局会话中共享状态信息的 Bean。

      6. 应用(Application)

      • 作用域描述:在 Servlet 上下文的生命周期内,只会创建该 Bean 的一个实例。类似于单例作用域,但单例作用域是针对 Spring 容器,而应用作用域是针对 Servlet 上下文。
      • 使用场景:适用于需要在整个 Web 应用中共享状态信息的 Bean,例如应用级别的配置信息。
      • 示例代码
      import org.springframework.stereotype.Component;
      import org.springframework.web.context.annotation.ApplicationScope;
      
      // 定义一个应用作用域的 Bean
      @Component
      @ApplicationScope
      public class MyApplicationBean {
          // Bean 的具体实现
      }
      

      综上所述,Spring 提供的这些 Bean 作用域可以满足不同场景下对 Bean 生命周期和可见范围的需求,开发者可根据具体业务需求选择合适的作用域。

      在 Bean 注入中涉及多种设计模式。

      首先是工厂模式简单工厂模式中,通过一个工厂类来创建 Bean 对象,根据传入的参数决定创建哪种具体的 Bean。工厂方法模式则是在抽象工厂类中定义创建 Bean 的抽象方法,由具体的工厂子类来实现,这样可以更灵活地创建不同类型的 Bean。抽象工厂模式可创建一系列相关的 Bean 对象,用于复杂的依赖关系。这些工厂模式将对象的创建和使用分离,方便统一管理和控制 Bean 的生成。

      其次是单例模式。Spring 容器中的单例 Bean 采用了单例模式的思想,在整个容器的生命周期内,对于单例 Bean 只会创建一次。通过容器来管理单例 Bean 的生命周期,既保证了资源的有效利用,又方便了对单例 Bean 的访问和维护。

      依赖注入模式本身也是一种设计模式。它将 Bean 之间的依赖关系从硬编码转变为通过容器进行管理。通过构造函数注入、 setter 注入和字段注入等方式,把一个 Bean 需要的依赖对象注入进去,使 Bean 之间的依赖关系更加清晰、可维护。

      代理模式也在 Bean 注入中有应用。在 AOP(Aspect - J Auto Configuration)场景下,当需要为 Bean 添加额外的功能(如事务管理、日志记录)时,会使用代理模式。Spring 通过创建代理 Bean 来代替原 Bean 执行相关操作,在不改变原 Bean 代码的基础上实现功能扩展,其中 JDK 代理和 CGLIB 代理是常用的代理方式。

      装饰器模式也存在于 Bean 注入。当需要对现有 Bean 的功能进行修饰和扩展时,可采用装饰器模式。通过创建装饰器类,在装饰器类中包含被装饰的 Bean,然后在装饰器类中对原 Bean 的功能进行补充或修改,这种模式可以灵活地改变 Bean 的功能特性。

      单例 bean 的线程安全

      大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。

      单例 bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候

      对这个对象的非静态成员变量的写操作会存在线程安全问题。

      常见的有两种解决办法

      1. 在Bean定义局部对象,尽量避免定义可变的成员变量。
      2. 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

      1. 问题根源

      • 单例 Bean 的特性:Spring 容器中仅存在一个实例,所有线程共享该实例。
      • 线程安全隐患:如果 Bean 中存在可变状态(如成员变量),多个线程并发访问时可能导致数据不一致。

      2. 解决方案

      方案一:设计无状态 Bean
      • 核心思想:Bean 不保存任何状态(即没有成员变量)。
      • 示例
        @Component
        public class StatelessService {
            public void process() {
                // 仅依赖局部变量或方法参数,无共享状态
            }
        }
        
      • 优点:天然线程安全,无需额外处理。
      • 适用场景:服务层、DAO 层等无状态组件。
      方案二:使用线程安全的类
      • 核心思想:依赖线程安全的类(如 ConcurrentHashMapAtomicInteger)存储状态。
      • 示例
        @Component
        public class ThreadSafeCounter {
            private final AtomicInteger count = new AtomicInteger(0);
            
            public int increment() {
                return count.incrementAndGet();
            }
        }
        
      • 优点:利用 JDK 提供的线程安全机制,避免同步开销。
      方案三:同步方法或代码块
      • 核心思想:通过 synchronized 关键字或 ReentrantLock 保证线程安全。
      • 示例
        @Component
        public class SynchronizedService {
            private int count = 0;
            
            public synchronized void increment() {
                count++;
            }
        }
        
      • 缺点:可能成为性能瓶颈,需谨慎使用。
      方案四:使用 ThreadLocal
      • 核心思想:为每个线程隔离状态。
      • 示例

        @Component
        public class ThreadLocalService {
            private final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
            
            public void increment() {
                threadLocal.set(threadLocal.get() + 1);
            }
        }
        
      • 适用场景:需要为每个线程独立保存状态的场景。

      3. 最佳实践

      1. 优先设计无状态 Bean:这是最简单且高效的方式。
      2. 避免共享可变状态:如果必须使用状态,优先选择线程安全类或同步机制。
      3. 谨慎使用作用域:若无法保证线程安全,可考虑将 Bean 作用域改为 prototype(每次请求创建新实例),但会增加资源消耗。

      4. 总结

      • 单例 Bean 本身并不必然不安全,只有存在可变状态时才需处理线程安全。
      • Spring 框架不强制要求单例 Bean 线程安全,需开发者根据业务逻辑合理设计。

      Spring创建对象的过程中,如何解决循环依赖问题

      在 Spring 创建对象的过程中,循环依赖指的是两个或多个 Bean 之间相互依赖,形成一个循环引用的情况。Spring 主要通过三级缓存机制来解决单例 Bean 的循环依赖问题,下面为你详细介绍。

      循环依赖的类型

      • 构造器循环依赖:通过构造函数注入形成的循环依赖。例如,A 的构造函数需要 B 的实例,而 B 的构造函数又需要 A 的实例,这种情况 Spring 无法解决,会抛出 BeanCurrentlyInCreationException 异常。
      • Setter 循环依赖:通过 Setter 方法注入形成的循环依赖。Spring 可以通过三级缓存机制解决这种类型的循环依赖。

      三级缓存机制

      Spring 的三级缓存分别是:

      • 一级缓存(singletonObjects):单例对象缓存,存储已经创建好的单例 Bean 实例。
      • 二级缓存(singletonFactories):单例工厂缓存,存储创建单例 Bean 的工厂对象。
      • 三级缓存(earlySingletonObjects):提前曝光的单例对象缓存,存储提前曝光的、还未完全初始化的单例 Bean 实例。

      解决循环依赖的过程

      假设存在 A 和 B 两个 Bean,A 依赖 BB 依赖 A,以下是 Spring 解决它们之间循环依赖的详细过程:

      1. 创建 A Bean
      • 实例化 A:Spring 开始创建 A Bean,首先调用 A 的构造函数实例化 A,此时 A 是一个未完全初始化的对象。
      • 将 A 的工厂对象放入三级缓存:创建一个 ObjectFactory 工厂对象,该工厂对象可以返回 A 的早期引用(即未完全初始化的 A 对象),并将其放入三级缓存 singletonFactories 中。
      • 填充 A 的属性:在填充 A 的属性时,发现 A 依赖 B,于是开始创建 B Bean。
      2. 创建 B Bean
      • 实例化 B:Spring 调用 B 的构造函数实例化 B,同样 B 是一个未完全初始化的对象。
      • 将 B 的工厂对象放入三级缓存:创建一个 ObjectFactory 工厂对象,该工厂对象可以返回 B 的早期引用,并将其放入三级缓存 singletonFactories 中。
      • 填充 B 的属性:在填充 B 的属性时,发现 B 依赖 A,于是从缓存中查找 A
      3. 从缓存中获取 A
      • 检查一级缓存:发现一级缓存 singletonObjects 中没有 A
      • 检查二级缓存:发现二级缓存 earlySingletonObjects 中也没有 A
      • 检查三级缓存:从三级缓存 singletonFactories 中获取 A 的工厂对象,调用该工厂对象的 getObject() 方法得到 A 的早期引用,将 A 的早期引用放入二级缓存 earlySingletonObjects 中,并从三级缓存 singletonFactories 中移除 A 的工厂对象。
      • 将 A 的早期引用注入到 B 中:将 A 的早期引用注入到 B 的属性中,完成 B 的属性填充。
      4. 完成 B Bean 的创建
      • 初始化 B:调用 B 的初始化方法,完成 B 的初始化。
      • 将 B 放入一级缓存:将完全初始化好的 B 实例放入一级缓存 singletonObjects 中,并从二级缓存 earlySingletonObjects 中移除 B
      5. 继续完成 A Bean 的创建
      • 将 B 注入到 A 中:从一级缓存 singletonObjects 中获取完全初始化好的 B 实例,将其注入到 A 的属性中,完成 A 的属性填充。
      • 初始化 A:调用 A 的初始化方法,完成 A 的初始化。
      • 将 A 放入一级缓存:将完全初始化好的 A 实例放入一级缓存 singletonObjects 中,并从二级缓存 earlySingletonObjects 中移除 A

      代码示例

      以下是一个简单的代码示例,演示了 A 和 B 之间的循环依赖:

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      
      @Component
      public class A {
          @Autowired
          private B b;
      
          public B getB() {
              return b;
          }
      }
      
      @Component
      public class B {
          @Autowired
          private A a;
      
          public A getA() {
              return a;
          }
      }
      
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.annotation.AnnotationConfigApplicationContext;
      import org.springframework.context.annotation.ComponentScan;
      
      @ComponentScan(basePackages = "com.example")
      public class Main {
          public static void main(String[] args) {
              ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
              A a = context.getBean(A.class);
              B b = context.getBean(B.class);
              System.out.println("A 依赖的 B: " + a.getB());
              System.out.println("B 依赖的 A: " + b.getA());
          }
      }
      

      在上述代码中,A 和 B 通过 @Autowired 注解实现了 Setter 注入的循环依赖,Spring 会使用三级缓存机制解决这个循环依赖问题。

      总结

      Spring 的三级缓存机制通过提前曝光未完全初始化的 Bean 实例,解决了单例 Bean 的 Setter 循环依赖问题。但对于构造器循环依赖,Spring 无法解决,需要开发者通过调整代码结构来避免。

      自己理解

      解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景

      原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常。

      首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。

      在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:

      1. singletonObjects 俗称“单例池”,容器,缓存创建完成单例Bean的地方。
      2. singletonFactories  映射创建Bean的原始工厂
      3. earlySingletonObjects 映射Bean的早期引用,

      也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,

      只是一个Instance.后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。

      spring 使用三级缓存去解决循环依赖的,

      其「核心逻辑就是把实例化和初始化的步骤分开,然后放入缓存中」,供另一个对象调用

      「第一级缓存」:用来保存实例化、初始化都完成的对象

      「第二级缓存」:用来保存实例化完成,但是未初始化完成的对象

      「第三级缓存」:用来保存一个对象工厂,提供一个匿名内部类,

      用于创建二级缓存中的对象

      假设只设计二级缓存能否解决循环依赖?

      只用二级缓存是可以解决缓存依赖的,(废弃第三级,保留第一第二)

      但是会有一个问题,在配置AOP切面的时候会出错,因为无法生成代理对象。

      所以三级缓存是为了处理AOP中的循环依赖。因为当配置了切面之后,在getEarlyBeanReference方法中,有可能会把之前的原始对象替换成代理对象,导致Bean的版本不是最终的版本,所以报错。

      什么情况下循环依赖可以被处理?

      在回答这个问题之前首先要明确一点,Spring解决循环依赖是有前置条件的

      1. 出现循环依赖的Bean必须要是单例
      2. 依赖注入的方式不能全是构造器注入的方式(很多博客上说,

      只能解决setter方法的循环依赖,这是错误的)-》@lazy注解

      Spring Boot 2.6及之后版本取消了循环依赖的支持

      Bean的初始化和销毁阶段有哪些扩展点?

      在 Spring 中,Bean 的初始化和销毁阶段提供了多个扩展点,允许开发者在特定时机执行自定义逻辑。下面将分别介绍初始化和销毁阶段的扩展点。

      初始化阶段扩展点

      1. 实现 InitializingBean 接口
      • 原理InitializingBean 接口定义了 afterPropertiesSet() 方法,Spring 容器在 Bean 的属性设置完成后会自动调用该方法。
      • 示例代码
      import org.springframework.beans.factory.InitializingBean;
      import org.springframework.stereotype.Component;
      
      @Component
      public class MyBean implements InitializingBean {
      
          @Override
          public void afterPropertiesSet() throws Exception {
              // 在这里编写初始化逻辑
              System.out.println("MyBean 初始化完成,执行 afterPropertiesSet 方法");
          }
      }
      
      2. 使用 @PostConstruct 注解
      • 原理@PostConstruct 是 JSR-250 规范定义的注解,Spring 支持该注解。被 @PostConstruct 注解标注的方法会在 Bean 实例化和属性注入完成后,在 InitializingBean.afterPropertiesSet() 方法之前执行。
      • 示例代码
      import javax.annotation.PostConstruct;
      import org.springframework.stereotype.Component;
      
      @Component
      public class MyBeanWithPostConstruct {
      
          @PostConstruct
          public void init() {
              // 在这里编写初始化逻辑
              System.out.println("MyBeanWithPostConstruct 执行 @PostConstruct 注解标注的方法");
          }
      }
      
      3. 自定义初始化方法
      • 原理:在 XML 配置或 Java 配置中指定 Bean 的初始化方法,Spring 容器会在 Bean 的属性设置完成后调用该方法。
      • XML 配置示例
      <bean id="myBean" class="com.example.MyBean" init-method="customInit"/>
      
      public class MyBean {
      
          public void customInit() {
              // 在这里编写初始化逻辑
              System.out.println("MyBean 执行自定义初始化方法 customInit");
          }
      }
      
      • Java 配置示例
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class AppConfig {
      
          @Bean(initMethod = "customInit")
          public MyBean myBean() {
              return new MyBean();
          }
      }
      
      4. BeanPostProcessor 接口
      • 原理BeanPostProcessor 是 Spring 提供的一个强大的扩展接口,它允许开发者在 Bean 初始化前后进行额外的处理。BeanPostProcessor 接口定义了两个方法:postProcessBeforeInitialization() 和 postProcessAfterInitialization(),分别在 Bean 初始化之前和之后被调用。
      • 示例代码
      import org.springframework.beans.BeansException;
      import org.springframework.beans.factory.config.BeanPostProcessor;
      import org.springframework.stereotype.Component;
      
      @Component
      public class MyBeanPostProcessor implements BeanPostProcessor {
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
              // 在 Bean 初始化之前执行的逻辑
              System.out.println("Before initializing bean: " + beanName);
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
              // 在 Bean 初始化之后执行的逻辑
              System.out.println("After initializing bean: " + beanName);
              return bean;
          }
      }
      

      销毁阶段扩展点

      1. 实现 DisposableBean 接口
      • 原理DisposableBean 接口定义了 destroy() 方法,Spring 容器在销毁 Bean 时会自动调用该方法。
      • 示例代码
      import org.springframework.beans.factory.DisposableBean;
      import org.springframework.stereotype.Component;
      
      @Component
      public class MyDisposableBean implements DisposableBean {
      
          @Override
          public void destroy() throws Exception {
              // 在这里编写销毁逻辑
              System.out.println("MyDisposableBean 执行 destroy 方法");
          }
      }
      
      2. 使用 @PreDestroy 注解
      • 原理@PreDestroy 是 JSR-250 规范定义的注解,Spring 支持该注解。被 @PreDestroy 注解标注的方法会在 Bean 销毁之前执行。
      • 示例代码
      import javax.annotation.PreDestroy;
      import org.springframework.stereotype.Component;
      
      @Component
      public class MyBeanWithPreDestroy {
      
          @PreDestroy
          public void cleanup() {
              // 在这里编写销毁逻辑
              System.out.println("MyBeanWithPreDestroy 执行 @PreDestroy 注解标注的方法");
          }
      }
      
      3. 自定义销毁方法
      • 原理:在 XML 配置或 Java 配置中指定 Bean 的销毁方法,Spring 容器会在销毁 Bean 时调用该方法。
      • XML 配置示例
      <bean id="myBean" class="com.example.MyBean" destroy-method="customDestroy"/>
      
      public class MyBean {
      
          public void customDestroy() {
              // 在这里编写销毁逻辑
              System.out.println("MyBean 执行自定义销毁方法 customDestroy");
          }
      }
      

      • Java 配置示例
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class AppConfig {
      
          @Bean(destroyMethod = "customDestroy")
          public MyBean myBean() {
              return new MyBean();
          }
      }
      

      执行顺序总结

      • 初始化阶段@PostConstruct 注解标注的方法 -> InitializingBean.afterPropertiesSet() 方法 -> 自定义初始化方法 -> BeanPostProcessor.postProcessAfterInitialization() 方法
      • 销毁阶段@PreDestroy 注解标注的方法 -> DisposableBean.destroy() 方法 -> 自定义销毁方法

      通过这些扩展点,开发者可以灵活地控制 Bean 的初始化和销毁过程,实现自定义的业务逻辑。

      @Component 和 @Bean 的区别是什么?

      在 Spring 框架里,@Component 和 @Bean 都能用于将对象纳入 Spring 容器的管理,但它们在使用场景、使用方式、作用目标等方面存在差异,下面为你详细介绍:

      1. 使用场景

      • @Component
        • 主要用于类级别,是一个通用的组件注解。当一个类被 @Component 注解标注时,Spring 会自动扫描并将其作为一个 Bean 注册到容器中。
        • 适用于那些可以自动发现并注册的组件,如服务层(@Service 是 @Component 的派生注解)、数据访问层(@Repository 是 @Component 的派生注解)、控制器层(@Controller 是 @Component 的派生注解)等。
      • @Bean
        • 通常用于方法级别,用于告诉 Spring 这个方法会返回一个 Bean 对象,需要将其注册到 Spring 容器中。
        • 适用于以下场景:
          • 当需要引入第三方库中的类作为 Bean 时,由于无法直接在第三方类上添加 @Component 注解,就可以使用 @Bean 方法将其注册到 Spring 容器中。
          • 当创建 Bean 的过程比较复杂,需要在方法中编写额外的逻辑时,使用 @Bean 方法可以灵活控制 Bean 的创建过程。

      2. 使用方式

      • @Component
        • 直接在类上添加 @Component 注解,同时需要确保 Spring 的组件扫描功能开启。一般会在配置类上使用 @ComponentScan 注解指定要扫描的包路径。

      import org.springframework.stereotype.Component;
      
      // 使用 @Component 注解标注类
      @Component
      public class MyComponent {
          public void doSomething() {
              System.out.println("Doing something...");
          }
      }
      

      import org.springframework.context.annotation.ComponentScan;
      import org.springframework.context.annotation.Configuration;
      
      // 开启组件扫描
      @Configuration
      @ComponentScan(basePackages = "com.example")
      public class AppConfig {
          // 配置类的其他内容
      }
      

      • @Bean
        • 在配置类的方法上添加 @Bean 注解,该方法返回一个 Bean 对象。配置类需要使用 @Configuration 注解标注。
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      // 使用 @Configuration 注解标注配置类
      @Configuration
      public class AppConfig {
          // 使用 @Bean 注解标注方法
          @Bean
          public MyBean myBean() {
              return new MyBean();
          }
      }
      
      class MyBean {
          public void doSomething() {
              System.out.println("MyBean is doing something...");
          }
      }
      

      3. 作用目标

      • @Component
        • 作用于类,表明这个类是一个 Spring 组件,Spring 会自动创建该类的实例并注册到容器中。
      • @Bean
        • 作用于方法,通过调用该方法返回的对象会被注册为 Spring Bean。可以在方法中对 Bean 的创建过程进行定制,如设置 Bean 的属性、调用初始化方法等。

      4. 灵活性

      • @Component
        • 相对来说灵活性较低,因为它只是简单地将类标记为组件,Spring 会按照默认的方式创建和管理 Bean。对于 Bean 的创建过程和属性设置,开发者无法进行太多的控制。
      • @Bean
        • 灵活性较高,开发者可以在 @Bean 方法中编写任意的逻辑来创建和配置 Bean。例如,可以根据不同的条件创建不同类型的 Bean,或者在创建 Bean 之前进行一些初始化操作。

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class AppConfig {
          @Bean
          public MyBean myBean() {
              MyBean bean = new MyBean();
              // 可以在这里对 Bean 进行额外的配置
              bean.setProperty("value");
              return bean;
          }
      }
      

      5. 生命周期管理

      • @Component
        • Spring 会自动管理被 @Component 注解标注的 Bean 的生命周期,包括创建、初始化和销毁。开发者可以通过实现 InitializingBeanDisposableBean 接口或使用 @PostConstruct@PreDestroy 注解来定制 Bean 的初始化和销毁逻辑。
      • @Bean
        • 同样可以使用 @PostConstruct@PreDestroy 注解来定制 Bean 的初始化和销毁逻辑。此外,还可以在 @Bean 注解中指定 initMethod 和 destroyMethod 属性来指定自定义的初始化和销毁方法。

      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      @Configuration
      public class AppConfig {
          @Bean(initMethod = "init", destroyMethod = "destroy")
          public MyBean myBean() {
              return new MyBean();
          }
      }
      
      class MyBean {
          public void init() {
              System.out.println("MyBean is initializing...");
          }
      
          public void destroy() {
              System.out.println("MyBean is destroying...");
          }
      }
      

      综上所述,@Component 和 @Bean 各有其适用场景,开发者可以根据具体需求选择合适的方式来将对象注册到 Spring 容器中。

      作用对象不同: 

      @Component 注解作用于类

      而@Bean注解作用于方法

      @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中,

      (我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。

      @Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,

      @Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。

      @Bean 注解比 Component 注解的自定义性更强,

      而且很多地方我们只能通过 @Bean 注解来注册bean。

      比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

      @Resource VS AutoWired bean的注入

      @Resource 和 @Autowired 都可用于在 Spring 框架里进行 Bean 的依赖注入,不过它们在来源、注入方式、属性等方面存在差异,下面为你详细介绍:

      来源

      • @Autowired:它是 Spring 框架自身定义的注解,专门用于实现依赖注入。
      • @Resource:属于 JSR-250 规范,是 Java 标准注解,这意味着它并非 Spring 特有,在其他支持 JSR-250 规范的框架中也能使用。

      注入方式

      • @Autowired:默认采用按类型(byType)注入的方式。也就是说,Spring 会依据被注入属性的类型,在容器中查找匹配的 Bean 进行注入。若容器中存在多个相同类型的 Bean,会抛出 NoUniqueBeanDefinitionException 异常。不过,你可以结合 @Qualifier 注解按名称(byName)来指定要注入的 Bean。
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.stereotype.Component;
      
      @Component
      class MyService {
          // 使用 @Autowired 按类型注入
          @Autowired
          private MyRepository myRepository;
      
          // 结合 @Qualifier 按名称注入
          @Autowired
          @Qualifier("specificRepository")
          private MyRepository specificRepository;
      }
      

      • @Resource:默认按名称(byName)注入。它会先根据属性名在容器中查找同名的 Bean 进行注入。若找不到同名的 Bean,再尝试按类型(byType)注入。
      import javax.annotation.Resource;
      import org.springframework.stereotype.Component;
      
      @Component
      class MyService {
          // 使用 @Resource 按名称注入
          @Resource
          private MyRepository myRepository;
      
          // 手动指定名称注入
          @Resource(name = "specificRepository")
          private MyRepository specificRepository;
      }
      

      属性

      • @Autowired:它有一个 required 属性,其默认值为 true,这表明注入的 Bean 必须存在,若不存在会抛出异常。若将 required 属性设置为 false,当容器中不存在匹配的 Bean 时,不会抛出异常,而是将该属性值设为 null
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      
      @Component
      class MyService {
          // 设置 required 为 false
          @Autowired(required = false)
          private MyRepository myRepository;
      }
      

      • @Resource:具备 name 和 type 等属性。name 属性用于指定要注入的 Bean 的名称,type 属性用于指定要注入的 Bean 的类型。
      import javax.annotation.Resource;
      import org.springframework.stereotype.Component;
      
      @Component
      class MyService {
          // 指定 name 属性
          @Resource(name = "myRepository")
          private MyRepository myRepository;
      
          // 指定 type 属性
          @Resource(type = MyRepository.class)
          private MyRepository anotherRepository;
      }
      

      依赖

      • @Autowired:使用时需要依赖 Spring 框架,若项目中没有引入 Spring 相关依赖,该注解将无法使用。
      • @Resource:由于它是 Java 标准注解,所以不需要额外依赖 Spring 框架,只要 Java 运行环境支持 JSR-250 规范即可使用。

      使用建议

      • 若你的项目仅使用 Spring 框架,且注重 Spring 自身的特性和功能,推荐使用 @Autowired 注解。
      • 若项目需要遵循 Java 标准规范,或者可能会在不同框架间进行迁移,使用 @Resource 注解会更合适。

      综上所述,@Autowired 和 @Resource 都能实现 Bean 的依赖注入,开发者可根据项目需求和具体场景选择合适的注解。

       共同点:@Resource和@Autowired都可以作为注入属性的修饰,在接口仅有单一实现类时,两个注解的修饰效果相同,可以互相替换,不影响使用。

      不同点:@Resource是Java自己的注解,

      @Resource有两个属性是比较重要的,分是name和type;

      Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型

      所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略

      @Autowired是spring的注解,是spring2.5版本引入的,Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰

      Autowired能够用在:构造器、方法、参数、成员变量和注解上,而

      @Resource能用用在:、成员变量和方法。

      https://zhuanlan.zhihu.com/p/425124701

      Bean生命周期

      给学妹看的SpringIOC 面试题(上)

      ApplicationContext.refresh()**这其中的包含了13个子方法

      BeanFactory

      从名字上也很好理解,生产 bean 的工厂,

      它负责生产和管理各个 bean 实例。

      同时也是Spring容器暴露在外获取bean的入口。

      BeanFactory的生产过程其实是利用反射机制实现的。

      BeanFactory 是 Spring 框架中 IOC(控制反转)容器的核心接口,它负责创建、管理和维护 Bean 对象。以下从基本概念、主要实现类、工作原理、使用场景几个方面详细介绍。

      基本概念

      在 Spring 里,Bean 指的是由 Spring 容器管理的对象。BeanFactory 作为 Spring 最基础的容器,提供了创建和获取 Bean 的基本功能,它定义了一套标准的 Bean 管理机制,使得开发者能够以统一的方式来管理应用中的对象。

      主要实现类

      • XmlBeanFactory:这是早期的一个实现类,主要用于从 XML 配置文件中读取 Bean 的定义信息。不过在 Spring 3.1 版本之后已被弃用。示例代码如下:
      import org.springframework.beans.factory.xml.XmlBeanFactory;
      import org.springframework.core.io.ClassPathResource;
      
      // 注意:此方式已不推荐使用
      @SuppressWarnings("deprecation")
      public class XmlBeanFactoryExample {
          public static void main(String[] args) {
              XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
              Object bean = factory.getBean("exampleBean");
          }
      }
      
      • DefaultListableBeanFactory:它是 BeanFactory 接口的一个重要实现类,是一个功能完整的 Bean 工厂,支持多种 Bean 定义方式,如 XML、注解等。同时,它也是 ApplicationContext 内部使用的基础 Bean 工厂。
      • ApplicationContext虽然 ApplicationContext 是 BeanFactory 的子接口,但它在 BeanFactory 的基础上进行了扩展,提供了更多的企业级特性,如国际化支持、事件发布等。常见的实现类有 ClassPathXmlApplicationContextFileSystemXmlApplicationContextAnnotationConfigApplicationContext 等。

      工作原理

      BeanFactory 的工作原理主要包含以下几个关键步骤:

      1. 资源定位:确定从哪里获取 Bean 的定义信息,比如 XML 文件、Java 注解等。
      2. Bean 定义加载:解析资源,将 Bean 的定义信息加载到内存中,转化为 BeanDefinition 对象,该对象包含了 Bean 的类名、作用域、依赖关系等元数据。
      3. Bean 注册:把 BeanDefinition 对象注册到 BeanDefinitionRegistry 中,BeanDefinitionRegistry 是一个存储 BeanDefinition 的注册表。
      4. Bean 创建与获取:当调用 getBean 方法获取 Bean 时,BeanFactory 会根据 BeanDefinition 信息创建 Bean 实例,并进行属性注入和初始化操作。

      使用场景

      • 简单应用场景:对于一些简单的 Java 应用,只需要基本的 Bean 管理功能时,可以直接使用 DefaultListableBeanFactory。示例代码如下:
      import org.springframework.beans.factory.support.DefaultListableBeanFactory;
      import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
      import org.springframework.core.io.ClassPathResource;
      
      public class DefaultListableBeanFactoryExample {
          public static void main(String[] args) {
              // 创建 DefaultListableBeanFactory 实例
              DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
              // 创建 XML Bean 定义读取器
              XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
              // 从类路径下加载 XML 资源并注册 Bean 定义
              reader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
              // 获取 Bean
              Object bean = factory.getBean("exampleBean");
          }
      }
      

      • 企业级应用场景:在企业级应用中,通常会使用 ApplicationContext,因为它提供了更多的高级特性,如 AOP 支持、事件发布、国际化等。示例代码如下:
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class ApplicationContextExample {
          public static void main(String[] args) {
              // 从类路径下加载 XML 配置文件创建 ApplicationContext
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              // 获取 Bean
              Object bean = context.getBean("exampleBean");
          }
      }
      

      与 ApplicationContext 的比较

      • 功能丰富度BeanFactory 是 Spring 最基础的容器,提供了基本的 Bean 管理功能;而 ApplicationContext 在 BeanFactory 的基础上进行了扩展,提供了更多的企业级特性。
      • 加载时机BeanFactory 采用懒加载机制,只有在调用 getBean 方法时才会创建 Bean 实例;而 ApplicationContext 在容器启动时就会创建所有的单例 Bean 实例。
      • 使用场景BeanFactory 适用于资源受限的环境或对性能要求较高的场景;ApplicationContext 适用于大多数企业级应用场景。

      BeanFactory和ApplicationContext的区别

      BeanFactory是一个底层的IOC容器,

      ApplicationContext是在其基础上增加了一些它的特性的,

      同时增加了一些其他的整合特性比如:更好的整合SpringAOP、

      国际化消息、以及事务的发布、资源访问等这些新的特性

      BeanFactory 与 FactoryBean的区别

      「BeanFactory 是 IOC 容器」,是用来承载对象的

      「FactoryBean 是一个接口」,为 Bean 提供了更加灵活的方式,

      通过代理一个Bean对象,对方法前后做一些操作。

      BeanFactory 和 FactoryBean 是 Spring 框架中两个不同的概念,它们在功能和用途上有着明显的区别,以下为你详细介绍:

      概念与定位
      • BeanFactory
        • BeanFactory 是 Spring 框架中 IOC 容器的核心接口,它定义了 Spring 容器的基本功能,负责创建、管理和维护 Bean 对象。BeanFactory 是 Spring 最基础的容器,为上层应用提供了统一的 Bean 管理机制。
        • 简单来说,BeanFactory 是一个工厂,负责生产和管理各种 Bean 实例,是整个 Spring IOC 容器体系的基础。
      • FactoryBean
        • FactoryBean 是一个特殊的 Bean,它本身是一个工厂,可以用来创建其他 Bean 实例。FactoryBean 是 Spring 提供的一种扩展机制,允许开发者自定义 Bean 的创建逻辑。
        • 当一个类实现了 FactoryBean 接口时,Spring 会将其作为一个工厂 Bean 来处理,通过该工厂 Bean 可以创建出其他类型的 Bean 实例。
      接口定义与使用方式
      • BeanFactory
        • BeanFactory 接口定义了一系列方法,如 getBean 用于获取 Bean 实例,containsBean 用于判断容器中是否包含某个 Bean 等。
        • 使用 BeanFactory 时,通常通过 ApplicationContextApplicationContext 是 BeanFactory 的子接口,功能更丰富)来获取 Bean 实例,示例代码如下:
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class BeanFactoryExample {
          public static void main(String[] args) {
              // 创建 ApplicationContext 实例
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              // 从容器中获取 Bean 实例
              Object bean = context.getBean("exampleBean");
          }
      }
      
      • FactoryBean
        • FactoryBean 接口定义了三个方法:getObject() 用于返回由该工厂创建的 Bean 实例;getObjectType() 用于返回所创建 Bean 的类型;isSingleton() 用于指示所创建的 Bean 是否为单例。
        • 实现 FactoryBean 接口的类需要重写这些方法,示例代码如下:
      import org.springframework.beans.factory.FactoryBean;
      
      // 自定义 FactoryBean
      public class MyFactoryBean implements FactoryBean<MyBean> {
      
          @Override
          public MyBean getObject() throws Exception {
              // 自定义 Bean 的创建逻辑
              return new MyBean();
          }
      
          @Override
          public Class<?> getObjectType() {
              return MyBean.class;
          }
      
          @Override
          public boolean isSingleton() {
              return true;
          }
      }
      
      class MyBean {
          // Bean 类的定义
      }
      

      在配置文件中配置该 FactoryBean

      <bean id="myFactoryBean" class="com.example.MyFactoryBean"/>
      

      获取 FactoryBean 创建的 Bean 实例:

      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class FactoryBeanExample {
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              // 获取 FactoryBean 创建的 Bean 实例
              MyBean myBean = (MyBean) context.getBean("myFactoryBean");
          }
      }
      
      应用场景
      • BeanFactory
        • 适用于各种需要使用 Spring IOC 容器的场景,是 Spring 应用中最基础的 Bean 管理工具。无论是简单的 Java 应用还是复杂的企业级应用,都可以使用 BeanFactory 来管理 Bean 对象。
        • 例如,在一个简单的 Java Web 应用中,可以使用 BeanFactory 来管理业务逻辑层和数据访问层的 Bean 实例,实现对象的解耦和统一管理。
      • FactoryBean
        • 适用于需要自定义 Bean 创建逻辑的场景。当创建 Bean 的过程比较复杂,或者需要根据不同的条件创建不同类型的 Bean 时,可以使用 FactoryBean 来封装复杂的创建逻辑。
        • 例如,在创建数据源 Bean 时,可能需要根据不同的配置信息创建不同类型的数据源(如 MySQL 数据源、Oracle 数据源等),这时可以使用 FactoryBean 来封装数据源的创建逻辑。
      总结
      • BeanFactory 是 Spring IOC 容器的核心接口,负责管理和创建各种 Bean 实例,是整个 Spring 容器体系的基础。
      • FactoryBean 是一个特殊的 Bean,它本身是一个工厂,用于自定义 Bean 的创建逻辑,为开发者提供了更灵活的 Bean 创建方式。

      欢迎收藏、点赞、一起研究

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

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

        相关文章

        Pytorch中预置数据集的加载方式

        Pytorch中数据集加载方式 数据类型​PyTorch 模块​是否预置数据集图像/视频torchvision.datasets✅ 是音频torchaudio.datasets✅ 是文本torchtext.datasets✅ 是&#xff08;需安装&#xff09;自定义数据torch.utils.data❌ 否&#xff08;需手动实现&#xff09;多模态/第三…

        ARM-----数据处理、异常处理、模式切换

        实列一&#xff1a; 1. 异常向量表 area reset, code, readonly code32 entry area reset, code, readonly&#xff1a;定义一个名为reset的代码区域&#xff0c;只读。 code32&#xff1a;指示编译器生成32位ARM指令。 entry&#xff1a;标记程序的入口点。 2. 程序入口…

        d202541

        目录 一、分隔链表 二、旋转链表 三、删除链表中重复的数字 一、分隔链表 用两个list存一下小于和大于等于 x的节点 最后串起来就行 public ListNode partition(ListNode head, int x) {ListNode ret new ListNode(1);ListNode cur ret;List<ListNode> small new A…

        YOLOv12 从预训练迈向自主训练,第一步数据准备

        视频讲解&#xff1a; YOLOv12 从预训练迈向自主训练&#xff0c;第一步数据准备 前面复现过yolov12&#xff0c;使用pre-trained的模型进行过测试&#xff0c;今天来讲下如何训练自己的模型&#xff0c;第一步先准备数据和训练格式 https://gitcode.com/open-source-toolkit/…

        【UVM学习笔记】更加灵活的UVM—通信

        系列文章目录 【UVM学习笔记】UVM基础—一文告诉你UVM的组成部分 【UVM学习笔记】UVM中的“类” 文章目录 系列文章目录前言一、TLM是什么&#xff1f;二、put操作2.1、建立PORT和EXPORT的连接2.2 IMP组件 三、get操作四、transport端口五、nonblocking端口六、analysis端口七…

        NSSCTF [HGAME 2023 week1]simple_shellcode

        3488.[HGAME 2023 week1]simple_shellcode 手写read函数shellcode和orw [HGAME 2023 week1]simple_shellcode (1) motalymotaly-VMware-Virtual-Platform:~/桌面$ file vuln vuln: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpret…

        数据集(Dataset)和数据加载器(DataLoader)-pytroch学习3

        pytorch网站学习 处理数据样本的代码往往会变得很乱、难以维护&#xff1b;理想情况下&#xff0c;我们希望把数据部分的代码和模型训练部分分开写&#xff0c;这样更容易阅读、也更好维护。 简单说&#xff1a;数据和模型最好“分工明确”&#xff0c;不要写在一起。 PyTor…

        数据结构|排序算法(一)快速排序

        一、排序概念 排序是数据结构中的一个重要概念&#xff0c;它是指将一组数据元素按照特定的顺序进行排列的过程&#xff0c;默认是从小到大排序。 常见的八大排序算法&#xff1a; 插入排序、希尔排序、冒泡排序、快速排序、选择排序、堆排序、归并排序、基数排序 二、快速…

        文件或目录损坏且无法读取:数据恢复的实战指南

        在数字化时代&#xff0c;数据的重要性不言而喻。然而&#xff0c;在日常使用电脑、移动硬盘、U盘等存储设备时&#xff0c;我们难免会遇到“文件或目录损坏且无法读取”的提示。这一提示如同晴天霹雳&#xff0c;让无数用户心急如焚&#xff0c;尤其是当这些文件中存储着重要的…

        leetcode数组-螺旋矩阵Ⅱ

        题目 题目链接&#xff1a;https://leetcode.cn/problems/spiral-matrix-ii/ 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7…

        小刚说C语言刷题——第14讲 逻辑运算符

        当我们需要将一个表达式取反&#xff0c;或者要判断两个表达式组成的大的表达式的结果时&#xff0c;要用到逻辑运算符。 1.逻辑运算符的分类 (1)逻辑非(!) &#xff01;a&#xff0c;当a为真时&#xff0c;&#xff01;a为假。当a为假时&#xff0c;&#xff01;a为真。 例…

        WPS宏开发手册——Excel实战

        目录 系列文章5、Excel实战使用for循环给10*10的表格填充行列之和使用for循环将10*10表格中的偶数值提取到另一个sheet页使用for循环给写一个99乘法表按市场成员名称分类&#xff08;即市场成员A、B、C...&#xff09;&#xff0c;统计月内不同时间段表1和表2的乘积之和&#x…

        【Cursor】切换主题

        右键顶部&#xff0c;把菜单栏勾上 首选项-主题-颜色主题 选择和喜欢的颜色主题即可&#xff0c;一般是“现代深色”

        spring druid项目中监控sql执行情况

        场景 在 Spring Boot 结合 MyBatis 的服务中&#xff0c;实现 SQL 执行覆盖情况的监控&#xff0c;可以基于Druid提供的内置的 SQL 监控统计功能。 开启监控 在 application.yml 中启用 Druid 的 stat 和 wall 过滤器&#xff0c;并配置监控页面的访问权限 …

        Obsidian按下三个横线不能出现文档属性

        解决方案: 需要在标题下方的一行, 按下 键盘数字0后面那个横线(英文横线), 然后回车就可以了 然后点击横线即可

        pyqt SQL Server 数据库查询-优化2

        1、增加导出数据功能 2、增加删除表里数据功能 import sys import pyodbc from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QListWidget, QLineEdit, QPushButton, \QTableWidget, QTableWidgetItem, QLabel, QMessageBox from PyQt6.QtGui i…

        Hyperlane:高性能 Rust HTTP 服务器框架评测

        Hyperlane&#xff1a;高性能 Rust HTTP 服务器框架评测 在当今快速发展的互联网时代&#xff0c;选择一个高效、可靠的 HTTP 服务器框架对于开发者来说至关重要。最近&#xff0c;我在评估各种服务器框架性能时&#xff0c;发现了一个名为 Hyperlane 的 Rust HTTP 服务器库&a…

        Laravel 中使用 JWT 作用户登录,身份认证

        什么是JWT&#xff1a; JWT 全名 JSON Web Token&#xff0c;是一种开放标准 (RFC 7519)。 用于在网络应用环境间安全地传输信息作为 JSON 对象。 它是一种轻量级的认证和授权机制&#xff0c;特别适合分布式系统的身份验证。 核心特点 紧凑格式&#xff1a;体积小&#x…

        VBA中类的解读及应用第二十二讲:利用类判断任意单元格的类型-5

        《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

        STM32F103_LL库+寄存器学习笔记13 - 梳理外设CAN与如何发送CAN报文(串行发送)

        导言 CAN总线因其高速稳定的数据传输与卓越抗干扰性能&#xff0c;在汽车、机器人及工业自动化中被广泛应用。它采用分布式网络结构&#xff0c;实现多节点间实时通信&#xff0c;确保各控制模块精准协同。在汽车领域&#xff0c;CAN总线连接发动机、制动、车身系统&#xff0c…