环境:Spring6.1.7
1. 简介
FactoryBean 接口是 Spring IoC 容器实例化逻辑的一个可插入点。如果你有复杂的初始化代码,而这些代码最好用 Java 而不是冗长的 XML 来表达,那么你可以通过自定义自己的 FactoryBean,在该类中编写复杂的初始化,然后将自定义 FactoryBean 添加到容器中。
FactoryBean<T> 接口提供了三种方法:
- T getObject(): 返回该工厂创建的对象的实例。该实例可能是共享的,这取决于该工厂返回的是单体还是原型
- boolean isSingleton(): 如果此 FactoryBean 返回单例,则返回 true;否则返回 false。此方法的默认实现返回 true。
- Class<?> getObjectType(): 返回 getObject() 方法返回的对象类型,如果事先不知道类型,则返回空值。
FactoryBean接口在Spring框架非多的地方被使用。Spring本身就包含了50多个FactoryBean接口的实现。
2. 实战案例
准备基本类;
class PersonService {
public void save() {
// TODO
System.out.println("save person...") ;
}
}
接下来会基于上面的PersonService类进行FactoryBean各种案例的使用;
2.1 简单示例
自定义FactoryBean<PersonService>实现;
@Component
public class PersonServiceFactoryBean implements FactoryBean<PersonService> {
// 具体的实例
public PersonService getObject() throws Exception {
PersonService ps = new PersonService() ;
return ps ;
}
// 具体的类型
public Class<?> getObjectType() {
return PersonService.class ;
}
// true,返回单例容器中只会有一个PersonService
public boolean isSingleton() {
return true ;
}
}
具体使用;
// 你可以直接在其它的Bean中注入
@Resource
private PersonService personService ;
// 你可以通过BeanFactory手动获取
private ApplicationContext contet ;
PersonService ps = context.getBean(PersonService.class) ;
使用时和普通的Bean的使用方式一致。
2.2 多例&不指定类型
多例;
如果你需要每次使用时返回的都是不同的实例对象,那么你可以将isSingleton返回为false。
// 将上面的isSingleton返回为false
public boolean isSingleton() {
return false ;
}
接下来使用时,不管是在其它组件中进行注入还是通过getBean获取,都能确保在每个组件中使用的都不是同一个实例。
@Component
public class PersonController {
@Resource
private PersonService personService ;
}
@Component
public class CommonService {
@Resource
private PersonService personService ;
}
上面两个类中注入的PersonService将不是同一个实例,如果你通过getBean获取每次也都不是同一个实例。
不指定类型;
如果你将FactoryBean#getObjectType方法返回为null。那么你将无法正确的注入PersonService对象,同时程序将抛出异常
图片
2.3 创建代理
你可以通过FactoryBean创建代理对象借助ProxyFactory。将getObject修改如下:
public PersonService getObject() throws Exception {
ProxyFactory factory = new ProxyFactory() ;
factory.setTarget(new PersonService()) ;
factory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...") ;
return invocation.proceed() ;
}
}) ;
return (PersonService) factory.getProxy() ;
}
通过FactoryBean能够非常方便的以编程的方式创建代理对象。
2.4 获取原始FactoryBean
如果你需要获取FactoryBean本身,而非getObjectType类型,那么你可以通过如下方式:
// 方式1:直接通过类型获取
private ApplicationContext context ;
context.getBean(PersonServiceFactoryBean.class) ;
// 方式2:通过beanName获取,但是需要添加'&'前缀
context.getBean("&psFactoryBean") ;
上面2种方式都能正确的获取原始的FactoryBean对象。
2.5 利用SPI获取对象
Spring为我们提供了ServiceFactoryBean类,通过该类我们能非常方便的获取SPI对象并注册为容器Bean。
@Configuration
public class AppConfig {
@Bean
public ServiceFactoryBean serviceFactoryBean() {
ServiceFactoryBean fb = new ServiceFactoryBean() ;
// 指定SPI接口类型
fb.setServiceType(DAO.class) ;
return fb ;
}
}
接下来你就可以在META-INF/services下建立DAO全限定名的文件
com.pack.bean.create.PersonDAO
com.pack.bean.create.StudentDAO
当在容器中注入DAO时,你将得到这里的第一个PersonDAO实例。