Spring中的IOC详解

文章目录

IOC

Spring的核心之一是IOC,IOC全称为Inversion of Control,中文译为控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。

所谓IOC,对于Spring框架来说,就是由Spring来负责对象的创建、配置和管理,所以可将IOC理解为一个大容器。IOC通过将对象创建和管理的控制权从应用代码转移到Spring容器中,实现了松耦合设计。IOC使用依赖注入(DI)来管理组成一个应用程序的组件,这些对象被称为Spring Beans。

管理Bean的创建、配置和生命周期,Spring提供了两个主要的IOC容器:BeanFactoryApplicationContext。IOC容器管理的对象,通常使用注解,如@Component@Service@Autowired,或XML配置声明Bean。IOC容器的工作流程:

  1. 读取配置,通过XML文件、注解或Java配置类读取Bean定义和依赖关系。
  2. 创建和配置Bean,容器根据配置实例化Bean,并注入它们的依赖。
  3. 管理Bean生命周期,容器负责调用Bean的初始化和销毁方法,管理其整个生命周期。

通过Spring的IOC容器,开发者可以更加专注于业务逻辑,而无需关心对象的创建和管理,从而提高了代码的可维护性和可扩展性。

IOC容器的工作原理

IOC容器是Spring框架的核心,它负责管理应用程序中对象的生命周期和依赖关系。

  1. 想要管理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");
  2. 一旦容器解析了Bean的配置信息,它会根据这些信息使用Java的反射机制来创建Bean的实例。通常情况下,Spring会调用Bean的默认构造方法来实例化对象。

    复制代码
    // 获取UserService Bean
    UserService userService = (UserService) context.getBean("userService");
  3. 对象实例化完成,容器会根据配置文件或者注解中定义的依赖关系,将其他Bean的实例或者值注入到当前Bean中。依赖注入可以通过构造函数注入、Setter方法注入或者字段注入来完成。

    复制代码
    public class UserService {
        private UserRepository userRepository;
    
        // Setter方法注入
        public void setUserRepository(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        // 其他方法
    }
  4. 在依赖注入完成后,如果配置了初始化方法,例如使用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生命周期:

  1. 实例化:在容器启动时创建该Bean的唯一实例。
  2. 初始化:
    • 初始化前置处理:调用所有注册的BeanPostProcessorpostProcessBeforeInitialization方法,可以在初始化之前对Bean进行修改。
    • 初始化:按照顺序执行以下方法,如果Bean实现了InitializingBean接口,则调用其afterPropertiesSet方法;如果在Bean定义中指定了init-method,则调用这个方法;如果Bean中有用@PostConstruct注解标记的方法,则调用该方法。
    • 初始化后处理:调用所有注册的BeanPostProcessorpostProcessAfterInitialization方法,可以在初始化之后对Bean进行修改。
  3. 使用:当Bean初始化之后,Bean处于就绪状态,可以被应用程序中的其他组件使用。
  4. 销毁:
    • 销毁前处理:在销毁之前,Spring容器会依次调用注册的所有BeanPostProcessorpostProcessBeforeDestruction方法。如果Bean类中有用@PreDestroy注解标记的方法,Spring容器会在销毁之前调用该方法。
    • 销毁:如果在Bean的定义中通过配置destroy-method属性指定了销毁方法,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的生命周期:

  1. 首先,在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);
        }
    }
  2. 在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>
  3. 当应用程序启动并且Spring容器加载配置时,将会执行以下步骤来管理UserServiceBean的生命周期:

    • 实例化:Spring容器根据配置文件或者注解,实例化 UserService 类的一个对象实例。
    • 依赖注入:将配置的属性(如message)注入到UserService实例中。
    • 初始化:调用init-method指定的初始化方法或者InitializingBean接口的afterPropertiesSet()方法,例如执行init()方法。在初始化过程中,还可以调用BeanNameAware接口的方法,获取和设置Bean的名称。
    • 使用:UserServiceBean可以被应用程序的其他组件使用,执行其业务逻辑,如打印消息。
    • 销毁:当应用程序关闭时,Spring容器会调用destroy-method指定的销毁方法或者DisposableBean接口的destroy()方法,例如执行destroy()方法。
Bean的自动装配

Bean的自动装配是Spring框架提供的一种便捷的方式,用于自动解析和设置Bean之间的依赖关系,而无需显式配置每一个依赖关系的方式。Spring支持以下几种自动装配的方式:

  1. 根据类型自动装配:Spring会自动将一个属性与同一上下文中具有兼容类型的Bean进行匹配。如果容器中存在多个符合类型的Bean,则会抛出异常。

    复制代码
    public interface UserRepository {
        // 接口定义
    }
    
    @Component
    public class UserRepositoryImpl1 implements UserRepository {
        // 实现1
    }
    
    @Component
    public class UserRepositoryImpl2 implements UserRepository {
        // 实现2
    }
    
    // 示例:根据类型自动装配
    @Autowired
    private UserRepository userRepository;
  2. 根据名称自动装配: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;
  3. 构造函数自动装配:Spring会自动通过构造函数来注入依赖,从而避免了使用@Autowired注解的繁琐。Spring会查找与构造函数参数类型相匹配的Bean,并自动进行注入。

    复制代码
    // 示例:构造函数自动装配
    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
  4. 自动装配标识符:可以使用@Autowired注解结合@Qualifier注解来指定具体的Bean名称,来解决多个相同类型Bean的自动装配歧义问题。

    复制代码
    // 示例:结合@Qualifier注解指定Bean名称
    @Autowired
    @Qualifier("userRepository")
    private UserRepository userRepository;
  5. 自动装配和主候选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可以放在构造器、参数、方法、属性上。

  1. 构造器注入:可以在构造器上使用@Autowired来完成构造器注入,Spring会自动根据类型进行注入。

    复制代码
    @Component
    public class UserService {
    
        private final UserRepository userRepository;
    
        @Autowired
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
  2. 属性注入:可以直接在属性上使用@Autowired注解来进行依赖注入。

    复制代码
    @Component
    public class UserService {
    
        @Autowired
        private UserRepository userRepository;
    }
  3. 方法注入:可以在方法上使用@Autowired注解,Spring会在初始化Bean时调用这些方法完成依赖注入。

    复制代码
    @Component
    public class UserService {
    
        private UserRepository userRepository;
    
        @Autowired
        public void setUserRepository(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
  4. 参数注入:可以在方法参数上使用@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注解修饰且符合条件(如非静态),则将其封装成对应的注入元素(AutowiredFieldElementAutowiredMethodElement)并添加到当前元素列表中。最后,这些注入元素会被封装到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方法,执行一些初始化之后的工作。

相关推荐
二闹5 分钟前
三个注解,到底该用哪一个?别再傻傻分不清了!
后端
用户490558160812516 分钟前
当控制面更新一条 ACL 规则时,如何更新给数据面
后端
林太白18 分钟前
Nuxt.js搭建一个官网如何简单
前端·javascript·后端
码事漫谈20 分钟前
VS Code 终端完全指南
后端
该用户已不存在1 小时前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
怀刃1 小时前
内存监控对应解决方案
后端
TT哇1 小时前
@[TOC](计算机是如何⼯作的) JavaEE==网站开发
java·redis·java-ee
码事漫谈1 小时前
VS Code Copilot 内联聊天与提示词技巧指南
后端
Tina学编程1 小时前
48Days-Day19 | ISBN号,kotori和迷宫,矩阵最长递增路径
java·算法
Moonbit1 小时前
MoonBit Perals Vol.06: MoonBit 与 LLVM 共舞 (上):编译前端实现
后端·算法·编程语言