0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

初学者必看的SpringBoo自动装配原理2

jf_78858299 来源:CSDN 作者:CC_且听风吟 2023-04-07 11:03 次阅读

4. BeanDefinition结构

既然讲到了BeanDefinition,我们来看一下BeanDefinition里面究竟定义了些什么

让我们点进AbstractBeanDefinition这个类,一探究竟

哇!好多成员变量,整个人都要看晕了@_@

我们来重点关注以下三个成员:

private volatile Object beanClass;
private int autowireMode = AUTOWIRE_NO;
private ConstructorArgumentValues constructorArgumentValues;

4.1 beanClass

这个属性决定了该Bean定义的真正class到底是谁,接下来我们来做点实验

我们定义两个Bean类,A和B

@Component
public class A {
    @Value("我是AAA")
    private String name;
}
@Component
public class B {
    @Value("我是BBB")
    private String name;
}

接下来我们实现上面的BeanFactoryPostProcessor接口,来创建一个自定义的bean后置处理器

/**
 * 自定义的bean后置处理器
 * 通过这个MyBeanPostProcessor来修改bean定义的属性
 * @author dzzhyk
 */
public class MyBeanPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition defA = (GenericBeanDefinition) beanFactory.getBeanDefinition("a");
        System.out.println("这里是MyBeanPostProcessor,我拿到了:" + defA.getBeanClassName());
    }
}

最后在XML配置文件中开启包扫描

<context:component-scan base-package="pojo"/>
<context:annotation-config />

注意: 这里不要使用JavaConfig类来配置bean,不然会报如下错误

ConfigurationClassBeanDefinition cannot be cast to org.springframework.beans.factory.support.GenericBeanDefinition

这个错误出自这一句:

GenericBeanDefinition defA = (GenericBeanDefinition) beanFactory.getBeanDefinition("a");

最后,我们创建一个测试类:

public class Test {
    @org.junit.Test
    public void test(){
        ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
        A aaa = ca.getBean("a", A.class);
        System.out.println("最终拿到了==> " + aaa);
    }
}

测试运行!

这里是MyBeanPostProcessor,我拿到了:pojo.A
最终拿到了==> A(name=我是AAA, b=B(name=我是BBB))

可以看到MyBeanPostProcessor成功拿到了A的Bean定义,并且输出了提示信息

接下来让我们做点坏事

我们在MyBeanPostProcessor中修改A的Bean对象,将A的beanClass修改为B.class

System.out.println("这里是MyBeanPostProcessor,我修改了:"+ defA.getBeanClassName() + " 的class为 B.class");
// 把A的class改成B
defA.setBeanClass(B.class);

重新运行Test类,输出了一些信息后:报错了!

这里是MyBeanPostProcessor,我拿到了:pojo.A
这里是MyBeanPostProcessor,我修改了:pojo.A 的class为 B.class

BeanNotOfRequiredTypeException: 
 Bean named 'a' is expected to be of type 'pojo.A' but was actually of type 'pojo.B'

我要拿到一个A类对象,你怎么给我一个B类对象呢?这明显不对

综上所述,我们可以得出beanClass属性控制bean定义的类

4.2 autowireMode

我们继续看第二个属性:autowireMode,自动装配模式

我们在AbstractBeanDefinition源码中可以看到:

private int autowireMode = AUTOWIRE_NO;

自动装配模式默认是AUTOWIRE_NO,就是不开启自动装配

可选的常量值有以下四种:不自动装配,通过名称装配,通过类型装配,通过构造器装配

  • AUTOWIRE_NO
  • AUTOWIRE_BY_NAME
  • AUTOWIRE_BY_TYPE
  • AUTOWIRE_CONSTRUCTOR

接下来我们来模拟一个自动装配场景,仍然是A和B两个类,现在在A类中添加B类对象

@Component
public class A {
    @Value("我是AAA")
    private String name;
    @Autowired
    private B b;
}

我们希望b对象能够自动装配,于是我们给他加上了@Autowired注解,其他的完全不变,我们自定义的MyBeanPostProcessor中也不做任何操作,让我们运行测试类:

这里是MyBeanPostProcessor,我拿到了:pojo.A
最终拿到了==> A(name=我是AAA, b=B(name=我是BBB))

自动装配成功了!我们拿到的A类对象里面成功注入了B类对象b

现在问题来了,如果我把@Autowired注解去掉,自动装配会成功吗?

这里是MyBeanPostProcessor,我拿到了:pojo.A
最终拿到了==> A(name=我是AAA, b=null)

必然是不成功的

但是我就是想要不加@Autowired注解,仍然可以实现自动装配,需要怎么做?

这时就要在我们的MyBeanPostProcessor中做文章了,加入如下内容:

defA.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);

再输出结果:

这里是MyBeanPostProcessor,我拿到了:pojo.A
最终拿到了==> A(name=我是AAA, b=B(name=我是BBB))

自动装配成功了!这次我们可没加@Autowired,在我们的自定义的bean后置处理器中设置了autowireMode属性,也实现了自动装配

综上,autowireMode属性是用来控制自动装配模式的,默认值是AUTOWIRE_NO,即不自动装配

4.3 constructorArgumentValues

constructorArgumentValues的字面含义是构造器参数

改变这个参数值,我们可以做到在实例化对象时指定特定的构造器

话不多说,show me your code:

因为要研究构造器,只能先”忍痛“关掉lombok插件,手写一个pojo.Student类

/**
 * Student类
 * @author dzzhyk
 */
@Component
public class Student {
    private String name;
    private Integer age;

    public Student() {
        System.out.println("==>使用空参构造器 Student()");
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
        System.out.println("==>使用双参数构造器 Student(String name, Integer age)");
    }
}

我们都知道,spring在实例化对象时使用的是对象的默认空参构造器:

我们新建一个测试方法test

@Test
public void test(){
    ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = ca.getBean("stu", Student.class);
    System.out.println("==>" + student);
}

运行可以得到下面结果:

这里是MyBeanPostProcessor,我拿到了:pojo.Student
==>使用空参构造器 Student()
==>pojo.Student@402e37bc

可以看到,确实使用了空参构造器

但是如何指定(自定义)使用哪个构造器呢?我根本看不见摸不着,Spring全帮我做了,实在是太贴心了。

接下来就聊聊constructorArgumentValues的使用:

我们在MyBeanPostProcessor中加入如下内容,对获取到的pojo.Student的bean定义进行操作:

ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addIndexedArgumentValue(0, "我指定的姓名");
args.addIndexedArgumentValue(1, 20);
defStu.setConstructorArgumentValues(args);

再次运行test:

这里是MyBeanPostProcessor,我拿到了:pojo.Student
==>使用双参数构造器 Student(String name, Integer age)
==>pojo.Student@2f177a4b

可以看到这次使用了双参数构造器

有人会好奇ConstructorArgumentValues到底是个什么东西,我点进源码研究一番,结果发现这个类就是一个普通的包装类,包装的对象是ValueHolder,里面一个List一个Map

而ValueHolder这个对象继承于BeanMetadataElement,就是构造器参数的一个包装类型

通过这个例子我们可以看到ConstructorArgumentValues就是用来管控构造器参数的,指定这个值会在进行bean注入的时候选择合适的构造器。

5. 装配对象

现在我们把目光放回到SpringBoot的自动装配上来,原来在真正进行bean实例化对象前,我们前面还有这些过程,尤其是存在使用后置处理器BeanFactoryPostProcessor来对bean定义进行各种自定义修改的操作。

经过上面我们漫长的研究过程,我们终于可以回答第一个问题了:

自动装配的对象:Bean定义 (BeanDefinition)

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • spring
    +关注

    关注

    0

    文章

    333

    浏览量

    14198
  • 源码分析
    +关注

    关注

    0

    文章

    5

    浏览量

    5521
  • 自动装配
    +关注

    关注

    0

    文章

    7

    浏览量

    627
收藏 人收藏

    评论

    相关推荐

    protel技术大全--初学者必看

    protel技术大全--初学者必看
    发表于 08-04 10:36

    PSOC1初学者必看的10个实例

    PSOC1初学者必看的10个实例
    发表于 11-19 16:33

    PCB LAYOUT技术大全---初学者必看

    PCB LAYOUT技术大全---初学者必看
    发表于 01-09 18:43

    MATLAB入门教程-初学者必看

    MATLAB入门教程-初学者必看
    发表于 06-28 15:39

    Linux初学者必看!!!

    Linux初学者必看!!!
    发表于 01-07 21:35

    单片机入门秘籍,初学者必看

    初学者必看的单片机秘籍
    发表于 04-29 16:10

    PCB LAYOUT初学者必看

    PCBLAYOUT技术大全---初学者必看! PROTEL相关疑问 1.原理图常见错误: (1)ERC报告管脚没有接入信号: a.创建封装时给管脚定义了I/O属性; b.创建元件或放置元件时修改了不一致的grid属性
    发表于 09-13 15:23 0次下载

    初学者必看的基本电子技术概念

    初学者必看的基本电子技术概念
    发表于 05-17 11:41 0次下载

    初学者必看的电源测试项目要点及教程

    初学者必看的电源测试项目要点及教程
    发表于 07-01 14:09 29次下载

    初学者必看的LABVIEW工程师编程经验

    初学者必看的LABVIEW工程师编程经验
    发表于 07-12 14:24 28次下载

    ARM与嵌入式linux入门的建议(初学者必看)

    ARM与嵌入式linux入门的建议(初学者必看)(嵌入式开发培训怎么样)-该文档为ARM与嵌入式linux入门的建议(初学者必看)总结文档,是一份很不错的参考资料,具有较高参考价值,感
    发表于 08-04 10:02 15次下载
    ARM与嵌入式linux入门的建议(<b class='flag-5'>初学者</b><b class='flag-5'>必看</b>)

    初学者必看的单片机程序汇总

    初学者必看的单片机程序汇总
    发表于 09-15 14:33 49次下载

    初学者必看SpringBoo自动装配原理1

    学习SpringBoot,绝对避不开自动装配这个概念,这也是SpringBoot的关键之一 本人也是SpringBoot的初学者,下面的一些总结都是结合个人理解和实践得出的,如果有错误或者疏漏,请一定一定一定(不是欢迎,是
    的头像 发表于 04-07 11:03 487次阅读
    <b class='flag-5'>初学者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自动</b><b class='flag-5'>装配</b>原理1

    初学者必看SpringBoo自动装配原理3

    学习SpringBoot,绝对避不开自动装配这个概念,这也是SpringBoot的关键之一 本人也是SpringBoot的初学者,下面的一些总结都是结合个人理解和实践得出的,如果有错误或者疏漏,请一定一定一定(不是欢迎,是
    的头像 发表于 04-07 11:03 405次阅读
    <b class='flag-5'>初学者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自动</b><b class='flag-5'>装配</b>原理3

    初学者必看SpringBoo自动装配原理4

    学习SpringBoot,绝对避不开自动装配这个概念,这也是SpringBoot的关键之一 本人也是SpringBoot的初学者,下面的一些总结都是结合个人理解和实践得出的,如果有错误或者疏漏,请一定一定一定(不是欢迎,是
    的头像 发表于 04-07 11:03 496次阅读
    <b class='flag-5'>初学者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自动</b><b class='flag-5'>装配</b>原理4