Spring——Bean注入几种方式(放入容器)

news2024/11/23 20:19:59

Bean注入几种方式

      • 1.XML方式注入
        • set方式注入
        • 构造方法注入
      • 2.注解方式注入
        • @Component+@ComponentScan
        • @Configuration+@Bean+@ComponentScan
        • @Import
      • 3.实现ImportBeanDefinitionRegistrar接口
      • 4.实现FactoryBean
      • 5.实现BeanDefinitionRegistryPostProcessor

1.XML方式注入

在现在这个Springboot横行的年代,以XML来注入的方式可能已经不多见了,因为压根用不着,但毕竟是注入方式之一也得提一提,这种方式就是依赖于XML的解析来获取我们需要注入的Bean对象

常见的方式有:set方法注入、构造方法注入

这里举几个常见的例子:

set方式注入

// 实体类如下:
@Data
public class test {
    private String  name;
    private Integer sex;
}

// XML文件如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--set方式注入
           id是注入bean中的名字
           class 是全限定类名
           property 是按照set方式注入
       -->
    <bean id="student1" class="com.example.spkie.model.test">
        <property name="name" value="test"/>
        <property name="sex" value="10"/>
    </bean>
</beans>

测试:

在这里插入图片描述

构造方法注入

// 实体类如下: 
@Data
public class test {
    private String  name;
    private Integer sex;
    
     public test(String name,Integer sex){
        this.name=name;
        this.sex=sex;
    }
}

// XML文件如下 test.xml  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--set方式注入
        id是注入bean中的名字
        class 是全限定类名
        constructor-arg 是按照构造方式注入
        index 是按照成员变量在构造函数中的参数的第几个
        name 表示成员变量名
        type 表示类型
        value 表示值
        ref 表示引用 可引用另外一个注入到Spring的中的值
    -->
    <bean id="student1" class="com.example.spkie.model.test">
        <constructor-arg index="0" name="name" value="构造方法注入"></constructor-arg>
        <constructor-arg index="1" name="sex" value="50"></constructor-arg>
    </bean>
</beans>

测试:

在这里插入图片描述

2.注解方式注入

@Component+@ComponentScan

我们开发中常用的 @Service@Controller 都是 @Component下的注解 ,需要配合 @ComponentScan 注解才能被扫描到并放入IOC容器中

为什么平时却没用@ComponentScan注解呢?

因为平时用的都是Springboot,Springboot启动类上的 @SpringbootApplication 注解类下已经带有 @ComponentScan 注解了,默认扫描启动类同级包下的@Component

例子如下:

我们先准备一个获取IOC容器内bean 的工具类 SpringUtils

@Component
public final class SpringUtils implements BeanFactoryPostProcessor {
    /**
     * Spring应用上下文环境
     */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        SpringUtils.beanFactory = beanFactory;
    }

    public static <T> T getBean(Class<T> clz) throws BeansException {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }
    
    public static <T> T getBean(String name) throws BeansException {
        return (T) beanFactory.getBean(name);
    }
}

测试要注入的Bean实体类:

@Component
@Data
public class ComponentTest {
    private String name="@Component 注解注入";
    private String remark="注意需要配合@ComponentScan 注解使用";
}

在这里插入图片描述

可以看到报错了,压根找不到这个bean,因为我们上面说过了springboot默认扫描的是启动类同级下的路径,我们把启动类放到了独立的包下,所以扫描不到了,这时候我们要么在用@ComponentScan注解配置一次扫描路径,要么把启动类提出来,我这里演示前者

我们在启动类上加上@ComponentScan注解配置一次扫描路径,就可以看到注入成功啦

在这里插入图片描述

@Configuration+@Bean+@ComponentScan

@Configuration注解相信大家也都不陌生,这个注解同样要配合@ComponentScan使用,那到底和@Component有什么区别呢?

@Configuration注入的是CGlib代理类,@Component注入的是类本身

我们与 @Component 一样准备个 @Configuration 注入类:

@Configuration
@Data
public class ConfigurationTest {
    private String name="@Configuration 注解注入";
    private String remark="注意需要配合@ComponentScan 注解使用";
}

在这里插入图片描述

可以看到Bean类的本质区别,难道为了这个就搞了@Configuration注解吗?当然不是,这个注解还可以配合@Bean注解一起使用,用来同时注入多个Bean

// 添加一个额外的Bean对象
public class ConfigurationTestBean {

    public void test(){
        System.out.println("我是在Configuration 内部注入的 bean ");
    }

}

// ConfigurationTest中添加Bean方法

@Configuration
@Data
public class ConfigurationTest {
    private String name="@Configuration 注解注入";
    private String remark="注意需要配合@ComponentScan 注解使用";
    // ConfigurationTest 中需要注入的Bean
    @Bean
    public ConfigurationTestBean configurationTestBean(){
        return new ConfigurationTestBean();
    }
}

在这里插入图片描述

这样的@Bean可以在同一个类中注入多个,所以 @Component 更多的用来注入配置文件类,@Configuration 更多的用来注入多个实例类

@Import

这种方式一般用在第三方包的加载比较多,使用起来呢也简单需要注入哪个Bean,导入哪个Bean的class就可以了,例如:

// 导入单个Bean
@Import(xxxxBean.class)

// 导入多个Bean
@Import({xxxxBean.class,xxxxBean.class})


但这个注解使用得注意,一定要能被扫描到才行,可以直接放在启动类上,如果是普通需要配合@Component或者@Configuration来使用,因为此注解单独使用是不会被扫描到的,也就不会被加载了

在一个注解上导入多个Bean要写这么多可能不是很优雅,所以还可以配合ImportSelector接口使用:

// 导入实现了ImportSelector接口的类即可
@Import(MyImportSelector.class)

// 实现ImportSelector 在数组中配置需要导入的Bean路径  返回一个数组
public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.example.spkie.importTest.xxxxBean","com.example.spkie.importTest.xxxxBean"};
    }
}

3.实现ImportBeanDefinitionRegistrar接口

看接口名称就知道了是不是有点像Bean的注册接口,需要配合@Import使用:

// 使用注解注入
@Import({MyImportBeanDefinitionRegistrar.class})

// 需要注入的Bean
public class DefinitionRegistrarBean {
    public void test(){
        System.out.println("我是通过Bean注册接口注入的Bean,需要配合@Import注解同样需要被扫描");
    }
}
// 自定义类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(DefinitionRegistrarBean.class);
        registry.registerBeanDefinition("definitionRegistrarBean",beanDefinitionBuilder.getBeanDefinition());
    }
}

测试:

在这里插入图片描述

我们平时开发中常用的openfeign也是采用的这种方式:

在这里插入图片描述

4.实现FactoryBean

用这个得先搞清楚FactoryBean和BeanFactory的区别:

  • BeanFactory: IOC容器顶层接口,用来Bean容器管理
  • FactoryBean: 是一个bean,是一个能产生bean的工厂bean,本身也会作为bean给容器管理,所以作为一个能产生Bean的工厂,我们可以自定义Bean(这也是最关键的点)

让我们来看看怎么用:

// 这是我们利用工厂想要生产的bean
public class FactoryTestBean {
    public void test(){
        System.out.println("我是通过实现FactoryBean接口注入的Bean");
    }
}

// 工厂Bean 实现两个方法
@Component
public class MyFactoryBean implements FactoryBean<FactoryTestBean> {

   //这个方法就是我们要生产的Bean
    @Override
    public FactoryTestBean getObject() throws Exception {
        return new FactoryTestBean();
    }

    @Override
    public Class<?> getObjectType() {
        return FactoryTestBean.class;
    }
}

测试:

可以看到通过Class无论是工厂bean还是工厂生产的bean我们都可以获取,但是发现通过beanName获取bean的区别没有,我们通过工厂的beanName获取到的是实际生产的对象,要获取真正的工厂需要在beanName前面加上&

为什么通过工厂的beanName获取到的是实际生产的对象?

其实从上述注入的过程中也能看到我们往容器中注入的其实是工厂Bean,并没有注入工厂生产的那个对象(可以打印容器所有的beanName验证),可以理解为在从容器中获取Bean的时候有判断是否实现了FactoryBean接口,实现了则会调用该bean的getObject()方法返回,所以此时会返回实际工厂生产的对象了

在这里插入图片描述

我们一样以openfeign框架举例:

此注入的feign接口实际注入的是FeignClientFactoryBean,所以在调用容器中feign接口的Bean对象的时候,实际执行的是FeignClientFactoryBean.getObject()方法

在这里插入图片描述

5.实现BeanDefinitionRegistryPostProcessor

这个接口继承了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor是BeanFactory的后置处理器,该接口多个了一个对BeanDefination处理的方法,可以在BeanFactory生成后对里面的BeanDefination做一次处理,所以当然可以注册BeanDefination啦,后续就成了Bean

BeanDefinitionRegistryPostProcessor源代码如下:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

怎么用呢?先直接上例子吧:

// 还是要搭配注解
@Import(MyBeanDefinitionRegistryPostProcessor.class)

// 要注入的bean对象
public class RegistrarPostProcessorBean {
    public void test(){
        System.out.println("我是通过后置处理器注入的bean");
    }
}
// 自定义类
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RegistrarPostProcessorBean.class);
        beanDefinitionRegistry.registerBeanDefinition("registrarPostProcessorBean",beanDefinitionBuilder.getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

乍一看和ImportBeanDefinitionRegistrar类似,都是用了BeanDefinitionRegistry 来注册的,但ImportBeanDefinitionRegistrar是Spring的扩展点之一,提供给第三方对接使用的

BeanDefinitionRegistryPostProcessor这个源码就不追溯了,后面再说(还是提一下吧,容器初始化的时候有调用)

在这里插入图片描述

既然是BeanFactory后置处理器,所以它还可以修改BeanDefination里面保存的Bean信息:

// 我们用到之前使用过的Bean
@Component
@Data
public class ComponentTest {
    private String name="@Component 注解注入";
    private String remark="注意需要配合@ComponentScan 注解使用";
}

// 修改后置处理器 
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        // 新增如下代码  修改ComponentTest Bean属性
        BeanDefinition configurationTestBean = beanDefinitionRegistry.getBeanDefinition("componentTest");
        MutablePropertyValues propertyValues = configurationTestBean.getPropertyValues();
        propertyValues.add("name","我是修改后的Bean属性" );

        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RegistrarPostProcessorBean.class);
        beanDefinitionRegistry.registerBeanDefinition("registrarPostProcessorBean",beanDefinitionBuilder.getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

结果如下:
在这里插入图片描述

这里演示了修改字段的值,当然还可以修改其他的比如是否加载优先级、是否懒加载、单例多例等

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

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

相关文章

jsp课程资源网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 课程资源网站系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使用…

怎么用docker将项目打包成镜像并导出给别人适用 (dockerfile)

前提 你得安装docker&#xff0c;没有安装的可以看看这篇文章 编写dockerfile 这个位置最好和我一样&#xff0c;不然后面打包成镜像可能出问题&#xff08;找不到jar包&#xff09; FROM openjdk:8-jdk-slim MAINTAINER JacksonNing COPY /target/iec104-1.0.0-SNAPSHOT.j…

这次把怎么做好一个PPT讲清-演讲篇

《商务演讲与汇报》 一、目标&#xff1a;演讲必须有清晰的目标 演讲&#xff1a;影响他人发生积极的**“改变”** 注意&#xff0c;目标就要设定的影响听众在听完你的演讲后发生积极的改变&#xff1b; 例&#xff1a;5月初向领导做月度工作汇报→→让领导在5月第一周例会…

QGradient(渐变填充)

QGradient&#xff08;渐变填充&#xff09; QGradient和QBrush一起使用来指定渐变填充。 Qt支持的填充&#xff1a; 线性渐变&#xff08;linear gradient&#xff09;,在起点和终点之间插值颜色辐射渐变&#xff08;radial gradient&#xff09;,在焦点和围绕它的圆的端点之…

2019上半年-2019下半年软件设计师上午题错题总结

2019上半年 30.以下关于极限编程&#xff08;XP&#xff09;的最佳实践的叙述中&#xff0c;不正确的是&#xff08;C &#xff09;。 A.只处理当前的需求&#xff0c;使设计保持简单 B.编写完程序之后编写测试代码 C.可以按日甚至按小时为客户提供可运行的版本 D.系统最…

【附源码】Python计算机毕业设计水库洪水预报调度系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

linux进阶-构建deb软件安装包

Linux软件包的组成&#xff1a;源码包和二进制包。 文件类型保存目录普通程序/usr/binroot权限程序/usr/sbin程序配置文件/etc日志文件/var/log文档文件/usr/share/doc 源码包优点&#xff1a;开源免费、自由裁剪、修改源代码。 源码包缺点&#xff1a;安装步骤繁琐、编译时间…

【信号处理】卡尔曼滤波(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

区块链解决方案-最新全套文件

区块链解决方案-最新全套文件一、建设背景区块链的五大场景1、合同存证2、产品防伪溯源3、供应链金融4、住房租赁5、贸易金融业务二、建设架构三、建设方案四、获取 - 区块链全套最新解决方案合集一、建设背景 区块链的五大场景 1、合同存证 传统的合同存证存在着被篡改、删…

【AcWing19】【LeetCode】DFS - 46/47/39/77/216/17

文章目录代码随想录在B站的视频讲得比AcWing好模板题1&#xff1a;排列数字模板题2&#xff1a;n皇后方法一方法二LeetCode 46. 全排列LeetCode 47. 全排列 II &#xff08;重复元素&#xff09;LeetCode 39. 组合总和LeetCode 77. 组合LeetCode 216. 组合总和 IIILeetCode 17.…

黑马点评--分布式锁

黑马点评–分布式锁 基本原理与不同实现方式对比&#xff1a; 什么是分布式锁&#xff1a; ​ 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心是实现多进程之间互斥&#xff0c;而满足这一点的方式有很多&#xff0c;常见的有三种…

Leetcode799. 香槟塔

文章目录题目链接题目大意解题思路代码(C)递推递归题目链接 点我 (^_^) 题目大意 解题思路 一开始看到这个 poured 范围这么大&#xff0c;以为是可以直接推出数学公式&#xff0c;但推了半天没推出来。 然后发现&#xff0c;直接从顶部开始模拟即可&#xff0c;某个row 下的…

HIve数仓新零售项目DWB层的构建

HIve数仓新零售项目 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark Flink Kaf…

October 2019 Twice SQL Injection

目录 首先&#xff0c;尝试一下回显位的个数 第二步获取数据库名称 第三步&#xff0c;获得表名 第四步&#xff0c;获得列名 最后获取flag 总结 &#xff1a;二次注入&#xff0c;首先我们需要锁定是哪里哪个文本框存在二次注入&#xff0c;以这道题为例&#xff0c;首先…

计算机视觉算法——基于Transformer的语义分割(SETR / Segmenter / SegFormer)

计算机视觉算法——基于Transformer的语义分割&#xff08;SETR / Segmenter / SegFormer&#xff09;1. SETR1.1 网络结构及特点1.1.1 Decoder1.2 实验2. Segmenter2.1 网络结构及特点2.1.1 Decoder2.2 实验3. SegFormer3.1 网络结构及特点3.1.1 Overlap Patch Merging3.1.2 E…

CMSC5713-IT项目管理之八、敏捷项目管理Agile Project Management

文章目录8.1. Traditional SDLC8.2. Agile Methodologies8.3. Scrum8.3.1. Roles8.3.2. User Story8.3.3. Product Backlog8.3.4. Release Backlog8.3.5. Sprint Backlog8.3.6. Estimation8.3.7. Burning-down Chart8.3.8. Planning Meetings8.3.8.1. Daily Scrum Meeting8.3.8…

stm32f334timer15-17

stm32f334timer15-17介绍TIM15主要功能TIM16-17主要功能TIM15/TIM16/TIM17功能描述时基单位预分频器描述计数器模式递增计数模式重复计数器时钟选择捕获/比较频道输入捕获模式PWM输入模式&#xff08;仅适用于TIM15&#xff09;强制输出模式输出比较模式组合PWM模式&#xff08…

基于Springboot+vue开发实现自行车租赁管理系统

作者主页&#xff1a;编程千纸鹤 作者简介&#xff1a;Java、前端、Pythone开发多年&#xff0c;做过高程&#xff0c;项目经理&#xff0c;架构师 主要内容&#xff1a;Java项目开发、毕业设计开发、面试技术整理、最新技术分享 收藏点赞不迷路 关注作者有好处 项目编号&…

基于java学生选课系统

开发工具eclipse,jdk1.8 技术&#xff1a;java swing 数据库&#xff1a;mysql5.7 学生选课系统功能&#xff1a;管理员、教师、学生三个角色 一、管理员功能&#xff1a; 1.登录、修改密码、退出系统 2.学生管理&#xff1a;添加、修改、删除、查询 3.班级管理&#xff1a;添加…

力扣(LeetCode)23. 合并K个升序链表(C++)

模拟k路归并 朴素思想&#xff0c;类比二路归并&#xff0c; kkk 路归并多了一些参与比较的链表。我们可以在循环体内多一层循环&#xff0c;找到值最小的结点&#xff0c;插入答案链表的尾部。 朴素算法的时间复杂度 O(k∑i0k−1listsi.size())O(k\times\sum_{i0}^{k-1} lis…