• 《Spring》(三)---- 工厂方法与FactoryBean


      面向接口编程时,虽然对象可以通过声明接口来避免对特定接口实现类的过渡耦合,但总归需要一种方式将生命依赖接口的对象与接口实现类关联起来。

      问题背景:

    public class Foo {
        private BarInterface barInterface;
        public Foo() {
            barInstance = new BarInterfaceImpl();
        }
    }

      这样接口与实现类的耦合性很高。

      如果BarInterfaceImpl类是我们设计开发的,可以直接通过依赖注入,让容器帮助我们解除接口与实现类的耦合性。但是,有时候我们需要依赖第三方库,需要实例化并使用第三方库中的相关类,可以通过xml方式配置,但却没法使用注解方式。

      通常做法是通过使用工厂方法模式,提供一个工厂类来实例化具体的接口实例类,这样,主体对象只需要依赖工厂类,具体使用的实现类有变更的话,只是变更工厂类,而主体对象不需要做任何改动。

    1.  静态工厂方法

    假设某个第三方库发布了BarInterface,为了向使用该接口的客户端对象屏蔽以后可能对BarInterface实现类的变动,同时还提供了一个静态的工厂方法实现类:

    public class StaticBarInterfaceFactory {
        public static BarInterface getInstance() {
            return new BarInterfaceImpl();
        }
    }

    使用以下配置方式,将该静态工厂方法返回的实现注入Foo.

    <bean id="foo" class="...Foo">
        <property name="barInterface">
            <ref bean="bar"/>
        </property>
    </bean>
    
    <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>

    class指定静态工厂方法类,factory-method指定工厂方法名称,然后容器调用该静态工厂方法类的指定工厂方法,并返回方法调用后的结果,即BarInterfaceImpl的实例。

    某些时候,有的工厂类的工厂方法可能需要参数来返回相应的实例,可以通过<constructor-arg>来指定工厂方法需要的参数

    public class StaticBarInterfaceFactory
    {
        public static BarInterface getInstance(Foobar foobar)
        {
            return new BarInterfaceImpl(foobar);
        }
    }  

    配置方式改为如下:

    <bean id="foo" class="...Foo">
        <property name="barInterface">
           <ref bean="bar"/>
        </property>
    </bean>
    <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance">
      <constructor-arg>
        <ref bean="foobar"/>
      </constructor-arg>
    </bean>
    <bean id="foobar" class="...FooBar"/>

    唯一需要注意的就是,针对静态工厂方法实现类的bean定义,使用<constructor-arg>传入的是工厂方法的参数,而不是静态工厂方法实现类的构造方法的参数(静态工厂方法实现类页没有提供显式的构造方法)

      

      2. 非静态工厂方法

    public class NonStaticBarInterfaceFactory {
        public BarInterface getInstance() {
            return new BarInterfaceImpl();
        }
    }

    因为工厂方法为非静态的,只能通过某个NonStaticBarInterfaceFactory实例来调用该方法,配置方法如下:

    <bean id="foo" class="...Foo"> 
        <property name="barInterface">
            <ref bean="bar"/>
        </property> 
    </bean>
    
    <bean id="barFactory" class="...NonStaticBarInterfaceFactory"/>
    
    <bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>

    bar的定义需要使用factory-bean来指定工厂方法所在的工厂类实例,而不是通过class属性来指定工厂方法所在类的类型。指定工厂方法名还是使用factory-method指定。如果非静态工厂方法调用时也需要提供参数的话,处理方式是与静态工厂方法相似的,都可以通过<constructor-arg>来指定方法调用参数。

      3. FactoryBean

    当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候,就可以实现org.spring-framework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑代码,当然,不使用FactoryBean,而像通常那样实现自定义的工厂方法类也是可以的。FactoryBean接口定义如下:

    public interface FactoryBean {
    
        Object getObject() throws Exception;
        Class getObjectType();
        boolean isSingleton();
    
    }

    例如:

    import org.joda.time.DateTime;
    import org.springframework.beans.factory.FactoryBean;
    
    public class NextDayDateFactoryBean implements FactoryBean {
        public Object getObject() throws Exception {
            return new DateTime().plusDays(1);
        }
        public Class getObjectType() {
            return DateTime.class;
        }
        public boolean isSingleton() {
            return false;
        }
    }        
    public class NextDayDateDisplayer
    {
        private DateTime dateOfNextDay;
        // 相应的setter方法
        // ...
    }
    <bean id="nextDayDateDisplayer" class="...NextDayDateDisplayer">
        <property name="dateOfNextDay">
            <ref bean="nextDayDate"/>
        </property>
    </bean>
    <bean id="nextDayDate" class="...NextDayDateFactoryBean">
    </bean>

    NextDayDateDisplayer所声明的dateOfNextDay的类型为DateTime, 而不是NextDayDateFactoryBean.也就是说FactoryBean类型的bean定义,通过正常的id引用,容器返回的是FactoryBean所生产的对象类型,而非FactoryBean实现本身。如果一定要取得FactoryBean本身的话,可以通过在bean定义的id之前加&来达到目的,如:

    Object factoryBean = container.getBean("&nextDayDate");
  • 相关阅读:
    iframe
    服务器 开发机 linux docker
    git
    iframe because an ancestor violates the following Content Security Policy directive: "frameancestors 'self'
    @babel/pluginproposaloptionalchaining
    jest
    富文本编辑器
    thymeleaf+layui渲染错误
    springboot静态资源访问
    layui的树型组件的使用
  • 原文地址:https://www.cnblogs.com/IvySue/p/6480215.html
Copyright © 2020-2023  润新知