Spring中的IOC详解

news2024/12/24 8:19:42

文章目录

    • IOC
      • IOC容器的工作原理
      • Bean的生命周期
      • Bean的自动装配
        • @Autowired
        • @Resource
        • @Inject
      • 使用Spring底层组件

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支持的属性
@AutowiredSpring 框架原生根据类型装配required (boolean),指定是否必须注入,默认为true
@ResourceJSR-250 (Java EE 标准)根据名称装配,按名称找不到时根据类型name (String),指定要装配的 Bean 名称,默认为属性名称。
@InjectJSR-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方法,执行一些初始化之后的工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1937111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

栈和队列(一) ------基本概念,循环队列

目录 栈 python实现 队列 python实现 循环队列 力扣622- --循环队列 力扣20 ----有效括号判断 分析 代码 栈 python实现 在Python中实现一个栈&#xff08;Stack&#xff09;可以通过使用列表&#xff08;list&#xff09;来完成&#xff0c;因为列表提供了动态数…

【网络安全的神秘世界】Error:Archives directory /var/cache/apt/archives/partial is missing.

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 ✨问题描述 在kali中想要安装beef-xss软件包时&#xff0c;发生如下报错&#xff1a; Error: Archives directory /var/cac…

d3d12.dll 文件缺失如何解决?五种修复丢失问题的方法

d3d12.dll 文件缺失如何解决&#xff1f;它为什么会不见呢&#xff1f;今天&#xff0c;我们将探讨 d3d12.dll 文件的重要性、原因以及丢失时的解决策略。本文将全面介绍 d3d12.dll 文件&#xff0c;并提供五种修复丢失问题的方法。 d3d12.dll文件是什么的详细介绍 d3d12.dll …

【RAGFlow】Ubuntu系统下实现源码启动RAGFlow

一、RAGFlow 是什么&#xff1f; RAGFlow 是一款基于深度文档理解构建的开源 RAG&#xff08;Retrieval-Augmented Generation&#xff09;引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程&#xff0c;结合大语言模型&#xff08;LLM&#xff09;针对用…

【C语言初阶】C语言数组基础:从定义到遍历的全面指南

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言函数 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀数组 &#x1f4d2;1. 什么是数组…

【医学影像】X86+FPGA:支持AI医学影像设备应用的工控主板,赋能CT、MRI、X线、超声等医学影像设备

支持AI医学影像设备应用的工控主板 在我国人口老龄化问题不断加剧&#xff0c;对影像诊断需求持续增长&#xff0c;和国家利好高端医学影像市场发展的系列法规和政策接连出台的大环境下&#xff0c;AI医学影像设备产业迎来发展黄金期。紧跟发展大势&#xff0c;基于12/13代 In…

天途无人机林业应用解决方案

林业应用现状分析 森林环境较为复杂&#xff0c;人员无法快速到达现场&#xff0c;工作人员通常会面临监控盲区&#xff0c;林区爬山涉水困难多&#xff1b;森林防火重要性不可忽视&#xff0c;2019年全国共发生森林火灾2345起&#xff0c;森林防火仍为重中之重&#xff1b;环…

SAPUI5基础知识16 - 深入理解MVC架构

1. 背景 经过一系列的练习&#xff0c;相信大家对于SAPUI5的应用程序已经有了直观的认识&#xff0c;我们在练习中介绍了视图、控制器、模型的概念和用法。在本篇博客中&#xff0c;让我们回顾总结下这些知识点&#xff0c;更深入地理解SAPUI5的MVC架构。 首先&#xff0c;让…

Jvm基础(一)

目录 JVM是什么运行时数据区域线程私有1.程序计数器2.虚拟机栈3.本地方法栈 线程共享1.方法区2.堆 二、对象创建1.给对象分配空间(1)指针碰撞(2)空闲列表 2.对象的内存布局对象的组成Mark Word类型指针实例数据&#xff1a;对齐填充 对象的访问定位句柄法 三、垃圾收集器和内存…

React基础学习-Day04

React基础学习-Day04 常见的钩子函数及基础使用方式 1.useState useState 是 React 的一个 Hook&#xff0c;用于在函数组件中添加状态。它返回一个状态变量和一个更新该状态的函数。与类组件的 this.state 和 this.setState 相对应&#xff0c;useState 让函数组件也能拥有…

【第10章】Spring Cloud之Nacos动态配置

文章目录 前言一、上下文1. 新增配置2. 启动类3. 效果 二、注解 ( 推荐 ) \color{#00FF00}{(推荐)} (推荐)1. 获取配置2. 测试2.1 未配置2.2 配置值2.3 修改值 总结 前言 这一章我们通过两个案例来学习Nacos动态配置&#xff0c;通过在控制台修改服务端配置文件值&#xff0c;…

linux环境交叉编译openssl库,以使Qt支持https

一.前言 Qt若需要支持https&#xff0c;则需要openssl的支撑,并且要注意&#xff0c;Qt不同版本会指定对应的openssl版本库&#xff0c;比方我用的Qt5.15.10他要求用的openssl版本是1.1.1&#xff0c;你就不能用其他版本&#xff0c;不然基本就是失败报错。 如何查看Qt对应ope…

TK秘籍:深度剖析机房IP与住宅IP的利与弊

大家好&#xff0c;今天我们来聊聊TikTok运营中的一个重要环节——IP地址的选择。 想象一下&#xff0c;你在TikTok上发布视频&#xff0c;就像是在一个热闹的市集上摆摊&#xff0c;而IP地址就是你的摊位位置。选对了位置&#xff0c;你的摊位就能吸引更多顾客&#xff0c;也…

浪漫情怀:红酒中的诗意与情感

在生活的点滴细节中&#xff0c;总有些元素能触动我们内心较柔软的地方&#xff0c;唤起那份深深的浪漫情怀。而红酒&#xff0c;便是这其中的一种神奇媒介。它以其不同的色泽、香气和口感&#xff0c;让人沉醉其中&#xff0c;感受那份诗意与情感的交织。今天&#xff0c;就让…

使用Kotlin Flow和协程开发高性能Android应用

使用Kotlin Flow和协程开发高性能Android应用:以电影业务为例 引言 在当今移动互联网快速发展的时代,用户对应用的性能和响应速度有了更高的要求。作为开发者,我们需要不断探索和采用新的技术来提升应用的性能和用户体验。Kotlin作为Android开发的首选语言,其协程和Flow特…

抖音/腾讯/百度ocpm深度回传如何操作?广告投放双出价的投放技巧?

要实现抖音、腾讯和百度的OCPM&#xff08;Optimized Cost Per Mille&#xff09;深度回传&#xff0c;可以通过借助第三方平台&#xff0c;例如&#xff08;转化宝&#xff09;实现广告数据精准回传&#xff0c;如此之外&#xff0c;在广告投放过程中还需要注重这些方面。 转化…

Python+Django+MySQL的新闻发布管理系统【附源码,运行简单】

PythonDjangoMySQL的新闻发布管理系统【附源码&#xff0c;运行简单】 总览 1、《新闻发布管理系统》1.1 方案设计说明书设计目标工具列表 2、详细设计2.1 登录2.2 程序主页面2.3 新闻新增界面2.4 文章编辑界面2.5 新闻详情页2.7 其他功能贴图 3、下载 总览 自己做的项目&…

云计算实训06——find、stat、touch、tree、scp、crontab指令相关应用

一、find命令 1.find的作用&#xff1a;对文件进行搜索 2. 基本语法&#xff1a; find [文件路径] [选项 选项的值 ] 3.常见的选项 -name 根据文件的名称搜索文件&#xff0c;支持通配符 * -type f 代表普通文件&#xff0c;-type d 代表目录 4.* 通配符 在 linux 系统…

QT--多页面、定时器和日期时间数据

多页面 widget.ui 负责实现一个页面; mian.cpp创建窗体对象,并监控上面的事件 窗口的显示和隐藏: 一个窗体,比如logpage widget,继承QWidget QWidget::show(); 显示该窗体 QWidget::hide(); 隐藏该窗体 页面间通信采用在main.cpp中创建connect槽函数。 代码如下 //main.c…