偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

@Configuration注解天天用,你真的了解它嗎?

開發(fā) 前端
通過CGLIB創(chuàng)建代理,這些不是我們關(guān)心的,我們主要關(guān)心的是它是如何攔截配置方法的。所以這里我們主要關(guān)注的上面createClass方法中設(shè)置的CALLBACKS。在這個(gè)數(shù)組中通過名稱也就清楚核心攔截器是BeanMethodInterceptor。

環(huán)境:Spring5.3.23

1. 簡(jiǎn)介

@Configuration 是一個(gè)類級(jí)注解,表示一個(gè)對(duì)象是 Bean 定義的來源。@Configuration 類通過 @Bean 注解的方法聲明 Bean。對(duì) @Configuration 類上 @Bean 方法的調(diào)用也可用于定義Bean之間的依賴關(guān)系。

使用@Configuration注解的主要作用是替代Spring的applicationContext.xml文件,使得配置更加靈活和方便。當(dāng)某個(gè)類標(biāo)注了@Configuration注解時(shí),表示這個(gè)類是Spring的一個(gè)配置類,能夠自動(dòng)注冊(cè)到IOC容器并進(jìn)行實(shí)例化。

2. 應(yīng)用示例

static class Person {}
@Configuration
static class AppConfig {
  @Bean
  public Person person() {
    return new Person() ;
  }
}

AppConfig是一個(gè)配置類,在該類中通過@Bean標(biāo)注方法注冊(cè)Bean對(duì)象。示例非常的簡(jiǎn)單,但是不是一定的用@Configuration呢?換成@Component試試

@Component
static class AppConfig {
  @Bean
  public Person person() {
    return new Person() ;
  }
}
// 測(cè)試是否能夠獲取Person bean對(duì)象
try (GenericApplicationContext context = new GenericApplicationContext()) {
  context.registerBean(AppConfig.class) ;
  // ...
  System.out.println(context.getBean(Person.class)) ;
}

上面的示例能夠正確的獲取Person bean對(duì)象,那這里的@Component與@Configuration有什么區(qū)別呢?接著看下面代碼示例:

@Configuration
static class AppConfig {
  @Bean
  public Person person() {
    return new Person() ;
  }
  @Bean
  public Date d1() {
    System.out.println(person()) ;
    return new Date() ;
  }
  @Bean
  public Date d2() {
    System.out.println(person()) ;
    return new Date() ;
  }
}

在上面的示例中,隨意定義了2個(gè)Date類型的Bean,這2個(gè)方法中都調(diào)用person()方法,執(zhí)行結(jié)果:

com.pack.m.b.CMain$Person@55183b20
com.pack.m.b.CMain$Person@55183b20

控制臺(tái)輸出的一模一樣,是不是感覺好奇怪,調(diào)用2次應(yīng)該是不同的Person對(duì)象才對(duì)是吧?先繼續(xù)往下看,吧@Configuration換成@Component(就是換注解,其它都沒有變化,代碼就不貼了),執(zhí)行結(jié)果:

com.pack.m.b.CMain$Person@78aab498
com.pack.m.b.CMain$Person@5dd6264

這次輸出是2個(gè)不同的Person對(duì)象了,此時(shí)你是不是覺得這次符合你的預(yù)期?如果你這么認(rèn)為那么就出大事了。

在 Spring 中,實(shí)例化的 Bean 默認(rèn)具有單例作用域,但是如上執(zhí)行情況,明顯成了多例,我們就應(yīng)該確保容器中任何時(shí)候使用的都是同一個(gè)實(shí)例。如果這里是DataSource那問題就更加嚴(yán)重了。

所以,這里雖然可以使用@Component定義配置類,但是強(qiáng)烈不建議你這樣使用,既然是配置類你就的按規(guī)矩來使用@Configuration注解。那@Configuration是如何保證在內(nèi)部方法調(diào)用返回的對(duì)象是同一個(gè)呢?

先給出答案:那是因?yàn)槭褂聾Configuration注解的類被生成了代理類(通過CGLIB)。接下來我們來看看它的原理。

3. 實(shí)現(xiàn)原理

Spring提供了一個(gè)ConfigurationClassPostProcessor處理器來注冊(cè)@Configuration注解。該處理器是一個(gè)BeanFactoryPostProcessor。我們就從這里看起

3.1 給@Configuration注解的類打標(biāo)記

這里所說的大標(biāo)記其實(shí)就確定你當(dāng)前這個(gè)配置類要不要生成代理,而這個(gè)標(biāo)記模式是生成代理

// 這里的proxyBeanMethods值為true,意為會(huì)為當(dāng)前的配置類生成代理
@Configuration(proxyBeanMethods = true)
static class AppConfig {}

處理配置類

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    // ...
    // 處理配置類bean
    processConfigBeanDefinitions(registry);
  }
  public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    // 獲取所有的bean
    String[] candidateNames = registry.getBeanDefinitionNames();
    // 遍歷
    for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName) ;
      // 判斷當(dāng)前的bean是否有ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE屬性
      // 默認(rèn)都是沒有的,所以這里進(jìn)入到else if 中
      if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
        // 打印日志
      } 
      // 在checkConfigurationClassCandidate會(huì)處理配置類的相應(yīng)屬性
      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
    }
    // ...
  }
}

ConfigurationClassUtils

abstract class ConfigurationClassUtils {
  public static final String CONFIGURATION_CLASS_FULL = "full";
  public static final String CONFIGURATION_CLASS_LITE = "lite";
  public static boolean checkConfigurationClassCandidate(
      BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    // 省去無關(guān)代碼
    // 獲取到當(dāng)前配置類的所有注解信息
    AnnotationMetadata metadata = AnnotationMetadata.introspect(beanClass) ;
    // 獲取注解類@Configuration信息
    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
      // 如果@Configuration中的proxyBeanMethods屬性為true,那么就將當(dāng)前配置類對(duì)應(yīng)的BeanDefinition設(shè)置屬性
      // 標(biāo)記為true,其實(shí)這里的目的就是要不要?jiǎng)?chuàng)建代理,如果為true創(chuàng)建代理
      beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    // 不創(chuàng)建代理
    else if (config != null || isConfigurationCandidate(metadata)) {
      beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
      return false;
    }
  }
}

上面對(duì)配置類進(jìn)行了標(biāo)記要不要?jiǎng)?chuàng)建代理,下面就是創(chuàng)建代理了。

3.2 為配置類生成代理

上面對(duì)配置類要不要?jiǎng)?chuàng)建代理是通過BeanDefinition 設(shè)置屬性的方式來標(biāo)記,標(biāo)記完后會(huì)在postProcessBeanFactory中創(chuàng)建代理。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // ...
    // 增強(qiáng)配置類,創(chuàng)建代理
    enhanceConfigurationClasses(beanFactory);
  }
  public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
      // 獲取設(shè)置的標(biāo)記屬性,要么是full,要么是lite
      Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
      if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
        // 先保存到集合匯總
        configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
    }
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
      AbstractBeanDefinition beanDef = entry.getValue();
      // 確定配置類的class
      Class<?> configClass = beanDef.getBeanClass();
      // 創(chuàng)建代理
      Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
      if (configClass != enhancedClass) {
        beanDef.setBeanClass(enhancedClass);
      }
    }
  }
}

接下來是核心通過ConfigurationClassEnhancer#enhance創(chuàng)建目標(biāo)配置類的代理對(duì)象。

class ConfigurationClassEnhancer {
  private static final Callback[] CALLBACKS = new Callback[] {
      new BeanMethodInterceptor(),
      new BeanFactoryAwareMethodInterceptor(),
      NoOp.INSTANCE
  }
  public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
    Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
    return enhancedClass;
  }
  private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(configSuperClass);
    // 設(shè)置生成的子類要實(shí)現(xiàn)的接口,該接口實(shí)現(xiàn)了BeanFactoryAware,
    // 所以容器在實(shí)例化初始化該代理對(duì)象的時(shí)候會(huì)自動(dòng)注入當(dāng)前容器的BeanFactory對(duì)象。
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    enhancer.setUseFactory(false);
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    // 這里有個(gè)作用就是為當(dāng)前的代理bean添加BeanFactory類型的字段。
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
  } 
  private Class<?> createClass(Enhancer enhancer) {
    Class<?> subclass = enhancer.createClass();
    Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
    return subclass;
  }
}

上面的代碼就是通過CGLIB創(chuàng)建代理,這些不是我們關(guān)心的,我們主要關(guān)心的是它是如何攔截配置方法的。所以這里我們主要關(guān)注的上面createClass方法中設(shè)置的CALLBACKS。在這個(gè)數(shù)組中通過名稱也就清楚核心攔截器是BeanMethodInterceptor。

private static class BeanMethodInterceptor implements MethodInterceptor {
  public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
          MethodProxy cglibMethodProxy) throws Throwable {
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    // 獲取當(dāng)前@Bean注解的方法名(也就是對(duì)應(yīng)的BeanName)
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
    // ...
    // 從容器中查找對(duì)應(yīng)的bean(也就是你調(diào)用的那個(gè)方法創(chuàng)建的bean對(duì)象)
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
  }
  private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
        ConfigurableBeanFactory beanFactory, String beanName) {
    // 獲取bean實(shí)例
    Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
            beanFactory.getBean(beanName));
    // ...
    return beanInstance;
  }
}

以上就是@Configuration注解創(chuàng)建代理及方法調(diào)用時(shí)的執(zhí)行原理。

你學(xué)到了嗎?

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2020-11-20 07:58:04

Java

2023-06-08 11:57:15

Matter協(xié)議家庭智能

2024-08-22 08:17:55

C#工具循環(huán)

2017-12-07 15:00:00

筆記本OLED屏幕

2023-05-29 08:11:42

@Value注解Bean

2014-04-17 16:42:03

DevOps

2022-07-26 00:00:22

HTAP系統(tǒng)數(shù)據(jù)庫

2019-09-02 08:39:02

路由器RAM內(nèi)存

2021-01-15 07:44:21

SQL注入攻擊黑客

2021-11-09 09:48:13

Logging python模塊

2014-11-28 10:31:07

Hybrid APP

2023-03-16 10:49:55

2020-02-27 10:49:26

HTTPS網(wǎng)絡(luò)協(xié)議TCP

2019-09-16 08:40:42

2012-05-31 09:56:54

云安全

2023-10-24 08:53:24

FutureTas并發(fā)編程

2017-10-18 22:01:12

2022-12-12 08:46:11

2015-07-31 10:35:18

實(shí)時(shí)計(jì)算

2019-11-06 09:52:01

JavaScript單線程非阻塞
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)