BeanDefinition
其实底层是个接口
BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如:
- class,表示Bean类型(其实叫beanClass)
- scope,表示Bean作用域,单例或原型等
- lazyInit:表示Bean是否是懒加载
- initMethodName:表示Bean初始化时要执行的方法
- destroyMethodName:表示Bean销毁时要执行的方法
- 还有很多...
在Spring中,我们经常会通过以下几种方式来定义Bean:
- <bean/>
- @Bean
- @Component(@Service,@Controller)
这些,我们可以称之声明式定义Bean。
如果底层用的xml,spring也会解析成一个beanDefinition对象
bean注解解析也会生成beanDefinition
这里加了Bean注解直接调用这个方法对吗?
不太对,
首先会生成BeanDefinition对象,然后放到BeanDefinitionMap里面,再从map里面取出非懒加载的单例bean再拿出来,才会调用这个方法。
(Spring提供定义bean的方式)
我们还可以编程式定义Bean,那就是直接通过BeanDefinition,比如:
也就是说这个编程式定义Bean可以替代@Bean注解的一种写法。
(但是平常不会这么干,spring底层是这么干的)
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(User.class);
// 还能指定作用域
beanDefinition.setScope("prototype");
// 指定beanName
context.registerBeanDefinition("user", beanDefinition);
System.out.println(context.getBean("user"));
我们还可以通过BeanDefinition设置一个Bean的其他属性
beanDefinition.setScope("prototype"); // 设置作用域
beanDefinition.setInitMethodName("init"); // 设置初始化方法
beanDefinition.setLazyInit(true); // 设置懒加载
和申明式事务、编程式事务类似,通过<bean/>,@Bean,@Component等申明式方式所定义的Bean,最终都会被Spring解析为对应的BeanDefinition对象,并放入Spring容器中。
被注解的BeanDefinitionReader(BeanDefinition的一个读取器 spring提供的一个工具类)
AnnotatedBeanDefinitionReader
可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解,比如
// 先构造一个spring的容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 读到哪里去 肯定读到容器 所以要传参一个context
AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(context);
// 利用了BeanDefinition的一个读取器去注册了一个User
// 将User.class解析为BeanDefinition 也就是最终这个User就会成为BeanDefinition,放在spring容器里
annotatedBeanDefinitionReader.register(User.class);
System.out.println(context.getBean("user"));
register底层源码:这里只传了一个类User.class
构造了一个BeanDefinition,类就是传进来的类
然后还会去判断当前类上有没有@Lazy注解之类的,然后设置到刚才的BeanDefinition中
注意:它能解析的注解是:@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description
User也没加注解
运行正常,说明我们的User是一个bean,报错就说明不是bean
比如AnnotationConfigApplicationContext底层就调用到了这个
被注解的BeanDefinitionReader(BeanDefinition的一个读取器 spring提供的一个工具类)
这个register底层其实就是调用的这个BeanDefinition的一个读取器
小总结:
他就是BeanDefinition的一个读取器 ,传给他一个类,他就能解析类上面的注解,然后变成一个BeanDefinition,然后放到spring的容器中,然后就成为了一个bean。
XmlBeanDefinitionReader
可以解析xml文件的,解析<bean/>标签
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
System.out.println(context.getBean("user"));
这里就不会报错,能拿到bean,底层解析xml
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner是扫描器,但是它的作用和BeanDefinitionReader类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition,比如:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.refresh();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
// 定义扫描路径 扫描得到的都是BeanDefinition 然后放到spring容器
scanner.scan("com.zhouyu");
System.out.println(context.getBean("userService"));
例子:
在UserService上加了@Component注解
问:能拿到UserService这个bean吗?
答:不行 因为这里构造了空的spring容器,没有传配置文件进去,而且没刷新
加个代码context.refresh();(强制要求,不过就算加了也没丝毫作用)
这里定义了扫描器,然后把容器传进扫描器里。然后去路径下扫描,发现有@Component注解,然后就生成BeanDefinition,接着会加入到spring容器里,这样就能拿到UserService的bean了
我们的spring容器 也就是这个context,既可以注册某一个类成为bean
也可以自己触发去扫描
用构造器的底层代码
底层先调用空的构造方法
再把传进来的类进行注册
通过AnnotatedBeanDefinitionReader进行注册 注册成为一个BeanDefinition
再去刷新
也可以这么操作实现一样的效果