Spring 生态
Spring注解
Spring认为所有的组件都应该放在ioc容器中,组件之间的关系通过容器依赖注入,而注解就是为了完成容器的注册,管理。
如图:当类上标识ConditionalOnBean,但是类又实现了BeanPostProcessor,那么此时的bean初始化有没有冲突呢?
1-ConditionalOnBean的功能:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean,也就是下面的方法想要获取实例bean,需要先实例化
2-BeanPostProcessor:相当于切面,在所有bean初始化之前执行,我们看一下执行顺序
代码语言:javascript复制AnnotationConfigApplicationContext(创建容器)
->refresh
->finishBeanFactoryInitialization
->preInstantiateSingletons
->getBean
->doGetBean
->createBean
->doCreateBean
-> this.populateBean(beanName, mbd, instanceWrapper);(类属性赋值)
this.initializeBean(beanName, exposedObject, mbd);
(BeanPostProcessor postProcessBeforeInstantiation和postProcessAfterInstantiation执行
那么此时的bean有没有被初始化呢?看起来是有冲突的,但是条件注解的解析一定发生在spring ioc的bean definition阶段,如何快速证明有没有冲突只需要构造器初始化一下,看一下当前的bean有没有被注入,当然也存在懒加载,多实例等情况,没有注册,这是后话了,看一下bean definition的入口
ConfigurationClassPostProcessor
代码语言:javascript复制/**
* Derive further bean definitions from the configuration classes in the registry.
*/
派生bean的定义
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " registry);
}
this.registriesPostProcessed.add(registryId);
bean定义入口 ↓
processConfigBeanDefinitions(registry);
}
processConfigBeanDefinitions
代码语言:javascript复制public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
...
...
// Read the model and create bean definitions based on its content
读取实例并且创建bean 在上下文
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
}
loadBeanDefinitions
代码语言:javascript复制/**
* Read {@code configurationModel}, registering bean definitions
* with the registry based on its contents.
*/
根据内容注册bean
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
代码语言:javascript复制/**
* Read a particular {@link ConfigurationClass}, registering bean definitions
* for the class itself and all of its {@link Bean} methods.
*/
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
if (configClass.isImported())当解析完注解解析时,beanDefinition已经被同步,
也就是ConditionalOnBean已经被同步,如果此时构造器初始化是不会报错的。
实际应用场景中在定义DataSourcePool时,加载外部配置类,此时的注解能够解析加载那个dataSource,但应用场景不多。
牛X哄哄的ExtensionLoader
Dubbo的SPIcom.alibaba.dubbo.common.extension.SPI
代码语言:javascript复制/**
* Dubbo使用的扩展点获取。<p>
* <ul>
* <li>自动注入关联扩展点。</li>
* <li>自动Wrap上扩展点的Wrap类。</li>
* <li>缺省获得的的扩展点是一个Adaptive Instance。
* </ul>
*
* @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service Provider">JDK5.0的自动发现机制实现</a>
*
* @author william.liangf
* @author ding.lid
*
* @see com.alibaba.dubbo.common.extension.SPI
* @see com.alibaba.dubbo.common.extension.Adaptive
* @see com.alibaba.dubbo.common.extension.Activate
*/
dubbo的服务发现机制,JDK也有自己的服务发现机制,SPI可看做是其实现,但布道的时候,对ExtensionLoader十分推崇,生成其可自动生成字节码文件,无须jvm编译,但其实调用的是非jdk的编译包,
代码语言:javascript复制private Class<?> createAdaptiveExtensionClass() {
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
当然,重要的还是开头类注释的注解@Adaptive注解标识类,还有个Active,另外这段代码好骚啊
代码语言:javascript复制private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
用注解当类型?
代码语言:javascript复制 if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
其调用的Map的put方法
将指定值与此映射中的指定键相关联,在使用时用来获取已经加载的扩展点的name,好在大部分的dubbo注释是中文的,可联系上下文看到其作用
推荐阅读
代码语言:javascript复制dubbo源码分析 1 -- ExtensionLoader.getExtensionLoader