相关背景及资源:html
曹工说Spring Boot源码系列开讲了(1)-- Bean Definition究竟是什么,附spring思惟导图分享java
工程结构图:web
我这里,先把org.springframework.beans.factory.config.BeanDefinition
接口的方法再简单列一下:spring
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // scope:单例 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; // scope:prototype String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // 角色:属于应用程序的bean int ROLE_APPLICATION = 0; // 角色:支持?不甚了解,先跳过 int ROLE_SUPPORT = 1; // 角色:属于框架自身的bean int ROLE_INFRASTRUCTURE = 2; // parent bean的名字 String getParentName(); void setParentName(String parentName); // 核心属性,此为bean的class名称 String getBeanClassName(); void setBeanClassName(String beanClassName); // 核心属性,本属性获取工厂bean的名称,getFactoryMethodName获取工厂方法的名称,配合使用,生成 // 本bean String getFactoryBeanName(); void setFactoryBeanName(String factoryBeanName); String getFactoryMethodName(); void setFactoryMethodName(String factoryMethodName); //scope,bean是单例的,仍是每次new一个(prototype),就靠它了 String getScope(); void setScope(String scope); // 懒bean?默认状况下,都是容器启动时,初始化;若是设置了这个值,容器启动时不会初始化,首次getBean // 时才初始化 boolean isLazyInit(); void setLazyInit(boolean lazyInit); // 在本bean初始化以前,须要先初始化的bean;注意,这里不是说本bean依赖的其余须要注入的bean String[] getDependsOn(); void setDependsOn(String[] dependsOn); // 是否够资格做为自动注入的候选bean。。。若是这里返回false,那就连自动注入的资格都没得 boolean isAutowireCandidate(); void setAutowireCandidate(boolean autowireCandidate); // 看成为依赖,要注入给某个bean时,当有多个候选bean时,本bean是否为头号选手 boolean isPrimary(); void setPrimary(boolean primary); // 经过xml <bean>方式定义bean时,经过<constructor-arg>来定义构造器的参数,这里即:获取构造器参数 ConstructorArgumentValues getConstructorArgumentValues(); // 经过xml <bean>方式定义bean时,经过 <property name="testService" ref="testService"/> 这种方 式来注入依赖,这里即:获取property注入的参数值 MutablePropertyValues getPropertyValues(); // 是否单例 boolean isSingleton(); // 是否prototype boolean isPrototype(); // 是否为抽象的,还记得<bean>方式定义的时候,能够这样指定吗?<bean id="testByPropertyController" class="org.springframework.simple.TestByPropertyController" abstract="true"> boolean isAbstract(); // 获取角色 int getRole(); // 获取描述 String getDescription(); String getResourceDescription(); // 未知。。。 BeanDefinition getOriginatingBeanDefinition(); }
虽然这个接口里没这个东西,但这个我要重点说下,默认规则是:beanClassName按驼峰转换后的名字。api
这里面有个重点是,org.springframework.beans.factory.support.DefaultListableBeanFactory
中,采用了下面的字段来存bean和对应的BeanDefinition。app
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
这里说了,key是beanName。框架
那你们想过没,我若是同一个上下文中,有两个beanName相同的BeanDefinition会怎样呢?函数
以前spring cloud项目集成feign时,咱们的代码是下面这样的,即假设生产者提供了10个服务,分属不一样的模块:spring-boot
@FeignClient(name = "SCM",path = "scm",configuration = { FeignConfig.class }) public interface ScmCenterService extends ScmFeignCenterService { }
@FeignClient(name = "SCM",path = "scm",configuration = { FeignConfig.class }) public interface ScmClientConfigService extends ScmFeignClientConfigService { }
咱们这里,就是按照不一样模块,在多个接口里来继承feign api。
结果呢,启动报错了,就是由于beanName重复了,具体能够参考下面的连接:
最终解决这个问题,就是要加个配置,
spring: main: allow-bean-definition-overriding: true
这个配置,在spring以前的版本里,默认是true,结果在spring boot里,默认改成false了。
我这边经过下面的代码测试了一下:
当这个配置为true时
public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:application-context.xml"},false); // 这里设为true,不设也能够,默认就是true context.setAllowBeanDefinitionOverriding(true); context.refresh(); DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(String.class); beanFactory.registerBeanDefinition("testService", beanDefinition);
console中有以下提示:
信息: Overriding bean definition for bean 'testService': replacing [Generic bean: class [org.springframework.simple.TestService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [application-context.xml]] with [Generic bean: class [java.lang.String]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]
当这个配置为false时
context.setAllowBeanDefinitionOverriding(false);
会直接报错:
Invalid bean definition with name 'testService' defined in null: Cannot register bean definition [Generic bean: class [java.lang.String]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'testService':
//这里说,早已存在xxx
There is already [Generic bean: class [org.springframework.simple.TestService]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [application-context.xml]] bound.
测试代码在:
这个很少说了,默认为singleton
,在容器内只会有一个bean。prototype,每次getBean的时候都会new一个对象,这个通常不会在启动时去初始化,若是写的有问题的话,启动时不报错,runtime的时候报运行时异常。
其余几个scope,web相关的,先很少说。
指定parentBean的名称,之前xml的时候可能会用,如今注解驱动了,基本不多用了。
核心属性,bean的class类型,这里说的是实际类型,而通常不是接口的名称。好比,咱们的注解通常是加在class上的,而不是加在接口上,对吧;即便加在接口上,那确定也是动态代理技术,对吧,毕竟,bean是要以这个class的元数据来进行建立(通常经过反射)
若是本bean是经过其余工厂bean来建立,则这两个字段为对应的工厂bean的名称,和对应的工厂方法的名称
是否延迟初始化,取值为true、false、default。
Indicates whether or not this bean is to be lazily initialized. If false, it will be instantiated on startup by bean factories that perform eager initialization of singletons. The default is "false".
简单说:若是设了这个为true,则启动时不初始化;不然在启动时进行初始化,这也是spring官方推荐的,能够尽早发现问题。
经过这个属性设置的bean,会保证先于本bean被初始化
The names of the beans that this bean depends on being initialized. The bean factory will guarantee that these beans get initialized before this bean.
这个是个boolean值,表示是否能够被其余的bean使用@autowired的方式来注入。若是设置为false的话,那完了,没资格被别人注入。
表示当有多个候选bean知足@autowired要求时,其中primary被设置为true的,会被注入;不然会报二义性错误,即:程序期待注入一个,却发现了不少个。
构造函数属性值。我测试发现,经过下面的方式,这个字段是用不上的:
public class TestController { TestService testService; @Autowired public TestController(TestService testService) { this.testService = testService; } }
这个字段,何时有值呢,当采用下面的方式的时候,就会用这种:
public class TestController { TestService testService; public TestController(TestService testService) { this.testService = testService; } }
<bean name="testService" class="org.springframework.simple.TestService" /> <bean id="testController" class="org.springframework.simple.TestController"> <constructor-arg ref="testService"/> </bean>
演示图以下:
property方式注入时的属性值。在如下方式时,生效:
public class TestByPropertyController { TestService testService; //注意,这里是set方法注入 public void setTestService(TestService testService) { this.testService = testService; } }
<bean name="testService" class="org.springframework.simple.TestService" /> <bean id="testByPropertyController" class="org.springframework.simple.TestByPropertyController" > <property name="testService" ref="testService"/> </bean>
演示图以下:
今天内容大概到这里,有问题请留言