这篇文章主要讲解了“Spring中的BeanDefinition是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring中的BeanDefinition是什么”吧!
在 Spring 容器中,我们广泛使用的是一个一个的 Bean,BeanDefinition 从名字上就可以看出是关于 Bean 的定义。
事实上就是这样,我们在 XML 文件中配置的 Bean 的各种属性,这些属性不仅仅是和对象相关,Spring 容器还要解决 Bean 的生命周期、销毁、初始化等等各种操作,我们定义的关于 Bean 的生命周期、销毁、初始化等操作总得有一个对象来承载,那么这个对象就是 BeanDefinition。
XML 中定义的各种属性都会先加载到 BeanDefinition 上,然后通过 BeanDefinition 来生成一个 Bean,从这个角度来说,BeanDefinition 和 Bean 的关系有点类似于类和对象的关系。
要理解 BeanDefinition,我们从 BeanDefinition 的继承关系开始看起。
BeanDefinition 是一个接口,继承自 BeanMetadataElement 和 AttributeAccessor 接口。
我们来看下 AttributeAccessor:
public interface AttributeAccessor { void setAttribute(String name, @Nullable Object value); @Nullable Object getAttribute(String name); @Nullable Object removeAttribute(String name); boolean hasAttribute(String name); String[] attributeNames();}
这里定义了元数据的访问接口,具体的实现则是 AttributeAccessorSupport,这些数据采用 LinkedHashMap 进行存储。
这是 BeanDefinition 所继承的两个接口。接下来我们来看下 BeanDefinition 接口:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; void setParentName(@Nullable String parentName); @Nullable String getParentName(); void setBeanClassName(@Nullable String beanClassName); @Nullable String getBeanClassName(); void setScope(@Nullable String scope); @Nullable String getScope(); void setLazyInit(boolean lazyInit); boolean isLazyInit(); void setDependsOn(@Nullable String... dependsOn); @Nullable String[] getDependsOn(); void setAutowireCandidate(boolean autowireCandidate); boolean isAutowireCandidate(); void setPrimary(boolean primary); boolean isPrimary(); void setFactoryBeanName(@Nullable String factoryBeanName); @Nullable String getFactoryBeanName(); void setFactoryMethodName(@Nullable String factoryMethodName); @Nullable String getFactoryMethodName(); ConstructorArgumentValues getConstructorArgumentValues(); default boolean hasConstructorArgumentValues() { return !getConstructorArgumentValues().isEmpty(); } MutablePropertyValues getPropertyValues(); default boolean hasPropertyValues() { return !getPropertyValues().isEmpty(); } void setInitMethodName(@Nullable String initMethodName); @Nullable String getInitMethodName(); void setDestroyMethodName(@Nullable String destroyMethodName); @Nullable String getDestroyMethodName(); void setRole(int role); int getRole(); void setDescription(@Nullable String description); @Nullable String getDescription(); ResolvableType getResolvableType(); boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); @Nullable String getResourceDescription(); @Nullable BeanDefinition getOriginatingBeanDefinition();}
BeanDefinition 中的方法虽然多,但是结合我们平时在 XML 中的配置,这些方法其实都很好理解:
<bean parent="">
配置。<bean class="">
配置。<bean lazy-init="">
配置。<bean depends-on="">
配置。<bean autowire-candidate="">
配置。<bean primary="">
配置。<bean factory-bean="">
配置。<bean factory-method="">
配置,不再赘述。这个就是 BeanDefinition 的定义以及它里边方法的含义。
上面只是 BeanDefinition 接口的定义,BeanDefinition 还拥有诸多实现类,我们也来大致了解下。
先来看一张继承关系图:
这么多实现类看着有点眼花缭乱,不过搞清楚了每一个接口和类的作用,再看就很容易了。
AbstractBeanDefinition 是一个抽象类,它根据 BeanDefinition 中定义的接口提供了相应的属性,并实现了 BeanDefinition 中定义的一部分方法。BeanDefinition 中原本只是定义了一系列的 get/set 方法,并没有提供对应的属性,在 AbstractBeanDefinition 中将所有的属性定义出来了。
后面其他的实现类也基本上都是在 AbstractBeanDefinition 的基础上完成的。
这是一个比较常用的实现类,对应了一般的元素标签。
可以让子 BeanDefinition 定义拥有从父 BeanDefinition 那里继承配置的能力。
GenericBeanDefinition 是从 Spring2.5 以后新加入的 BeanDefinition 实现类。GenericBeanDefinition 可以动态设置父 Bean,同时兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能。
表示注解类型 BeanDefinition,拥有获取注解元数据和方法元数据的能力。
使用了 @Configuration 注解标记配置类会解析为 AnnotatedGenericBeanDefinition。
理论讲了这么多,接下来我们通过几行代码来实践下,验证一下我们前面所说的对不对。
首先项目中添加 spring-context 依赖,如下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.6.RELEASE</version></dependency>
然后我们来创建一个 User 类,如下:
public class User { private String username; private String address; @Override public String toString() { return "User{" + "username='" + username + '\'' + ", address='" + address + '\'' + '}'; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; }}
接下来我们先来验证 RootBeanDefinition。我们自己纯手工定义一个 RootBeanDefinition,并且将之注册到 Spring 容器中去。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();MutablePropertyValues pvs = new MutablePropertyValues();pvs.add("username", "javaboy");pvs.add("address", "www.javaboy.org");RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class, null, pvs);ctx.registerBeanDefinition("user",rootBeanDefinition);ctx.refresh();User bean = ctx.getBean(User.class);System.out.println(bean);
MutablePropertyValues 是定义对象中的一个一个属性,构造 RootBeanDefinition 的时候,我们传入了类名称和属性集合,最终把 rootBeanDefinition 注册到容器中去。剩下的事情由容器完成,然后我们就可以从容器中获取到 User 对象了。
最终输出结果如下:
User{username='javaboy', address='www.javaboy.org'}
看了这个例子,小伙伴们应该能够大致明白,我们在 XML 中定义的各种属性,就是先被解析到 BeanDefinition 中,然后再注册到 Spring 容器中去,最后拿到我们需要的 Bean。
ChildBeanDefinition 具有从父 Bean 继承数据的能力,我们来看下这个怎么用。
首先新建一个 Person 类,Person 类在 User 类的基础上增加一个 nickname 属性,这样 Person 就可以继承到 User 的 username 和 address 两个属性的值了:
public class Person { private String username; private String address; private String nickname; @Override public String toString() { return "Person{" + "username='" + username + '\'' + ", address='" + address + '\'' + ", nickname='" + nickname + '\'' + '}'; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; }}
接下来自定义 ChildBeanDefinition:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();MutablePropertyValues pvs = new MutablePropertyValues();pvs.add("username", "javaboy");pvs.add("address", "www.javaboy.org");RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class, null, pvs);ctx.registerBeanDefinition("user",rootBeanDefinition);ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("user");childBeanDefinition.setBeanClass(Person.class);childBeanDefinition.getPropertyValues().add("nickname", "江南一点雨");ctx.registerBeanDefinition("person", childBeanDefinition);ctx.refresh();User user = ctx.getBean(User.class);Person person = ctx.getBean(Person.class);System.out.println("user = " + user);System.out.println("person = " + person);
首先定义 RootBeanDefinition 并注册到 Spring 容器中,然后再定义 ChildBeanDefinition,ChildBeanDefinition 继承了 RootBeanDefinition 中现有的属性值。
最后我们从 Spring 容器中获取 User 和 Person,打印结果如下:
user = User{username='javaboy', address='www.javaboy.org'}person = Person{username='javaboy', address='www.javaboy.org', nickname='江南一点雨'}
可以看到,Person 确实继承了 User 的属性值。
RootBeanDefinition 和 ChildBeanDefinition 都可以被 GenericBeanDefinition 代替,效果一样,如下:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();MutablePropertyValues pvs = new MutablePropertyValues();pvs.add("username", "javaboy");pvs.add("address", "www.javaboy.org");GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();rootBeanDefinition.setBeanClass(User.class);rootBeanDefinition.setPropertyValues(pvs);ctx.registerBeanDefinition("user",rootBeanDefinition);GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();childBeanDefinition.setParentName("user");childBeanDefinition.setBeanClass(Person.class);childBeanDefinition.getPropertyValues().add("nickname", "江南一点雨");ctx.registerBeanDefinition("person", childBeanDefinition);ctx.refresh();User user = ctx.getBean(User.class);Person person = ctx.getBean(Person.class);System.out.println("user = " + user);System.out.println("person = " + person);
运行结果如下:
user = User{username='javaboy', address='www.javaboy.org'}person = Person{username='javaboy', address='www.javaboy.org', nickname='江南一点雨'}
可以看到,和前面的运行效果一致。
在我们本系列前面文章(Spring 源码第一篇开整!配置文件是怎么加载的?)的案例中,默认使用的也是 GenericBeanDefinition,如下:
现在 Spring Boot 广泛流行之后,Java 配置使用越来越多,以 @Configuration 注解标记配置类会被解析为 AnnotatedGenericBeanDefinition;以 @Bean 注解标记的 Bean 会被解析为 ConfigurationClassBeanDefinition。
我们新建一个 MyConfig 配置类,如下:
@Configurationpublic class MyConfig { @Bean User user() { return new User(); }}
查看获取到的 BeanDefinition 结果如下:
而其他 @Service、@Controller、@Repository 以及 @Component 等注解标记的 Bean 则会被识别为 ScannedGenericBeanDefinition。
感谢各位的阅读,以上就是“Spring中的BeanDefinition是什么”的内容了,经过本文的学习后,相信大家对Spring中的BeanDefinition是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/3669799/blog/4393878