写在前面的话

相关背景及资源:

曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

工程代码地址 思维导图地址

工程结构图:

正文

我这里,先把org.springframework.beans.factory.config.BeanDefinition接口的方法再简单列一下:

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();
}

beanName

虽然这个接口里没这个东西,但这个我要重点说下,默认规则是:beanClassName按驼峰转换后的名字。

这里面有个重点是,org.springframework.beans.factory.support.DefaultListableBeanFactory中,采用了下面的字段来存bean和对应的BeanDefinition。

/** 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个服务,分属不同的模块:

@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重复了,具体可以参考下面的链接:

【Feign】@FeignClient相同名字错误 The bean 'xxx.FeignClientSpecification', defined in null, could not be registered

最终解决这个问题,就是要加个配置,

spring:
main:
allow-bean-definition-overriding: true

这个配置,在spring之前的版本里,默认是true,结果在spring boot里,默认改为false了。

我这边通过下面的代码测试了一下:

  1. 当这个配置为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]

  2. 当这个配置为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.

    测试代码在:

    https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-xml-demo

scope

这个不多说了,默认为singleton,在容器内只会有一个bean。prototype,每次getBean的时候都会new一个对象,这个一般不会在启动时去初始化,如果写的有问题的话,启动时不报错,runtime的时候报运行时异常。

其他几个scope,web相关的,先不多说。

parentName

指定parentBean的名称,以前xml的时候可能会用,现在注解驱动了,基本很少用了。

beanClassName

核心属性,bean的class类型,这里说的是实际类型,而一般不是接口的名称。比如,我们的注解一般是加在class上的,而不是加在接口上,对吧;即使加在接口上,那肯定也是动态代理技术,对吧,毕竟,bean是要以这个class的元数据来进行创建(一般通过反射)

factoryBeanName、factoryMethodName

如果本bean是通过其他工厂bean来创建,则这两个字段为对应的工厂bean的名称,和对应的工厂方法的名称

lazyInit

是否延迟初始化,取值为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官方推荐的,可以尽早发现问题。

dependsOn

通过这个属性设置的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.

autowireCandidate,布尔

这个是个boolean值,表示是否可以被其他的bean使用@autowired的方式来注入。如果设置为false的话,那完了,没资格被别人注入。

primary,布尔

表示当有多个候选bean满足@autowired要求时,其中primary被设置为true的,会被注入;否则会报二义性错误,即:程序期待注入一个,却发现了很多个。

constructorArgumentValues

构造函数属性值。我测试发现,通过下面的方式,这个字段是用不上的:

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>

演示图如下:

propertyValues

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>

演示图如下:

总结

今天内容大概到这里,有问题请留言

曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解的更多相关文章

  1. 曹工说Spring Boot源码(14)-- AspectJ的Load-Time-Weaving的两种实现方式细细讲解,以及怎么和Spring Instrumentation集成

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  2. 曹工说Spring Boot源码系列开讲了(1)-- Bean Definition到底是什么,附spring思维导图分享

    写在前面的话&&About me 网上写spring的文章多如牛毛,为什么还要写呢,因为,很简单,那是人家写的:网上都鼓励你不要造轮子,为什么你还要造呢,因为,那不是你造的. 我不是要 ...

  3. 曹工说Spring Boot源码(15)-- Spring从xml文件里到底得到了什么(context:load-time-weaver 完整解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  4. 曹工说Spring Boot源码(16)-- Spring从xml文件里到底得到了什么(aop:config完整解析【上】)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. 曹工说Spring Boot源码(17)-- Spring从xml文件里到底得到了什么(aop:config完整解析【中】)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  6. 曹工说Spring Boot源码(18)-- Spring AOP源码分析三部曲,终于快讲完了 (aop:config完整解析【下】)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  7. 曹工说Spring Boot源码(19)-- Spring 带给我们的工具利器,创建代理不用愁(ProxyFactory)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  8. 曹工说Spring Boot源码(20)-- 码网灰灰,疏而不漏,如何记录Spring RedisTemplate每次操作日志

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  9. 曹工说Spring Boot源码(21)-- 为了让大家理解Spring Aop利器ProxyFactory,我已经拼了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  10. 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

随机推荐

  1. 在开发框架中扩展微软企业库,支持使用ODP.NET(Oracle.ManagedDataAccess.dll)访问Oracle数据库

    在前面随笔<在代码生成工具Database2Sharp中使用ODP.NET(Oracle.ManagedDataAccess.dll)访问Oracle数据库,实现免安装Oracle客户端,兼容3 ...

  2. CSS:CSS弹性盒子布局 Flexible Box

    一.简介 flexbox:全称Flexible Box, 弹性盒子布局.可以简单实现各种伸缩性的设计,它是由伸缩容器和伸缩项目组成.任何一个元素都可以指定为flexbox布局.这种新的布局方案在200 ...

  3. 大公司 vs 小公司,你会选哪个?

    找工作跟找对象差不多,在确立关系领证前,彼此要多些了解.在了解的基础上,你再确认是否真心喜欢对方,彼此身上有没有相互吸引的特质,两个人的性格是否互补.三观是否匹配.契合度越高,往后才能相互扶持.彼此成 ...

  4. Redis报错: StackExchange.Redis.RedisServerException: Endpoint 39.105.22.111:7200 serving hashslot 12448 is not reachable at this point of time.

    emmmm……要下班了,简单记录一下. 如果是127.0.0.1:7200报这个错,请移步 https://blog.csdn.net/foreverhot1019/article/details/7 ...

  5. vue防抖节流之v-debounce--throttle使用指南

    最新封装了一个vue防抖节流自定义指令,发布到npm上,有用欢迎star,谢谢! npm地址:https://www.npmjs.com/package/v-debounce-throttle git ...

  6. Paramiko的SSH和SFTP使用

    目录 1. 概述 2. Paramiko的基本使用 2.1 SSHClient关键参数介绍 2.2 SSHClient常用示例 2.2.1 通过用户名和密码方式登陆: 2.2.2 通过用户名和密码方式 ...

  7. Spring-boot(一)通过向导快速创建Spring-boot项目

    通过向导快速创建Spring-boot项目 创建步骤: 选择Spring Initializr 填写组织和模块名 选择对应的模块 注:这里左侧的模块比较多,玩家可以根据自己的实际需要自由选择,此处暂时 ...

  8. Precision,Recall,F1的计算

    Precision又叫查准率,Recall又叫查全率.这两个指标共同衡量才能评价模型输出结果. TP: 预测为1(Positive),实际也为1(Truth-预测对了) TN: 预测为0(Negati ...

  9. spring+cxf 开发webService(主要是记录遇到spring bean注入不进来的解决方法)

    这里不介绍原理,只是记录自己spring+cxf的开发过程和遇到的问题 场景:第三方公司需要调用我们的业务系统,以xml报文的形式传递数据,之后我们解析报文存储到我们数据库生成业务单据: WebSer ...

  10. PowerMock学习(十一)之Mock private methods的使用

    Mock  private methods 就是mock私有方法啦,学到这不难发现,我们其实大部分都是通过反射去完成单元测试的,但是在实际中,某个类中的私有方法,个人不建议使用反射来测试,因为有时候会 ...