理解Spring框架中的BeanFactory和FactoryB
BeanFactory和FactoryBean直面翻译过来的意思是“Bean”工厂和工厂“Bean”,其中"bean"在spring中的意思是类的实例,也就是说这两个类分别是实例工厂和工厂实例。
既然都和工厂有关那么我们先来聊一下工厂模式。
工厂模式的作用
工厂模式是用来创建不同但是类型相关的对象,它可以封装对象复杂的创建过程,将对象的创建和使用分离,让代码更加清晰。举个例子:
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = null;
if ("json".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new JsonRuleConfigParser();
} else if ("xml".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new XmlRuleConfigParser();
} else if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new YamlRuleConfigParser();
} else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)) {
parser = new PropertiesRuleConfigParser();
} else {
throw new InvalidRuleConfigException( "Rule config file format is not supported: " + ruleConfigFilePath);
}
String configText = getFileText(ruleConfigFilePath);
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
private String getFileExtension(String filePath) {
//...解析文件名获取扩展名,比如rule.json,返回json
return "json";
}
private String getFileText(String filePath) {
String fileText = "";
//...解析文件内容
return fileText;
}
}
为了让代码更清晰,类的职责更单一我们来使用工厂模式:
public class RuleConfigSource {
public RuleConfig load(String ruleConfigFilePath) {
String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
IRuleConfigParser parser = RuleConfigParserFactory.createParser(ruleConfigFileExtension);
if (parser == null) {
throw new InvalidRuleConfigException( "Rule config file format is not supported: " + ruleConfigFilePath);
}
String configText = getFileText(ruleConfigFilePath);
RuleConfig ruleConfig = parser.parse(configText);
return ruleConfig;
}
private String getFileExtension(String filePath) {
//...解析文件名获取扩展名,比如rule.json,返回json
return "json";
}
private String getFileText(String filePath) {
String fileText = "";
//...解析文件内容
return fileText;
}
}
public class RuleConfigParserFactory {
public static IRuleConfigParser createParser(String configFormat) {
IRuleConfigParser parser = null;
if ("json".equalsIgnoreCase(configFormat)) {
parser = new JsonRuleConfigParser();
} else if ("xml".equalsIgnoreCase(configFormat)) {
parser = new XmlRuleConfigParser();
} else if ("yaml".equalsIgnoreCase(configFormat)) {
parser = new YamlRuleConfigParser();
} else if ("properties".equalsIgnoreCase(configFormat)) {
parser = new PropertiesRuleConfigParser();
}
return parser;
}
}
我们通过一个工厂类RuleConfigParserFactory
来创建我们所需要的对象,调用者可以不关心实例的创建过程,这让我们的项目更加“解耦”了。这个例子我们可以具体点叫它“简单工厂模式”,因为它足够的简单,除此之外还有“工厂方法模式”和“抽象工厂模式”。他们都是为了解决同样的问题。
工厂模式可以解决很多的问题,但是于此同时它也带来了一些问题。假如项目很大,有很多的实例需要用工厂创建,并且创建时机和类型都不尽相同。这个时候假如使用的工厂模式(即使是抽象工厂模式),整个项目就会充斥着各种工厂类,而且各个工厂内部又会充斥着各种判断语句,这样反倒让项目更加难以维护了。
这个时候更高级的工厂模式就站出来了,他不需要创建很多的工厂类就可以帮助我们创建各种实例,还可以管理对象的生命周期。这就是“DI容器(Dependency Injection Container)”,也可以叫做依赖注入容器。
其实标题上写的“Spring框架中的BeanFactory“就是一个牛逼哄哄的DI容器的核心接口。
Spring框架中的BeanFactory
Spring的核心是IOC容器,而Spring容器最基本的接口就是BeanFactory。BeanFactory负责配置、创建、管理Bean,它定义了getBean()、containsBean()等管理Bean的通用方法。
public interface BeanFactory {
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
* will return the factory, not the instance returned by the factory.
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* Return an instance, which may be shared or independent, of the specified bean.
*/
Object getBean(String name) throws BeansException;
/**
* Return an instance, which may be shared or independent, of the specified bean.
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* Return an instance, which may be shared or independent, of the specified bean.
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* Return the bean instance that uniquely matches the given object type, if any.
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* Return an instance, which may be shared or independent, of the specified bean.
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* Does this bean factory contain a bean definition or externally registered singleton
*/
boolean containsBean(String name);
/**
* Is this bean a shared singleton? That is, will {@link #getBean} always
* return the same instance?
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* Is this bean a prototype? That is, will {@link #getBean} always return
* independent instances?
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* Check whether the bean with the given name matches the specified type.
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
/**
* Check whether the bean with the given name matches the specified type.
*/
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**
* Determine the type of the bean with the given name. More specifically,
* determine the type of object that {@link #getBean} would return for the given name.
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* Return the aliases for the given bean name, if any.
*/
String[] getAliases(String name);
}
它还有几个关键的子接口:
- HierarchicalBeanFactory接口:在继承BeanFactory的基础上,提供父容器的访问功能
- ListableBeanFactory接口:在继承BeanFactory的基础上,提供了批量获取Bean的方法
- ConfigurableBeanFactory接口:在继承HierarchicalBeanFactory的基础上,提供了配置管理功能
- AutowireCapableBeanFactory接口:在继承BeanFactory的基础上,提供了Bean的自动装配功能
-
ConfigurableListableBeanFactory接口:继承了上述的所有接口,增加了类加载器,类型转化,属性编辑等功能
Beanfactory类关系图
他们最终的实现都是DefaultListableBeanFactory
,也可以说DefaultListableBeanFactory
就是IOC容器的实现。
可以总结一下BeanFactor接口是Spring中工厂的顶层规范,是IOC容器的核心接口,它的实现是DefaultListableBeanFactory。
Spring框架中的FactoryBean
FactoryBean
虽然名字和BeanFactory
很像,但是在spring中的地位和BeanFactory
完全不能比。FactoryBean
只是被SpringIOC容器管理的一类Bean的接口规范。可以这么讲Spring容器可以管理两类bean,一类是普通的bean,像我们在项目中定义的userServcice
这类可以直接为业务做事的bean就是普通bean。另一类就是实现了FactoryBean
接口的工厂bean,他只做一件事就是生产普通bean。可以把他理解成"spring的黄埔军校",他不直接上战场打仗,他为战场培养(生产)合格和军官。
那就很奇怪了,spring容器BeanFactory
不就是一个大工厂吗,它本身不就可以生产bean吗?为啥还要先有一个实现FactoryBean
接口的工厂bean呢?原因很简单,因为有这一类的bean需要复杂的配置才能生产。拿上面“黄埔军校”来举例,也就是说培养一个合格的军官去打赢一场仗并非那么简单,首先需要有一个好学校。
我们来看一个例子:
@Bean
public SqlSessionFactory getSqlSessionFactory (@Qualifier ("dataCenterDbDataSource") DataSource dataSource) throws Exception
{
SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); //创建一个FactoryBean
//配置数据源
bean.setDataSource (dataSource);
// 分页插件
PageHelper pageHelper = new PageHelper ();
Properties properties = new Properties ();
// false: pageNum<1和大于总页数时,返回空;true: pageNum<1返回第一页,大于总页数返回最后一页
properties.setProperty ("reasonable", "false");
properties.setProperty ("supportMethodsArguments", "true");
properties.setProperty ("returnPageInfo", "check");
properties.setProperty ("params", "count=countSql");
pageHelper.setProperties (properties);
// 添加插件
bean.setPlugins (new Interceptor[]{ pageHelper });
List <Resource> resources = new ArrayList <Resource> ();
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver ();
resources.addAll (Arrays.asList (pathMatchingResourcePatternResolver.getResources ("classpath:mybatis/*.xml")));
bean.setMapperLocations (resources.toArray (new Resource[resources.size()]));
bean.getObject ().getConfiguration ().setMapUnderscoreToCamelCase (true);
return bean.getObject ();
}
这个是spring整合mybatis(操作数据库框架)的一段很常见的代码,其中SqlSessionFactoryBean
就继承了FactoryBean
接口,它就是一个名副其实的工厂bean。它为我们配置好了SqlSessionFactory
这个工厂的所有设施,让这个工厂去生产SqlSession
再去操作数据库。
我们再看一下FactoryBean
这个接口
public interface FactoryBean<T> {
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
*/
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates
*/
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton?
*/
boolean isSingleton();
}
这个接口非常简单,如果我们也需要一个工厂bean直接实现这三个方法就可以了。
举个例子,例如我们要一个生产UserService
的工厂,首先定义一个UserServiceFactoryBean
public class UserServiceFactoryBean implements FactoryBean<UserService> {
private UserService userService;
//定义一个配置userService的入口
public void setConfig(Conf conf){
userService.setConf(conf);
}
@Override
public UserService getObject() throws Exception {
return userService;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
@Override
boolean isSingleton() {
return true;
}
}
然后再配置进spring容器就可以了
@Configuration
public class ItemConfig {
@Bean
public UserService getUserService(Conf conf) throws Exception {
UserServiceFactoryBean factoryBean = new UserServiceFactoryBean();
factoryBean.setConf(conf)
return factoryBean.getObject();
}
}
使用的时候跟直接创建的bean一样用就可以了
@Service
public class Test1 {
@Autowired
private UserService userService;
@Test
public void test() {
userService.sayHello();
}
}
这里需要解释一下,不同于上面mybatis的SqlSessionFactoryBean
这个例子。SqlSessionFactoryBean
并没有直接去生产SqlSession
这个对象,而是生产了SqlSessionFactory
这个工厂对象。这个是因为mybatis是一个独立于spring的框架,它的内部实现需要SqlSessionFactory
这个工厂才能使用SqlSession
的功能,并没有向调用者直接开放SqlSession
这个类的使用(这个设计思想叫做迪米特法则)。其实SqlSessionFactoryBean
这个类的全称应该叫SqlSessionFactoryFactoryBean
,假如SqlSession
这个类的是spring的一部分,那就不会再有SqlSessionFactory
这个工厂类了。