实力总结四类Bean注入Spring的方式

news2024/10/5 5:23:41
  • xml 方式

  • 注解方式

    • @Configuration + @Bean

    • @Import

  • FactoryBean

  • BDRegistryPostProcessor

    • 源码

    • 实战

一提到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="阿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<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 : 是 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,成功运行。

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方法,会发现没有实现PriorityOrderedOrderedbean(这种跟我们自定义的实现类有关)会执行以下代码。

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容器的方式,赶快行动起来实战演练一下吧!

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

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

相关文章

Vue组件之间的通信

1、组件&#xff1a;是vue的重要的特征之一&#xff0c;可以扩展html的功能&#xff0c;也可以封装代码实现重复使用 2、组件的创建 &#xff08;1&#xff09;非脚手架方式创建&#xff1a; 1️⃣使用vue.extend创建组件 2️⃣使用vue.component注册组件 3️⃣在html页面…

一个平凡打工人在 CSDN 的 2022 与 2023

平凡又不平凡的一年 2022 年是不平凡的一年&#xff0c;这一年经历了疫情的起起伏伏&#xff0c;随着身边好多同学的毕业离开&#xff0c;手头的工作也愈发的繁重&#xff0c;2022 年也顺理成章的成为了工作3年来最忙碌的一年&#xff0c;但却也是博客产出与自己收获最多的一年…

大数据hadoop和spark怎么选择?

Hadoop框架的主要模块包括如下&#xff1a; Hadoop Common Hadoop分布式文件系统(HDFS) Hadoop YARN Hadoop MapReduce 虽然上述四个模块构成了Hadoop的核心&#xff0c;不过还有其他几个模块。这些模块包括&#xff1a;Ambari、Avro、Cassandra、Hive、 Pig、Oozie、Flume…

spring之静态代理

文章目录前言一、代理模式中的三大角色二、静态代理引入1.业务接口2.目标对象总结前言 在Java程序中代理模式的作用&#xff1a; 当一个对象需要收到保护的时候可以考虑使用代理对象去完成某个行为需要给某个对象的功能进行功能增强的时候&#xff0c;可以考虑找一个代理进行…

Java内存模型(JMM)详解!

文章目录什么是JMM?现代计算机内存模型缓存一致性JMM内存模型与计算机内存模型的关系线程间通信JMM三大问题原子性可见性有序性什么是JMM? JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。 JMM可以理解为是一个规范&#xff0c;一个抽象概念&#xff0c;并不真实…

Java 单元测试

目录 一、Junit 1.1、单元测试初始化与清理资源 1.2、捕获异常 1.3、条件测试 1.4、标记失效测试方法 1.5、参数化测试 单元测试&#xff1a;是对最小功能单元编写的测试代码。 示例&#xff0c;当开发好一个 Java 阶乘的方法。 n&#xff01; 1 x 2 x 3 x ..…

CRM软件哪个好?该如何选择?

CRM软件哪个好&#xff1f;该如何选择&#xff1f; CRM是集营销、销售、服务为一体的围绕客户全生命周期管理的系统&#xff0c;在各行各业的数字化转型大潮中&#xff0c;作为以消费者、终端用户、客户为主导的企业经营管理核心系统&#xff0c;CRM选型的难度和复杂度也在不断…

关于ETL的两种架构(ETL架构和ELT架构)

ETL&#xff0c;是英文 Extract-Transform-Load 的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;extract&#xff09;、转换&#xff08;transform&#xff09;、加载&#xff08;load&#xff09;至目的端的过程。ETL一词较常用在数据仓库&#xff0c;但其对象…

Java的JVM垃圾回收机制GC概述

JVM——GC机制1、什么是GC&#xff1f;2、GC算法的总体概述3、JVM所处的位置4、JVM整体结构5、JVM架构模型6、Java垃圾回收机制优缺点7、GC主要关注的区域垃圾回收算法&#xff1a;标记阶段&#xff0c;引用计数循环引用标记阶段&#xff1a;可达性分析算法GC root可以是哪些&a…

JavaScript代码题--以及一些奇奇怪怪的发现

解析 let a{b:10,c:{d:[11,12],e:13}}&#xff0c;实现 10111213 效果 解 const a{b:10,c:{d:[11,12],e:13}}function sum(obj) {let total 0;const value Object.values(obj)value.forEach(item>{total typeof item number ? item : sum(item)})return total }const …

Java家教系统家教网站家教兼职系统

简介&#xff1a; 用户可以注册成为学员也可以是教员。教员发布家教信息&#xff0c;学员根据自己的要求查找符合自己的教员。学员预约教员的某一天去家教&#xff0c;教员可以在个人中心里查看&#xff0c;是否接受该预约。在教员接受或拒绝之前&#xff0c;学员随时可以取消…

数据库,计算机网络、操作系统刷题笔记23

数据库&#xff0c;计算机网络、操作系统刷题笔记23 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

基于 Vue 制作一个猜拳小游戏

目录前言&#xff1a;项目效果展示&#xff1a;对应素材&#xff1a;代码实现思路&#xff1a;实现代码&#xff1a;总结&#xff1a;前言&#xff1a; 在工作学习之余玩一会游戏既能带来快乐&#xff0c;还能缓解生活压力&#xff0c;跟随此文一起制作一个小游戏吧。 描述&…

【2042. 检查句子中的数字是否递增】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 句子是由若干 token 组成的一个列表&#xff0c;token 间用 单个 空格分隔&#xff0c;句子没有前导或尾随空格。每个 token 要么是一个由数字 0-9 组成的不含前导零的 正整数 &#xff0c;要么是一个…

ORA-00600 kcratr_nab_less_than_odr 问题处理

问题&#xff1a;ORA-00600: 内部错误代码, 参数: [kcratr_nab_less_than_odr], [1], [196495], [39399], [39460], [], [], [], [], [], [], []导致原因&#xff1a;可能是由于服务器宕机&#xff0c;控制文件的缺失&#xff0c;或者在线日志文件在实例恢复时不完整1、数据库未…

5G边缘计算网关助力5G工业物联网智能化建设

5G边缘计算&#xff0c;凭借高带宽、高可靠、低时延、移动性等特性&#xff0c;推动工业生产物联网发展趋势&#xff0c;实现工业更快、更精准通信及数据共享。边缘计算网关下5G工业物联网远程感知生产一线&#xff0c;工控数字化、自动化、智能化&#xff0c;降低人物力资源成…

LeetCode 2042. 检查句子中的数字是否递增

【LetMeFly】2042.检查句子中的数字是否递增 力扣题目链接&#xff1a;https://leetcode.cn/problems/check-if-numbers-are-ascending-in-a-sentence/ 句子是由若干 token 组成的一个列表&#xff0c;token 间用 单个 空格分隔&#xff0c;句子没有前导或尾随空格。每个 tok…

【计算机体系结构】指令集并行(ILP)动态调度算法:Tomasulo实现代码(Tomasulo Algorithm Implementation)

Tomasulo Algorithm Implementation &#xff08;本文章仅提供算法实现过程&#xff0c;具体算法思想请查阅教科书&#xff09; 如果觉得这篇文章有用&#xff0c;请记得点个赞并收藏哦&#xff01; 1.Introduction Tomasulo算法用于指令的动态调度&#xff0c;允许乱序执行…

C C++内存对齐以及特殊类的大小

目录C语言内存对齐现象内存对齐规则为什么存在内存对齐如果struct or class中存在成员函数时的大小空类大小为1Cclass存在虚函数时的大小C语言 内存对齐现象 C语言中结构体的大小往往不是结构体中各种数据类型的加和&#xff0c;因为存在内存对齐; struct S {double d;//8字…

Linux常用系统日志

文章目录一 常用系统日志二 系统日志优先级三 其他日志文件一 常用系统日志 日志文件用途/var/log/messages记录大多数系统日志信息&#xff0c;包括启动、IO错误、网络和程序等问题/var/log/secure记录安全和身份验证等相关消息和错误/var/logrsyslog将所有日志文件写入到该目…