前言
如果說Spring最核心的東西是什麼,那就非Beans元件莫屬了,Bean對於Spring的意義就象OOP對於Java的意義一樣。
今天要講的是Spring中的 BeanFactory與FactoryBean的區別以及具體使用。
1. BeanFactory
BeanFactory,以Factory結尾,表示它是一個工廠類(介面),用於管理Bean的一個工廠。在Spring中,BeanFactory是IOC容器的核心介面,它的職責包括:例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。
Spring為我們提供了許多易用的BeanFactory實現,XmlBeanFactory就是常用的一個,該實現將以XML方式描述組成應用的物件及物件間的依賴關係。XmlBeanFactory類將持有此XML配置後設資料,並用它來構建一個完全可配置的系統或應用。
例項化容器
1 Resource resource = new FileSystemResource("beans.xml");
2 BeanFactory factory = new XmlBeanFactory(resource);
複製程式碼
1 ClassPathResource resource = new ClassPathResource("beans.xml");
2 BeanFactory factory = new XmlBeanFactory(resource);
複製程式碼
1 ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
2 BeanFactory factory = (BeanFactory) context;
複製程式碼
基本就是這些了,接著使用getBean(String beanName)方法就可以取得bean的例項;BeanFactory提供的方法及其簡單,僅提供了六種方法供客戶呼叫:
boolean containsBean(String beanName) 判斷工廠中是否包含給定名稱的bean定義,若有則返回true
Object getBean(String) 返回給定名稱註冊的bean例項。根據bean的配置情況,如果是singleton模式將返回一個共享例項,否則將返回一個新建的例項,如果沒有找到指定bean,該方法可能會丟擲異常
Object getBean(String, Class) 返回以給定名稱註冊的bean例項,並轉換為給定class型別
Class getType(String name) 返回給定名稱的bean的Class,如果沒有找到指定的bean例項,則排除NoSuchBeanDefinitionException異常
boolean isSingleton(String) 判斷給定名稱的bean定義是否為單例模式
String[] getAliases(String name) 返回給定bean名稱的所有別名
2. FactoryBean
以Bean結尾,表示它是一個Bean,不同於普通Bean的是:它是實現了FactoryBean<T>介面的Bean,根據該Bean的ID從BeanFactory中獲取的實際上是FactoryBean的getObject()返回的物件,而不是FactoryBean本身,如果要獲取FactoryBean物件,請在id前面加一個&符號來獲取。
例如自己實現一個FactoryBean,功能:用來代理一個物件,對該物件的所有方法做一個攔截,在呼叫前後都輸出一行LOG,模仿ProxyFactoryBean的功能。
/**
* my factory bean<p>
* 代理一個類,攔截該類的所有方法,在方法的呼叫前後進行日誌的輸出
*
*/
public class MyFactoryBean implements FactoryBean<Object>, InitializingBean, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(MyFactoryBean.class);
private String interfaceName;
private Object target;
private Object proxyObj;
@Override
public void destroy() throws Exception {
logger.debug("destroy......");
}
@Override
public void afterPropertiesSet() throws Exception {
proxyObj = Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[] { Class.forName(interfaceName) },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.debug("invoke method......" + method.getName());
logger.debug("invoke method before......" + System.currentTimeMillis());
Object result = method.invoke(target, args);
logger.debug("invoke method after......" + System.currentTimeMillis());
return result;
}
});
logger.debug("afterPropertiesSet......");
}
@Override
public Object getObject() throws Exception {
logger.debug("getObject......");
return proxyObj;
}
@Override
public Class<?> getObjectType() {
return proxyObj == null ? Object.class : proxyObj.getClass();
}
@Override
public boolean isSingleton() {
return true;
}
public String getInterfaceName() {
return interfaceName;
}
public void setInterfaceName(String interfaceName) {
this.interfaceName = interfaceName;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Object getProxyObj() {
return proxyObj;
}
public void setProxyObj(Object proxyObj) {
this.proxyObj = proxyObj;
}
}
複製程式碼
XML-Bean配置如下
1 <bean id="fbHelloWorldService" class="com.ebao.xxx.MyFactoryBean">
2 <property name="interfaceName" value="com.ebao.xxx.HelloWorldService" />
3 <property name="target" ref="helloWorldService" />
4 </bean>
複製程式碼
Junit Test class
@RunWith(JUnit4ClassRunner.class)
@ContextConfiguration(classes = { MyFactoryBeanConfig.class })
public class MyFactoryBeanTest {
@Autowired
private ApplicationContext context;
/**
* 測試驗證FactoryBean原理,代理一個servcie在呼叫其方法的前後,列印日誌亦可作其他處理
* 從ApplicationContext中獲取自定義的FactoryBean
* context.getBean(String beanName) ---> 最終獲取到的Object是FactoryBean.getObejct(),
* 使用Proxy.newInstance生成service的代理類
*/
@Test
public void testFactoryBean() {
HelloWorldService helloWorldService = (HelloWorldService) context.getBean("fbHelloWorldService");
helloWorldService.getBeanName();
helloWorldService.sayHello();
}
}
複製程式碼
其實FactoryBean這種特點,可以實現很多有用的功能。有興趣的同學不妨瞭解下。
總結
Spring總是面試中面試官衷情的知識點,雖說Spring原始碼看起來在實際工作當中並沒有什麼特別的用處,但是隨著技術經驗的不斷增長,會發現瞭解Spring底層原理有多大用處!因此我給大家推薦一個Java架構群:895244712
,裡面有分散式,微服務,效能優化等技術點底層原理的視訊,也有眾多想要提升的小夥伴討論技術,歡迎大家加群一起交流學習。
瞭解Spring中的 BeanFactory與FactoryBean不僅可以幫助我們更好地完成工作,還能夠使我們對spring的理解更加深入,如果以後面試官問起來,也能做到胸有成竹。