Spring注入bean的四种方式总结

 更新时间:2025年11月27日 09:11:46   作者:K***6589  
我们谈到Spring的时候一定会提到IOC容器、DI依赖注入,Spring通过将一个个类标注为Bean的方法注入到IOC容器中,达到了控制反转的效果,Spring帮我们实现了一种通过注解来实现注入的方法,所以本文为大家介绍了Spring注入bean的四种方式,需要的朋友可以参考下

一提到 Spring,大家最先想到的是啥?是 AOPIOC 的两大特性?是 SpringBean 的初始化流程?还是基于 SpringSpring Cloud 全家桶呢?

今天我们就从 SpringIOC 特性入手,聊一聊 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="张老师"></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<beans> 标签
  • @Bean 用来声明一个 bean,将其加入到 Spring 容器中,可以理解为 xml<bean> 标签

简单样例:将 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 {}

@Importvalue 值是一个数组,一个一个注入比较繁琐,因此我们可以搭配 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 : 是 FactoryIOC 容器或者对象工厂,所有的 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,成功运行。

四、BDRegistryPostProcessor

看到这个接口,不知道对于翻看过 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 的后置处理器,方法postProcessBeanFactorybean 的定义进行控制。今天我们重点来看看 postProcessBeanDefinitionRegistry 方法:它的参数是 BeanDefinitionRegistry,顾名思义就是与 BeanDefinition 注册相关的。

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

看下 invokeBeanFactoryPostProcessors 方法,会发现没有实现 PriorityOrdered和Orderedbean(这种跟我们自定义的实现类有关)会执行以下代码。

while (reiterate) {
    ......
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    ......
}

进入该方法

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的四种方式总结的文章就介绍到这了,更多相关Spring注入bean内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot集成MongoDB的实现

    SpringBoot集成MongoDB的实现

    本文主要介绍了SpringBoot集成MongoDB的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-01-01
  • 基于Protobuf动态解析在Java中的应用 包含例子程序

    基于Protobuf动态解析在Java中的应用 包含例子程序

    下面小编就为大家带来一篇基于Protobuf动态解析在Java中的应用 包含例子程序。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • SpringBoot整合Docker实现一次构建到处运行的操作方法

    SpringBoot整合Docker实现一次构建到处运行的操作方法

    本文讲解的是 SpringBoot 引入容器化技术 Docker 实现一次构建到处运行,包括镜像构建、Docker仓库搭建使用、Docker仓库可视化UI等内容,需要的朋友可以参考下
    2022-10-10
  • java编程枚举类型那些事!枚举类型定义和重写枚举的方法

    java编程枚举类型那些事!枚举类型定义和重写枚举的方法

    本文主要介绍了枚举类型的有关内容,涉及简单的枚举类型定义,以及枚举类型的值在Java中的定义方法,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • 史上最全的java随机数生成算法分享

    史上最全的java随机数生成算法分享

    这篇文章主要介绍了史上最全的java随机数生成算法,我分享一个最全的随机数的生成算法,最代码的找回密码的随机数就是用的这个方法
    2014-01-01
  • 在Spring中配置Quartz的三种方式

    在Spring中配置Quartz的三种方式

    Spring Quartz 是一个任务调度框架,它允许我们定期执行特定的任务,在 Spring 中,我们可以通过多种方式来配置 Quartz,包括使用 ​​@Scheduled​​ 注解、XML 配置和 Java 配置,本文将介绍如何在 Spring 中使用这三种方式来配置 Quartz,需要的朋友可以参考下
    2025-01-01
  • SpringCloud2020整合Nacos-Bootstrap配置不生效的解决

    SpringCloud2020整合Nacos-Bootstrap配置不生效的解决

    这篇文章主要介绍了SpringCloud2020整合Nacos-Bootstrap配置不生效的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • nacos在liunx系统中启动成功浏览器却访问不了的解决方法

    nacos在liunx系统中启动成功浏览器却访问不了的解决方法

    在linux下搭建nacos,现在想要启动,访问nacos页面,访问不了,所以本文小编将给大家介绍nacos在liunx系统中启动成功,浏览器却访问不了?全面的解决办法,需要的朋友可以参考下
    2023-09-09
  • springBoot静态资源加载不到,并且配置了也不生效问题及解决

    springBoot静态资源加载不到,并且配置了也不生效问题及解决

    这篇文章总结了一个在Spring Boot 2.6.x版本中,由于路径匹配策略改变导致静态资源无法加载的问题,并提供了解决方案:通过配置类或在配置文件中设置路径匹配策略为AntPathMatcher,或者直接降级Spring Boot版本
    2025-02-02
  • MyBatis执行SQL查询的完整流程

    MyBatis执行SQL查询的完整流程

    本文详细介绍了MyBatis SQL执行的完整过程,包括核心对象、代码实现与执行链路,通过编程式调用示例和流程图,展示了从环境配置、SqlSession创建到结果获取的全步骤,需要的朋友可以参考下
    2026-01-01

最新评论