深入探索Spring的Bean注入:四种方式解析与循环依赖探讨

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 深入探索Spring的Bean注入:四种方式解析与循环依赖探讨

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁

🦄 个人主页——libin9iOak的博客🎐
🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐
🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🍁🐥

【摘要】:

本博客将深入探讨Spring中Bean的注入方式以及循环依赖的问题。我们将逐一介绍XML方式、注解方式(@Configuration + @Bean、@Import)、FactoryBean以及BeanDefinitionRegistryPostProcessor,通过实战演示向读者展示如何将Bean成功注入Spring容器。

【引言】:

Spring作为一个功能强大的开源框架,广泛应用于Java企业级应用的开发。其中的IOC特性为我们提供了依赖注入的能力,让对象的创建和依赖关系的管理从我们手动控制转向了Spring容器自动完成。本文将带您深入了解Spring的Bean注入机制,探讨不同的注入方式以及循环依赖的处理方法,助您更好地应用Spring框架。

【前沿】:

随着Spring不断发展,不同的Bean注入方式逐渐涌现。除了传统的XML方式,我们还可以利用注解和接口的特性来实现更加优雅和灵活的Bean注入。同时,循环依赖是在实际应用中可能遇到的问题之一,本文也将探讨如何处理循环依赖,确保应用的正常运行。

【正文】:

当我们提到Spring时,你首先想到的是什么?是AOP和IOC这两大特性吗?还是Spring中Bean的初始化流程?又或者是基于Spring的Spring Cloud全家桶呢?

在今天的博客中,我们将从Spring的IOC特性入手,深入探讨在Spring中将Bean注入Spring容器的几种方式。

首先,让我们简单了解一下IOC的概念:IOC即控制反转,也称为依赖注入,是指将对象的创建或依赖关系的引用从具体的对象控制转移到框架或IOC容器来完成,也就是依赖对象的获得被反转了。

“可以简单理解为原来由我们来创建对象,现在由Spring来创建并控制对象。”

xml方式

还记得最早接触Spring的时候,使用的是SSH框架吗?所有的Bean的注入都依靠xml文件来完成。

它的注入方式分为:set方法注入、构造方法注入、字段注入,而注入类型分为值类型注入(8种基本数据类型)和引用类型注入(将依赖对象注入)。

以下是set方法注入的简单样例:

<bean name="teacher" class="org.springframework.demo.model.Teacher">
    <property name="name" value="阿Q"></property>
</bean>

对应的实体类代码:

public class Teacher {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
}

xml方式存在的缺点如下:

  1. xml文件配置起来比较麻烦,既要维护代码又要维护配置文件,开发效率低;
  2. 项目中配置文件过多,维护起来比较困难;
  3. 程序编译期间无法对配置项的正确性进行验证,只能在运行期发现并且出错之后不易排查;
  4. 解析xml时,无论是将xml一次性装进内存,还是一行一行解析,都会占用内存资源,影响性能。

注解方式

随着Spring的发展,Spring 2.5开始出现了一系列注解,除了我们经常使用的@Controller、@Service、@Repository、@Component之外,还有一些比较常用的方式,接下来我们简单了解下。

@Configuration + @Bean

当我们需要引入第三方的jar包时,可以用@Bean注解来标注,同时需要搭配@Configuration来使用。

  • @Configuration用来声明一个配置类,可以理解为xml的标签
  • @Bean用来声明一个Bean,将其加入到Spring容器中,可以理解为xml的标签

简单样例:将RedisTemplate注入Spring

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        //......
        return redisTemplate;
    }
}
@Import

我们在翻看Spring源码的过程中,经常会看到@Import注解,它也可以用来将第三方jar包注入Spring,但是它只可以作用在类上。

例如在注解@EnableSpringConfigured上就包含了@Import注解,用于将SpringConfiguredConfiguration配置文件加载进Spring容器。

@Import(SpringConfiguredConfiguration.class)
public @interface EnableSpringConfigured {}

@Import的value值是一个数组,一个一个注入比较繁琐,因此我们可以搭配ImportSelector接口来使用,用法如下:

@Configuration
@Import(MyImportSelector.class)
public class MyConfig {}
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"org.springframework.demo.model
.Teacher","org.springframework.demo.model.Student"};
    }
}

其中selectImports方法返回的数组就会通过@Import注解注入到Spring容器中。

无独有偶,ImportBeanDefinitionRegistrar接口也为我们提供了注入Bean的方法。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    //......
}

我们点击AspectJAutoProxyRegistrar类,发现它实现了ImportBeanDefinitionRegistrar接口,它的registerBeanDefinitions方法便是注入Bean的过程,可以参考下。

如果觉得源代码比较难懂,可以看一下我们自定义的类:

@Configuration
@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        RootBeanDefinition tDefinition = new RootBeanDefinition(Teacher.class);
        // 注册Bean,并指定Bean的名称和类型
        registry.registerBeanDefinition("teacher", tDefinition);
    }
}

这样我们就把Teacher类注入到Spring容器中了。

FactoryBean

提到FactoryBean,就不得不与BeanFactory比较一番。

  • BeanFactory: 是Factory,IOC容器或者对象工厂,所有的Bean都由它进行管理。
  • FactoryBean: 是Bean,是一个能产生或者修饰对象生成的工厂Bean,实现与工厂模式和修饰器模式类似。

那么FactoryBean是如何实现Bean注入的呢?

首先定义实现了FactoryBean接口的类:

public class TeacherFactoryBean implements FactoryBean<Teacher> {
    /**
     * 返回此工厂管理的对象实例
     **/
    @Override
    public Teacher getObject() throws Exception {
        return new Teacher();
    }
    /**
     * 返回此FactoryBean创建的对象的类型
     **/
    @Override
    public Class<?> getObjectType() {
        return Teacher.class;
    }
}

然后通过@Configuration + @Bean的方式将TeacherFactoryBean加入到容器中:

@Configuration
public class MyConfig {
    @Bean
    public TeacherFactoryBean teacherFactoryBean(){
        return new TeacherFactoryBean();
    }
}

注意:我们没有向容器中注入Teacher,而是直接注入的TeacherFactoryBean,然后从容器中拿Teacher这个类型的Bean,成功运行。

BeanDefinitionRegistryPostProcessor

看到这个接口,不知道对于翻看过Spring源码的你来说熟不熟悉。如果不熟悉的话请继续往下看,要是熟悉的话就再看一遍吧😃。

源码
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    // 注册Bean到Spring容器中
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanFactoryPostProcessor接口是BeanFactory的后置处理器,方法postProcessBeanFactory对Bean的定义进行控制。今天我们重点来看看postProcessBeanDefinitionRegistry方法:它的参数是BeanDefinitionRegistry,顾名思义就是与BeanDefinition注册相关的。

通过观察该类,我们发现它里边包含了registerBeanDefinition方法,这个不就是我们想要的吗?为了能更好的使用该接口来达到注入Bean的目的,我们先来看看Spring是如何操作此接口的。

private static void invokeBeanDefinitionRegistryPostProcessors(
    Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, 
    BeanDefinitionRegistry registry) {
    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}

会发现实现了BeanDefinitionRegistryPostProcessor接口的Bean,其postProcessBeanDefinitionRegistry方法会被调用,也就是说如果我们自定义接口实现该接口,它的postProcessBeanDefinitionRegistry方法也会被执行。

实战

话不多说,直接上代码。自定义接口实现类:

public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    /**
     * 初始化过程中先执行
     **/
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Teacher.class);
        //Teacher的定义注册到Spring容器中
        registry.registerBeanDefinition("teacher", rootBeanDefinition);
    }
    /**
     * 初始化过程中后执行
     **/
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}
}

启动类代码:

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    MyBeanDefinitionRegistryPostProcessor postProcessor = new MyBeanDefinitionRegistryPostProcessor();
    //将自定义实现类加入Spring容器
    context.addBeanFactoryPostProcessor(postProcessor);
    context.refresh();
    Teacher bean = context.getBean(Teacher.class);
    System.out.println(bean);
}

启动并打印结果:

org.springframework.demo.model.Teacher@2473d930

发现已经注入到Spring容器中了。

以上就是我们总结的几种将Bean注入Spring容器的方式,赶快行动起来实战演练一下吧!

【今日学习总结】:

在今天的学习中,我们深入了解了Spring中Bean的注入方式。通过XML方式、注解方式(@Configuration + @Bean、@Import)、FactoryBean以及BeanDefinitionRegistryPostProcessor的实战演示,我们掌握了将Bean成功注入Spring容器的方法。同时,我们了解了循环依赖的处理方式,保障了应用的正常运行。在后续的学习中,我们还将继续深入探索Spring框架的更多强大功能和实用技巧。让我们继续努力,成为Spring框架的高级使用者吧!

原创声明

=======

作者: [ libin9iOak ]


本文为原创文章,版权归作者所有。未经许可,禁止转载、复制或引用。

作者保证信息真实可靠,但不对准确性和完整性承担责任。

未经许可,禁止商业用途。

如有疑问或建议,请联系作者。

感谢您的支持与尊重。

点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,共同成长。


目录
相关文章
|
23天前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
7天前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
9 0
Spring高手之路22——AOP切面类的封装与解析
|
10天前
|
Java 微服务 Spring
Spring Cloud全解析:配置中心之解决configserver单点问题
但是如果该configserver挂掉了,那就无法获取最新的配置了,微服务就出现了configserver的单点问题,那么如何避免configserver单点呢?
|
22天前
|
XML Java 数据格式
Spring Cloud全解析:注册中心之zookeeper注册中心
使用ZooKeeper作为Spring Cloud的注册中心无需单独部署服务器,直接利用ZooKeeper服务端功能。项目通过`spring-cloud-starter-zookeeper-discovery`依赖实现服务注册与发现。配置文件指定连接地址,如`localhost:2181`。启动应用后,服务自动注册到ZooKeeper的`/services`路径下,形成临时节点,包含服务实例信息。
106 3
|
22天前
|
缓存 Java Spring
spring如何解决循环依赖
Spring框架处理循环依赖分为构造器循环依赖与setter循环依赖两种情况。构造器循环依赖不可解决,Spring会在检测到此类依赖时抛出`BeanCurrentlyInCreationException`异常。setter循环依赖则通过缓存机制解决:利用三级缓存系统,其中一级缓存`singletonObjects`存放已完成的单例Bean;二级缓存`earlySingletonObjects`存放实例化但未完成属性注入的Bean;三级缓存`singletonFactories`存放创建这些半成品Bean的工厂。
|
9天前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
26 0
|
9天前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
20 0
|
10天前
|
Java Spring 容器
循环依赖难破解?Spring Boot神秘武器@RequiredArgsConstructor与@Lazy大显神通!
【8月更文挑战第29天】在Spring Boot应用中,循环依赖是一个常见问题。当两个或多个Bean相互依赖形成闭环时,Spring容器会陷入死循环。本文通过对比@RequiredArgsConstructor和@Lazy注解,探讨它们如何解决循环依赖问题。**@RequiredArgsConstructor**:通过Lombok生成包含final字段的构造函数,优先通过构造函数注入依赖,简化代码但可能导致构造函数复杂。**@Lazy**:延迟Bean的初始化,直到首次使用,打破创建顺序依赖,增加灵活性但可能影响性能。根据具体场景选择合适方案可有效解决循环依赖问题。
20 0
|
10天前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
17 0
|
17天前
|
Java 测试技术 数据库
容器镜像解析问题之解析 Java 应用依赖时识别 jar 包如何解决
容器镜像解析问题之解析 Java 应用依赖时识别 jar 包如何解决
14 0

热门文章

最新文章

推荐镜像

更多
下一篇
DDNS