Spring強大的FactoryBean還能這樣用,漲知識
環(huán)境:Spring6.1.7
1. 簡介
FactoryBean 接口是 Spring IoC 容器實例化邏輯的一個可插入點。如果你有復雜的初始化代碼,而這些代碼最好用 Java 而不是冗長的 XML 來表達,那么你可以通過自定義自己的 FactoryBean,在該類中編寫復雜的初始化,然后將自定義 FactoryBean 添加到容器中。
FactoryBean<T> 接口提供了三種方法:
- T getObject(): 返回該工廠創(chuàng)建的對象的實例。該實例可能是共享的,這取決于該工廠返回的是單體還是原型
- boolean isSingleton(): 如果此 FactoryBean 返回單例,則返回 true;否則返回 false。此方法的默認實現(xiàn)返回 true。
- Class<?> getObjectType(): 返回 getObject() 方法返回的對象類型,如果事先不知道類型,則返回空值。
FactoryBean接口在Spring框架非多的地方被使用。Spring本身就包含了50多個FactoryBean接口的實現(xiàn)。
2. 實戰(zhàn)案例
準備基本類;
class PersonService {
public void save() {
// TODO
System.out.println("save person...") ;
}
}
接下來會基于上面的PersonService類進行FactoryBean各種案例的使用;
2.1 簡單示例
自定義FactoryBean<PersonService>實現(xiàn);
@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 創(chuàng)建代理
你可以通過FactoryBean創(chuàng)建代理對象借助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能夠非常方便的以編程的方式創(chuàng)建代理對象。
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實例。