掌握Spring中的beanfactory與factorybean有什麼好處?

lu_s發表於2018-10-22

掌握Spring中的beanfactory與factorybean有什麼好處?

前言

  如果說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的理解更加深入,如果以後面試官問起來,也能做到胸有成竹。


相關文章