Spring中BeanFactory与FactoryBean的区别
为了搞明白这个问题,下载了spring源码在本地ide编译调试,debug进去一步步跟踪查看spring的启动流程,运行机制。
之前一直有在小破站看过spring源码的课程,但spring的源码还是太过强大,类库多,各种设计模式灵活运用,有时候一个小的方法跟进去就有好多相关的扩展代码,跟着跟着就偏离了原来的的代码逻辑,方法的调用链很深,幸好看过spring源码的课程讲解,对spring源码的整体启动流程和大体逻辑有所了解,不然看spring源码会懵逼,即便这样,在看debug跟spring源码到具体的一些细节的时候还是会略显吃力,总体来讲还是对spring底层的一些类库和设计的理解有限吧,大体上知道,具体细节到各个具体的类,就还需要再次看代码逻辑
至此,我们切入今天的主题Spring中BeanFactory 和FactoryBean的区别:
先谈谈我个人的理解:
2.由两个接口类的名字和方法可以看出
BeanFactory 是生产Bean实例的一个工厂(Factory),通过这个接口下的方法来获取spring IOC容器中的各种bean实例,在Spring中所有的Bean 都是由 BeanFactory来管理的
FactoryBean 是一个工厂Bean,实际上也是一个Bean 由BeanFactory 创建,只是这个FactoryBean 有点不一样,是一个能生产或修饰对象生成的工厂Bean
个人理解比较浅薄,来抄作业
在网上找找资料总结如下:
beanFactory 接口是spring容器的核心接口,负责:实例化,定位配置应用程序中的对象及建立这些对象间的依赖
spring 提供了多个易用的BeanFactory的实现, 在spring启动中常用的有DefaultListableBeanFactory, AbstractBeanFactory, AbstractAutoCapableBeanFactory
FactoryBean 接口是spring的核心接口,用于编写增强一些逻辑复杂的bean,在bean创建时可以添加逻辑
public interface FactoryBean<T> {
/**
* 返回对象的实例
*/
T getObject() throws Exception;
/**
* 返回对象的类型
*/
Class<?> getObjectType();
/**
* 是否是单例
*/
boolean isSingleton();
}
简单实现
/**
* @author xiaolong.ge
* @since 08 五月 2022
*/
@Component
public class MyFactoryBean implements FactoryBean<UserService> {
@Override
public boolean isSingleton() {
return true;
}
@Override
public UserService getObject() throws Exception {
System.out.println("MyFactoryBean-->getObject()实例化。。。");
UserService userService = new UserServiceImpl();
return userService;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
public class AppTest {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class);
UserService us = (UserService)context.getBean("myFactoryBean");
System.out.println(us);
MyFactoryBean myFactoryBean = (MyFactoryBean)context.getBean("&myFactoryBean");
UserService us2 = myFactoryBean.getObject();
System.out.println(us2);
System.out.println(us == us2);
}
// 输出结果:
MyFactoryBean-->getObject()实例化。。。
com.gexl.service.impl.UserServiceImpl@1440c311
MyFactoryBean-->getObject()实例化。。。
com.gexl.service.impl.UserServiceImpl@189b5fb1
false
增强实现
/**
* @author xiaolong.ge
* @since 08 五月 2022
*/
@Component
public class MyFactoryBean implements FactoryBean<UserService>, InitializingBean, DisposableBean {
private Object proxyObject;
private Object target;
private String interfaceName;
public MyFactoryBean() {
this.interfaceName = UserService.class.getName();
this.target = new UserServiceImpl();
}
@Override
public UserService getObject() throws Exception {
return (UserService) proxyObject;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("myFactoryBean afterPropertiesSet..");
proxyObject = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Class.forName(interfaceName)}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用之前增强。。。method="+method.getName());
Object obj = method.invoke(target,args);
System.out.println("方法调用之后增强。。。method="+method.getName());
return obj;
}
});
}
@Override
public Class<?> getObjectType() {
return proxyObject == null ? Object.class : proxyObject.getClass();
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void destroy() throws Exception {
System.out.println("destory...");
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
}
public class AppTest {
@Test
public void testFactoryBean() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class);
MyFactoryBean myFactoryBean = (MyFactoryBean) context.getBean("&myFactoryBean");
System.out.println("myFactoryBean="+myFactoryBean);
UserService userService = myFactoryBean.getObject();
System.out.println("userService="+userService);
UserServiceImpl targetService = (UserServiceImpl)myFactoryBean.getTarget();
String beanName = "userDao";
UserDao userDao = (UserDao)context.getBean(beanName);
Field field = targetService.getClass().getDeclaredField(beanName);
field.setAccessible(true);
field.set(targetService,userDao);
// 调用方法
userService.getById(1);
}
}
// 输出结果:
myFactoryBean afterPropertiesSet..
myFactoryBean=com.gexl.service.impl.MyFactoryBean@65bb9029
方法调用之前增强。。。method=toString
方法调用之后增强。。。method=toString
userService=com.gexl.service.impl.UserServiceImpl@1bfe3203
方法调用之前增强。。。method=getById
方法调用之后增强。。。method=getById
应用实例
Spring在整合mybatis框架时 SqlSessionFactoryBean 就是应用的FactoryBean
Spring会调用SqlSessionFactoryBean这个实现了FactoryBean的工厂Bean 同时加载dataSource,Mapper文件的路径,对sqlSessionFactory进行初始化。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
.....
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
// 创建 sqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
/**
* Build a {@code SqlSessionFactory} instance.
*
* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
* {@code SqlSessionFactory} instance based on an Reader.
* Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
*
* @return SqlSessionFactory
* @throws Exception if configuration is failed
*/
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
targetConfiguration = new Configuration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
... 省略
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
... 省略
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
...
}
BeanFactory是Spring中IOC容器最核心的接口,遵循了IOC容器中所需的基本接口。比如:ApplicationContext, AbstractBeanFactory等 都使用了BeanFactory接口。
FactoryBean是工厂类接口,只想简单构造Bean,不希望实现原有大量的方法。它是一个Bean,不过这个Bean能够做为工厂去创建Bean, 同时还能修饰对象的生成
FactoryBean比BeanFactory在生产Bean的时候灵活,还能修饰对象,带有工厂模式和装饰模式的意思在里面,不过它的存在还是以Bean的形式存在