🌷🍁 博主 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方式存在的缺点如下:
- xml文件配置起来比较麻烦,既要维护代码又要维护配置文件,开发效率低;
- 项目中配置文件过多,维护起来比较困难;
- 程序编译期间无法对配置项的正确性进行验证,只能在运行期发现并且出错之后不易排查;
- 解析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技术核心学习团队。一起探索科技的未来,共同成长。