文章目录
一、Spring之IoC原理
1.1 词义辨析
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
是没有值的。
1.2 Spring初始化Bean的过程
1.2.1 入口——构造函数
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(); }
1.2.2 向容器中注册——Register
在构造方法中调用了三个方法,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定义
才能生产对象。
1.2.3 初始化Bean——Refresh
(一)有待研究的方法 | 可跳过
- org.springframework.beans.factory.support.AbstractBeanFactory#
getObjectForBeanInstance
从Bean实例中获取对象 - org.springframework.beans.factory.support.AbstractBeanFactory#
initBeanWrapper
初始化BeanWrapper - org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#
applyPropertyValues
在给属性赋值的之后没有属性…
(二)获取单例是怎么一回事?
在register方法执行完之后,只是将Bean定义即BeanDefinition
放入到了容器中。接下来,执行refresh
方法刷新容器,会将Bean定义中一些原始类实例化。然后查看register注解的用户的类上是否有 @ComponentScan
注解,读取包下的Bean定义加入到容器中。然后对 lazy-init=false
的bean进行实例化。自动注入
(三)refresh源码剖析
-
从断点可以看出:在执行完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就完成了
总结一下流程是这样的:时序图是最简单的
1.3 BeanPostProcessor后置处理器
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
1.4 BeanDefinition详解
BeanDefinition描述一个bean实例,该实例具有属性值、构造函数参数值和具体实现提供的进一步信息。
1.4.1 继承体系一览
BeanDefinition
继承体系
其中RootBeanDefinition
最为常见
1.4.2 BeanDefinition接口的内容
// 定义两个常量
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
1.4.3 AbstractBeanDefinition抽象类
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
预处理方法覆盖
1.4.4 RootBeanDefinition类
根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相关处理- 添加是否能缓存
- 添加工厂bean方法是否唯一
- 添加处理ConstructorArguments的方法
1.5 Spring循环依赖
-
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); } }
二、Spring知识
1.1 Spring常用注解
1.1.1 骨灰级别
@Service
、@Controller
、@RequestMapping
、 @Component
、@Mapper
、 @Autowired
1.1.2 一般级别
@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
1.1.3 少见注解
@PostConstruct @PreDestory
@Value
用来给属性注入Pperties
我罕见的:
-
@Role
标识Bean的类别- ROLE_APPLICATION = 0
- bean的默认角色
- 标识应用的主要的主要角色
- 通常对应于用户定义的bean
- ROLE_SUPPORT = 1
- 标识较大配置的一部分
- 通常是仔细观察特定的ComponentDefinition时重要的提示,而不是体现在应用
- ROLE_INFRASTRUCTURE = 2
- 标识后台角色,与最终用户无关
- 通常供是内部工作的bean使用
- ROLE_APPLICATION = 0
-
@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:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
- 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
- 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
@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);
}
}