Spring进阶-BeanFactory

背景了解

说起Spring大家能想到的两点:IOC和AOP。基本上大家都知道这两点,但是事实上初学者并不是很明白这两个点真正的意思。

IOC

在学习BeanFactory之前我认为我们应该先理解了IOC本质的意思,然后再去学习BeanFactory相关的知识会有更好的理解。那么到底什么是IOC?
例如我现在在开发一个用户查询相关的模块。

public class UserService {
    private HikariConfig config = new HikariConfig();
    private DataSource dataSource = new HikariDataSource(config);

    public User getUser(String userName){
        try {
            Connection connection = dataSource.getConnection();
            PreparedStatement statement = connection.prepareStatement("select * from t_user where name = ?");
            statement.setString(1,userName);
            ResultSet resultSet = statement.executeQuery();
            .....
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

UserService中为了从数据库中查询用户信息,我需要自己创建DataSource,但是创建DataSource我又依赖HikariConfig,我又不得不创建一个HikariConfig。类似的这种情况在我们实际开发中很多而且有的更复杂。这样的情况会造成什么问题呢?

  • 实例化一个组件很难。在复杂的工程中,一个组件的创建需要依赖A、B、C等等。错综复杂的依赖关系处理起来很麻烦。
  • 组件共享问题。有些组件完全可以共享使用,如果每次都需要用到该组件时我们都创建新的,这个在程序效率上来说也不高。
  • 测试很麻烦。例如我再测试一个组件的部分功能时,我需要将这个组件的所有依赖都处理完成之后才能创建出待测试的组件。但实际上我只需要测试组件的一小部分功能且并没有依赖组件的内容,这会使我在做测试时很麻烦。

上面的痛点都是我们实际开发中的问题,而且这些痛点只是一部分并没有包括所有。那IOC能给我们解决什么问题呢?
IOC中文译名叫控制反转,这个名字咋一听感觉就是满头的问号。在维基百科中的解释如下:

控制反转(英语:Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

首先我们要明确一点,IOC是一种编程设计思想,它的作用是用来减低代码间的耦合的。对于控制反转的解释我个人理解是这样的,在不使用IOC的思想时我们创建一个组件它的控制权完全由我们自己掌握。简单的说就是new一个实例,实例依赖的组件我们自己提供,实例使用完成之后如何销毁由我们控制。而这里面组件的管理的控制权是完全在程序员自己手里。
而使用IOC时,实例的创建销毁以及依赖全部由IOC容器去处理,我们只用告诉IOC容器如何去创建销毁等等。这时候的控制权由程序员转换到IOC容器,也就是说控制反转了。

BeanFactory

前面说了这么一大堆关于IOC的相关概念,现在正式来介绍BeanFactory。那么BeanFactory是什么呢?从代码层面来说,它只是Spring框架中的一个接口,而从IOC层面来说它是一个最基础的IOC容器。

public interface BeanFactory {

 String FACTORY_BEAN_PREFIX = "&";

 Object getBean(String name) throws BeansException;

 <T> getBean(String name, Class<T> requiredType) throws BeansException;

 Object getBean(String name, Object... args) throws BeansException;

 <T> getBean(Class<T> requiredType) throws BeansException;

 <T> getBean(Class<T> requiredType, Object... args) throws BeansException;

 <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

 <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

 boolean containsBean(String name);

 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

 boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

 @Nullable
 Class<?> getType(String name) throws NoSuchBeanDefinitionException;

 @Nullable
 Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

 String[] getAliases(String name);

}

从上面的方法可以看出,BeanFactory接口主要提供了获取Bean,判断Bean是否在容器中存在这些基础的IOC容器相关方法。其中「getBeanProvider」方法是4.3中注入机制改进新增的方法,后面有机会讲到这里就不铺开讲了。

扩展以及实现

BeanFactory定义了最基础的IOC容器相关方法,很显然太过于简陋这些方法并不能满足我们实际的需求,所以基于BeanFactory有很多扩展,我们可以通过下图看到BeanFactory所有子类以及相关具体实现。

Spring进阶-BeanFactory
BeanFactory扩展

从上图可以看出,基于BeanFactory有很多扩展的子接口,同时还有很多实现类。其中我们可以发现基础的ApplicationContext就是BeanFactory的一个子接口,在学习BeanFactory这节内容时我们暂时不需要去关注ApplicationContext相关内容,后续我们再来进一步分许。

HierarchicalBeanFactory

从名字我们可以知道这是一个分层级的BeanFactory,它里面只提供了两个方法:

  //获取父BeanFactory
 @Nullable
 BeanFactory getParentBeanFactory();
  //返回当前BeanFactory是否含有指定Bean。该返回不会去父BeanFactory中查找
 boolean containsLocalBean(String name);

需要注意的是,BeanFactory中定义的getBean方法查找Bean时,如果在当前BeanFactory中查找不到是会去父容器中查找的,HierarchicalBeanFactory正好提供了一个获取父BeanFactory的方法。

  • 示例源码
public class HierarchicalBeanFactoryDemo {
    public static void main(String[] args) {
        //创建父容器
        BeanFactory parentFactory = new DefaultListableBeanFactory();
        //加载XML配置的BeanDefinition
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((DefaultListableBeanFactory)parentFactory);
        reader.loadBeanDefinitions("spring-bean-factory-1.xml");
        //创建子容器
        HierarchicalBeanFactory childFactory = new DefaultListableBeanFactory(parentFactory);
        BeanFactory pbf = childFactory.getParentBeanFactory();
        //判断获取的父容器与parentFactory是否相等
        System.out.printf("pbf == parentFactory : %bn",pbf == parentFactory);
        //判断当前容器是否存在tom
        boolean existTom = childFactory.containsLocalBean("tom");
        System.out.printf("tom exist tom ? %bn",existTom);
        //从父容器中获取Bean
        Person tom = childFactory.getBean("tom", Person.class);
        System.out.println(tom);
    }
}
  • spring-bean-factory-1.xml配置文件
    <bean id="tom" class="com.buydeem.share.beanfactory.Person">
        <property name="name" value="tom"/>
        <property name="age" value="18"/>
    </bean>
  • 打印结果
pbf == parentFactory : true
tom exist tom ? false
Person(name=tom, age=18)

ListableBeanFactory

public interface ListableBeanFactory extends BeanFactory {

 //检查BeanFactory是否包含给出名字的BeanDefinition。
 //该方法不会去父BeanFactory中查找,同时忽略没有通过BeanDefinition方式注册的其他任何单例Bean
 boolean containsBeanDefinition(String beanName);

 //返回BeanDefinition数量。限制同containsBeanDefinition
 int getBeanDefinitionCount();
 
 //返回BeanDefinition集合,限制同containsBeanDefinition
 String[] getBeanDefinitionNames();

 //返回匹配给定类型(包括子类)的所有bean的名字,如果是普通bean,则是bean定义的名字,如果是FactoryBean,则是其getObjectType方法返回的对象的名字
 String[] getBeanNamesForType(ResolvableType type);

 //includeNonSingletons:false表示只查单例Bean。true表示包含prototype或者其它Scope的Bean
 //allowEagerInit:主要是解决FactoryBean的情况。若为false,只会去检查FactoryBean本身,若为true,FactoryBean本身和它的产生的对象都会被检查匹配
 String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit);
 
 String[] getBeanNamesForType(@Nullable Class<?> type);

 String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);

 //返回匹配给定类型(包含子类)的实例,可能是通过bean定义创建,也可以是FactoryBean时其getObjectType返回(注意:此处返回的是实例了,不再是Bean定义了)
 //此方法一调用,即使有些Bean只是Bean定义的话,也会被立马初始化的
 <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;

 <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
   throws BeansException
;
 
 //把所有标注有指定注解的Bean的定义信息的BeanName返回
 String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
 
 //返回实例
 Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;

 //查找指定bean的指定类型的注解
 //如果本类没找到,还会去它的接口、它的父类里面找
 @Nullable
 <A extends Annotation> findAnnotationOnBean(String beanName, Class<A> annotationType)
   throws NoSuchBeanDefinitionException
;

}

ListableBeanFactory接口定义如上所示,从定义可以知道它与BeanFactory很大的不同在于它可以枚举出所有的实例,这个是BeanFactory不具有的功能。

  • 示例代码
public class ListableBeanFactoryDemo {
    public static void main(String[] args) {
        //设置父IOC容器
        DefaultListableBeanFactory parentFactory = new DefaultListableBeanFactory();
        //构建BeanDefinition并注册
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
                .addPropertyValue("name", "rose")
                .addPropertyValue("age", 19)
                .getBeanDefinition()
;
        parentFactory.registerBeanDefinition("rose",beanDefinition);
        //创建IOC容器
        ListableBeanFactory factory = new DefaultListableBeanFactory(parentFactory);
        //加载XML配置的BeanDefinition
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((DefaultListableBeanFactory)factory);
        reader.loadBeanDefinitions("spring-bean-factory-2.xml");

        String[] beanDefinitionNames = factory.getBeanDefinitionNames();
        System.out.println("beanDefinitionNames = " + Arrays.toString(beanDefinitionNames));
        String[] beanNames = factory.getBeanNamesForType(Person.class);
        System.out.println("beanNames = " + Arrays.toString(beanNames));

        Person rose = factory.getBean("rose",Person.class);
        System.out.println(rose);
    }
}
  • 配置文件spring-bean-factory-2.xml
    <bean id="tom" class="com.buydeem.share.beanfactory.Person">
        <property name="name" value="tom"/>
        <property name="age" value="18"/>
    </bean>

    <bean id="jack" class="com.buydeem.share.beanfactory.Person">
        <property name="name" value="jack"/>
        <property name="age" value="18"/>
    </bean>
  • 运行结果
beanDefinitionNames = [tom, jack]
beanNames = [tom, jack]
Person(name=rose, age=19)

AutowireCapableBeanFactory

从名称可以知道它是一个自动装配BeanFactory。对于想要拥有自动装配能力,并且想把这种能力暴露给外部应用的BeanFactory需要实现该接口。

public interface AutowireCapableBeanFactory extends BeanFactory {

 //不自动注入
 int AUTOWIRE_NO = 0;

 //根据名称注入
 int AUTOWIRE_BY_NAME = 1;

 //根据类型注入
 int AUTOWIRE_BY_TYPE = 2;
 
 //根据构造器注入
 int AUTOWIRE_CONSTRUCTOR = 3;
 
 //自动识别一种方式,已弃用
 @Deprecated
 int AUTOWIRE_AUTODETECT = 4;

 String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";

 //创建一个给定的Class实例。它会执行Bean的完整初始化,其中包括BeanPostProcessor。
 //此方法并不会进行自动装配,如果需要使用下面的重载方法
 <T> createBean(Class<T> beanClass) throws BeansException;
 
 //通过after-instantiation和post-processing对Bean进行装配值
 //此方法主要用于处理Bean中带有注解的字段和方法
 //这个方法与传统的by-name和by-type的自动装配是不同的,如果需要这些功能,应该调用autowireBeanProperties方法
 void autowireBean(Object existingBean) throws BeansException;

 //配置给定的原始Bean。自动装配Bean的属性以及属性值,应用factory回调以及BeanPostProcessor
 Object configureBean(Object existingBean, String beanName) throws BeansException;
 
 //见createBean方法
 Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

 //通过指定的自动装配策略来初始化一个Bean。注意它会创建一个新的Bean
 Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;

 //通过指定的自动装配方式来对给定的已经存在的Bean进行自动装配,
 //不过会调用指定Bean注册的BeanPostProcessors等回调函数来初始化Bean
 void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
   throws BeansException
;

 //把BeanDefinition中定义的一些信息赋值到已存在的Bean里面
 void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;

 //实例化一个Bean
 Object initializeBean(Object existingBean, String beanName) throws BeansException;

 //调用参数中指定Bean的postProcessBeforeInitialization/postProcessorsAfterInitialization方法。
 Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
   throws BeansException
;

 Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
   throws BeansException
;

 //销毁参数中指定的Bean
 void destroyBean(Object existingBean);


 //查找唯一符合指定类的实例,如果有,则返回实例的名字和实例本身
 //底层实现依赖于BeanFactory中的getBean(Class)方法
 <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;

 //通过给定的Bean名称解析实例,提供暴露目标工厂方法的依赖项描述符
 Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;

 //解析出在Factory中与指定Bean有指定依赖关系的Bean
 @Nullable
 Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;

 @Nullable
 Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
   @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)
 throws BeansException
;

}

从方法定义可以看出,该接口定义了自动装配有关的方法,执行BeanPostProcessor方法还有解析依赖的方法。总台来说它提供了两大能力:对已经实例化的对象装配属性,实例化一个类型时进行自动装配。

为容器外的实例提供依赖注入

在我们通常的认知里面,在Spring中要想有依赖注入的功能,那么我们的Bean是需要交给Springl来管理的。但是事实真的是这样吗?直接看示例

public class AutowireCapableBeanFactoryDemo1 {

    public static void main(String[] args) {
        //创建BeanFactory。DefaultListableBeanFactory实现了AutowireCapableBeanFactory接口
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //创建BeanDefinition
        AbstractBeanDefinition userDaoBd = BeanDefinitionBuilder.genericBeanDefinition(UserDao.class).getBeanDefinition();
        factory.registerBeanDefinition("userDao",userDaoBd);
        //从容器中获取UserDao实例
        UserDao userDao = factory.getBean(UserDao.class);
        System.out.println("userDao = " + userDao);
        //创建UserService
        UserService userService = (UserService) factory.createBean(UserService.classAutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,false);
        //判断两个UserDao是否相同
        System.out.println("userService.getUserDao() == userDao : " + (userService.getUserDao() == userDao));
        //判断BeanFactory中是否存在UserService实例
        UserService iocUserService = factory.getBean(UserService.class);
    }
}

@Data
class UserService{

    private UserDao userDao;
}

class UserDao{
    public String getUserName(){
        return "test get userName";
    }
}

我们定义了两个类UserDaoUserService,其中我们将UserDao交给Spring来管理而UserService则我们自己管理。然后再看是不是能将UserDao成功注入到UserService。代码运行结果如下:

userDao = com.buydeem.share.beanfactory.UserDao@3632be31
userService.getUserDao() == userDao : true
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.buydeem.share.beanfactory.UserService' available

从运行结果可以看出,UserService中的UserDao实例与Spring容器中的实例是同一个对象。结果最后的异常也说明了UserService实例确实没有交给Spring来管理,但是它同样可以通过byType的方式进行依赖注入。

自动注入

这个算是AutowireCapableBeanFactory提供的另外一个强大功能,不过需要注意的一点是我们编写代码时使用的@Autowired并不是自动注入,后面有机会将会讲到。那么什么是自动注入呢?

  • xml配置
    <bean id="personDao1" class="com.buydeem.share.beanfactory.PersonDao1"/>
    <bean id="personDao2" class="com.buydeem.share.beanfactory.PersonDao2"/>
    <bean id="perService" class="com.buydeem.share.beanfactory.PersonService"/>
  • 示例代码
public class AutowireCapableBeanFactoryDemo2 {
    public static void main(String[] args) {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions("spring-bean-factory-3.xml");

        PersonService personService = factory.getBean(PersonService.class);
        System.out.println("personService.getPersonDao1() = " + personService.getPersonDao1());
        System.out.println("personService.getDao2() = " + personService.getDao2());

    }
}

@Data
class PersonService{
    private PersonDao1 personDao1;
    private PersonDao2 dao2;
}

class PersonDao1{
}

class PersonDao2{
}

现在的问题是personDao1personDao2能不能注入到personService?运行代码从结果可知personDao1personDao2是不能注入到personService。这个时候自动注入就起作用了,Spring的自动注入可以通过名称、类型、构造方法方式来进行自动注入,这里我们可以通过类型来完成自动注入。怎么做呢?答案很简单,只需要在配置中设置autowire="byType"就可以完成按照类型的自动注入了。

ConfigurableBeanFactory

前面说的都是BeanFactory的直接继承子接口,而ConfigurableBeanFactory则是简介的继承了BeanFactory的子接口。

Spring进阶-BeanFactory
ConfigurableBeanFactory接口继承关系

从UML图中可以看出,该接口是一个组合接口。它同时继承了HierarchicalBeanFactorySingletonBeanRegistry接口这也代表了它具有了这两个接口的功能。该接口主要提供了配置BeanFactory的相关方法,在大多数的业务中我们不需要使用到这部分功能,一般在扩展框架是会用到。它的内部方法主要如下:

  • 设置父容器。在HierarchicalBeanFactory中提供了获取父容器的方法,在该接口中提供了配置父容器的方法。
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
  • 类加载相关配置
void setBeanClassLoader(@Nullable ClassLoader beanClassLoader);

ClassLoader getBeanClassLoader();

void setTempClassLoader(@Nullable ClassLoader tempClassLoader);

ClassLoader getTempClassLoader();
  • Bean元数据的缓存。
void setCacheBeanMetadata(boolean cacheBeanMetadata);

boolean isCacheBeanMetadata();
  • Bean定义值中的表达式解析器
void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver);

BeanExpressionResolver getBeanExpressionResolver();
  • 属性转换器
void setConversionService(@Nullable ConversionService conversionService);

ConversionService getConversionService();
  • 属性编辑器
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);

void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);

void copyRegisteredEditorsTo(PropertyEditorRegistry registry);
  • 类型转换器
void setTypeConverter(TypeConverter typeConverter);

TypeConverter getTypeConverter();
  • 为嵌入的值(如注释属性)添加字符串解析器
void addEmbeddedValueResolver(StringValueResolver valueResolver);

boolean hasEmbeddedValueResolver();

String resolveEmbeddedValue(String value);
  • BeanPostProcessor
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

int getBeanPostProcessorCount();
  • 作用域
void registerScope(String scopeName, Scope scope);

String[] getRegisteredScopeNames();

Scope getRegisteredScope(String scopeName);
  • 提供与此工厂相关的安全访问控制上下文
AccessControlContext getAccessControlContext();
  • 复制ConfigurableBeanFactory
void copyConfigurationFrom(ConfigurableBeanFactory otherFactory);

鉴于该接口的方法太多了,这里就不一一分析了。不过从这些方法可以发现,该接口提供的方法基本上都是与配置BeanFactory相关。例如在对Bean进行扩展时需要用到了BeanPostProcessor,我们就是通过ConfigurableBeanFactory将其注册到BeanFactory中的,关于这点我们后面会细讲。

ConfigurableListableBeanFactory

该接口继承了ListableBeanFactory,AutowireCapableBeanFactory,ConfigurableBeanFactory三个接口。

Spring进阶-BeanFactory
ConfigurableListableBeanFactory 结构图

该接口主要提供了忽律自动装配、依赖、BeanDefinition、清除元数据和lazy-init相关方法。

DefaultListableBeanFactory

上面介绍了BeanFactory的很多子接口,而DefaultListableBeanFactory中基本上实现了IOC的大多数功能,在后面的学习中我们会经常的使用这个类。同时在分析源码时,我建议从这个类开始,这样思路会清洗很多。

Spring进阶-BeanFactory
DefaultListableBeanFactory 结构图

总结

在读完上面的内容之后,不出意外你应该还是感觉没有什么收获,读完之后感觉整篇文章讲的全是接口和方法。其实个人感觉Java中很多框架在分析源码之前最好先不要直接去看实现,而应该先去看接口。为什么呢?在Java中接口定义的往往就是规则,了解接口提供的功能,分析接口之间的关系能让你首先有个大致的轮廓。如果先不去了解具体功能,立马就去看实现个人感觉大多数时候是能看代码在做什么,但是不知道为什么要这么做,这么做的作用和意义是什么。

这里我们先大致了解一下BeanFactory的一些整体结构,这样在后面的学习中有个整体观念会更利于后续的学习。认真看完全部文章我希望你能回答下面几个问题:

  • 什么是IOC?为什么要使用IOC?IOC是一种编程思想,使用IOC能大大的减少组件之间的耦合,最明显的就是能大大的提高我们的开发效率。
  • BeanFactory是什么?简单的说BeanFactory它是一个最基础的IOC容器,它提供了最基础的IOC容器的相关方法。
  • BeanFactory为什么有这么子类?Spring并没有将IOC容器的方法全部定义在一个接口中,而是根据职责单一原则划分很多不同的子接口,然后通过接口组合的方式来丰富IOC容器。
  • BeanFactory接口及其子类大概提供了哪些方法?

欢迎关注本公众号,后续还会不定时的更新Spring进阶的相关内容。


原文始发于微信公众号(一只菜鸟程序员):Spring进阶-BeanFactory

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/73011.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!