zoukankan      html  css  js  c++  java
  • Spring源码解析之BeanDefinition(一)

    BeanDefinition

    在spring中,BeanDefinition是十分重要的概念,可以说绝大部分的bean,都是从BeanDefinition产生的。那么BeanDefinition到底是什么呢?在回答这个问题前,我们先来思考Java是如何产生一个对象的?要产生一个Java对象,最基础是是要有一个class对象,好让Java知道如何描述这个对象,这个对象有多少个字段,这个对象有什么行为,需要为这个对象开辟多大的空间。同理,spring中的bean,我们可以选择是单例还是原型?是否懒加载?实现不同的回调方法让bean在不同的生命周期里进行回调……等等,描述一个bean的性质,同样需要一个对象,也就是BeanDefinition。

    我们先来看下BeanDefinition接口的概览,从下面的图可以知道,BeanDefinition继承了AttributeAccessor和BeanMetadataElement两个接口,并且BeanDefinition有众多实现,比如:AnnotatedGenericBeanDefinition、ChildBeanDefinition、ConfigurationClassBeanDefinition、GenericBeanDefinition、RootBeanDefinition、ScannedGenericBeanDefinition……,这些不同的BeanDefinition分贝在不同的场景下使用。

    虽然上面存在众多接口和实现,但笔者会带大家逐一认识这些接口、以及接口的作用。首先还是从我们最重要的BeanDefinition接口讲起:

    public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    	……
    	void setParentName(String parentName);
    
    	String getParentName();
    
    	void setBeanClassName(String beanClassName);
    
    	String getBeanClassName();
    
    	void setScope(String scope);
    
    	String getScope();
    
    	void setLazyInit(boolean lazyInit);
    
    	boolean isLazyInit();
    
    	void setDependsOn(String... dependsOn);
    
    	String[] getDependsOn();
    
    	void setAutowireCandidate(boolean autowireCandidate);
    
    	boolean isAutowireCandidate();
    
    	void setPrimary(boolean primary);
    
    	boolean isPrimary();
    
    	void setFactoryBeanName(String factoryBeanName);
    
    	String getFactoryBeanName();
    
    	void setFactoryMethodName(String factoryMethodName);
    
    	String getFactoryMethodName();
    
    	ConstructorArgumentValues getConstructorArgumentValues();
    
    	default boolean hasConstructorArgumentValues() {
    		return !getConstructorArgumentValues().isEmpty();
    	}
    
    	MutablePropertyValues getPropertyValues();
    
    	default boolean hasPropertyValues() {
    		return !getPropertyValues().isEmpty();
    	}
    
    	void setInitMethodName(String initMethodName);
    
    	String getInitMethodName();
    
    	void setDestroyMethodName(String destroyMethodName);
    
    	String getDestroyMethodName();
    	
    	void setDescription(String description);
    
    	String getDescription();
    	
    	boolean isSingleton();
    
    	boolean isPrototype();
    
    	boolean isAbstract();
    	……
    }
    

        

    上面是BeanDefinition的主要方法,我们先挑几个易于理解的说明:

    1. setBeanClassName(String beanClassName)和getBeanClassName()用于设置和获取一个class对象,spring可以根据BeanDefinition所指向的class对象,生成一个bean。那么,是不是说所有的BeanDefinition必须要设置一个class呢?毕竟spring是根据BeanDefinition所指向的class来生成bean的,答案是:否,BeanDefinition不一定要指定一个class。至于是为什么后面我们再详细解释。(记得补解释)
    2. setScope(String scope)和getScope()用于获取一个bean的作用域,是否是单例(singleton)还是原型(prototype)。
    3. setLazyInit(boolean lazyInit)和isLazyInit()用于设置和判断一个bean是否是懒加载。
    4. setDependsOn(String... dependsOn)和getDependsOn()用于设置和获取bean在初始化前的依赖项。
    5. setDescription(String description)和getDescription()用来设置和描述这个bean,在程序运行时并不会有太大的影响。
    6. isSingleton()判断这个bean是否是单例。
    7. isPrototype()判断这个bean是否是原型。

    AutowireCandidate和Primary

    setAutowireCandidate(boolean autowireCandidate)和isAutowireCandidate()用于设置和判断bean是否自动参与候选,在AbstractBeanDefinition抽象类中默认为true。来看下面这段代码,A1Service和A2Service分别实现了AService,然后我们在TestAService注入AService。

    package org.example.service;
    
    public interface AService {
    }
    
    package org.example.service;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class A1Service implements AService {
    }
    
    package org.example.service;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class A2Service implements AService {
    }
    
    package org.example.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TestAService {
        @Autowired
        private AService service;
    
        public AService getService() {
            return service;
        }
    }
    

      

    正常来说spring容器是无法启动的,因为在注入TestAService的AService时spring会检查到存在两个候选bean,从而报错。但我们可以通过修改A1Service或A2Service对应BeanDefinition的autowireCandidate属性,让容器只存在一个实现了AService候选的bean,从而完成注入。在修改BeanDefinition的属性之前,这里要介绍一个接口BeanFactoryPostProcessor:

    public interface BeanFactoryPostProcessor {
    	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
    }
    

      

    我们一般是通过@ComponentScan注解或XML的<context:component-scan base-package="..."/>标签来配置bean的扫描路径,在spring生成bean的时候,spring会为扫描路径下的每个有需要生成bean类生成对应的BeanDefinition,然后根据这些BeanDefinition生成对应的bean。在根据BeanDefinition生成bean的中间,会执行我们所实现的BeanFactoryPostProcessor 接口,所以我们可以在这个接口修改BeanDefinition的autowireCandidate属性。我们调用beanFactory的getBeanDefinition(String beanName)方法获取A1Service对应的BeanDefinition,然后修改autowireCandidate为false,不参与bean的候选。

    package org.example.service;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.stereotype.Component;
    
    @Component
    public class AServiceBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition("a1Service");
            beanDefinition.setAutowireCandidate(false);
        }
    }
    

      

    测试用例:

        @Test
        public void test01() {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);
            TestAService test1Service = ac.getBean(TestAService.class);
            System.out.println(test1Service.getService().getClass());
        }
    

      

    运行结果:

    class org.example.service.A2Service
    

      

    如果不设置两个BeanDefinition的autowireCandidate属性,我们也可以设置primary属性,setPrimary(boolean primary)和isPrimary()是用来设置和判断主要候选bean,在AbstractBeanDefinition类的实现中primary属性默认为false,如果存在多个候选bean的时候,spring会选择primary为true的bean进行注入。大家可以修改AServiceBeanFactoryPostProcessor的代码将A1Service对应的BeanDefinition的primary属性设置为true,这时候TestAService的service属性会以A1Service的bean进行注入,就不再是之前的A2Service了。

    autowireCandidate和primary在XML中如下配置:

        <bean id="..." class="..." autowire-candidate="true"/>
        <bean id="..." class="..." primary="true"/>

    注:这里不能将A1Service和A2Service对应BeanDefinition的autowireCandidate属性同时改为false,也不能将primary同时改为true,否则spring容器找不到可参与候选的bean或者找到多个主要参与候选的bean,都会报错。

    BeanDefinition的继承

    我们之前说过,spring容器的bean都必须根据一个BeanDefinition来生成,但BeanDefinition又不一定要指定class,这是为什么呢?因为BeanDefinition可以继承,每个BeanDefinition都有属于自己的beanName,而子BeanDefinition通过setParentName(String parentName)和getParentName()来获取父BeanDefinition的beanName。来看下面这段配置,我们配置了两个bean,abPerson的abstract属性为true,代表这个bean不需要实例化,作用域scope是原型,同时bean的内部还指定了一个属性的值,age是18。第二个bean我们指定了Person类,指定了它的父BeanDefinition为abPerson,设置了一个属性name为sam。于是abPerson的age属性,作用域都得以继承。

        <bean id="abPerson" abstract="true" scope="prototype">
            <property name="age" value="18"></property>
        </bean>
        <bean id="sam" class="org.example.beans.Person" parent="abPerson">
            <property name="name" value="Sam"></property>
        </bean>

    注:abPerson不指定abstract属性也不会报错,但最好还是指定下,告诉spring这个不用根据这个BeanDefinition实例化bean。

    Person.java

    package org.example.beans;
    
    public class Person {
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

            

    测试用例:

        @Test
        public void test02() {
            ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
            Person sam = cc.getBean("sam", Person.class);
            System.out.println(sam);
            System.out.println(sam.getName());
            System.out.println(sam.getAge());
            System.out.println("_____________");
            sam = cc.getBean("sam", Person.class);
            System.out.println(sam);
            System.out.println(sam.getName());
            System.out.println(sam.getAge());
        }
    

      

    运行结果:

    org.example.beans.Person@77f99a05
    Sam
    18
    _____________
    org.example.beans.Person@63440df3
    Sam
    18
    

       

    可以看到,两次获取sam这个bean内存地址都不一样,age打印都是18,表明abPerson的作用域和age属性也得以继承。

    下面,我们在测试用例里获取sam和abPerson对应的BeanDefinition,在获取abPerson的BeanDefinition时,我们通过samBeanDefinition的parentName来获取,并且我们打印这两个BeanDefinition的class:

        @Test
        public void test03() {
            ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("spring.xml");
            BeanDefinition samBd = cc.getBeanFactory().getBeanDefinition("sam");
            BeanDefinition parentBd = cc.getBeanFactory().getBeanDefinition(samBd.getParentName());
            System.out.println("samBd parentName:" + samBd.getParentName());
            System.out.println("samBd class:" + samBd.getClass());
            System.out.println("parentBd class:" + parentBd.getClass());
        }
    

      

    运行结果:

    samBd parentName:abPerson
    samBd class:class org.springframework.beans.factory.support.GenericBeanDefinition
    parentBd class:class org.springframework.beans.factory.support.GenericBeanDefinition
    

      

    可以看到samBd正确打印了parentName,且通过XML配置的bean,其BeanDefinition的实现为GenericBeanDefinition。事实上,标记了@Component、@Configuration的类都会生成不同实现的BeanDefinition,后续还会介绍。

    我们注意到,BeanDefinition有个 getPropertyValues()方法,这个方法是返回我们在<bean/>标签里配置的<property/>属性,在samBd的propertyValues属性中,只有一个元素,即为<name:Sam>,而我们在abPerson配置的<age:18>并不存在,而我们获取sam这个bean的age是有值的,之所以能完成这样的功能是因为spring有一个合并BeanDefinition的概念,合并BeanDefinition就是逐级查找父BeanDefinition的属性(property、scope、lazy)进行合并,然后spring根据这个合并完的BeanDefinition生成我们所设定的bean。

  • 相关阅读:
    Java ,python面向对象的继承及其区别
    谈谈我理解的敏捷开发
    Centos7 升级python3,解决升级后不兼容问题
    Django + Uwsgi + Nginx 的生产环境部署
    理解Python协程:从yield/send到yield from再到async/await
    【转】Python 数据库连接池
    为何GET只发一次TCP连接,POST发两次TCP连接
    Python简单密码加密程序,加盐(salt)md5
    spring学习(三) ———— spring事务操作
    spring学习(二) ———— AOP之AspectJ框架的使用
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/13837261.html
Copyright © 2011-2022 走看看