溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring中的@Configuration配置類是怎樣的

發(fā)布時間:2021-12-02 16:31:38 來源:億速云 閱讀:132 作者:柒染 欄目:大數(shù)據(jù)

本篇文章給大家分享的是有關(guān)Spring中的@Configuration配置類是怎樣的,小編覺得挺實用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

一、前言

在這里我不得不感慨Spring的代碼的完善與優(yōu)秀,從之前看源碼迷迷糊糊到現(xiàn)在基本了解Spring的部分源碼后,愈來愈發(fā)現(xiàn)Spring開發(fā)者的思慮之周全!

之前說過學(xué)習(xí)源碼的目的在哪?對于Spring的了解僅僅局限于使用遠(yuǎn)遠(yuǎn)不夠,Spring作為一個國內(nèi)絕大多數(shù)java開發(fā)者使用的一個項目管理框架,他是一個生態(tài),什么是生態(tài)?比如現(xiàn)在的SpringBootSpringCloud,他們是什么?是Spring生態(tài)中的一個組成部分!他們利用Spring生態(tài)中提供的各種擴(kuò)展點,一步一步的封裝,成就了現(xiàn)在Spring快速啟動、自動配置等亮眼的功能!作為Spring的使用者,我們理應(yīng)了解Spring的實現(xiàn)和各種擴(kuò)展點,從而能夠真正的深入Spring生態(tài)!深入了,再去研究生態(tài)中的組成部分如:SpringBoot之流的框架,也就水到渠成了!

   

二、開篇一問

相信大部分開發(fā)者對于Spring的使用都是水到渠成的!那么下面一段代碼大家一定很熟悉!

/**
 * 全局配置類
 *
 * @author huangfu
 */
@Configuration
public class ExpandRunConfig {
 @Bean
 public TestService testService() {
  return new TestServiceImpl();
 }

 @Bean
 public UserService userService() {
  testService();
  return new UserServiceImpl();
    }
}
   

可以很清楚的看到,這里交給Spring管理了兩個類TestService,UserService,但是在userService()里面又引用了testService()! 那么問題來了,你覺得TestService會被實例化幾次?

相信有不少同學(xué),張口就說一次,對,沒錯,但是為什么呢?我當(dāng)時對這里的問題深深的感到自我懷疑!甚至一度懷疑自己的java基礎(chǔ),明明這里調(diào)用了另外一個方法,但是為什么沒有進(jìn)行兩次實例化呢?

我問了很多同事、朋友,他們只知道這樣寫是沒有問題的!但是具體原因不知道!為什么呢?我們帶著這個問題往下看!

   

三、你看到的配置類是真的配置類嗎?

我們從bean容器里面把這個配置類取出來,看一下有什么不一樣!

public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ExpandRunConfig.class);
    ExpandRunConfig bean = ac.getBean(ExpandRunConfig.class);
    System.out.println(bean);

}
   

我們debug看一下,我們?nèi)〕鰜砹藗€什么玩意!

Spring中的@Configuration配置類是怎樣的      
被代理的Spring配置類

果然,他不是他了,他被(玷污)代理了,而且使用的代理是cglib,那么這里就可以猜測一個問題,在Bean方法中調(diào)用另外一個Bean方法,他一定是通過代理來做的,從而完成了多次調(diào)用只實例化一次的功能!

到這里,解決了,原來是這樣!那么現(xiàn)在有兩個疑問:

  1. 什么時候給配置類加的代理?
  2. 代理邏輯里面是怎么完成多次調(diào)用返回同一個實例的功能的?

下面我們就帶著兩個疑問,去追一下Spring源碼,看看到底是如何進(jìn)行的!

   

四、代理圖示

Spring中的@Configuration配置類是怎樣的      
cglib代理配置類的流程圖

這張圖我放出來,如果你沒有了解過的話,一定是很迷惑,沒關(guān)系,后面會用源碼解釋,而且源碼看完之后,我們會大概手寫一個,幫助你理解!

   

五、源碼詳解

不妨猜一下,看過我以前的文章的讀者都應(yīng)該了解!Spring創(chuàng)建bean實例的時候,所需要的信息是在beanDefinitionMap里面存放的,那么在初始化的時候解析bean的bd的時候,一定是替換了配置類bd里面的類對象,才會使后面實例化config的時候變成了一個代理對象,所以我們的入口應(yīng)該在這里:

Spring中的@Configuration配置類是怎樣的      
invokerBeanFactory入口方法

那么這里面的代碼是在哪增強的呢?

/**
  * 準(zhǔn)備配置類以在運行時為Bean請求提供服務(wù)
  * 通過用CGLIB增強的子類替換它們。
  */
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    ..................忽略對應(yīng)的邏輯................
    //字節(jié)碼增強配置類  貌似用的cglib
    enhanceConfigurationClasses(beanFactory);
    ..................忽略對應(yīng)的邏輯................
}
   

調(diào)用配置類的增強邏輯  enhanceConfigurationClasses

/**
 * 對BeanFactory進(jìn)行后處理以搜索配置類BeanDefinitions; 然后,任何候選人都將通過{@link ConfigurationClassEnhancer}.
 * 候選狀態(tài)由BeanDefinition屬性元數(shù)據(jù)確定。
 * @see ConfigurationClassEnhancer
 */
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    // 最終需要做增強的Bean定義們
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
        //什么是Full類,簡單來說就是加了 @Configuration 的配置類
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
           .....忽略日志打印......
            //// 如果是Full模式,才會放進(jìn)來
            configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
        }
    }
    if (configBeanDefs.isEmpty()) {
        // 沒有什么可增強的->立即返回
        return;
    }
    //配置類增強器
    // ConfigurationClassEnhancer就是對配置類做增強操作的核心類
    //初始化會初始化兩個chlib攔截類  BeanFactoryAwareMethodInterceptor 和  BeanMethodInterceptor
    //這個是重點  這個類里面的方法會產(chǎn)生最終的代理類
    //這個方法里面有個
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    //對每個Full模式的配置類,一個個做enhance()增強處理
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
        AbstractBeanDefinition beanDef = entry.getValue();
        // 如果@Configuration類被代理,請始終代理目標(biāo)類
        beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
        try {
            // 設(shè)置用戶指定的bean類的增強子類
            //CGLIB是給父類生成子類對象的方式實現(xiàn)代理,所以這里指定“父類”類型
            Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
            if (configClass != null) {
                //做增強處理,返回enhancedClass就是一個增強過的子類
                //這個是重點,這個會構(gòu)建一個cglib的增強器,最終返回被代理完成的類對象!
                Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
                //不相等,證明代理成功,那就把實際類型設(shè)置進(jìn)去
                if (configClass != enhancedClass) {
                    ..... 忽略日志打印 ....
                    //這樣后面實例化配置類的實例時,實際實例化的就是增強子類嘍
                    //這里就是替換 config類的beanClass對象的!
                    beanDef.setBeanClass(enhancedClass);
                }
            }
        }
        catch (Throwable ex) {
            。。。。。忽略異常處理。。。。。。。
        }
    }
}
   

這個類至關(guān)重要,總共做了這樣幾件事:

  1. 篩選配置類,只有加了 @Configuration的配置類才會被增強!
  2. 使用 enhancer.enhance構(gòu)建一個增強器,返回增強后的代理類對象!
  3. 替換配置類原始的beanClass,為代理后的class!

那么,我們最關(guān)心的是如何實現(xiàn)的,肯定要看enhancer.enhance里面的邏輯~

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
  // 如果已經(jīng)實現(xiàn)了該接口,證明已經(jīng)被代理過了,直接返回
  if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
   。。。。忽略日志打印。。。。
   return configClass;
  }
  //沒被代理過。就先調(diào)用newEnhancer()方法創(chuàng)建一個增強器Enhancer
  //然后在使用這個增強器,生成代理類字節(jié)碼Class對象
  //創(chuàng)建一個新的CGLIB Enhancer實例,并且做好相應(yīng)配置
        //createClass是設(shè)置一組回調(diào)(也就是cglib的方法攔截器)
  Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
  if (logger.isTraceEnabled()) {
   。。。。忽略日志打印。。。。
  }
  return enhancedClass;
 }
   

這是一個過度方法,真正去構(gòu)建一個代理增強器的是newEnhancer方法,我們似乎接近了我們要的答案!

/**
  * 創(chuàng)建一個新的CGLIB {@link Enhancer}實例。
  */
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    Enhancer enhancer = new Enhancer();
    // 目標(biāo)類型:會以這個作為父類型來生成字節(jié)碼子類
    enhancer.setSuperclass(configSuperClass);
    //代理類實現(xiàn)EnhancedConfiguration接口,這個接口繼承了BeanFactoryAware接口
    //這一步很有必要,使得配置類強制實現(xiàn) EnhancedConfiguration即BeanFactoryAware 這樣就可以輕松的獲取到beanFactory
    enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
    // 設(shè)置生成的代理類不實現(xiàn)org.springframework.cglib.proxy.Factory接口
    enhancer.setUseFactory(false);
    //設(shè)置代理類名稱的生成策略:Spring定義的一個生成策略 你名稱中會有“BySpringCGLIB”字樣
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
    //設(shè)置攔截器/過濾器  過濾器里面有一組回調(diào)類,也就是真正的方法攔截實例
    enhancer.setCallbackFilter(CALLBACK_FILTER);
    enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
    return enhancer;
}
   

如果你熟悉cglib的話,肯定對這幾行代碼熟悉無比,主要做了這樣幾件事!

  1. 設(shè)置需要代理的類
  2. 設(shè)置生成的代理類需要實現(xiàn)的接口,這里設(shè)置實現(xiàn)了 EnhancedConfiguration,注意這個是一個很騷的操作,他是能夠保證最終類能夠從beanFactory返回的一個重要邏輯,為什么?因為 EnhancedConfigurationBeanFactoryAware的子類,Spring會回調(diào)他,給他設(shè)置一個 beanFactory ,如果你看不懂不妨先把和這個記下來,等看完在回來仔細(xì)品味一下!
  3. 設(shè)置過濾器,過濾器里面其實是一組回調(diào)方法,這個回調(diào)方法是最終方法被攔截后執(zhí)行的真正邏輯,我們一會要分析的也是過濾器里面這一組回調(diào)實例!
  4. 返回最終的增強器!

剛剛也說了,我們需要重點關(guān)注的是這一組攔截方法,我們進(jìn)入到攔截器里面,找到對應(yīng)的回調(diào)實例!

CALLBACK_FILTER:常量對應(yīng)的是一個過濾器,我們看它如何實現(xiàn)的:

private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
   

那么此時 CALLBACKS 就是我么要找的回調(diào)方法,點進(jìn)去可以看到:

// 要使用的回調(diào)。請注意,這些回調(diào)必須是無狀態(tài)的。
private static final Callback[] CALLBACKS = new Callback[] {
    //這個是真正能夠Bean方法多次調(diào)用返回的是一個bean實例的實際攔截方法,這個攔截器就是完全能夠說明,為什么多次調(diào)用只返回
    //一個實例的問題
    new BeanMethodInterceptor(),
    //攔截 BeanFactoryAware 為里面的 setBeanFactory 賦值
    //剛剛也說了,增強類會最終實現(xiàn) BeanFactoryAware 接口,這里就是攔截他的回調(diào)方法 setBeanFactory方法,獲取bean工廠!
    new BeanFactoryAwareMethodInterceptor(),
    //這個說實話  真魔幻  我自己實現(xiàn)cglib的時候一直在報錯  報一個自己拋出的異常,異常原因是沒有處理object里面的eques等
    //方法,這個就是為了處理那些沒有被攔截的方法的實例  這個些方法直接放行
    //這個實例里面沒有實現(xiàn)任何的東西,空的,代表著不處理!
    NoOp.INSTANCE
};
   

具體里面每一個攔截器究竟是干嘛的,注釋說的很明白,我們從第二個說起!為什么不從第一個呢?第一個比較麻煩,我們由淺入深,逐步的說!

BeanFactoryAwareMethodInterceptor

/**
  * 攔截對任何{@link BeanFactoryAware#setBeanFactory(BeanFactory)}的調(diào)用 {@code @Configuration}類實例,用于記錄{@link BeanFactory}。
  * @see EnhancedConfiguration
  */
private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {

    @Override
    @Nullable
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //找到本類(代理類)里名為`$$beanFactory`的字段
        Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
        //若沒找到直接報錯。若找到了此字段,就給此字段賦值
        Assert.state(field != null, "Unable to find generated BeanFactory field");
        field.set(obj, args[0]);

        // 實際的(非CGLIB)超類是否實現(xiàn)BeanFactoryAware?
        // 如果是這樣,請調(diào)用其setBeanFactory()方法。如果沒有,請退出。
        //如果用戶類(也就是你自己定義的類)自己實現(xiàn)了該接口,那么別擔(dān)心,也會給你賦值上
        if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
            return proxy.invokeSuper(obj, args);
        }
        return null;
    }

    /**
  * 執(zhí)行到setBeanFactory(xxx)方法時匹配成功
  * @param candidateMethod 當(dāng)前執(zhí)行的方法
  * @return
  */
    @Override
    public boolean isMatch(Method candidateMethod) {
        //判斷方法是不是 `setBeanFactory` 方法 
        return isSetBeanFactory(candidateMethod);
    }
    
    .........忽略不必要邏輯.........
}
   

不知道你注意沒有,在最終生成的代理配置類里面有一個 $$beanFactory屬性,這個屬性就是在這里被賦值的!再把圖片放出來,看最后一個屬性!

Spring中的@Configuration配置類是怎樣的      
被代理的Spring配置類

這個攔截器的主要作用:

  1. 攔截 setBeanFactory方法,為 $$beanFactory賦值!

好了,這個攔截器介紹完了,功能大家也記住了,那么,我們分析下一個攔截器,這個是重點!

BeanMethodInterceptor

/**
 * 增強{@link Bean @Bean}方法以檢查提供的BeanFactory中的 這個bean對象的存在。
 * @throws Throwable 作為所有在調(diào)用時可能引發(fā)的異常的統(tǒng)籌 代理方法的超級實現(xiàn),即實際的{@code @Bean}方法
 * 當(dāng)該方法經(jīng)過匹配成功后 會進(jìn)入到這個攔截方法  這個是解決bean方法只被創(chuàng)建一次的重要邏輯
 */
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                        MethodProxy cglibMethodProxy) throws Throwable {
    //通過反射,獲取到Bean工廠。也就是 $$beanFactory 這個屬性的值
    //也就是上一個攔截器被注入的值
    ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
    //拿到Bean的名稱
    String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

    // 確定此bean是否為作用域代理
    //方法頭上是否標(biāo)注有@Scoped注解
    if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
        String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
        if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
            beanName = scopedBeanName;
        }
    }
    。。。。。。忽略與本題無關(guān)的代碼。。。。。。。。。。
        
    // 檢查給定的方法是否與當(dāng)前調(diào)用的容器相對應(yīng)工廠方法。
    // 比較方法名稱和參數(shù)列表來確定是否是同一個方法
    // 怎么理解這句話,參照下面詳解吧
    //在整個方法里面,我認(rèn)為這個判斷是核心,為什么說他是核心,因為只有這個判斷返回的是false的時候他才會真正的走增強的邏輯
    //什么時候會是false呢?
    //首先  spring會獲取到當(dāng)前使用的方法   其次會獲取當(dāng)前調(diào)用的方法,當(dāng)兩個方法不一致的時候會返回false
    //什么情況下胡不一致呢?
    //當(dāng)在bean方法里面調(diào)用了另一個方法,此時當(dāng)前方法和調(diào)用方法不一致,導(dǎo)致返回課false然后去執(zhí)行的增強邏輯
    if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
        // 這是個小細(xì)節(jié):若你@Bean返回的是BeanFactoryPostProcessor類型
        // 請你使用static靜態(tài)方法,否則會打印這句日志的~~~~
        // 因為如果是非靜態(tài)方法,部分后置處理失效處理不到你,可能對你程序有影像
        // 當(dāng)然也可能沒影響,所以官方也只是建議而已~~~
        if (logger.isInfoEnabled() &&
            BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
            ...... 忽略日志打印......
        }
        // 這表示:當(dāng)前方法,就是這個被攔截的方法,那就沒啥好說的
        // 相當(dāng)于在代理代理類里執(zhí)行了super(xxx);
        // 但是,但是,但是,此時的this依舊是代理類
        //這個事實上上調(diào)用的是本身的方法  最終會再次被調(diào)用到下面的 resolveBeanReference 方法
        //這里的設(shè)計很奇妙  為什么這么說呢?
        //了解這個方法首先要對cglib有一個基礎(chǔ)的認(rèn)識 為什么這么說唄?
        //首先要明白 cglib是基于子類集成的方式去增強的目標(biāo)方法的
        //所以在不進(jìn)行增強的時候就可以以很輕松的調(diào)用父類的原始方法去執(zhí)行實現(xiàn)
        //當(dāng)前調(diào)用的方法和調(diào)用的方法是一個方法的時候  就直接調(diào)用cglib父類  也就是原始類的創(chuàng)建方法直接創(chuàng)建
        //當(dāng)不一樣的時候  會進(jìn)入到下面的方法  直接由beanFactory返回  精妙?。?br/>        return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
    }
    //方法里調(diào)用的實例化方法會交給這里來執(zhí)行
    //這一步的執(zhí)行是真正的執(zhí)行方式,當(dāng)發(fā)現(xiàn)該方法需要代理的時候不調(diào)用父類的原始方法
    //而是調(diào)用我需要代理的邏輯去返回一個對象,從而完成對對象的代理
    return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
   

乍一看,是不是好多,沒事我們一點一點分析:

  1. 首先我么看那個判斷 if (isCurrentlyInvokedFactoryMethod(beanMethod))這個判斷是很重要的!他就是從 ThreadLocal里面取出本次調(diào)用的工廠方法,前面提到過很多次工廠方法,什么是工廠方法?就是你寫的那個@Bean對應(yīng)的方法,我們就叫做工廠方法,我們以上面 開篇一問里的那個代碼為例!
    • 當(dāng)創(chuàng)建         UserServiceImpl的時候,會先存儲當(dāng)前的方法對象也就是         UserServiceImpl的方法對象,也就是放置到         ThreadLocal里面去!
    • 然后發(fā)現(xiàn)是一個代理對象,進(jìn)入到代理邏輯,在代理邏輯里面,走到這個判斷邏輯,發(fā)現(xiàn)本次攔截的方法和         ThreadLocal里面的方法是一致的,然后就放行,開始調(diào)用真正的         userService()方法,執(zhí)行這個方法的時候,方法內(nèi)部調(diào)用了         testService();方法!
    • 發(fā)現(xiàn)         testService()又是一個代理對象,于是又走代理邏輯,然后走到這個判斷,判斷發(fā)現(xiàn)當(dāng)前攔截的方法是         testService而ThreadLocal里面的方法卻是         userService,此時判斷就失敗了,于是就走到另外一個分支!
    • 另外一個分支就不再執(zhí)行這個方法了,而是直接去beanFactory去取這個bean,直接返回!
  2. return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);這個是當(dāng)攔截的方法是工廠方法的時候直接放行,執(zhí)行父類的邏輯,為什么是父類!Cglib是基于繼承來實現(xiàn)的,他的父類就是原始的那個沒有經(jīng)過代理的方法,相當(dāng)于調(diào)用 super.userService()去調(diào)用原始邏輯!
  3. resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);這個也是一會我們要看的代碼邏輯,這個就是當(dāng)判斷不成立,也就是發(fā)現(xiàn)工廠方法里面還調(diào)用了另外一個工廠方法的時候,會進(jìn)入到這里面!那我們看一下這里面的邏輯吧!

resolveBeanReference方法邏輯

private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
                                    ConfigurableBeanFactory beanFactory, String beanName) {
  。。。。。。。。。忽略不必要代碼。。。。。。。。。
        //通過getBean從容器中拿到這個實例
        //這個beanFactory是哪里來的,就是第一個攔截器里面注入的`$$beanFactory`
        Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
                               beanFactory.getBean(beanName));

        。。。。。。。。。忽略不必要代碼。。。。。。。。。
        return beanInstance;
    }
 
}
   

這里面的主要邏輯就是從beanFactory里面獲取這個方法對應(yīng)的bean對象,直接返回!而不是再去調(diào)用對應(yīng)的方法創(chuàng)建!這也就是為什么多次調(diào)用,返回的實例永遠(yuǎn)只是一個的原因!

   

六、總結(jié)

整個過程比較繞,讀者可以自己跟著文章調(diào)試一下源碼,相信經(jīng)過過深度思考,你一定有所收獲!

整個過程分為兩大部分:

  1. 增強配置類
    • 檢測加了         @Configuration注解的配置類!
    • 創(chuàng)建代理對象(BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor)作為增強器的回調(diào)方法!
    • 返回代理后的類對象!
    • 設(shè)置進(jìn)配置類的beanClass!
  2. 創(chuàng)建bean
    • 一致的話就走原始的創(chuàng)建邏輯!
    • 不一致,就從bean工廠獲取!
    • 發(fā)現(xiàn)該bean創(chuàng)建的時候依附配置類(也就是加了@Bean的方法)!
    • 回調(diào)增強配置類的方法,并記錄該方法!
    • 判斷攔截的方法和記錄的方法是否一致
    • 返回創(chuàng)建好的bean

以上就是Spring中的@Configuration配置類是怎樣的,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降摹OM隳芡ㄟ^這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI