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容器中进行的,因此第一步是容器的创建和初始化。
- spring容器ApplicationContext被创建和初始化,该容器中包含了所有bean的依赖元信息。
- 容器中的bean被依次实例化和装配,此时这些依赖被提供,注入到这个正在被装配的bean中。
- 容器被创建时,容器会校验所有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方法。
浙公网安备 33010602011771号