Bean
和对象
的区别:
简单来说就是Bean最大的特性是拥有一套完整的生命周期
Bean:
在Spirng中Bean不仅仅是一个对象这么简单,Spring中的Bean具有一套完整的生命周期。如
@Component class A{
@Autowired private B b; // 依赖B
}
// ---------
@Component class B{
@Autowired private A a; // 依赖A
}
class A —— new A() —— autowired —— 生命周期处理 —— AOP —— 等等操作 ——发现依赖B —— getBean(B)
——new B() —— autowired —— 生命周期处理 —— AOP —— 等等操作 ——发现依赖A —— 循环依赖……
对象:
只要new出来就好了。就行上面的例子,A a = new A();
完全没问题,但是A对象中的属性b
是没有值的。
Spring中管理 注解Bean定义 的容器有两个:AnnotationConfigApplicationContext
和 AnnotationConfigWebApplicationContext
。这两个类时专门处理Spring注解方式配置的容器,直接依赖于将注解作为容器配置信息来源的IoC容器。而后者是前者的Web版本,两者的用法及对注解的处理方式几乎没有差别。
以 AnnotationConfigApplicationContext
为例,在初始化atx容器的时候,传入一个参数AppConfig
类
AnnotationConfigApplicationContext atx = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig.java
@Configuration("abc")
@ComponentScan("com.abc")
@Scope("singleton")
public class AppConfig {
private int a;
public AppConfig() {
System.out.println("com.abc.AppConfig");
}
}
在new AnnotationConfigApplicationContext
的时候,来追踪一下构造方法。
// 最常用的构造函数
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 实例化了一个DefaultListableBeanFactory
this();
// AppConfig是没有Component注解的。但它被扫描是必要的。
// 将AppConfig这个类注册成一个BeanDefinition添加到Map中
register(componentClasses);
// refresh是SpringFramework中最重要的方法,没有之一
refresh();
}
在构造方法中调用了三个方法,this,register,refresh。其中初始化IoC容器atx
的是register方法
register
方法
// 注册一个注解Bean
// 新注册的注解Bean必须手动调用refresh方法,来刷新容器对Bean的处理。
@Override
public void register(Class<?>... componentClasses) {
this.reader.register(componentClasses);
}
继续调用reader
的register
方法,其中reader是一个bean定义读取器 AnnotatedBeanDefinitionReader
// 注册多个{注解Bean定义}类
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}
继续调用registerBean
方法来注册指定的类
// 注册一个{注解Bean定义}类
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null);
}
继续调用doRegisterBean
方法来注册一个指定的类
<T> void doRegisterBean(Class<T> beanClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
// 注解生成bean定义
// 根据指定的注解类,创建一个数据结构——封装注解bean定义的数据结构
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
// 设置实例提供者
abd.setInstanceSupplier(instanceSupplier);
// 1. 解析bean作用域
// 获取 Scope注解元数据 对象
// 解析bean的作用域
// prototype为原型模式,singleton为单例
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
// 设置注解bean定义的作用域
abd.setScope(scopeMetadata.getScopeName());
// 获取beanName ,如果name没有传值就从beanNameGenerator生成一个
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 2. 处理通用注解
// 处理注解bean定义中的通用注解(如:@Lazy @Primary等)
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}
// 创建一个指定bean名称的Bean定义对象。封装 注解bean定义数据
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
// 3. 创建作用域的代理对象
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 4. 通过BeanDefinitionReaderUtils向容器注册bean
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
可以看到doRegisterBean
注册Bean分为4步:
1 、解析bean作用域,是通过这个方法resolveScopeMetadata
来解析的。
2 、处理通用注解,如Primary,Qualifer等注解。
3 、创建作用域的代理对象
4 、通过BeanDefinitionReaderUtils向容器注册bean最终会看到 this.beanDefinitionMap.put(beanName, beanDefinition);
这样的一句话,意思就是放入容器中一个Bean定义。
先来进行第一步:
第一步: AnnotationScopeMetadataResolver#resolveScopeMetadata
解析bean的作用域。
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
// 获取Bean定义,强转为子类型,注解Bean定义
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
// AnnotationAttributes这个类继承自LinkedHashMap
// annDef.getMetadata();会查询出bean中所有的注解放在一个数组中。StarandAnnoationMetadata
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
// 获取所有Scope注解的值设置到要返回的对象中。
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
// 获取Scope注解中proxyMode的值,在创建代理对象的时候会用到。
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
// 如果proxyMode的值为NO 或者 DEFAULT
if (proxyMode == ScopedProxyMode.DEFAULT) {
// 则设置成默认的代理
proxyMode = this.defaultProxyMode;
}
// 为返回的元数据设置proxyMode
metadata.setScopedProxyMode(proxyMode);
}
}
// 返回解析的作用域元信息对象
return metadata;
}
第二步:处理通用注解==@Lazy,@DependsOn,@Role,@Description== 由这个方法AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
调用:
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(), Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
}
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
abd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
abd.setDescription(description.getString("value"));
}
}
第三步:根据Bean定义 类中配置的作用域为其应用相应的代理策略
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
// 如果为NO,则不设置代理对象。
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
// 获取配置的@Scope注解中的proxyMode值,如果为TARGET_CLASS返回true,如果为INTERFACE返回false
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
第四步:通过BeanDefinitionReaderUtils向容器注册bean
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 向容器中注册Bean定义
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果存在别名,注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
在第四步中,可以看到调用 registerBeanDefinition
方法向容器注入了Bean定义。这个方法是DefaultListableBeanFactory实现的。这是默认的列表Bean工厂,向工厂注册,也就是把原材料(BeanDefinition)提供给工厂。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 如果是抽象BeanDefin
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 尝试验证Bean定义
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 从Bean工厂的beanDefinitionMap中获取指定名称的Bean定义
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 第一次肯定获取不到。
// 如果获取到Bean定义
if (existingDefinition != null) {
// ...省略不重要的代码
// 放入bean定义
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 如果没有获取到Bean定义
else {
// 判断是否开始创建Bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 把Bean定义放入工厂容器中
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
// 如果没有正在创建
else {
// Still in startup registration phase
// 直接放入工程容器中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 添加BeanName
this.beanDefinitionNames.add(beanName);
// 删除单例名称
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
至此SpringBean就已经全部注册完成了。在这里需要理解注册这个词,注册到容器中就是把bean定义
放到容器
中。
bean定义
就是原材料——bean的属性,属性名,方法名称,方法参数,是否有父类,是否是抽象等等。容器
就是工厂。工厂有了原材料BeanDefinition即Bean定义
才能生产对象。getObjectForBeanInstance
从Bean实例中获取对象initBeanWrapper
初始化BeanWrapperapplyPropertyValues
在给属性赋值的之后没有属性…在register方法执行完之后,只是将Bean定义即BeanDefinition
放入到了容器中。接下来,执行refresh
方法刷新容器,会将Bean定义中一些原始类实例化。然后查看register注解的用户的类上是否有 @ComponentScan
注解,读取包下的Bean定义加入到容器中。然后对 lazy-init=false
的bean进行实例化。自动注入
从断点可以看出:在执行完register方法之后,执行refresh之前,就已经存在了五个开天辟地的 BeanDefinition
和自己在启动容器atx时传入的配置类AppConfig
(名字自己起的abc,自己随便起就好)。
而在refresh方法中,执行了许多过程。refresh
源码如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1.调用容器准备刷新的方法,获取容器当前时间,同时给容器设置同步标识
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2. 告诉子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3. 准备bean工厂以供在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 4. 允许在上下文子类中对bean工厂进行后处理。
postProcessBeanFactory(beanFactory);
// 5. 调用了getBean方法,也就是初始化实例对象。
// 重要的方法
// 在spring的环境中区执行已经被注册的factory processors
// 设置执行自定义的ProcessBeanFactory和spring自己定义的
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 6. 注册拦截bean创建的bean处理器。
registerBeanPostProcessors(beanFactory);
// 7Initialize message source for this context.
// 初始化此上下文的消息源。
initMessageSource();
// 8. 初始化事件广播器
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 9. Initialize other special beans in specific context subclasses.
// 初始化特定上下文子类中的其他特殊bean。
onRefresh();
// Check for listener beans and register them.
// 10. 检查侦听器bean并注册它们。
registerListeners();
// new 对象。被扫描的对象会被实例化。可调用构造方法证明
// Instantiate all remaining (non-lazy-init) singletons.
// 11* 实例化所有剩余的(非延迟初始化)单例。
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 12. 最后一步:发布对应的事件。
finishRefresh();
.....省略不重要方法。
}
}
}
把refresh的方法分为12个过程
。其中主要关注 11----finishBeanFactoryInitialization
这个方法。因为在这里实例化了所有的对象放入对象池中。
在执行完5---invokeBeanFactoryPostProcessors
这个方法后(如下有调试截图),可以看到在配置路径下的类被扫描了。并且将Bean定义放入到了容器中。同时还调用了 getBean
方法。实例化了一些后置处理器、监听器、环境、等
一些原始类。供框架使用。同时,也扫描了配置路径下的类。将Bean定义放入容器中——第二张图可证明。
执行 6、7、8、10
这几个步骤时 还实例化了几个框架级别的类放到了单例池 singletonObjects
中。现在来看 10--finishBeanFactoryInitialization
方法。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 这是Spring3提供的一个方法,为容器指定一个转换服务,在对某些bean属性进行转换时使用
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// 如果此时没有bean后置处理器注册任何been(例如PropertyPlaceholderConfigurer bean
// 则注册默认的嵌入式值解析器,主要是为了在注释属性值中进行解析。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 为了类型匹配,停止使用临时的类加载器。
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
//缓存容器中所有注册的BeanDefinition元数据,以防被修改。
beanFactory.freezeConfiguration();
// 实例化所有的单例对象
// 对配置了lazy = false的对象进行实例化。
beanFactory.preInstantiateSingletons();
}
在这个方法中概括性的将就一句话:执行了 beanFactory.preInstantiateSingletons();
这条语句。
作用:
将配扫描到的lazy=false(默认为false)的bean定义实例化。
来看preInstantiateSingletons
方法:省略了日志打印代码
public void preInstantiateSingletons() throws BeansException {
// 省略了日志打印代码
// 获得所有bean的名字
// 里面的名字都是可能要去实例化的class
// 为什么是可能呢。1. lazy=true 2.bean本身定义错误..等等,所以是可能
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
// 获取指定名称的Bean定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// bean不是抽象的,并且是单例的,并且不是懒加载
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 如果是工厂bean,也就是FactoryBean
if (isFactoryBean(beanName)) {// 这里面的先不理解}
// 如果是普通Bean
else {
// 获取一下bean,确认bean是否存在,如果存在就不再创建实例,如果不存在程序才继续执行。
// DI的核心方法!!!!!!!!
getBean(beanName);
}
}
}
}
只研究自己的 BeanDefinition
是如何被实例化的。也就是abc和cityService。在preInstantiateSingletons
方法中,先做了一些判断,然后调用getBean
进行实例化.
接着看 getBean
方法,getBean
本身只是一个壳,它调用了doGetBean
方法,doGetBean
才是主角。来看doGetBean
源码
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 根据指定名称获取被管理的bean的名称,剥离指定名称对容器的相关依赖
// 如果指定的是别名,将别名转换为规范的bean名称。
// step1. 获取单例对象
final String beanName = transformedBeanName(name);
// 该方法的返回值。
Object bean;
// 经典方法getSingleton
// 从缓存中读取看是否已经有被创建过得单例模式的Bean
// 对于单例模式的Bean,整个IoC容器只创建一次。不需要重复创建。
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 获取指定的Bean的实例对象,主要完成FactoryBean的相关处理
// 注意:FactoryBean是创建对象。BeanFactory管理对象。两者的区别
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 缓存中没有正在创建的单例Bean
else {
// step2. 检查循环依赖
// 循环依赖
// 缓存中已有原型模式的bean,但是由于循环引用,导致实例化对象失败.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 在IoC容器中查找是否存在指定名称的BeanDefinition。
// 首先检查是否能在当前的BeanFactory中获取所需要的Bean,
// 如果不能,则委托当前容器的父容器去查找,还是找不到继续沿着容器的继承体系一直查找。
// 3. 获取BeanDefinition
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// 解析指定Bean名称的原始名称
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
} else if (args != null) {
// Delegation to parent with explicit args.
// 委托父容器根据指定名称和显式参数进行查找
return (T) parentBeanFactory.getBean(nameToLookup, args);
} else if (requiredType != null) {
// No args -> delegate to standard getBean method.
// 委托父容器根据指定名称和指定类型进行查找
return parentBeanFactory.getBean(nameToLookup, requiredType);
} else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// step4. 标记Bean被创建
// 创建的Bean是否需要类型验证,一般不需要
// 向容器标记bean正在创建过程中
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 根据指定Bean名称获取其父级的bean定义
// 主要解决bean继承时子类和父类的公共问题所在
// step5. 合并bean定义
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 获取当前Bean所有依赖bean的名称
// step6. 获取@DependsOn注解。检查是否有依赖其他Bean
String[] dependsOn = mbd.getDependsOn();
// 如果当前Bean有依赖Bean
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 把被依赖的bean注册给当前bean
registerDependentBean(dep, beanName);
try {
// 递归调用getBean()方法,获取依赖Bean的依赖Bean
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 7. 根据作用域和BeanDefinition创建实例对象。
if (mbd.isSingleton()) {
// 这里使用匿名内部类创建的Bean实例对象,并且注册给当所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
// 7.1 重中之重!!!!!!!
// 创建一个指定的Bean实例对象,如果有父级继承,则合并当前子类和父类的定义
return createBean(beanName, mbd, args);
} catch (BeansException ex) {...省略异常信息...}
});
// 获取给定的实例Bean对象,单例的。 // 上面已经得到bean了,为什么还要再获取一次呢?是为了判断获取的是普通Bean还是工厂Bean产生的Bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 如果是原型模式,每次都创建一个新对象。
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
// 回调beforePrototypeCreation方法,默认的功能室注册当前创建的原型对象。
beforePrototypeCreation(beanName);
// 创建指定的Bean对象的实例
prototypeInstance = createBean(beanName, mbd, args);
} finally {
// 回调afterPrototypeCreation方法,默认的功能是告诉IoC容器不再创建指定的Bean的原型对象
afterPrototypeCreation(beanName);
}
// 获取给定的实例Bean对象
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 如果bean既不是单例的也不是原型的
// 则根据bean定义资源中配置的声明周期返回,选择实例化bean的合适方法,
// 这种方式在Web中比较常用,如:request、session、application
else {....这里不重要。先理解别的代码.... }
} catch (BeansException ex) {}
}
// 对创建的bean实例对象进行类型检查
if (requiredType != null && !requiredType.isInstance(bean)) {....这里不重要。先理解别的代码....}
return (T) bean;
}
代码过多,来理解一下流程,根据``doGetBean`的源码,结为以下7步:其中第七步才是真的创建Bean
从单例池中获取单例对象,(第一次肯定不存在嘛,Spring正在创建这个对象,单例池里是没有的。所以获取不到。)
检查循环依赖
从当前容器中获取BeanDefinition
重点:在Spring中Bean是产品,BeanDefinition是原料,只有获取了原料才能生产产品,如果当前容器获取不到,就沿着容器的继承体系向上递归查找。
标记Bean已经被创建
合并Bean定义
获取@DependsOn注解。检查是否有依赖其他Bean
根据作用域和BeanDefinition创建实例对象。
单例——创建单例Bean
原型——创建原型Bean
session、request、application——一般web中用
返回Bean
在 doGetBean
方法中真正创建Bean的过程是在第七步中的 7.1 ---- createBean
这条语句。注意,createBean
是在一个lambda表达式中。createBean这个方法是作为匿名内部类的方式被 getSingleton
调用的。先记住createBean这个方法,它会被调用,但调用的地方并不是当前方法。而是 getSingleton
来看一下getSingleton
到底有什么神奇的地方。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// step1. 加锁
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new 异常信息省略// 循环注入
}
// step2. 创建对象之前先给对象标注一下状态,表示bean正在创建
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// step3. 通过这句代码来创建单例对象,但是singletonFactory其实传进来的是一个lambda表达式。调用这个方法的调用者传递。
// lambda一般是createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {...省略异常信息...}
catch (BeanCreationException ex) {...省略异常信息...}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
// step4. 向容器中添加单例对象。!!!!! TODO
addSingleton(beanName, singletonObject);
}
}
// step5. 返回创建好的Bean
return singletonObject;
}
}
整个getSingleton
方法共有5处关键的语句:
加锁,进入方法的第一步是先给单例池加上锁,保证线程安全。
标记一下该bean正在被创建。点开这个方法可知通过 !this.singletonsCurrentlyInCreation.add(beanName)
这条语句来标记的。singletonsCurrentlyInCreation
是一个集合。其中存储了正在被创建的beanName
==重头戏:==上面说的 createBean
方法就是getObject方法来执行的。因为是一个匿名内部类嘛。
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
RootBeanDefinition mbdToUse = mbd;
// 判断当前创建的Bean是否可以实例化,即是否能通过当前类加载器加载。
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
try {
// 校验和准备bean中的方法覆盖。
mbdToUse.prepareMethodOverrides();
} catch (BeanDefinitionValidationException ex) {...省略异常信息...}
try {
// 如果配置了bean初始化前和初始化后的处理器,则试图返回一个需要创建bean的代理对象。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
} catch (Throwable ex) {...省略异常信息...}
try {
// 创建Bean的入口
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
} catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {...省略异常信息...
} catch (Throwable ex) {...省略异常信息...}
}
唉 createBean
只做了一些基本逻辑依旧要委托给doCreateBean
方法来创建实例。
来看doCreateBean
源码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 封装被创建的Bean对象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) { // 如果是单例的,就要从缓存池中移除原有的对象
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// step1. 生成Java对象,注意只是创建了Java对象,但不能称之为bean因为还没有依赖注入等生命周期
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 获取实例化对象的类型
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 调用postProcess后置处理提
synchronized (mbd.postProcessingLock) {...省略,自己研究...}
// 向容器中缓存单例模式的Bean对象,防止循环引用。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 这是一个匿名内部类,为了防止循环引用,尽早持有对象的引用。
if (earlySingletonExposure) {...省略,自己研究...}
// bean 对象的初始化,依赖注入在此生效。
// 这个exposedObject对象在初始化完成之后,返回依赖注入完成后的bean
Object exposedObject = bean;
try {
// 将bean实例对象封装,并且将bean定义中配置的属性值赋给实例对象。
// 英文单词:populate迁移,填充数据的意思
// step2. 具体依赖注入实现二
// 注入Bean所包含的Java对象
populateBean(beanName, mbd, instanceWrapper);
// 初始化bean对象 初始化给定的bean实例,应用工厂回调以及init方法和bean后处理器。对于传统定义的bean,从{createBean}调用,对于现有bean实例,从{initializeBean}调用。
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {...省略异常信息...}
if (earlySingletonExposure) {...省略,自己研究...
// 获取指定名称已经注册的单例模式的Bean对象
// 根据名称获取的已经注册的bean和正在实例化的bean 是同一个
// 当前实例化的bean初始化完成
// 当前bean依赖其他bean,并且当发生循环引用时不允许创建新的实例对象
// 获取bean所依赖的其他bean
// 对依赖的其他bean进行类型检查
}
// step3. 返回bean!
return exposedObject;
}
这里又分为两步:
生成Java对象,注意只是创建了Java对象,但不能称之为bean因为还没有依赖注入等生命周期
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
...不看一眼望不到头的代码了...
// 调用默认无参构造方法来创建Java对象
return instantiateBean(beanName, mbd);
}
来看instantiateBean
方法,
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
...不看一眼望不到头的代码了...
Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
return bw;
}
在instantiateBean
方法中调用了instantiate
来实例化对象(注意这个单词和初始化的区别)
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 如果没有覆盖方法,就不需要CGLIB父类的方法。
if (!bd.hasMethodOverrides()) {
// 通过JDK反射机制来创建对象
// 使用工具类BeanUtils来实例化bean,通过反射机制调用"构造方法.newInstance(args)"来进行实例化。
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 使用CGLIB来实例化对象
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
点进去instantiateClass
方法还可以往下看到 return ctor.newInstance(args));
这样的源码通过JDK反射来new对象。至此!!!终于!!对象创建好了。但只是一个Java对象,并不是Spring的Bean。想要创建Bean还差一步——依赖注入。
依赖注入,给bean注入属性。来看populateBean
注入bean的源码
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
...只看核心代码吧...
// 获取容器在解析Bean定义时,BeanDefinition中的属性
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
if (needsDepCheck) {
// 检查依赖
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
// 对属性进行注入
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
其中这个方法中判断了很多关于BeanDefinition的属性东西。业务逻辑暂时抛开,最后一句 applyPropertyValues
是对属性的依赖注入。然后在该方法又进行了一堆判断。调用了 BeanWrapper.setPropertyValues
方法来注入属性的。具体源码就不看了。有兴趣自己研究。
向单例池中添加创建好的单例对象,通过addSingleton
方法向单例池
中添加创建好的单例对象
,this.singletonObjects.put(beanName, singletonObject);
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
返回该单例对象
至此doGetBean就完成了
总结一下流程是这样的:时序图是最简单的
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
词义辨析:
初始化、实例化
实例化:
是指对象已经创建好放在内存之中。
初始化:
初始化可以理解为,将对象装载进虚拟机中,然后对对象的属性赋值、执行静态代码快等操作。
postProcessBeforeInstantiation // 初始化,BeanPostProcessor插手初始化的过程
postProcessBeforeInitialization // 实例化,InstantiationAwareBeanPostProcessor插手实例化的过程
后置处理器拦截bean生命周期
A —— new —— xxxBeanPostProcessor —— A() —— lifeCiclyCallBack|@PostConstruct—— 自动注入AutowiredAnnotationBeanPostProcessor —— BeanFactoryProcessor
BeanDefinition描述一个bean实例,该实例具有属性值、构造函数参数值和具体实现提供的进一步信息。
BeanDefinition
继承体系
其中RootBeanDefinition
最为常见
// 定义两个常量
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;// 原型作用域 singleton
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;// 原型作用域 prototype
// 获得Bean定义的一些名字:父类名字,bean全名,工厂bean的名称,工厂方法的名称,初始化方法的名称,销毁方法的名称
parentName: String
beanClassName: String
factoryBeanName: String
factoryMethodName: String
initMethodName: String
destroyMethodName: String
// 获取Bean的一些注解:作用域,懒加载,依赖
scope: String
lazyInit: boolean
dependsOn: String...
dependsOn: String[]
// 获取Bean的一些状态:是否单例,是否首先,是否原型,是否抽象,是否自动注入候选
isAutowireCandidate: boolean
isPrimary: boolean
isSingleton: boolean
isPrototype: boolean
isAbstract: boolean
// 获取Bean的一些内容:属性值,构造参数的集合,资源描述,原生的BeanDefinition
constructorArgumentValues: ConstructorArgumentValues
propertyValues: MutablePropertyValues
resourceDescription: String
originatingBeanDefinition: BeanDefinition
AbstractBeanDefinition
是一个抽象类,继承自BeanDefinition
。BeanDefinition主要定义了很多对Bean定义的方法。AbstractBeanDefinition
抽象类基本实现了getter和setter。并且添加了自动类型匹配规则
——匹配规则定义了五种,有一种过时了。AbstractBeanDefinition
还添加了beanClass这个方法的判断逻辑。就是Bean的Class类型。具体添加的内容如下:
添加的常量如下:
public static final String SCOPE_DEFAULT = "";
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
@Deprecated
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
public static final int DEPENDENCY_CHECK_NONE = 0;
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
public static final int DEPENDENCY_CHECK_ALL = 3;
public static final String INFER_METHOD = "(inferred)";
resolveBeanClass
解析bean是否有Class,能否被加载
hasBeanClass
getBeanClass
setBeanClass
有关bean的Class的处理逻辑
public void applyDefaults(BeanDefinitionDefaults defaults)
应用默认的BeanDefinition
public void overrideFrom(BeanDefinition other)
用其他的BeanDefinition覆盖
public void setAbstract(boolean abstractFlag)
添加抽象的相关处理
setAutowireMode
getAutowireMode
添加自动注入类型的相关处理
getResolvedAutowireMode
setDependencyCheck
getDependencyCheck
检查依赖检查的相关处理
addQualifier
hasQualifier
getQualifier
getQualifiers
添加qulifier的相关处理
setPropertyValues
hasPropertyValues
添加Bean属性的相关处理
getMethodOverrides
hasMethodOverrides
添加Bean方法覆盖的相关处理
setInitMethodName
setEnforceInitMethod
setDestroyMethodName
setEnforceDestroyMethod
添加初始化方法,销毁方法的相关处理
setSynthetic
isSynthetic
添加合成Bean的相关处理
setResource
validate
验证相关方法
prepareMethodOverrides
预处理方法覆盖
根bean定义表示在运行时支持Spring BeanFactory中的特定bean的合并bean定义。它可能是从相互继承的多个原始bean定义创建的,通常注册为{@link GenericBeanDefinition GenericBeanDefinitions}。根bean定义本质上是运行时的“统一”bean定义视图。根bean定义也可用于在配置阶段注册单个bean定义。但是,***自Spring2.5以来***,以编程方式注册bean定义的首选方法是{@link GenericBeanDefinition}类。GenericBeanDefinition的优点是它允许动态定义父依赖项,而不是将角色“硬编码”为根bean定义。
RootBeanDefinition
继承自AbstractBeanDefinition
,在父类的基础上最重要的是扩展了Bean定义的内容 解析Bean的类型
, 工厂方法
等
BeanDefinitionHolder
添加BeanDefinitionHolder相关处理Spring中只有单例对象支持循环依赖,如果是原型对象就不支持了。
1.0的例子中,在getBean(A)
的时候,会给A附上一个状态标注A正在创建中
,这样,在初始化A的过程中去循环依赖B,在创建B的过程中会发现A正在创建
中,而此时A就是一个对象(没有赋值属性b的对象)。解决循环依赖
源码解释:markBeanAsCreated
方法用来标识Bean正在创建中。由AbstractBeanFactory
的属性alreadyCreated
来标识。全文一共3处调用了markBeanAsCreated
方法。分别是doGetBean,configureBean,applyBeanPropertyValues,后两个正好发生在依赖注入上。
protected void markBeanAsCreated(String beanName) {
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we're actually creating
// the bean... just in case some of its metadata changed in the meantime.
clearMergedBeanDefinition(beanName);
this.alreadyCreated.add(beanName);
}
}
}
}
getBean方法中调用doGetBean
:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
....
// 创建单例模式的Bean实例对象。
// Create bean instance.
if (mbd.isSingleton()) {
// 这里使用匿名内部类创建的Bean实例对象,并且注册给当所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建一个指定的Bean实例对象,如果有父级继承,则合并当前子类和父类的定义
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// 显式的从容器中单例模式的Bean缓存中清除实例对象
destroySingleton(beanName);
throw ex;
}
});
// 获取给定的实例Bean对象,单例的。
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
....
}
doGetBean调用getSingleton
。
并且传递过来一个参数singletonFactory
,这是上面doGetBean传过来的一个lambda表达式
。真正创建Bean就是在回调方法CreateBean里执行的。
@Nullable public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Object singletonObject = this.singletonObjects.get(beanName);
......
// 创建对象之前先给对象标注一下状态,表示bean正在创建
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 通过这句代码来创建单例对象,但是singletonFactory其实传进来的是一个lambda表达式。调用这个方法的调用者传递。
// lambda一般是createBean方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
.......
}
getSingleton请注意beforeSingletonCreation
这个方法。这个方法里往singletonsCurrentlyInCreation
这个Set集合中添加了当前正在实例化的Bean名称,标识当前bean的状态
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) &&
//给bean标明正在创建bean。
!this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
骨灰级别
@Service
、@Controller
、@RequestMapping
、 @Component
、@Mapper
、 @Autowired
一般级别
@GetMapping… @Qualifier @Lazy @Primary @RequestParam
ComponentScan
指定的包会被Spring扫描,进行初始化Bean对象
@Qualifier
A,B都实现了C,向D类注入C的时候有两个选择,可以使用@Qualifier注入指定名称的类
@Primary
A,B都实现了C,向D类注入C的时候有两个选择,优先选择被@Primary标注的类
@RequestBody
post请求接收参数
@ReponseBody
响应参数为Json格式
@Scope
作用域单例还是原型
@DeleteMapping
@PutMapping
新增数据
@PatchMapping
put请求的补充,局部更新,只能算是一个标准,和put没什么分别
@Configuration
和Service等注解一样,源码里指定了别名为Component
少见注解
@PostConstruct @PreDestory
@Value
用来给属性注入Pperties
我罕见的:
@Role
标识Bean的类别
@Description
描述方法或者类
@Cacheable
可缓存
@AliasFor
在Spring的众多注解中,经常会发现很多注解的不同属性起着相同的作用,比如@RequestMapping的value属性和path属性,这就需要做一些基本的限制,比如value和path的值不能冲突,比如任意设置value或者设置path属性的值,都能够通过另一个属性来获取值等等。为了统一处理这些情况,Spring创建了@AliasFor标签。
@Import
在应用中,有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到@Import注解。
示例:没有疤Cat类和Dog类交给Spring管理,但是又要从容器中获取Dog类和Cat类。
class Cat(){}
class Dog(){}
@SpringBootApplication
@ComponentScan
// 把用到的资源导入到当前容器中
@Import({Dog.class, Cat.class})
class App(){
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(Dog.class));
System.out.println(context.getBean(Cat.class));
context.close();
}
}
@Profile
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
开发环境develop、测试环境test、生产环境master
数据源:(/dev) (/test) (/master)
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}")
private String user;
private String driverClass;
@Profile("default")
@Bean("test")
public DataSource testDataSource(@Value("${db.password}")String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("dev")
public DataSource devDataSource(@Value("${db.password}")String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("master")
@Bean("master")
public DataSource masterDataSource(@Value("${db.password}")String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driverClass);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String driverClass = resolver.resolveStringValue("${db.driverClass}");
this.driverClass = driverClass;
}
}
public class IOCTestProfile {
//1. 使用命令行动态参数:在虚拟机参数位置加载 -Dspring.profiles.active=test
//2. 使用代码的方式激活某种环境;
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
//1. 创建一个applicationContext
//2. 设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev","master");
//3. 注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4. 启动刷新容器
applicationContext.refresh();
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
System.out.println(Arrays.toString(beanNamesForType));
applicationContext.close();
}
@Test
public void test02() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
System.out.println(Arrays.toString(beanNamesForType));
applicationContext.close();
}
}
先创建两个类,不用注解注入到IOC容器中,在应用的时候在导入到当前容器中。
@Conditional
表示组件只有在被具体Condition匹配的情况下才会被注册
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
// 所有条件都匹配该组件才能被注册
Class<? extends Condition>[] value();
}
@PropertySource @ConfigurationProperties(“jdbc”) 示例如下:
@RestController public class HelloController {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@RequestMapping("/getUandP")
public String getProperties() {
return username + ":" + password;
}
@RequestMapping("HELLO")
public String say() {
return "你好SpringBoot";
}
}
@RestController
@ConfigurationProperties("jdbc")
@Setter // 必须有set方法才能批量注入属性
@RequestMapping("/h2")
public class HelloController2 {
private String username;
private String password;
@RequestMapping("/getMsg")
public String getProperties() {
return username + ":" + password;
}
@RequestMapping("/hello/{test}")
public String say(@PathVariable("test") String test) {
return "你好SpringBoot | " + test;
}
}
@RestController
@Setter
@RequestMapping("/user/")
@PropertySource("classpath:/properties/user.properties")
@ConfigurationProperties(prefix = "cat")
public class PropertiesController {
private Integer id;
private String name;
@RequestMapping("getUser")
public String getMsg() {
return id + ":" + name;
}
}
@DependOn
控制bean加载顺序,有很多场景需要bean B应该被先于bean A被初始化,从而避免各种负面影响。我们可以在bean A上使用@DependsOn
注解,告诉容器bean B应该先被初始化。下面通过示例来说明。
这是一个配置类,配置类之间相互引用,但是又有要求,所以需要DependsOn注解。
publish一定是在listen之前的。适用于观察者模式。
@Configuration
@ComponentScan("com.logicbig.example")
public class AppConfig {
@Bean(initMethod = "initialize")
@DependsOn("eventListener")
public EventPublisherBean eventPublisherBean () {
return new EventPublisherBean();
}
@Bean(name = "eventListener", initMethod = "initialize")
// @Lazy
public EventListenerBean eventListenerBean () {
return new EventListenerBean();
}
public static void main (String... strings) {
new AnnotationConfigApplicationContext(AppConfig.class);
}
}