zoukankan      html  css  js  c++  java
  • Spring中老生常谈的FactoryBean

    本文完整代码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/factorybean

    FactoryBeanBeanFactory由于在命名上极其相似,一直以来困扰了不少的开发者。

    BeanFactory,耳熟能详的Spring核心接口,提供IoC容器的最基本功能。但要解释FactoryBean一句话可能就说不清楚了。我们将从下面的例子逐步说明,FactoryBean是什么,它提供了什么样的能力。

    /**
     * 布料
     * 包含颜色属性
     * Created by OKevin On 2019/9/3
     **/
    public class Cloth {
        private Red red;
    	//省略setter/getter方法
    }
    

    当初始化一个Cloth对象时,我希望Red对象也被赋值,此时我将在Cloth的构造方法中new一个Red对象。

    public Cloth() {
        red = new Red();
    }
    

    但是随着业务的发展,我希望Cloth的颜色属性将是Blue蓝色,这时我将修改代码将Red和Blue类抽象出一个Color接口,Cloth代码将重构:

    /**
     * 布料
     * 包含颜色属性
     * Created by OKevin On 2019/9/3
     **/
    public class Cloth {
        private Color color;
        public Cloth() {
            color = new Blue();
        }
    	//省略setter/getter方法
    }
    

    业务又进一步发展,Cloth类中的颜色属性将会根据一定的条件赋值为Red红色,此时我们将代码继续重构:

    /**
     * 布料
     * 包含颜色属性
     * Created by OKevin On 2019/9/3
     **/
    public class Cloth {
        private Color color;
        public Cloth() {
            if (condition()) {
            	color = new Blue();    
            } else {
                color = new Red();
            }
        }
    	//省略setter/getter方法
    }
    

    这样的代码的确能运行,但如果有新的条件继续加入到业务中,此时我们又将改动Cloth类的构造方法,而我们认为Cloth方法是一个比较核心的业务对象,不应该经常对它进行修改,并且在构造方法中对于Color对象创建过于冗余,不符合单一职责的原则,所以我们将Color对象的创建过程通过工厂方法模式来完成。

    静态工厂方法

    我们再次将Cloth类进行如下重构(为了使示例代码更加简洁,下面的示例将只创建Red对象):

    /**
     * 布料
     * 包含颜色属性
     * Created by OKevin On 2019/9/3
     **/
    public class Cloth {
        private Color color;
        public Cloth() {
            color = StaticColorFactory.newInstance();
        }
    	//省略setter/getter方法
    }
    
    /**
     * 静态工厂方法
     * Created by OKevin On 2019/9/3
     **/
    public class StaticColorFactory {
        public static Color getInstance() {
            return new Red();
        }
    }
    

    如果我们在Spring容器中要通过静态工厂方法,创建具体的对象实例应该怎么做呢?

    众所周知,要将一个对象实例交由Spring容器管理,我们通常是通过以下XML配置:

    <bean id="cloth" class="com.coderbuff.bean.Cloth">
    	<property name="color" ref="red"/>
    </bean>
    <bean id="red" class="com.coderbuff.bean.Red" />
    

    但此时,Red对象实例并不是由Spring容器管理,而是由静态工厂方法创建的,此时我们应该讲XML配置修改为以下方式:

    <bean id="cloth" class="com.coderbuff.bean.Cloth">
    	<property name="color" ref="red"/>
    </bean>
    <bean id="red" class="com.coderbuff.factory.StaticColorFactory" factory-method="getInstance" />
    

    这是Spring支持静态工厂方法创建对象实例的特定方式。这样我们就能在Spring中通过静态工厂方法创建对象实例。

    实例工厂方法

    有静态工厂方法,就有非静态工厂方法,区别就是方法不是静态的。

    /**
     * 实例工厂方法
     * Created by OKevin On 2019/9/3
     **/
    public class ColorFactory {
        public Color getInstance() {
            return new Red();
        }
    }
    

    实例工厂方法在Spring中XML配置略有不同:

    <bean id="cloth" class="com.coderbuff.bean.Cloth">
    	<property name="color" ref="red"/>
    </bean>
    <bean id="colorFactory" class="com.coderbuff.factory.ColorFactory"/>
    <bean id="red" factory-bean="colorFactory" factory-method="getInstance"/>
    

    通过配置可以看到,我们需要首先在Spring中实例化工厂,再通过工厂对象实例化Red对象。

    在有了对工厂方法在Spring中创建对象实例的认识后,FactoryBean实际上就是为我们简化这个操作。下面我们将通过FactoryBean来创建Red对象。

    FactoryBean

    /**
     * Created by OKevin On 2019/9/3
     **/
    public class ColorFactoryBean implements FactoryBean<Color> {
        public Color getObject() throws Exception {
            return new Red();
        }
        public Class<?> getObjectType() {
            return Red.class;
        }
        public boolean isSingleton() {
            return false;
        }
    }
    

    通过实现FactoryBean的方式,XML配置如下:

    <bean id="cloth" class="com.coderbuff.bean.Cloth">
    	<property name="color" ref="red"/>
    </bean>
    <bean id="red" class="com.coderbuff.factory.ColorFactoryBean"/>
    

    这样就不用像工厂方法那样配置相应的属性,直接按照普通的Bean注入即可,由于Spring内部做了特殊处理,此时名称为“red”的Bean并不是ColorFactoryBean,而是它方法中getObject中返回的对象。如果实在想要获取ColorFactoryBean的对象实例,则在Bean的名称前加入“&”即可(“&red”)。

    看到这里,是否对FactoryBean有了一点认识呢?FactoryBean在Spring中最为典型的一个应用就是用来创建AOP的代理对象。

    我们知道AOP实际上是Spring在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP代理对象通过Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是——ProxyFactoryBean。

    ProxyFactoryBean

    我们将通过比较“古老”的方式创建一个Red对象的切面,在它的print方法执行前和执行后分别执行一条语句。之所以古老是因为我们往往通过注解的方式,而不会这么折磨自己去写一个切面对象。

    /**
     * 环绕通知
     * Created by OKevin On 2019/9/4
     **/
    public class LogAround implements MethodInterceptor {
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("调用目标方法【前】打印日志");
            Object result = invocation.proceed();
            System.out.println("调用目标方法【后】打印日志");
            return result;
        }
    }
    

    此时我们需要ProxyFactoryBean的介入为我们创建一个代理对象并由Spring容器管理,根据上面ColorFactoryBean的经验,ProxyFacoryBean也应该如下配置:

    <bean id="xxx" class="org.springframework.aop.framework.ProxyFactoryBean" />
    

    答案是肯定的,只是ProxyFactoryBean多了几个参数,既然是生成代理对象,那么目标对象、目标方法就必不可少,实际的XLM配置如下:

    <bean id="logAround" class="com.coderbuff.aop.LogAround"/>
    <bean id="red" class="com.coderbuff.bean.Red"/>
    <bean id="proxyRed" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="proxyInterfaces" value="com.coderbuff.bean.Color"/>
    	<property name="interceptorNames" value="logAround"/>
    	<property name="target" ref="red"/>
    	<property name="proxyTargetClass" value="true"/>
    </bean>
    

    通过测试程序,ProxyFactoryBean的确生成了一个代理对象。

    public class ProxyFactoryBeanTest {
        private ClassPathXmlApplicationContext ctx;
        @Before
        public void init() {
            ctx = new ClassPathXmlApplicationContext("spring-proxyfactorybean.xml");
        }
        @Test
        public void testProxyFactory() {
            Red proxyRed = (Red) ctx.getBean("proxyRed");
            proxyRed.print();
        }
    }
    

    所以,FactoryBean为我们实例化Bean提供了一个更为灵活的方式,我们可以通过FactoryBean创建出更为复杂的Bean实例。

    本文完整代码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/factorybean

    这是一个能给程序员加buff的公众号 (CoderBuff)
  • 相关阅读:
    MySql自定义函数-关于保留小数位的特殊需求
    MySQL-时区导致的时间前后端不一致
    [计算机基础] 汇编学习(3)
    [计算机基础] 汇编学习(2)
    [计算机基础] 汇编学习(1)
    [安全] metasploit(2)
    [安全] Kali Linux安装TheFatRat
    [安全] metasploit(1)
    [安全] tcpdump使用
    [安全] netcat和socat的使用
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/11456587.html
Copyright © 2011-2022 走看看