【Spring注解驱动开发】面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?
写在前面
在前面的文章中,我们知道可以通过多种方式向Spring容器中注册bean。可以使用@Configuration结合@Bean向Spring容器中注册bean;可以按照条件向Spring容器中注册bean;可以使用@Import向容器中快速导入bean对象;可以在@Import中使用ImportBeanDefinitionRegistrar向容器中注册bean。
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
FactoryBean概述
一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean 。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在
FactoryBean接口对于Spring框架来说占有重要的地位,Spring 自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0 开始, FactoryBean开始支持泛型,即接口声明改为FactoryBean
在Spring 5.2.6版本中,FactoryBean接口的定义如下所示。
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
- T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。
- boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
- Class
getObjectType():返回FactoryBean创建的bean类型。
这里,需要注意的是:当配置文件中
FactoryBean实例
首先,创建一个PersonFactoryBean,实现FactoryBean接口,如下所示。
package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.factory.FactoryBean;
/**
* @author binghe
* @version 1.0.0
* @description 商品的FactoryBean,测试FactoryBean
*/
public class PersonFactoryBean implements FactoryBean<Person> {
//返回一个Person对象,这个对象会被注册到Spring容器中
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
//bean是否为单例;true:是;false:否
@Override
public boolean isSingleton() {
return true;
}
}
接下来,我们在PersonConfig2类中加入PersonFactoryBean的声明,如下所示。
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}
这里需要小伙伴们注意的是:我在这里使用@Bean注解向Spring容器中添加的是PersonFactory对象。那我们就来看看Spring容器中有哪些bean。接下来,运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
可以看到,结果信息中输出了一个personFactoryBean,我们看下这个personFactoryBean到底是个什么鬼!此时,我们对SpringBeanTest类中的testAnnotationConfig7()方法稍加改动,添加获取personFactoryBean的代码,并输出personFactoryBean实例的类型,如下所示。
@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
Object personFactoryBean = context.getBean("personFactoryBean");
System.out.println("personFactoryBean实例的类型为:" + personFactoryBean.getClass());
}
再次运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
personFactoryBean实例的类型为:class io.mykit.spring.plugins.register.bean.Person
可以看到,虽然我在代码中使用@Bean注解注入的PersonFactoryBean对象,但是,实际上从Spring容器中获取到的bean对象却是调用PersonFactoryBean类中的getObject()获取到的Person对象。
看到这里,是不是有种豁然开朗的感觉!!!
在PersonFactoryBean类中,我们将Person对象设置为单实例bean,接下来,我们在SpringBeanTest类中的testAnnotationConfig7()方法多次获取Person对象,并输出多次获取的对象是否为同一对象,如下所示。
@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
Object personFactoryBean1 = context.getBean("personFactoryBean");
Object personFactoryBean2 = context.getBean("personFactoryBean");
System.out.println(personFactoryBean1 == personFactoryBean2);
}
运行testAnnotationConfig7()方法输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
true
可以看到,在PersonFactoryBean类的isSingleton()方法中返回true时,每次获取到的Person对象都是同一个对象,说明Person对象是单实例bean。
这里,可能就会有小伙伴要问了,如果将Person对象修改成多实例bean呢?别急,这里我们只需要在PersonFactoryBean类的isSingleton()方法中返回false,即可将Person对象设置为多实例bean,如下所示。
//bean是否为单例;true:是;false:否
@Override
public boolean isSingleton() {
return false;
}
再次运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
false
可以看到,最终结果返回了false,说明此时Person对象是多实例bean。
如何在Spring容器中获取到FactoryBean对象?
之前,我们使用@Bean注解向Spring容器中注册的PersonFactoryBean,获取出来的确实Person对象。那么,小伙伴们可能会问:我就想获取PersonFactoryBean实例,该怎么办呢?
其实,这也很简单, 只需要在获取bean对象时,在id前面加上&符号即可。
打开我们的测试类SpringBeanTest,在testAnnotationConfig7()方法中添加获取PersonFactoryBean实例的代码,如下所示。
@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
Object personFactoryBean1 = context.getBean("personFactoryBean");
Object personFactoryBean2 = context.getBean("personFactoryBean");
System.out.println("personFactoryBean1类型:" + personFactoryBean1.getClass());
System.out.println("personFactoryBean2类型:" + personFactoryBean2.getClass());
System.out.println(personFactoryBean1 == personFactoryBean2);
Object personFactoryBean3 = context.getBean("&personFactoryBean");
System.out.println("personFactoryBean3类型:" + personFactoryBean3.getClass());
}
运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
personFactoryBean1类型:class io.mykit.spring.plugins.register.bean.Person
personFactoryBean2类型:class io.mykit.spring.plugins.register.bean.Person
false
personFactoryBean3类型:class io.mykit.spring.plugins.register.bean.PersonFactoryBean
可以看到,在获取bean时,在id前面加上&符号就会获取到PersonFactoryBean实例对象。
那问题又来了!!为什么在id前面加上&符号就会获取到PersonFactoryBean实例对象呢?
接下来,我们就揭开这个神秘的面纱,打开BeanFactory接口,
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
/**************以下省略n行代码***************/
}
看到这里,是不是明白了呢?没错,在BeanFactory接口中定义了一个&前缀,只要我们使用bean的id来从Spring容器中获取bean时,Spring就会知道我们是在获取FactoryBean本身。
好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
写在最后
如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。
相关文章
- spring boot自动配置原理面试题_Spring boot面试
- Spring学习笔记(十八)——spring日志框架的配置和使用
- SSH 框架总结与整合 | Spring学习笔记
- 手把手教你搭建第一个Spring Batch项目
- [Spring]~@SpringBootTest(单元测试)
- Spring JpaTransactionManager事务管理
- spring官方文档 中文_Spring软件
- SpringBoot基础学习文章-Java环境变量配置Maven介绍Spring Boot介绍
- spring boot 的thymeleaf真的是那么随便就可以用的吗?弄不好你就会遇到很头疼的事
- Java 近期新闻:Spring Framework 6、JCP 选举、Valhalla 项目、OpenJDK 更新
- Spring Modulith使用模块和事件组织Spring Boot 3应用
- Spring Boot的安全配置(三)
- Spring Cloud Security的核心组件-Cloud Security Filter示例
- Spring Cloud Data Flow 和 Spring Cloud Stream 集成实现基于消息驱动的数据流应用程序
- 【愚公系列】2023年04月 Java教学课程 138-Spring MVC框架的Servlet相关接口详解
- Spring Bean生命周期详解编程语言
- Spring错误:org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.B详解编程语言
- spring的AOP(四)—-Spring AOP 编程(Advisor)详解编程语言
- Spring之AOP XML方式详解编程语言
- 获取Spring容器Bean对象工具类详解编程语言
- Spring Boot(二十):使用spring-boot-admin对spring-boot服务进行监控详解编程语言
- Spring Boot(十三):spring boot小技巧详解编程语言
- Spring Boot(十二):spring boot如何测试打包部署详解编程语言
- Spring框架下整合Redis的实现(spring整合redis)