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


    个人博客 全是干货,相信不会让你失望

    1.XML方式注入

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

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

    这里举几个常见的例子:

    set方式注入
    // 实体类如下:
    @Data
    public class test {
        private String  name;
        private Integer sex;
    }
    
    // XML文件如下
    
    
        
        
            
            
        
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    测试:

    在这里插入图片描述

    构造方法注入
    // 实体类如下: 
    @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  
    
    
    
        
        
            
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    测试:

    在这里插入图片描述

    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 getBean(Class clz) throws BeansException {
            T result = (T) beanFactory.getBean(clz);
            return result;
        }
        
        public static  T getBean(String name) throws BeansException {
            return (T) beanFactory.getBean(name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    测试要注入的Bean实体类:

    @Component
    @Data
    public class ComponentTest {
        private String name="@Component 注解注入";
        private String remark="注意需要配合@ComponentScan 注解使用";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    可以看到报错了,压根找不到这个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 注解使用";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    可以看到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();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

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

    @Import

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

    // 导入单个Bean
    @Import(xxxxBean.class)
    
    // 导入多个Bean
    @Import({xxxxBean.class,xxxxBean.class})
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    但这个注解使用得注意,一定要能被扫描到才行,可以直接放在启动类上,如果是普通需要配合@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"};
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试:

    在这里插入图片描述

    我们平时开发中常用的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 {
    
       //这个方法就是我们要生产的Bean
        @Override
        public FactoryTestBean getObject() throws Exception {
            return new FactoryTestBean();
        }
    
        @Override
        public Class getObjectType() {
            return FactoryTestBean.class;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    测试:

    可以看到通过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;
    }
    
    • 1
    • 2
    • 3

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

    // 还是要搭配注解
    @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 {
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    乍一看和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 {
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

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

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

  • 相关阅读:
    7. Django 模型与数据库
    苍穹外卖-day05
    设计模式学习笔记 - 装饰者模式
    一阶微分形式不变性
    NumPy数组与矩阵(二)
    react 路由拦截通过tocken进行简单拦截
    架构师选择题--计算机网络
    C++基础——引用讲解1
    手把手教你实现一个流动的渐变色边框
    算法70-解析算式并返回结果
  • 原文地址:https://blog.csdn.net/weixin_44102992/article/details/127952285