电子说
既然讲到了BeanDefinition,我们来看一下BeanDefinition里面究竟定义了些什么
让我们点进AbstractBeanDefinition这个类,一探究竟
哇!好多成员变量,整个人都要看晕了@_@
我们来重点关注以下三个成员:
private volatile Object beanClass;
private int autowireMode = AUTOWIRE_NO;
private ConstructorArgumentValues constructorArgumentValues;
这个属性决定了该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定义的类
我们继续看第二个属性:autowireMode,自动装配模式
我们在AbstractBeanDefinition源码中可以看到:
private int autowireMode = AUTOWIRE_NO;
自动装配模式默认是AUTOWIRE_NO,就是不开启自动装配
可选的常量值有以下四种:不自动装配,通过名称装配,通过类型装配,通过构造器装配
接下来我们来模拟一个自动装配场景,仍然是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,即不自动装配
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注入的时候选择合适的构造器。
现在我们把目光放回到SpringBoot的自动装配上来,原来在真正进行bean实例化对象前,我们前面还有这些过程,尤其是存在使用后置处理器BeanFactoryPostProcessor来对bean定义进行各种自定义修改的操作。
经过上面我们漫长的研究过程,我们终于可以回答第一个问题了:
自动装配的对象:Bean定义 (BeanDefinition)
全部0条评论
快来发表一下你的评论吧 !