IOC
Spring的核心之一是IOC,IOC全称为Inversion of Control
,中文译为控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection
,依赖注入)来实现的。
所谓IOC,对于Spring框架来说,就是由Spring来负责对象的创建、配置和管理,所以可将IOC理解为一个大容器。IOC通过将对象创建和管理的控制权从应用代码转移到Spring容器中,实现了松耦合设计。IOC使用依赖注入(DI)来管理组成一个应用程序的组件,这些对象被称为Spring Beans。
管理Bean的创建、配置和生命周期,Spring提供了两个主要的IOC容器:BeanFactory
和ApplicationContext
。IOC容器管理的对象,通常使用注解,如@Component
、@Service
、@Autowired
,或XML配置声明Bean。IOC容器的工作流程:
- 读取配置,通过XML文件、注解或Java配置类读取Bean定义和依赖关系。
- 创建和配置Bean,容器根据配置实例化Bean,并注入它们的依赖。
- 管理Bean生命周期,容器负责调用Bean的初始化和销毁方法,管理其整个生命周期。
通过Spring的IOC容器,开发者可以更加专注于业务逻辑,而无需关心对象的创建和管理,从而提高了代码的可维护性和可扩展性。
IOC容器的工作原理
IOC容器是Spring框架的核心,它负责管理应用程序中对象的生命周期和依赖关系。
- 想要管理Bean,首先需要将Bean加载进来。IOC容器首先需要加载应用程序的配置元数据,这些配置可以通过XML文件、Java注解或者Java配置类等方式定义。加载完配置之后,容器会使用相应的解析器(如Dom4j解析XML配置文件),将配置信息转换为容器可以理解的数据结构,通常是
BeanDefinition
对象。BeanDefinition
包含了类的名称、依赖关系、初始化方法、销毁方法等元数据信息。<!-- applicationContext.xml --> <bean id="userRepository" class="com.example.UserRepository"> <!-- 定义UserRepository的依赖或配置 --> </bean> <bean id="userService" class="com.example.UserService"> <property name="userRepository" ref="userRepository" /> </bean> // 加载和解析XML配置 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- 一旦容器解析了Bean的配置信息,它会根据这些信息使用Java的反射机制来创建Bean的实例。通常情况下,Spring会调用Bean的默认构造方法来实例化对象。
// 获取UserService Bean UserService userService = (UserService) context.getBean("userService");
- 对象实例化完成,容器会根据配置文件或者注解中定义的依赖关系,将其他Bean的实例或者值注入到当前Bean中。依赖注入可以通过构造函数注入、Setter方法注入或者字段注入来完成。
public class UserService { private UserRepository userRepository; // Setter方法注入 public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } // 其他方法 }
- 在依赖注入完成后,如果配置了初始化方法,例如使用
init-method
指定的方法、实现InitializingBean
接口的方法或者使用@PostConstruct
注解标记的方法),容器会调用这些方法来执行一些初始化的操作,例如加载资源、建立连接等。<!-- applicationContext.xml --> <bean id="userRepository" class="com.example.UserRepository" init-method="init" destroy-method="destroy"> <!-- 定义UserRepository的依赖或配置 --> </bean> // UserRepository.java public class UserRepository { // 初始化方法 public void init() { System.out.println("UserRepository 初始化方法被调用"); } // 销毁方法 public void destroy() { System.out.println("UserRepository 销毁方法被调用"); } }
Bean的生命周期
Spring中的Bean是指由Spring容器管理的对象实例。在Spring框架中,Bean是应用程序的核心组件,它们由Spring容器创建、组装和管理,以帮助开发者实现松耦合、可测试和可维护的代码。
Spring Bean的生命周期包含从创建到销毁的一系列过程。即Bean的 实例化->初始化->使用->销毁
的过程。Spring中的Bean可以根据其作用域的不同可分为,单例Bean、原型Bean,不同作用域的Bean生命周期也不同。
特征 | 单例Bean | 原型Bean |
---|---|---|
创建 | 容器启动时创建一次。 | 每次请求时创建新实例。 |
作用域管理 | 由Spring容器管理。 | 每次请求时由Spring容器管理新实例。 |
线程安全性 | 单例Bean在多线程环境下共享。 | 原型Bean本身不具备线程安全性。 |
适用性 | 适用于无状态Bean、缓存对象、共享资源等。 Spring中的默认作用域。 | 适用于有状态Bean、需要频繁重新初始化的对象等。 在每次请求时需要新实例。 |
销毁管理 | 由Spring容器自动管理。 - @PreDestroy 方法(如果存在)。 - DisposableBean.destroy() 方法(如果实现)。 - 自定义销毁方法(如果在Bean定义中指定)。 | 没有自动的Spring管理销毁过程。 - 需要由客户端手动管理销毁。 - 可以通过实现DisposableBean接口或自定义方法手动释放资源。 |
单实例Bean生命周期:
- 实例化:在容器启动时创建该Bean的唯一实例。
- 初始化:
- 初始化前置处理:调用所有注册的
BeanPostProcessor
的postProcessBeforeInitialization
方法,可以在初始化之前对Bean进行修改。 - 初始化:按照顺序执行以下方法,如果Bean实现了
InitializingBean
接口,则调用其afterPropertiesSet
方法;如果在Bean定义中指定了init-method
,则调用这个方法;如果Bean中有用@PostConstruct
注解标记的方法,则调用该方法。 - 初始化后处理:调用所有注册的
BeanPostProcessor
的postProcessAfterInitialization
方法,可以在初始化之后对Bean进行修改。
- 初始化前置处理:调用所有注册的
- 使用:当Bean初始化之后,Bean处于就绪状态,可以被应用程序中的其他组件使用。
- 销毁:
- 销毁前处理:在销毁之前,Spring容器会依次调用注册的所有
BeanPostProcessor
的postProcessBeforeDestruction
方法。如果Bean类中有用@PreDestroy
注解标记的方法,Spring容器会在销毁之前调用该方法。 - 销毁:如果在Bean的定义中通过配置
destroy-method
属性指定了销毁方法,Spring容器会调用这个方法来执行特定的清理操作。
- 销毁前处理:在销毁之前,Spring容器会依次调用注册的所有
单例Bean和多实例Bean的生命周期主要区别在于实例化和销毁的管理方式,单例Bean在容器启动时创建一个实例,并由容器负责管理其生命周期的完整过程。而多实例Bean在每次请求时创建新的实例,并且销毁过程需要开发者手动管理。
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
@Scope("singleton")
public SingletonBean singletonBean() {
return new SingletonBean();
}
@Bean(initMethod = "init", destroyMethod = "destroy")
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
public static class SingletonBean implements InitializingBean, DisposableBean {
public SingletonBean() {
System.out.println("SingletonBean 实例化");
}
@PostConstruct
public void postConstruct() {
System.out.println("SingletonBean @PostConstruct 方法调用");
}
@Override
public void afterPropertiesSet() {
System.out.println("SingletonBean afterPropertiesSet 方法调用");
}
public void init() {
System.out.println("SingletonBean 自定义初始化方法调用");
}
@PreDestroy
public void preDestroy() {
System.out.println("SingletonBean @PreDestroy 方法调用");
}
@Override
public void destroy() {
System.out.println("SingletonBean destroy 方法调用");
}
public void customDestroy() {
System.out.println("SingletonBean 自定义销毁方法调用");
}
}
public static class PrototypeBean implements InitializingBean, DisposableBean {
public PrototypeBean() {
System.out.println("PrototypeBean 实例化");
}
@PostConstruct
public void postConstruct() {
System.out.println("PrototypeBean @PostConstruct 方法调用");
}
@Override
public void afterPropertiesSet() {
System.out.println("PrototypeBean afterPropertiesSet 方法调用");
}
public void init() {
System.out.println("PrototypeBean 自定义初始化方法调用");
}
@PreDestroy
public void preDestroy() {
System.out.println("PrototypeBean @PreDestroy 方法调用");
}
@Override
public void destroy() {
System.out.println("PrototypeBean destroy 方法调用");
}
public void customDestroy() {
System.out.println("PrototypeBean 自定义销毁方法调用");
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SingletonBean singletonBean1 = context.getBean(SingletonBean.class);
SingletonBean singletonBean2 = context.getBean(SingletonBean.class);
System.out.println("singletonBean1 == singletonBean2 : " + (singletonBean1 == singletonBean2));
PrototypeBean prototypeBean1 = context.getBean(PrototypeBean.class);
PrototypeBean prototypeBean2 = context.getBean(PrototypeBean.class);
System.out.println("prototypeBean1 == prototypeBean2 : " + (prototypeBean1 == prototypeBean2));
context.close();
// 手动销毁 Prototype Bean
prototypeBean1.destroy();
prototypeBean2.destroy();
}
}
举个例子,来更好的理解Bean的生命周期:
- 首先,在Spring的配置文件(如XML配置)或者使用注解方式,我们定义
UserService
类作为一个Bean,并配置它的初始化方法、销毁方法以及其他属性。// UserService.java public class UserService implements InitializingBean, DisposableBean, BeanNameAware { private String message; // 初始化方法 public void init() { System.out.println("UserService 初始化方法被调用"); } // 销毁方法 public void destroy() { System.out.println("UserService 销毁方法被调用"); } // Setter 方法 public void setMessage(String message) { this.message = message; } // Getter 方法 public String getMessage() { return message; } // 实现 InitializingBean 接口的方法 @Override public void afterPropertiesSet() throws Exception { System.out.println("UserService InitializingBean 的 afterPropertiesSet 方法被调用"); } // 实现 DisposableBean 接口的方法 @Override public void destroy() throws Exception { System.out.println("UserService DisposableBean 的 destroy 方法被调用"); } // 实现 BeanNameAware 接口的方法 @Override public void setBeanName(String name) { System.out.println("UserService BeanNameAware 的 setBeanName 方法被调用,Bean的名称为:" + name); } }
- 在Spring的配置文件中,我们将UserService类定义为一个Bean,并配置初始化方法、销毁方法以及其他属性。
<!-- applicationContext.xml --> <bean id="userService" class="com.example.UserService" init-method="init" destroy-method="destroy"> <property name="message" value="Hello, Spring!" /> </bean>
- 当应用程序启动并且Spring容器加载配置时,将会执行以下步骤来管理
UserService
Bean的生命周期:- 实例化:Spring容器根据配置文件或者注解,实例化
UserService
类的一个对象实例。 - 依赖注入:将配置的属性(如
message
)注入到UserService
实例中。 - 初始化:调用
init-method
指定的初始化方法或者InitializingBean
接口的afterPropertiesSet()
方法,例如执行init()
方法。在初始化过程中,还可以调用BeanNameAware
接口的方法,获取和设置Bean的名称。 - 使用:
UserService
Bean可以被应用程序的其他组件使用,执行其业务逻辑,如打印消息。 - 销毁:当应用程序关闭时,Spring容器会调用
destroy-method
指定的销毁方法或者DisposableBean
接口的destroy()
方法,例如执行destroy()
方法。
- 实例化:Spring容器根据配置文件或者注解,实例化
Bean的自动装配
Bean的自动装配是Spring框架提供的一种便捷的方式,用于自动解析和设置Bean之间的依赖关系,而无需显式配置每一个依赖关系的方式。Spring支持以下几种自动装配的方式:
- 根据类型自动装配:Spring会自动将一个属性与同一上下文中具有兼容类型的Bean进行匹配。如果容器中存在多个符合类型的Bean,则会抛出异常。
public interface UserRepository { // 接口定义 } @Component public class UserRepositoryImpl1 implements UserRepository { // 实现1 } @Component public class UserRepositoryImpl2 implements UserRepository { // 实现2 } // 示例:根据类型自动装配 @Autowired private UserRepository userRepository;
- 根据名称自动装配:Spring会自动将一个属性与容器中相同名称的Bean进行匹配,要求Bean的名称必须与属性名称完全一致。
public interface UserRepository { // 接口定义 } @Component("userRepository1") public class UserRepositoryImpl1 implements UserRepository { // 实现1 } @Component("userRepository2") public class UserRepositoryImpl2 implements UserRepository { // 实现2 } // 示例:根据名称自动装配 @Autowired private UserRepository userRepository;
- 构造函数自动装配:Spring会自动通过构造函数来注入依赖,从而避免了使用
@Autowired
注解的繁琐。Spring会查找与构造函数参数类型相匹配的Bean,并自动进行注入。// 示例:构造函数自动装配 @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; }
- 自动装配标识符:可以使用
@Autowired
注解结合@Qualifier
注解来指定具体的Bean名称,来解决多个相同类型Bean的自动装配歧义问题。// 示例:结合@Qualifier注解指定Bean名称 @Autowired @Qualifier("userRepository") private UserRepository userRepository;
- 自动装配和主候选Bean:可以使用
@Primary
注解来标识一个主要的Bean候选者,当存在多个匹配的Bean时,Spring会优先选择标有@Primary
注解的Bean进行注入。// 示例:使用@Primary注解标识主候选Bean @Component @Primary public class PrimaryUserRepository implements UserRepository { // 实现代码 }
在Spring中用于实现自动装配的注解有三个,它们都能自动注入依赖,但在一些细节上有所区别。
自动装配 | 来源 | 装配方式 | 支持 @Primary | 支持的属性 |
---|---|---|---|---|
@Autowired | Spring 框架原生 | 根据类型装配 | 是 | required (boolean),指定是否必须注入,默认为true 。 |
@Resource | JSR-250 (Java EE 标准) | 根据名称装配,按名称找不到时根据类型 | 否 | name (String),指定要装配的 Bean 名称,默认为属性名称。 |
@Inject | JSR-330 (Java EE 标准) | 根据类型装配 | 是 | 无 |
在日常开发中,都是使用SpringBoot进行开发,一般使用@Autowired
注解就够了,适合大多数Spring应用场景。
@Autowired
@Autowired
是Spring框架中用于自动装配Bean的主要方式之一。它可以根据类型来自动注入依赖关系。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
在使用@Autowired
时,Spring会尝试将一个属性与容器中具有兼容类型的Bean进行匹配。
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
如果存在多个同类型的Bean,可以结合@Primary
注解,指定优先级最高的Bean进行注入。
@Component
public class UserRepositoryImpl1 implements UserRepository {
// implementation
}
@Component
@Primary
public class UserRepositoryImpl2 implements UserRepository {
// implementation
}
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
除了使用@Primary
还可以使用@Qualifier
注解来指定具体的Bean名称,来解决多个相同类型Bean的自动装配歧义问题。
@Component("userRepository1")
public class UserRepositoryImpl1 implements UserRepository {
// 实现1
}
@Component("userRepository2")
public class UserRepositoryImpl2 implements UserRepository {
// 实现2
}
// 示例:结合@Qualifier注解指定Bean名称
@Autowired
@Qualifier("userRepository2")
private UserRepository userRepository;
@Autowired
可以使用required
属性控制是否要求依赖关系存在,默认为true
,表示必须存在兼容的Bean,设为false
可以允许null
值注入。
@Component
public class UserService {
private UserRepository userRepository;
@Autowired(required = false)
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
@Autowired
可以放在构造器、参数、方法、属性上。
- 构造器注入:可以在构造器上使用
@Autowired
来完成构造器注入,Spring会自动根据类型进行注入。@Component public class UserService { private final UserRepository userRepository; @Autowired public UserService(UserRepository userRepository) { this.userRepository = userRepository; } }
- 属性注入:可以直接在属性上使用
@Autowired
注解来进行依赖注入。@Component public class UserService { @Autowired private UserRepository userRepository; }
- 方法注入:可以在方法上使用
@Autowired
注解,Spring会在初始化Bean时调用这些方法完成依赖注入。@Component public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } }
- 参数注入:可以在方法参数上使用
@Autowired
注解,Spring会根据参数类型自动注入对应的Bean。@Component public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } public void processUserData(@Autowired User user) { } }
@Autowired
的实现原理,是通过@Autowired
后置处理器实现的。在@Autowired
注解文档注释上面,可以看到与之息息相关的一个类AutowiredAnnotationBeanPostProcessor
,即@Autowired
后置处理器。看到该类实现了MergedBeanDefinitionPostProcessor
接口,在postProcessMergedBeanDefinition
方法上打一个断点,就可以看到@Autowired
的调用栈。
/*
* @see AutowiredAnnotationBeanPostProcessor
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired{}
@Autowired
注解调用栈:
AbstractApplicationContext.refresh(容器初始化)
---> registerBeanPostProcessors (注册AutowiredAnnotationBeanPostProcessor)
---> finishBeanFactoryInitialization
---> AbstractAutowireCapableBeanFactory.doCreateBean
---> AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors
---> MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition
---> AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata
核心调用:
postProcessMergedBeanDefinition
--->findAutowiringMetadata
--->buildAutowiringMetadata
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 调用 findAutowiringMetadata
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 调用buildAutowiringMetadata
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
Class<?> targetClass = clazz;//需要处理的目标类
do {
final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
// 通过反射获取该类所有的字段,并遍历每一个字段,并通过方法findAutowiredAnnotation遍历每一个字段的所用注解,
// 如果用autowired修饰了,则返回auotowired相关属性
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {//校验autowired注解是否用在了static方法上
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}//判断是否指定了required
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 和上面一样的逻辑,但是是通过反射处理类的method
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterCount() == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 用@Autowired修饰的注解可能不止一个,因此都加在currElements这个容器里面,一起处理
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
通过上面的源码,可以看到Spring在运行时通过反射查找@Autowired
注解,并自动注入相关字段。Spring框架利用反射遍历目标类及其超类的所有字段和方法,查找并收集所有使用了@Autowired
注解的元素。对于每个字段和方法,首先通过反射获取注解信息,如果字段或方法被@Autowired
注解修饰且符合条件(如非静态),则将其封装成对应的注入元素(AutowiredFieldElement
或AutowiredMethodElement
)并添加到当前元素列表中。最后,这些注入元素会被封装到InjectionMetadata
对象中,并用于实际的依赖注入过程,从而实现Spring的自动注入功能。
@Resource
@Resource
注解来自JSR-250
,JDK自带,主要用于通过名称注入依赖。它的行为类似于@Autowired
,但它更倾向于按名称进行注入。默认情况下,@Resource
注解按名称进行注入。如果找不到同名的Bean,再按类型进行匹配。它不支持@Primary
,如果存在多个同类型的Bean且未指定name
属性,会抛出异常。
@Component
public class UserService {
@Resource(name = "userRepositoryImpl1")
private UserRepository userRepository;
}
假设我们有一个旧项目,其中大量使用了JDK标准的@Resource
注解进行依赖注入,而我们现在想要将项目迁移到Spring,同时保持现有的依赖注入逻辑不变。在这种情况下,我们可以继续使用@Resource
注解进行依赖注入。
@Inject
@Inject
注解来自JSR-330
,需要导入javax.inject
包。它的行为与@Autowired
类似,但没有任何属性。
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Inject
注解按类型进行注入,可以结合@Primary
注解,指定优先级最高的Bean进行注入。
@Component
public class UserService {
@Inject
@Named("userRepositoryImpl1")
private UserRepository userRepository;
}
// Define multiple implementations
@Component
@Named("userRepositoryImpl1")
public class UserRepositoryImpl1 implements UserRepository {
// implementation details
}
@Component
@Named("userRepositoryImpl2")
public class UserRepositoryImpl2 implements UserRepository {
// implementation details
}
也可以结合@Named
注解,显式指定要注入的Bean名称,解决多个同类型Bean的注入问题。
@Component("userRepository1")
public class UserRepositoryImpl1 implements UserRepository {
// 实现1
}
@Primary
@Component("userRepository2")
public class UserRepositoryImpl2 implements UserRepository {
// 实现2
}
@Component
public class UserService {
@Inject
@Named("userRepository1")
private UserRepository userRepository;
}
假设我们有一个项目,需要在不同的环境中运行。在本地开发时,我们使用Spring,但在生产环境中,我们使用 Java EE 容器,这些容器使用 CDI(Contexts and Dependency Injection)作为依赖注入框架。为了在不同的环境中都能够使用相同的代码进行依赖注入,我们可以使用JSR-330
标准的@Inject
注解。这种方式使得代码能够在Spring和Java EE环境中都能正常运行。
CDI(Contexts and Dependency Injection,上下文与依赖注入)是 Java EE 标准的一部分,定义了一种类型安全的依赖注入机制,主要用于管理 Java EE 应用程序中的生命周期和依赖关系。CDI 提供了一种统一的、标准的依赖注入方式,使得开发者可以更容易地管理对象的创建、销毁以及对象之间的依赖关系。
使用Spring底层组件
为了在Spring框架的基础上实现更加细粒度的控制或定制化需求,可以使用Spring底层组件。
Aware
接口是一组特定于Spring容器的接口,允许beans感知和与Spring容器进行交互。通过实现Aware
接口的子接口,来使用Spring的底层的组件。Aware
接口类似于回调方法的形式在Spring加载的时候将我们自定以的组件加载。
/**
* A marker superinterface indicating that a bean is eligible to be notified by the
* Spring container of a particular framework object through a callback-style method.
* The actual method signature is determined by individual subinterfaces but should
* typically consist of just one void-returning method that accepts a single argument.
*/
public interface Aware {}
常用的Aware
接口:
ApplicationContextAware
,允许Bean访问ApplicationContext
,从而可以访问容器中的其他Bean或执行更高级的容器操作。@Component public class MyBean implements ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void someMethod() { // 使用 ApplicationContext 获取其他 Bean AnotherBean anotherBean = applicationContext.getBean(AnotherBean.class); // 执行更高级的容器操作,如发布事件等 applicationContext.publishEvent(new CustomEvent(this, "Some message")); } }
BeanFactoryAware
允许Bean访问配置它的Bean工厂。@Component public class MyBean implements BeanFactoryAware { private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } public void someMethod() { // 使用 BeanFactory 获取其他 Bean AnotherBean anotherBean = beanFactory.getBean(AnotherBean.class); // 可以进一步操作 BeanFactory,如获取 Bean 的定义信息等 String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); } }
BeanPostProcessor
也是常用的底层组件。它是Bean
的后置处理器,在初始化前后进行处理工作。需要在Bean实例化后和初始化前后执行自定义的处理逻辑,如AOP切面的实现、自定义注解处理等。调用顺序为:
创建对象 --> postProcessBeforeInitialization --> 初始化 --> postProcessAfterInitialization --> 销毁
public class MainTest {
public static void main(String[] args) {
// 获取Spring IOC容器
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(DemoConfiguration.class);
System.out.println("容器初始化完成...");
annotationConfigApplicationContext.close();
System.out.println("容器销毁了...");
}
}
@Configuration
class DemoConfiguration implements BeanPostProcessor {
@Bean(initMethod = "init", destroyMethod = "destroy")
public DemoEntity getDemoEntity(){
return new DemoEntity();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用了 postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用了 postProcessAfterInitialization");
return bean;
}
}
@Component
class DemoEntity {
public DemoEntity(){
System.out.println("调用了构造器...");
}
public void destroy(){
System.out.println("调用了销毁方法...");
}
public void init() {
System.out.println("调用了初始化方法...");
}
}
通过打断点可以看到,在创建Bean的时候会调用AbstractAutowireCapableBeanFactory
类的doCreateBean
方法,这也是创建Bean的核心方法。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
// 创建 Bean 实例
Object beanInstance = createBeanInstance(mbd, beanName, args);
// 提前暴露已经创建的 Bean 实例,用于解决循环依赖问题
Object exposedObject = beanInstance;
try {
// 给 Bean 实例应用属性填充,包括依赖注入
populateBean(beanName, mbd, instanceWrapper);
// 初始化 Bean,执行各种初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}catch (Exception ex) {
throw new BeanCreationException(beanName, "Initialization of bean failed", ex);
}
// 注册销毁回调,用于在 Bean 销毁时执行清理操作
registerDisposableBeanIfNecessary(beanName, exposedObject, mbd);
return exposedObject;
}
doCreateBean
方法中核心方法为populateBean
方法,其调用栈大致如下:
populateBean(){
applyBeanPostProcessorsBeforeInitialization()
--> invokeInitMethods()
--> applyBeanPostProcessorsAfterInitialization()
}
在初始化之前调用populateBean()
方法给Bean进行属性赋值,之后再调用applyBeanPostProcessorsBeforeInitialization
方法。
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
该方法作用是,遍历容器中所有的BeanPostProcessor
挨个执行postProcessBeforeInitialization
方法,一旦返回null
,将不会执行后面Bean的postProcessBeforeInitialization
方法。之后在调用invokeInitMethods
方法,进行Bean的初始化,最后在执行applyBeanPostProcessorsAfterInitialization
方法,执行一些初始化之后的工作。