【Spring面试全家桶】Spring之初始化前、初始化、初始化后详解
  2QrAa7u9TTys 2023年11月02日 53 0

(文章目录) Spring是一个基于IoC(Inversion of Control)的容器,其核心是IoC容器,而IoC容器的核心是Bean工厂。初始化过程是IoC容器创建Bean工厂的过程,其中包括初始化前、初始化、初始化后三个阶段。

1. 初始化前

在IoC容器创建Bean工厂之前,Spring允许用户在容器初始化前执行一些定制化的操作,通常涉及到一些配置文件的载入、环境变量的设置等。

在Spring中,允许用户通过实现BeanFactoryPostProcessor接口来自定义一些定制化操作。该接口包含一个方法postProcessBeanFactory(),该方法会在BeanFactory创建之后,所有BeanDefinition加载之前被调用。用户可以在该方法中,修改BeanFactory中的BeanDefinition,或者添加一些新的BeanDefinition,从而对容器中的Bean进行自定义配置。

具体而言,用户可以通过实现BeanFactoryPostProcessor接口,来实现以下几种操作:

  1. 修改BeanDefinition的属性:用户可以通过修改BeanDefinition的属性来改变Bean的行为。例如,可以将一个Bean的scope从singleton改为prototype,或者修改Bean的初始化方法等。

  2. 添加新的BeanDefinition:用户可以在该方法中,使用BeanDefinitionRegistry接口动态地添加新的BeanDefinition,从而向容器中添加新的Bean。

  3. 载入外部配置文件:用户可以在该方法中,通过读取外部配置文件的方式,来对Bean进行自定义配置。例如,可以读取一个properties文件,将其中的属性值设置到对应的Bean中。

总之,BeanFactoryPostProcessor允许用户在容器初始化之前,对Bean进行自定义配置,从而满足不同场景下的个性化需求。同时,这也是Spring框架的一大特色,充分体现了Spring对扩展性的支持和重视。 以下是一个简单的Java代码示例,展示了如何通过实现BeanFactoryPostProcessor接口,来实现动态添加新的BeanDefinition:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 获取BeanDefinitionRegistry
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

        // 创建一个新的BeanDefinition
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class);

        // 将新的BeanDefinition添加到容器中
        registry.registerBeanDefinition("myBean", builder.getBeanDefinition());
    }

    public static class MyBean {
        // ...
    }
}

上述代码中,我们创建了一个名为MyBean的新BeanDefinition,并将它添加到容器中。在实际使用中,我们还可以使用BeanDefinitionBuilder来设置该Bean的其他属性,例如它的作用域、依赖等。

需要注意的是,BeanFactoryPostProcessor会在所有BeanDefinition加载完成之后被调用,因此我们只能添加新的BeanDefinition,而不能修改已有的。如果需要修改已有的BeanDefinition,可以使用BeanPostProcessor接口。

2. 初始化

在IoC容器创建Bean工厂之后,Spring开始对容器中的bean进行初始化。这一阶段是Spring核心的部分,它包括了以下几个步骤:

(1)定位

第一步是通过Bean定义读取器(BeanDefinitionReader)读取配置文件或注解信息,解析成Bean定义(BeanDefinition),并将其存储在Bean定义注册表(BeanDefinitionRegistry)中。 在Java Spring框架中,Bean定义读取器(BeanDefinitionReader)是用于读取配置文件或注解信息的组件,它将配置文件或注解信息解析成Bean定义(BeanDefinition),并将这些Bean定义存储在Bean定义注册表(BeanDefinitionRegistry)中。

Bean定义是描述Spring IoC容器中Bean的对象实例的元数据,包括Bean的名称、类型、作用域、依赖关系等信息。Bean定义注册表是一个容器,用于存储Bean定义,并在需要时创建Bean实例并注入依赖。

Bean定义读取器可以读取多种类型的配置文件或注解信息,如XML、Java注解等。在读取配置文件或注解信息时,Bean定义读取器会使用相应的解析器(如XML解析器或注解解析器)将其转换成Bean定义。解析后的Bean定义被保存在Bean定义注册表中,并在需要时被使用。

通过Bean定义读取器,Spring框架实现了依赖注入和控制反转的功能,实现了松耦合的应用设计。这种设计方式使得应用程序更加可扩展、可维护,并使得开发人员能够更加专注于业务逻辑的实现。

Java代码示例:

以下是一个简单的使用Bean定义读取器和Bean定义注册表的示例。这个示例使用XML配置文件定义了两个Bean,一个是UserService,另一个是UserDao。UserService依赖于UserDao,通过Bean定义注册表,我们可以将其注入到UserService中。

首先,我们需要在Spring配置文件中定义Bean:

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

然后,在Java代码中使用Bean定义读取器和Bean定义注册表:

import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

public class Main {
  public static void main(String[] args) {
    // 创建Bean定义注册表
    BeanDefinitionRegistry beanRegistry = new DefaultListableBeanFactory();

    // 创建Bean定义读取器
    BeanDefinitionReader reader = new XmlBeanDefinitionReader(beanRegistry);

    // 读取配置文件中定义的Bean定义
    reader.loadBeanDefinitions("classpath:applicationContext.xml");

    // 从注册表中获取UserService Bean
    UserService userService = ((DefaultListableBeanFactory) beanRegistry).getBean(UserService.class);

    // 使用UserService
    User user = userService.getUser(1);
    System.out.println(user.getName());
  }
}

在这个例子中,我们创建了一个DefaultListableBeanFactory作为Bean定义注册表,并创建了一个XmlBeanDefinitionReader作为Bean定义读取器。我们使用XmlBeanDefinitionReader从Spring配置文件中读取Bean定义,并将它们存储在Bean定义注册表中。最后,我们从注册表中获取UserService Bean,并使用它来获取用户信息。

(2)载入

第二步是通过Bean工厂将Bean定义实例化成Bean对象,并将其存储在IoC容器中。 在IoC容器中,Bean定义是通过配置文件或注解等方式进行定义的,它描述了一个Bean的属性及其对应的值。在第二步中,IoC容器会通过Bean工厂来实例化这些Bean定义,并将它们转化为实际的Bean对象,然后将这些对象存储在IoC容器中。

Bean工厂是一个用于创建和管理Bean的工厂类。在Spring框架中,Bean工厂负责加载Bean定义并根据这些定义创建Bean对象,同时也管理这些Bean对象的生命周期。

在Java中,Bean对象的实例化过程是通过反射来完成的。通过Bean定义中的类路径信息以及构造方法等信息,IoC容器利用Java反射机制来动态创建Bean对象并完成其属性的注入。

Bean工厂在实例化Bean对象时,往往需要考虑依赖注入等问题。例如,如果一个Bean对象依赖于另一个Bean对象,那么Bean工厂会在实例化前先实例化依赖对象,并将其注入到目标Bean中。

总之,通过Bean工厂将Bean定义实例化成Bean对象,并将其存储在IoC容器中,是实现Spring IoC容器的重要步骤之一。深入了解这些底层原理,可以帮助我们更好地掌握Spring框架的使用,并且能更好地理解和设计基于IoC容器的应用程序。

示例代码:

下面是一个简单的Java类,它定义了一个Student类和一个Teacher类,其中Teacher类依赖于Student类:

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class Teacher {
    private Student student;

    public Teacher(Student student) {
        this.student = student;
    }
}

在Spring框架中,可以通过配置文件来定义Bean工厂中的Bean对象,如下所示:

<bean id="student" class="Student">
    <property name="name" value="张三"/>
    <property name="age" value="18"/>
</bean>

<bean id="teacher" class="Teacher">
    <constructor-arg ref="student"/>
</bean>

在上面的配置文件中,定义了一个id为student的Bean对象和一个id为teacher的Bean对象。其中,teacher对象在创建时通过构造函数注入了student对象。

在Spring容器启动时,会自动加载并解析配置文件,然后根据定义的Bean对象信息实例化对应的Java对象。具体的实现过程可以参考Spring的源码。

通过上面的示例可以看到,Spring框架通过Bean工厂和反射机制来实现Bean对象的实例化和属性注入,使得Java开发人员可以更加专注于业务逻辑的实现,而无需过多关注对象的实例化和管理细节,提高了开发效率和代码质量。

(3)初始化

第三步是对Bean对象进行一些必要的初始化操作,包括依赖注入、属性设置、初始化方法的调用等。 在Java中,Bean对象指的是一个具有属性和方法的Java对象。在Spring框架中,Bean对象是应用程序的主要组成部分,它们由Spring IoC容器负责管理。在IoC容器中,Bean对象通过配置文件或注解定义,并且在运行时由容器自动创建和注入到其它对象中使用。

第三步中对Bean对象进行必要的初始化操作,包括依赖注入、属性设置和初始化方法的调用等。具体来说,它包括以下几个步骤:

  1. 依赖注入

依赖注入是指将Bean对象所依赖的其它对象注入到它们之中。在Spring中,依赖注入可以通过构造函数注入、Setter方法注入、字段注入等方式进行。通过依赖注入,Bean对象可以获得它所需要的其它对象,从而完成特定的业务逻辑。

  1. 属性设置

属性设置是指对Bean对象所具有的属性进行初始化或修改。在Spring中,可以通过配置文件或注解的方式对Bean对象的属性进行设置。通过属性设置,可以将Bean对象的属性初始化为特定的值,或者对已存在的属性进行修改。

  1. 初始化方法的调用

初始化方法是指在Bean对象被构造完成后,在容器对其进行依赖注入和属性设置后,再次调用其指定的初始化方法。在Spring中,初始化方法可以通过@Bean注解、@PostConstruct注解或实现InitializingBean接口等方式进行定义。通过初始化方法的调用,Bean对象可以完成一些必要的初始化工作,如连接数据库、加载配置文件等。

在Spring中,Bean对象的初始化是由IoC容器控制的。在创建Bean对象时,IoC容器会先对其进行依赖注入、属性设置等操作,然后再调用其初始化方法。这样可以确保Bean对象在被使用之前已经完成了所有必要的初始化工作。同时,由于IoC容器可以管理多个Bean对象,因此可以实现Bean对象间的解耦和复用,提高了应用程序的可维护性和可扩展性。

在Spring中,Bean对象是通过配置文件或注解定义的,配置文件通常是XML文件或JavaConfig文件。下面分别介绍XML配置和注解配置的示例:

  1. XML配置示例

在XML配置文件中,可以使用<bean>元素定义Bean对象,如下所示:

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

<bean id="userRepository" class="com.example.UserRepository">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" class="com.example.DataSource">
    <property name="url" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

上述示例中,定义了UserService、UserRepository和DataSource三个Bean对象。其中,UserService依赖于UserRepository,UserRepository依赖于DataSource。在容器创建UserService对象时,会自动创建UserRepository和DataSource,并注入到UserService中。

  1. 注解配置示例

在注解配置中,可以使用@Configuration注解定义配置类,使用@Bean注解定义Bean对象,如下所示:

@Configuration
public class AppConfig {
    @Bean
    public UserService userService(UserRepository userRepository) {
        UserService userService = new UserService();
        userService.setUserRepository(userRepository);
        return userService;
    }

    @Bean
    public UserRepository userRepository(DataSource dataSource) {
        UserRepository userRepository = new UserRepository();
        userRepository.setDataSource(dataSource);
        return userRepository;
    }

    @Bean
    public DataSource dataSource() {
        DataSource dataSource = new DataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

上述示例中,定义了一个名为AppConfig的配置类,其中使用@Bean注解定义了三个Bean对象:UserService、UserRepository和DataSource。在容器创建UserService对象时,会自动创建UserRepository和DataSource,并注入到UserService中。

在编写Bean对象时,需要注意以下几点:

  1. Bean对象通常应该是无状态的,不应该包含应用程序的状态信息。如果需要保存状态信息,可以使用单例或原型作用域来管理Bean对象的生命周期。

  2. Bean对象应该尽量轻量级,尽量避免对其它Bean对象的直接依赖。如果需要依赖其它Bean对象,可以使用接口或抽象类来解藕。

  3. Bean对象应该使用构造函数注入或Setter方法注入方式,尽量避免使用字段注入方式。这样可以提高Bean对象的可测试性和可维护性。

下面给出一个简单的Bean对象示例:

public class UserService {
    private UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void addUser(User user) {
        userRepository.add(user);
    }

    public User getUserById(int id) {
        return userRepository.getById(id);
    }

    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

上述示例中,定义了一个名为UserService的Bean对象,其中依赖于另一个名为UserRepository的Bean对象。通过构造函数注入方式完成依赖注入,并定义了添加用户和获取用户信息的业务方法。

3. 初始化后

在所有Bean对象都初始化完成之后,Spring允许用户在IoC容器初始化后执行一些操作,通常涉及到一些清理工作、资源释放等。 在Spring IoC容器初始化完成后,可以通过实现InitializingBean接口或在XML配置文件中定义init-method方法来执行一些操作。

InitializingBean接口中有一个方法afterPropertiesSet(),可以在该方法中编写需要执行的操作。例如,释放一些资源,验证依赖关系等。

在XML配置文件中,可以使用init-method属性指定一个方法,在Bean初始化之后立即执行。例如:

<bean id="exampleBean" class="com.example.ExampleBean" init-method="init"/>

在ExampleBean类中,实现init()方法来执行需要的操作。

这些操作通常涉及到一些清理工作、资源释放等。例如,关闭数据库连接、释放文件句柄等。在确保不再需要资源时清理它们可以使应用程序更加健壮,并且可以避免资源泄漏。

Spring允许用户在容器初始化后执行一些操作,通常用于清理工作、资源释放等。实现InitializingBean接口或在XML配置文件中定义init-method方法可以实现这一功能。

在Java中初始化方法的使用,可以用@PostConstruct注解来代替实现InitializingBean接口或在XML配置文件中定义init-method方法。@PostConstruct注解会在依赖注入完成后自动执行一次,通常用于进行一些初始化操作。

使用方法很简单,只需要在需要执行初始化操作的方法上添加@PostConstruct注解即可。

下面是一个示例代码:

@Component
public class ExampleBean {

    @PostConstruct
    public void init() {
        // 执行初始化操作
    }

    // ...
}

需要注意的是,@PostConstruct注解需要在使用的类上添加@Component或@Service等注解,以便被Spring扫描并纳入管理。

总之,Spring初始化前、初始化、初始化后三个阶段都非常重要,对于Spring的整个运行过程都有着深刻的影响,开发人员需要对其有一定的了解和掌握。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  P3nxyT0LRuwj   2023年11月24日   61   0   0 缓存redis配置文件
  UP4ONKOBnkdD   2023年11月28日   11   0   0 java
  9JCEeX0Eg8g4   2023年12月10日   14   0   0 应用程序javaApache
  KRsXEGSB49bk   2023年11月27日   11   0   0 javaApache
  9JCEeX0Eg8g4   2023年12月11日   19   0   0 mysql配置文件
2QrAa7u9TTys