spring framework官方文档解析

1 核心技术

1.1 Ioc容器

spring本质上是一个IoC容器。spring中使用org.springframework.context.ApplicationContext接口表示IoC容器,它负责bean的实例化、配置、组装。ApplicationContext是BeanFactory的子接口,BeanFactory接口提供了先进的配置机制,可以管理任意类型的对象,它提供了配置框架和基本功能,而ApplicationContext添加了更多特定功能。

容器通过读取配置元数(configuration metadata)据来获取实例化、配置和组装组件的说明书。配置元数据可以表示为带注解的组件类、具有工厂方法的配置类、外部XML文件等

在spring中有一些ApplicationContext接口的实现类,他们是spring的核心功能类,比如:AnnotationConfigApplicationContext 或 ClassPathXmlApplicationContext

配置元信息有两种配置方式:基于Java的配置和基于xml的配置
基于Java的配置又有如下几种情况:

  • 注解配置:1 比如我们经常使用的@Component、@Service、@Repository、@Controller来定义bean;2 在字段上我们使用@Autowired、@Qualifier等注解来实现依赖注入
  • Java配置类:使用@Configuration注解来标记这些类为配置类。在配置类中,开发者可以使用@Bean注解来定义bean。此外,@Import注解允许开发者导入其他配置类,而@DependsOn注解则用于指定bean之间的依赖顺序

1.1.1 bean的实例化

在spring容器内部,使用BeanDefinition实例表示bean,它包含了实例化及其组装该bean所需要的所有信息。

Spring容器中的bean是通过bean元信息配置文件(如XML配置、注解配置或Java配置类)在容器内部创建的。

首先,spring容器会将要创建bean的类封装成BeanDefinition,然后,ApplicationContext的BeanFactory负责创建这些bean。比如,可以通过调用ApplicationContext的getAutowireCapableBeanFactory()方法返回一个DefaultListableBeanFactory实例。DefaultListableBeanFactory是Spring IoC容器中用于管理bean定义和bean实例的核心类之一。DefaultListableBeanFactory提供了registerSingleton方法注册外部创建的对象:

 registerSingleton(String beanName, Object singletonObject) 

这个方法用于注册一个单例对象。开发者需要提供一个bean名称和已经创建好的对象实例。注册后,这个对象就可以通过名称在容器中获取,并且Spring会将其视为一个单例bean进行管理。

bean的实例化方式有如下几种:

1 通过构造器实例化

在spring容器中,当我们使用反射机制获取到该类的所有信息时,我们同样可以获取到该类的构造器信息,这样我们就可以通过调用构造器来操控这个类的实例化过程,这和使用new关键字创建类的实例是等效的。因为这里我们是通过构造器来实例化对象的,因此通常的做法是在类中需要有一个默认无参数的构造器。

 <bean id="exampleBean" class="examples.ExampleBean"/> 

spring应用中,我们自定义的bean基本上都是通过构造器实例化的

2 通过静态工厂方法实例化

class属性:用于指定包含静态工厂方法的类。这个类不必是bean本身的类,而是包含用于创建bean实例的静态方法的类。
factory-method属性:用于指定静态工厂方法的方法名。这个方法必须是一个静态方法,且能够返回一个实例,该实例将被Spring容器管理。

 <bean id="connectionPool" class="com.example.LegacyConnectionPool" factory-method="createInstance"/> 

这种定义bean的方式特别适用于需要调用遗留代码中的静态工厂方法的情况。例如,在迁移旧系统到Spring框架时,可能已经有一些类通过静态工厂方法创建实例,而不是通过构造函数。
使用静态工厂方法可以让Spring在不修改现有代码的情况下,管理和集成这些遗留组件。

3 通过实例工厂方法实例化

实例工厂方法是指通过调用容器中已经存在的bean的一个非静态方法来创建新的bean实例。这与静态工厂方法不同,因为实例工厂方法需要一个已经实例化的bean来调用该方法

class属性:在使用实例工厂方法时,class属性留空,因为bean的实际类不是通过这个配置来指定的。
factory-bean属性:指定容器中已经存在的bean的名称,这个bean包含了用于创建新bean实例的非静态工厂方法。这个bean可以位于当前容器、父容器或祖先容器中。
factory-method属性:指定工厂bean中用于创建新bean实例的非静态方法的方法名

<bean id="connectionFactory" class="com.example.ConnectionFactory"/>
<bean id="connection" factory-bean="connectionFactory" factory-method="createConnection"/>

Spring容器首先实例化并管理factory-bean指定的bean。
然后,容器调用该bean的factory-method指定的方法来创建新的bean实例。
新创建的bean实例随后由Spring容器管理,类似于通过构造函数创建的bean。

实例工厂方法适用于需要从一个已经存在的bean实例中创建新实例的情况。这在某些复杂对象创建逻辑中非常有用,特别是当创建过程依赖于容器中已经存在的其他bean的状态或配置时

1.1.2 依赖及其注入

1.1.2.1 依赖注入的两种方式

依赖注入分为:基于构造器的依赖注入和基于setter的依赖注入

1 基于构造器的依赖注入

public class UserService {
    private final UserRepository userRepository;

    // 通过构造函数注入依赖
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(User user) {
        userRepository.save(user);
    }
}

xml配置

<bean id="userService" class="com.example.UserService">
    <constructor-arg ref="userRepository"/>
</bean>
<bean id="userRepository" class="com.example.UserRepository"/>

或使用注解

@Configuration
public class AppConfig {
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
}

2 基于setter的依赖注入

public class UserService {
    private UserRepository userRepository;

    // 通过Setter方法注入依赖
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void createUser(User user) {
        userRepository.save(user);
    }
}

xml配置

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

或使用注解

@Configuration
public class AppConfig {
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService() {
        UserService userService = new UserService();
        userService.setUserRepository(userRepository()); // 手动注入依赖
        return userService;
    }
}

1.1.2.2 依赖处理过程

依赖的处理和注入都是在spring容器中进行的,因此第一步是容器的创建和初始化。

  1. spring容器ApplicationContext被创建和初始化,该容器中包含了所有bean的依赖元信息。
  2. 容器中的bean被依次实例化和装配,此时这些依赖被提供,注入到这个正在被装配的bean中。
  3. 容器被创建时,容器会校验所有bean的配置的有效性和正确性(比如是否存在循环依赖、是否是有效的依赖)。对于默认情况下(lazy-init="false"),bean的实例化和装配是在容器启动的时候完成的,如果依赖有问题,会抛出异常。但是如果bean的实例化是在需要时进行的(比如lazy-init="true"),那么依赖注入时可能会有问题,此时会抛出异常。

1.1.2.3 depends-on

depends-on 是一个用于控制 bean 初始化顺序的机制

作用:

  • 强制初始化顺序:depends-on 显式指定一个或多个 bean 必须在当前 bean 初始化之前完成初始化。这确保了依赖的 bean 在当前 bean 使用前已就绪。
  • 控制销毁顺序:对于单例 bean,depends-on 还会影响销毁顺序,被依赖的 bean 会在当前 bean 之后销毁。

使用场景:

  • 非直接依赖关系:当 bean A 需要 bean B 初始化完成(例如调用静态方法或全局变量初始化),但 A 并不直接持有 B 的实例时,可以使用 depends-on。示例:数据库连接池(jedisPool)需要配置类(jedisPoolConfig)先初始化。
  • 隐性依赖:当 bean 之间存在隐性依赖(如通过静态方法调用、全局变量引用)时,depends-on 可以显式控制初始化顺序。示例:BeanA 通过静态方法调用 BeanB 的功能,但未通过 Spring 注入 BeanB,此时可通过 @DependsOn("beanB") 强制 BeanB 先初始化。
  • 第三方库集成:在集成第三方库时,可能需要确保某些 bean 先初始化(如配置类、监听器)。示例:自定义监听器依赖配置类,可通过 @DependsOn 确保配置类先初始化。
<bean id="dao" class="com.example.Dao" depends-on="database"/>
<bean id="database" class="com.example.Database"/>

或者注解方式

@Service
@DependsOn("configBean")
public class UserService {
    public UserService() {
        // 假设此处需要依赖 configBean 的全局配置已加载
        ConfigBean config = ConfigBean.getInstance();
    }
}

 

@Configuration
public class AppConfig {
    @Bean
    @DependsOn("dataSource")
    public DatabaseInitializer dbInitializer() {
        return new DatabaseInitializer();
    }

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

1.1.2.4 lazy-init

懒加载。默认情况下,bean的实例化和装配是在spring容器启动过程中进行的,即它是容器启动的一个阶段。如果我们希望它在使用时才加载,则我们可以使用lazy-init选项

<bean id="lazyBean" class="com.example.LazyBean" lazy-init="true"/>

注解方式

@Configuration
public class AppConfig {
    @Bean
    @Lazy  // 单个bean懒加载
    public HeavyBean heavyBean() {
        return new HeavyBean();
    }
}

@Service
@Lazy  // 类级别懒加载
public class LazyService {
    // ...
}

1.1.2.5 自动装配

自动装配(Autowiring)是一种简化依赖注入的机制,允许Spring容器根据类型、名称或其他规则自动为bean注入依赖关系,而无需显式编写<property>

1.1.2.5.1 xml配置中的自动装配

在XML中,可以通过<bean>标签的autowire属性启用自动装配:

  • no(默认):不启用自动装配,依赖必须显式配置。
  • byName:根据属性名匹配bean的名称。
  • byType:根据属性类型匹配bean的类型。
  • constructor:根据构造器参数类型匹配bean的类型。
  • autodetect:Spring会先尝试constructor,如果失败则尝试byType。
<bean id="userService" class="com.example.UserService" autowire="byType"/>
<bean id="userDao" class="com.example.UserDaoImpl"/>
<!-- 无需显式配置 <property name="userDao" ref="userDao"/> -->

UserService中有一个private UserDao userDao属性,Spring会根据byType自动注入userDao

1.1.2.5.2 @Autowired注解

Spring提供了@Autowired注解来实现自动装配:

  • 按类型注入:默认情况下,@Autowired会根据类型匹配依赖。
  • 按名称注入:结合@Qualifier注解可以按名称注入。
  • 支持字段、构造器、Setter方法
@Service
public class UserService {
    @Autowired  // 按类型注入
    private UserDao userDao;
    // 或者通过构造器注入
    private final UserDao userDao;
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    // 或者通过Setter方法注入
    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
1.1.2.5.3 @Resource注解

@Resource是Java标准注解,默认按名称注入

@Service
public class UserService {
    @Resource(name = "customUserDao")  // 按名称注入
    private UserDao userDao;
}
1.1.2.5.4 @Inject注解

@Inject是Java标准注解,与@Autowired类似,但需要额外依赖javax.inject

@Service
public class UserService {
    @Inject  // 按类型注入
    private UserDao userDao;
}
1.1.2.5.4 @Qualifier注解和@Primary注解

按照类型匹配时,Spring会查找容器中所有与目标属性类型匹配的bean。如果找到唯一匹配的bean,则直接注入。如果找到多个匹配的bean,可以通过@Qualifier指定名称或@Primary标记首选bean。

@Service
public class UserService {
    @Autowired
    @Qualifier("jdbcUserDao")  // 指定名称
    private UserDao userDao;
}

1.1.3 bean的作用域

Bean的作用域(Scope)定义了Bean在容器中的生命周期和可见性,spring中有如下几种作用域

1.1.3.1 Singleton

默认作用域。每个Spring容器中,一个Bean定义只对应一个对象实例

@Component
public class SingletonBean {
    // 每次注入时都是同一个实例
}

1.1.3.2 Singleton

每次注入时都是全新的对象

@Component
@Scope("prototype")
public class PrototypeBean {
    // 每次注入时都是新实例
}

1.1.3.3 Request

每个HTTP请求创建一个新的Bean实例,仅适用于Web应用

@Component
@Scope("request")
public class RequestScopedBean {
    // 每个HTTP请求一个新实例
}

1.1.3.4 Session

每个HTTP会话创建一个新的Bean实例,仅适用于Web应用

@Component
@Scope("session")
public class SessionScopedBean {
    // 每个用户会话一个实例
}

1.1.4 bean的生命周期回调(Lifecycle Callback)

在Spring框架中,生命周期回调(Lifecycle Callback)是Spring IoC容器管理Bean生命周期时,允许开发者在Bean的特定生命周期阶段插入自定义逻辑的机制。这些回调方法在Bean的初始化、销毁等关键阶段被触发,帮助开发者实现资源管理、依赖检查、初始化配置等操作

1.1.4.1 InitializingBean接口,DisposableBean接口

分别在bean初始化和销毁的时候被调用

public class MyBean implements InitializingBean, DisposableBean  {
    @Override
    public void afterPropertiesSet() {
        System.out.println("Bean初始化完成");
    }
    @Override
    public void destroy() {
        System.out.println("Bean销毁");
    } 
}

1.1.4.2 @PostConstruct注解,@PreDestroy注解

public class MyBean {
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct初始化");
    }
    @PreDestroy
    public void cleanup() {
        System.out.println("@PreDestroy销毁");
    }
}

1.1.4.3 BeanPostProcessor接口

需要注意的是BeanPostProcessor对容器中的所有Bean都有效,一旦容器中注入了BeanPostProcessor的实例,则容器中每个bean被初始化时都会在这个postProcessor中被加工和处理。

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("初始化前处理: " + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("初始化后处理: " + beanName);
        return bean;
    }
}

1.1.5 ApplicationContextAware和BeanNameAware

在Spring框架中,ApplicationContextAware和BeanNameAware是两个常用的Aware接口,用于让Bean在初始化过程中获取Spring容器的上下文信息或自身的Bean名称

ApplicationContextAware

  • 让Bean能够获取到创建它的ApplicationContext实例
  • 通过实现ApplicationContextAware接口,Bean可以访问Spring容器的所有功能(如获取其他Bean、发布事件、访问资源等)。
@Component
public class MyBean implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void doSomething() {
        // 动态获取其他Bean
        SomeService service = applicationContext.getBean(SomeService.class);
        service.execute();

        // 访问资源
        Resource resource = applicationContext.getResource("classpath:config.properties");
    }
}

BeanNameAware

  • 让Bean能够获取到自身在Spring容器中的名称(即<bean id="...">或@Component("...")中定义的名称)。
  • 适用于需要知道自身名称的场景(如日志记录、动态配置等)。

注意,通常,使用依赖注入就足够了,仅在必要时使用Aware接口

可以使用Aware接口的场景:

  • 当Bean需要访问Spring容器的高级功能(如事件发布、资源加载)时,使用ApplicationContextAware。
  • 当Bean需要知道自身名称时,使用BeanNameAware。

我们要遵守的一个原则是:避免滥用。过度使用Aware接口会增加代码与Spring的耦合,降低可测试性和可维护性。

ApplicationContextAware和BeanNameAware是Spring提供的扩展点,用于让Bean获取容器信息或自身名称。它们应谨慎使用,仅在确实需要时引入

底层机制

  • Spring在初始化Bean时,会检测Bean是否实现了ApplicationContextAware或BeanNameAware接口。
  • 如果是,Spring会在Bean初始化完成后(populateBean和initializeBean阶段之后)调用对应的setXXX方法。

 

posted @ 2025-06-07 16:36  zhenjingcool  阅读(124)  评论(0)    收藏  举报