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

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

【摘要】:本博客将深入探讨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容器来完成,也就是依赖对象的获得被反转了。

代码语言:javascript复制“可以简单理解为原来由我们来创建对象,现在由Spring来创建并控制对象。”xml方式还记得最早接触Spring的时候,使用的是SSH框架吗?所有的Bean的注入都依靠xml文件来完成。

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

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

代码语言:javascript复制

对应的实体类代码:

代码语言:javascript复制public class Teacher {

private String name;

public void setName(String name) {

this.name = name;

}

}xml方式存在的缺点如下:

xml文件配置起来比较麻烦,既要维护代码又要维护配置文件,开发效率低;项目中配置文件过多,维护起来比较困难;程序编译期间无法对配置项的正确性进行验证,只能在运行期发现并且出错之后不易排查;解析xml时,无论是将xml一次性装进内存,还是一行一行解析,都会占用内存资源,影响性能。注解方式随着Spring的发展,Spring 2.5开始出现了一系列注解,除了我们经常使用的@Controller、@Service、@Repository、@Component之外,还有一些比较常用的方式,接下来我们简单了解下。

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

@Configuration用来声明一个配置类,可以理解为xml的标签@Bean用来声明一个Bean,将其加入到Spring容器中,可以理解为xml的标签简单样例:将RedisTemplate注入Spring

代码语言:javascript复制@Configuration

public class RedisConfig {

@Bean

public RedisTemplate redisTemplate(LettuceConnectionFactory redisConnectionFactory) {

RedisTemplate redisTemplate = new RedisTemplate();

//......

return redisTemplate;

}

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

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

代码语言:javascript复制@Import(SpringConfiguredConfiguration.class)

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

代码语言:javascript复制@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的方法。

代码语言:javascript复制@Import(AspectJAutoProxyRegistrar.class)

public @interface EnableAspectJAutoProxy {

//......

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

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

代码语言:javascript复制@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接口的类:

代码语言:javascript复制public class TeacherFactoryBean implements FactoryBean {

/**

* 返回此工厂管理的对象实例

**/

@Override

public Teacher getObject() throws Exception {

return new Teacher();

}

/**

* 返回此FactoryBean创建的对象的类型

**/

@Override

public Class getObjectType() {

return Teacher.class;

}

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

代码语言:javascript复制@Configuration

public class MyConfig {

@Bean

public TeacherFactoryBean teacherFactoryBean(){

return new TeacherFactoryBean();

}

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

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

源码代码语言:javascript复制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是如何操作此接口的。

代码语言:javascript复制private static void invokeBeanDefinitionRegistryPostProcessors(

Collection postProcessors,

BeanDefinitionRegistry registry) {

for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {

postProcessor.postProcessBeanDefinitionRegistry(registry);

}

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

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

代码语言:javascript复制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 {}

}启动类代码:

代码语言:javascript复制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);

}启动并打印结果:

代码语言:javascript复制org.springframework.demo.model.Teacher@2473d930发现已经注入到Spring容器中了。

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

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

相关推荐

太空沙官方旗舰店
365365bet体育在线

太空沙官方旗舰店

2026-01-21 👁️ 3621
宝兰德BES中间件基本操作命令
365365bet体育在线

宝兰德BES中间件基本操作命令

2025-09-19 👁️ 5701
中国哪个城市被称为'羊城'? 羊城的历史与文化
手机版office365破解版

中国哪个城市被称为'羊城'? 羊城的历史与文化

2025-08-09 👁️ 544
成语词典
365365bet体育在线

成语词典

2026-01-05 👁️ 5834
王者荣耀装备噬神之书具体讲解
bt365体育投注

王者荣耀装备噬神之书具体讲解

2025-08-10 👁️ 645
西联汇款在中国的取款地点及国内取款流程介绍
手机版office365破解版

西联汇款在中国的取款地点及国内取款流程介绍

2025-10-12 👁️ 4616