zoukankan      html  css  js  c++  java
  • Spring中的BeanFactory与FactoryBean看这一篇就够了

    前言
    理解FactoryBean是非常非常有必要的,因为在Spring中FactoryBean最为典型的一个应用就是用来创建AOP的代理对象,不仅如此,而且对理解Mybatis核心源码也非常有帮助!如果甘愿crud,做个快乐的码农,那我就哦豁豁豁豁豁豁豁豁豁豁豁豁豁豁......
    @

    BeanFactory和FactoryBean同样都是spring的接口,名字看起来很相似,但是我觉得要混淆还是很困难的!尽管Spring揭秘一书的作者都喜欢写上这一句。

    请不要混淆BeanFactory 和 FactoryBean。

    1、BeanFactory

    BeanFactory,以Factory结尾,表示它是一个工厂(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是工厂的顶层接口,也是IOC容器的核心接口,因此BeanFactory中定义了管理Bean的通用方法,如 getBeancontainsBean 等,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,所以Spring容器给出了很多种实现,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。

    1.1 BeanFactory 源码

    public interface BeanFactory {
    	//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
    	//如果需要得到工厂本身,需要转义
    	String FACTORY_BEAN_PREFIX = "&";
    
    	//根据bean的名字,获取在IOC容器中得到bean实例
    	Object getBean(String name) throws BeansException;
    
    	//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
    	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    
    	Object getBean(String name, Object... args) throws BeansException;
    
    	<T> T getBean(Class<T> requiredType) throws BeansException;
    
    	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    	//提供对bean的检索,看看是否在IOC容器有这个名字的bean
    	boolean containsBean(String name);
    
    	//根据bean名字得到bean实例,并同时判断这个bean是不是单例
    	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    
    	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    	//得到bean实例的Class类型
    	@Nullable
    	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    	//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
    	String[] getAliases(String name);
    }
    

    1.2、BeanFactory 使用场景

    1、从Ioc容器中获取Bean(byName or byType)
    2、检索Ioc容器中是否包含指定的Bean
    3、判断Bean是否为单例

    2、FactoryBean

    首先FactoryBean是一个Bean,但又不仅仅是一个Bean,这样听起来矛盾,但为啥又这样说呢?其实在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个FactoryBean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

    2.1、为什么会有FactoryBean?

    一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean。至于为什么会有FactoryBean?原因有两个:

    1、
    在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

    2、
    由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean接口,然后给出自己对象的实例化代码即可。

    2.2 、FactoryBean 源码

    public interface FactoryBean<T> {
    	//从工厂中获取bean【这个方法是FactoryBean的核心】
    	@Nullable
    	T getObject() throws Exception;
    	
    	//获取Bean工厂创建的对象的类型【注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型】
    	@Nullable
    	Class<?> getObjectType();
    	
    	//Bean工厂创建的对象是否是单例模式
    	default boolean isSingleton() {
    		return true;
    	}
    }
    

    方法介绍:
    1、
    getobject ()方法会返回该FactoryBean “生产” 的对象实例,我们需要实现该方法以给出自己的对象实例化逻辑;
    2、
    getobjectTYype ()方法仅返回getobject ()方法所返回的对象的类型,如果预先无法确定,则返回null; 特别注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型,也就是说ioc中有很多类型的bean,要找到这个bean就是通过getobjectTYype ()方法的返回值类型!好吧,我举个例子

    public class XXX implements FactoryBean {
        @Override
        public Object getObject() throws Exception {
            return new YYY;
        }       
        @Override
        public Class<?> getObjectType() {  //注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型
            return AAA.class;
        }
    }
    
    那么要想在ioc中找到XXX这个类的bean(实际上是YYY) ,在getbean的时候写法如下
    
    public class Demo1 {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = 
                    new AnnotationConfigApplicationContext(Appconfig.class);
                    
            annotationConfigApplicationContext.getBean( AAA.class ); // 【注意这里是AAA.class】
    
        }
    }
    

    3、
    isSingleton ()方法返回结果用于表明,工厂方法(getobject ())所“生产”的对象是否要以singleton形式存在于容器中。如果以singleton形式存在,则返回true,否则返回false;

    FactoryBean表现的是一个工厂的职责。 即一个Bean A如果实现了FactoryBean接口,那么A就变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()返回的对象,而不是A本身,如果要获取工厂A自身的实例,那么需要在名称前面加上'&'符号。 通俗点表达就是

    getObject(' name ')返回工厂中的实例
    getObject(' &name ')返回工厂本身的实例

    通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的 角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。

    2.3 、FactoryBean代码示例

    1、创建一个Appconfig类,扫描com.yichun下的所有子包

    @Configuration
    @ComponentScan("com.yichun")
    public class Appconfig {
    }
    

    2、创建一个StudentBean类并实现FactoryBean,并重写其两个方法

    @Component("studentBean")
    public class StudentBean implements FactoryBean {
        @Override
        public Object getObject() throws Exception {
            return new TeacherBean();
        }
       
        @Override
        public Class<?> getObjectType() {  //注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型
            return StudentBean.class;
        }
        //一个学生学习方法
        public void study(){
            System.out.println("学生学习。。。");
        }
    }
    

    3、再创建一个TeacherBean类

    public class TeacherBean {
        public void teacher(){
            System.out.println("老师教书。。。。");
        }
    }
    

    4、测试StudentBean类型

    public class Demo1 {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
            StudentBean studentBean = (StudentBean)annotationConfigApplicationContext.getBean("studentBean");
            studentBean.study();
        }
    }
    

    在这里插入图片描述
    加上“&”符号

    public class Demo1 {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
            //加上了“&”符号
            StudentBean studentBean = (StudentBean)annotationConfigApplicationContext.getBean("&studentBean");
            studentBean.study();
        }
    }
    

    在这里插入图片描述
    运行成功

    5、测试一下teacherBean类型

    public class Demo1 {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);    
            TeacherBean teacherBean = (TeacherBean) annotationConfigApplicationContext.getBean("studentBean");       
            teacherBean.teacher();
        }
    }
    

    运行成功
    在这里插入图片描述

    2.4 FactoryBean使用场景

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

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

    当然在spring中,Spring容器内部许多地方了使用FactoryBean。下面是一些比较常见的FactoryBean实现:

    JndiobjectFactoryBean
    LocalSessionFactoryBean
    SqlMapClientFactoryBean
    ProxyFactoryBean
    TransactionProxyFactoryBean

    使用场景二:
    Mybatis中的SqlSessionFactoryBean

    <bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="trade" />
            <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
            <property name="configLocation" value="classpath:mybatis-config.xml" />
            <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
        </bean>
    
    
    package org.mybatis.spring;
    
    public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
            ......
    }
    
    

    另外提一下,阿里开源的分布式服务框架 Dubbo中的Consumer 也使用到了FactoryBean,

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        ">
         
        <!-- 当前应用信息配置 -->
        <dubbo:application name="demo-consumer" />
       
       <!-- 暴露服务协议配置 -->
        <dubbo:protocol name="dubbo" port="20813" />    
    
        <!-- 暴露服务配置 -->
        <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.spring.api.DemoService"  />
         
    </beans>
    

    <dubbo:reference 对应的Bean是com.alibaba.dubbo.config.spring.ReferenceBean 类。

    使用场景三:
    Hibernate中的SessionFactoryBean。这里就不再概述。

    最后本文难免会有不正之处,欢迎指正评判!欢迎指正评判!欢迎指正评判!!!

    参考:
    《Spring揭秘》王福强
    https://zhuanlan.zhihu.com/p/87382038
    https://www.cnblogs.com/aspirant/p/9082858.html

  • 相关阅读:
    HAproxy 1.5 dev14 发布
    IBM/DW 使用 Java 测试网络连通性的几种方法
    Skype 4.1 Linux 发布,支持微软帐号登录
    Dorado 7.1.20 发布,Ajax的Web开发平台
    Aspose.Slides for Java 3.0 发布
    开发版本 Wine 1.5.18 发布
    BitNami Rubystack 开始支持 Ruby 2.0
    XWiki 4.3 正式版发布
    Silverlight实例教程 Out of Browser的Debug和Notifications窗口
    Silverlight实例教程 Out of Browser与Office的互操作
  • 原文地址:https://www.cnblogs.com/yichunguo/p/13922189.html
Copyright © 2011-2022 走看看