前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring注解里的那些骚操作

Spring注解里的那些骚操作

作者头像
疯狂的KK
发布2021-01-05 14:28:59
5040
发布2021-01-05 14:28:59
举报
文章被收录于专栏:Java项目实战Java项目实战

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%20Provider">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
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 赵KK日常技术记录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档