亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring實(shí)現(xiàn)三級(jí)緩存機(jī)制

 更新時(shí)間:2025年02月28日 09:22:25   作者:碼上生花  
三級(jí)緩存機(jī)制是Spring解決循環(huán)依賴問(wèn)題的關(guān)鍵,本文主要介紹了Spring實(shí)現(xiàn)三級(jí)緩存機(jī)制,具有一定的參考價(jià)值,感興趣的可以了解一下

一、什么是三級(jí)緩存

就是在Bean生成流程中保存Bean對(duì)象三種形態(tài)的三個(gè)Map集合,如下:

// 一級(jí)緩存Map 存放完整的Bean(流程跑完的)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);

// 二級(jí)緩存Map 存放不完整的Bean(只實(shí)例化完,還沒(méi)屬性賦值、初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);

// 三級(jí)緩存Map 存放一個(gè)Bean的lambda表達(dá)式(也是剛實(shí)例化完)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);

用來(lái)解決什么問(wèn)題?

這個(gè)大家應(yīng)該熟知了,就是循環(huán)依賴

什么是循環(huán)依賴?

就像下面這樣,AService 中注入了BService ,而B(niǎo)Service 中又注入了AService ,這就是循環(huán)依賴

@Service
public class AService {

    @Resource
    private BService bService;
}

@Service
public class BService {

    @Resource
    private AService aService;
}

二、Bean的加載源碼

我們先通過(guò)getBean()流程圖,來(lái)了解Spring的getBean()方法的工作流程,接著根據(jù)這個(gè)工作流程一步一步的閱讀源碼

在這里插入圖片描述

//在spring中我們平時(shí)用到的getbean()這個(gè)方法實(shí)際上是調(diào)用的AbstractBeanFactory這個(gè)抽象工廠中得getbean方法
public Object getBean(String name) throws BeansException {
//看源碼 我們首先看其返回值   如下返回得是doGetBean這個(gè)方法
        return this.doGetBean(name, null, null, false);
        }
//接下來(lái)我們看這個(gè)doGetBean這個(gè)方法
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {

 //獲取name對(duì)應(yīng)的真正beanName
//有這么幾種情況 傳入得參數(shù)有可能是某個(gè)參數(shù)得別名,也有可能是FactoryBean的name  //根據(jù)具體得實(shí)例去解析最終的得name
final String beanName = this.transformedBeanName(name);

        Object bean;

      //   在創(chuàng)建單例bean的時(shí)候會(huì)存在依賴注入的情況,而在創(chuàng)建依賴的時(shí)候?yàn)榱吮苊庋h(huán)依賴
       //  Spring創(chuàng)建bean的原則是不等bean創(chuàng)建完成就會(huì)將創(chuàng)建bean的ObjectFactory提前曝光(將對(duì)應(yīng)的ObjectFactory加入到緩存)
      //   一旦下一個(gè)bean創(chuàng)建需要依賴上一個(gè)bean,則直接使用ObjectFactory對(duì)象
        // 獲取單例
        Object sharedInstance = this.getSingleton(beanName);
        if (sharedInstance != null && args == null) {
        // 實(shí)例已經(jīng)存在
        if (logger.isDebugEnabled()) {
	        if (this.isSingletonCurrentlyInCreation(beanName)) {
	        	logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
	        } else {
	        	logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
	        }
        }
        // 返回對(duì)應(yīng)的實(shí)例
        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, null);
        } else {
	        // 單例實(shí)例不存在
	        if (this.isPrototypeCurrentlyInCreation(beanName)) {
	         // 只有在單例模式下才會(huì)嘗試解決循環(huán)依賴問(wèn)題
	         // 對(duì)于原型模式,如果存在循環(huán)依賴,也就是滿足this.isPrototypeCurrentlyInCreation(beanName),拋出異常
	        throw new BeanCurrentlyInCreationException(beanName);
        }

        // 獲取parentBeanFactory實(shí)例
        BeanFactory parentBeanFactory = this.getParentBeanFactory();
        // 如果在beanDefinitionMap中(即所有已經(jīng)加載的類中)不包含目標(biāo)bean,則嘗試從parentBeanFactory中獲取
        if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
        	String nameToLookup = this.originalBeanName(name);  // 獲取name對(duì)應(yīng)的真正beanName,如果是factoryBean,則加上“&”前綴
	        if (args != null) {
	        // 遞歸到BeanFactory中尋找
	        	return (T) parentBeanFactory.getBean(nameToLookup, args);
	        } else {
	        	return parentBeanFactory.getBean(nameToLookup, requiredType);
	        }
        }

        // 如果不僅僅是做類型檢查,標(biāo)記bean的狀態(tài)已經(jīng)創(chuàng)建,即將beanName加入alreadyCreated集合中
        if (!typeCheckOnly) {
        	this.markBeanAsCreated(beanName);
        }

        try {
		 //將存儲(chǔ)XML配置的GenericBeanDefinition實(shí)例轉(zhuǎn)換成RootBeanDefinition實(shí)例,方便后續(xù)處理
		 // 如果存在父bean,則同時(shí)合并父bean的相關(guān)屬性
		final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
        // 檢查bean是否是抽象的,如果是則拋出異常
        this.checkMergedBeanDefinition(mbd, beanName, args);

        // 加載當(dāng)前bean依賴的bean
        String[] dependsOn = mbd.getDependsOn();
        if (dependsOn != null) {
        // 存在依賴,遞歸實(shí)例化依賴的bean
        for (String dep : dependsOn) {
        if (this.isDependent(beanName, dep)) {
        // 檢查dep是否依賴beanName,從而導(dǎo)致循環(huán)依賴
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
        }
        // 緩存依賴調(diào)用
        this.registerDependentBean(dep, beanName);
        this.getBean(dep);
        }
        }

        // 完成加載依賴的bean后,實(shí)例化mbd自身
        if (mbd.isSingleton()) {
        // scope == singleton
        sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
        try {
        return createBean(beanName, mbd, args);
        } catch (BeansException ex) {
        // 清理工作,從單例緩存中移除
        destroySingleton(beanName);
        throw ex;
        }
        }
        });
        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        } else if (mbd.isPrototype()) {
        // scope == prototype
        Object prototypeInstance;
        try {
        // 設(shè)置正在創(chuàng)建的狀態(tài)
        this.beforePrototypeCreation(beanName);
        // 創(chuàng)建bean
        prototypeInstance = this.createBean(beanName, mbd, args);
        } finally {
        this.afterPrototypeCreation(beanName);
        }
        // 返回對(duì)應(yīng)的實(shí)例
        bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        } else {
        // 其它范圍得實(shí)例
        String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
        if (scope == null) {
        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
        }
        try {
        Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
        beforePrototypeCreation(beanName);
        try {
        return createBean(beanName, mbd, args);
        } finally {
        afterPrototypeCreation(beanName);
        }
        }
        });
        // 返回對(duì)應(yīng)的實(shí)例
        bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
        } catch (IllegalStateException ex) {
        throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);
        }
        }
        } catch (BeansException ex) {
        cleanupAfterBeanCreationFailure(beanName);
        throw ex;
        }
        }

        // 檢查需要的類型是否符合bean的實(shí)際類型,對(duì)應(yīng)getBean時(shí)指定的需要類型
        if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
        try {
        // 執(zhí)行類型轉(zhuǎn)換,轉(zhuǎn)換成對(duì)應(yīng)的類型
        return this.getTypeConverter().convertIfNecessary(bean, requiredType);
        } catch (TypeMismatchException ex) {
        if (logger.isDebugEnabled()) {
        logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex);
        }
        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
        }
        到這里才是真正意義上返回一個(gè)bean而已
        return (T) bean;
        }

三、三級(jí)緩存詳解

不管你了不了解源碼,我們先看一下Bean的生成流程,看看三級(jí)緩存是在什么地方有調(diào)用,就三個(gè)地方:

  • Bean實(shí)例化前會(huì)先查詢緩存,判斷Bean是否已經(jīng)存在
  • Bean屬性賦值前會(huì)先向三級(jí)緩存中放入一個(gè)lambda表達(dá)式,該表達(dá)式執(zhí)行則會(huì)生成一個(gè)半成品Bean放入二級(jí)緩存
  • Bean初始化完成后將完整的Bean放入一級(jí)緩存,同時(shí)清空二、三級(jí)緩存

接下來(lái)我們一個(gè)一個(gè)看!

在這里插入圖片描述

3.1 Bean實(shí)例化前

AbstractBeanFactory.doGetBean

Bean實(shí)例化前會(huì)從緩存里面獲取Bean,防止重復(fù)實(shí)例化

在這里插入圖片描述

DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference)

我們看看這個(gè)獲取的方法邏輯:

  • 從一級(jí)緩存獲取,獲取到了,則返回
  • 從二級(jí)緩存獲取,獲取到了,則返回
  • 從三級(jí)緩存獲取,獲取到了,則執(zhí)行三級(jí)緩存中的lambda表達(dá)式,將結(jié)果放入二級(jí)緩存,清除三級(jí)緩存
public Object getSingleton(String beanName) {
    return this.getSingleton(beanName, true);
}

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 從一級(jí)緩存中獲取Bean 獲取到了則返回 沒(méi)獲取到繼續(xù)
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        // 從二級(jí)緩存中獲取Bean  獲取到了則返回 沒(méi)獲取到則繼續(xù)
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            // 加一把鎖防止 線程安全 雙重獲取校驗(yàn)
            synchronized(this.singletonObjects) {
                // 從一級(jí)緩存中獲取Bean 獲取到了則返回 沒(méi)獲取到繼續(xù)
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    // 從二級(jí)緩存中獲取Bean  獲取到了則返回 沒(méi)獲取到則繼續(xù)
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // 從三級(jí)緩存中獲取 沒(méi)獲取到則返回
                        ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            // 獲取到了 執(zhí)行三級(jí)緩存中的lambda表達(dá)式
                            singletonObject = singletonFactory.getObject();
                            // 并將結(jié)果放入二級(jí)緩存
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 從三級(jí)緩存中移除
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }

    return singletonObject;
}

3.2 屬性賦值/注入前

AbstractAutowireCapableBeanFactory.doCreateBean

在這里插入圖片描述

DefaultSingletonBeanRegistry.addSingletonFactory

這里就是將一個(gè)lambda表達(dá)式放入了三級(jí)緩存,我們需要去看一下這個(gè)表達(dá)式是干什么的??!

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized(this.singletonObjects) {
        // 一級(jí)緩存中不存在的話 
        if (!this.singletonObjects.containsKey(beanName)) {
            // 將lambda表達(dá)式放入三級(jí)緩存
            this.singletonFactories.put(beanName, singletonFactory);
            // 清除二級(jí)緩存 
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

AbstractAutowireCapableBeanFactory.getEarlyBeanReference

該方法說(shuō)白了就是會(huì)判斷該Bean是否需要被動(dòng)態(tài)代理,兩種返回結(jié)果:

  • 不需要代理,返回未屬性注入、未初始化的半成品Bean
  • 需要代理,返回未屬性注入、未初始化的半成品Bean的代理對(duì)象
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
        Iterator var5 = this.getBeanPostProcessors().iterator();
        // 遍歷后置處理器
        while(var5.hasNext()) {
            BeanPostProcessor bp = (BeanPostProcessor)var5.next();
            // 找到實(shí)現(xiàn)SmartInstantiationAwareBeanPostProcessor接口的
            // 該接口getEarlyBeanReference方法什么時(shí)候會(huì)執(zhí)行?
            // AOP動(dòng)態(tài)代理的時(shí)候 該方法執(zhí)行就是判斷該Bean是否需要被代理
            // 需要代理則會(huì)創(chuàng)建代理對(duì)象返回
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor)bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    // 這個(gè)Object有兩種情況,一是實(shí)例化后的半成品Bean,二是半成品Bean動(dòng)態(tài)代理后的代理對(duì)象
    return exposedObject;
}

注意:這里只是把lambda表達(dá)式放入了三級(jí)緩存,如果不從三級(jí)緩存中獲取,這個(gè)表達(dá)式是不執(zhí)行的,一旦執(zhí)行了,就會(huì)把半成品Bean或者半成品Bean的代理對(duì)象放入二級(jí)緩存中了

3.3初始化后

AbstractBeanFactory.doGetBean

這里注意啊,這個(gè)getSingleton方法傳參傳了個(gè)lambda表達(dá)式,這個(gè)表達(dá)式內(nèi)部就是Bean的實(shí)例化過(guò)程,初始化完成后,是要需要執(zhí)行這個(gè)getSingleton方法的

在這里插入圖片描述

DefaultSingletonBeanRegistry.getSingleton(beanName, singletonFactory)

這個(gè)方法與上面那個(gè)不一樣,重載了

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
     
        synchronized(this.singletonObjects) {
            // 第一次進(jìn)來(lái)這里獲取肯定為null
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               // 省略................
                try {
                    // 注意啊,這個(gè)就是執(zhí)行外面那個(gè)傳參的lambda表達(dá)式
                    // 所以這里才會(huì)跳到createBean方法那里去執(zhí)行
                    singletonObject = singletonFactory.getObject();
                    newSingleton = true;
                } 
                // 省略................
                finally {
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = null;
                    }
                    this.afterSingletonCreation(beanName);
                }
                // 到了這說(shuō)明Bean創(chuàng)建完了
                if (newSingleton) {
                    // 這里就會(huì)把Bean放入一級(jí)緩存中了 同時(shí)清除二、三級(jí)緩存
                    this.addSingleton(beanName, singletonObject);
                }
            }
            return singletonObject;
        }
    }

DefaultSingletonBeanRegistry.addSingleton

protected void addSingleton(String beanName, Object singletonObject) {
  synchronized(this.singletonObjects) {
     // 放入一級(jí)緩存  
      this.singletonObjects.put(beanName, singletonObject);
      // 清除二、三級(jí)緩存
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
  }
}

總結(jié)

整個(gè)過(guò)程就三個(gè)地方跟緩存有關(guān),我們假設(shè)現(xiàn)在要實(shí)例化A這個(gè)Bean,看看緩存是怎么變化的:

  • 實(shí)例化前,獲取緩存判斷(三個(gè)緩存中肯定沒(méi)有A,獲取為null,進(jìn)入實(shí)例化流程)
  • 實(shí)例化完成,屬性注入前(往三級(jí)緩存中放入了一個(gè)lambda表達(dá)式,一、二級(jí)為null)
  • 初始化完成(將A這個(gè)Bean放入一級(jí)緩存,清除二、三級(jí)緩存)

以上則是單個(gè)Bean生成過(guò)程中緩存的變化??!

四、怎么解決的循環(huán)依賴

上面我們把Bean流程中利用緩存的三個(gè)重要的點(diǎn)都找出來(lái)了,也分析了會(huì)帶來(lái)什么變化,接下來(lái)看看是怎么解決的循環(huán)依賴,我們看個(gè)圖就懂了:

以A注入B,B注入A為例:

A屬性注入前就把lambda表達(dá)式放入了第三級(jí)緩存,所以B再注入A的時(shí)候會(huì)從第三級(jí)緩存中找到A的lambda表達(dá)式并執(zhí)行,然后將半成品Bean放入第二級(jí)緩存,所以此時(shí)B注入的只是半成品的A對(duì)象,B創(chuàng)建完成后返回給A注入,A繼續(xù)初始化,完成創(chuàng)建。

注意: B注入的半成品A對(duì)象只是一個(gè)引用,所以之后A初始化完成后,B這個(gè)注入的A就隨之變成了完整的A

在這里插入圖片描述

從上述看第三級(jí)緩存是用來(lái)提前暴露Bean對(duì)象引用的,所以解決了循環(huán)依賴,但是第二級(jí)緩存的這個(gè)半成品Bean對(duì)象干嘛的呢?

假設(shè)A同時(shí)注入了B和C,B和C又都注入了A,這時(shí)A注入B,實(shí)例化B的過(guò)程和上述是一樣的,但隨后還會(huì)注入C,那這個(gè)C在注入A的時(shí)候還會(huì)有第三級(jí)緩存用嗎?沒(méi)了吧,所以它就只能用第二級(jí)緩存的半成品Bean對(duì)象了,同樣也是引用而已

五、不用三級(jí)緩存不行嗎

可能很多小伙伴得到的答案就是不行,而且答案是因?yàn)椴淮_定這個(gè)Bean是不是代理對(duì)象,所以搞了個(gè)lambda表達(dá)式?答案真的是這樣嗎??

我們分析一下:AOP動(dòng)態(tài)代理在沒(méi)有循環(huán)依賴的時(shí)候是在哪里執(zhí)行的?Bean初始化后!有循環(huán)依賴的時(shí)候是在屬性賦值前,中間就間隔了一個(gè)屬性注入對(duì)吧,沒(méi)錯(cuò),在屬性注入的時(shí)候注入的是原始對(duì)象的引用還是代理對(duì)象的引用這個(gè)很重要,但是屬性注入會(huì)影響AOP的結(jié)果嗎?是否AOP創(chuàng)建代理對(duì)象和切面有關(guān),和屬性注入無(wú)關(guān),所以我們完全可以在屬性注入之前就知道這個(gè)Bean是代理對(duì)象還是非代理對(duì)象,就像下面這樣,我不將表達(dá)式放入第三級(jí)緩存了,而是直接執(zhí)行,將結(jié)果放入第二級(jí)緩存

在這里插入圖片描述

這樣可不可以?可以吧,這樣用二級(jí)緩存就解決了,但是在一個(gè)對(duì)象沒(méi)有屬性賦值、初始化前就創(chuàng)建代理對(duì)象是有風(fēng)險(xiǎn)的!像這么做不管有沒(méi)有產(chǎn)生循環(huán)依賴,只要有AOP動(dòng)態(tài)代理對(duì)象的產(chǎn)生就有一分風(fēng)險(xiǎn),這么做是得不償失的,所以有了三級(jí)緩存,三級(jí)緩存是只有在循環(huán)依賴以及AOP動(dòng)態(tài)代理同時(shí)產(chǎn)生時(shí)才會(huì)有風(fēng)險(xiǎn)??梢哉f(shuō)是因?yàn)榇嬖谘h(huán)依賴所以被迫的導(dǎo)致Bean對(duì)象提前的暴露了引用?。?! 所以這下懂了吧

至于為什么多例、構(gòu)造器注入這兩種情況解決不了循環(huán)依賴就很簡(jiǎn)單了:

循環(huán)依賴的解決原理是在對(duì)象實(shí)例化后提前暴露了引用,而這兩種情況都還沒(méi)實(shí)例化呢

六、總結(jié)

  • 一級(jí)緩存:用于存儲(chǔ)被完整創(chuàng)建了的bean。也就是完成了初始化之后,可以直接被其他對(duì)象使用的bean。
  • 二級(jí)緩存:用于存儲(chǔ)半成品的Bean。也就是剛實(shí)例化但是還沒(méi)有進(jìn)行初始化的Bean
  • 三級(jí)緩存:三級(jí)緩存存儲(chǔ)的是工廠對(duì)象(lambda表達(dá)式)。工廠對(duì)象可以產(chǎn)生Bean對(duì)象提前暴露的引用(半成品的Bean或者半成品的代理Bean對(duì)象),執(zhí)行這個(gè)lambda表達(dá)式,就會(huì)將引用放入二級(jí)緩存中

經(jīng)過(guò)以上的分析,現(xiàn)在應(yīng)該懂了吧:

循環(huán)依賴是否一定需要三級(jí)緩存來(lái)解決? 不一定,但三級(jí)緩存會(huì)更合適,風(fēng)險(xiǎn)更小

二級(jí)緩存能否解決循環(huán)依賴? 可以,但風(fēng)險(xiǎn)比三級(jí)緩存更大

第二級(jí)緩存用來(lái)干嘛的? 存放半成品的引用,可能產(chǎn)生多對(duì)象循環(huán)依賴,第三級(jí)緩存產(chǎn)生引用后,后續(xù)的就可以直接注入該引用

多例、構(gòu)造器注入為什么不能解決循環(huán)依賴? 因?yàn)檠h(huán)依賴的原理的實(shí)例化后提前暴露的引用,這兩種情況還沒(méi)實(shí)例化

到此這篇關(guān)于Spring實(shí)現(xiàn)三級(jí)緩存機(jī)制的文章就介紹到這了,更多相關(guān)Spring 三級(jí)緩存機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 2024版本IDEA創(chuàng)建Servlet模板的圖文教程

    2024版本IDEA創(chuàng)建Servlet模板的圖文教程

    新版IDEA?2024.1.4中,用戶需要自行創(chuàng)建Servlet模板以解決Web項(xiàng)目無(wú)法通過(guò)右鍵創(chuàng)建Servlet的問(wèn)題,本文詳細(xì)介紹了添加ServletAnnotatedClass.java模板的步驟,幫助用戶快速配置并使用新的Servlet模板,需要的朋友可以參考下
    2024-10-10
  • Java之WeakHashMap源碼淺析

    Java之WeakHashMap源碼淺析

    這篇文章主要介紹了Java之WeakHashMap源碼淺析,WeakHashMap從名字可以得知主要和Map有關(guān),不過(guò)還有一個(gè)Weak,我們就更能自然而然的想到這里面還牽扯到一種弱引用結(jié)構(gòu),因此想要徹底搞懂,我們還需要知道四種引用,需要的朋友可以參考下
    2023-09-09
  • Spring Boot 集成Mybatis實(shí)現(xiàn)主從(多數(shù)據(jù)源)分離方案示例

    Spring Boot 集成Mybatis實(shí)現(xiàn)主從(多數(shù)據(jù)源)分離方案示例

    本篇文章主要介紹了Spring Boot 集成Mybatis實(shí)現(xiàn)主從(多數(shù)據(jù)源)分離方案實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-03-03
  • c#和java base64不一致的解決方法

    c#和java base64不一致的解決方法

    最近非常郁悶的處理這個(gè)base64的問(wèn)題,同樣的一個(gè)圖片文件,在java和c#進(jìn)行base64編碼后結(jié)果不一樣,苦惱了很久,下面這篇文章主要給大家介紹了關(guān)于c#和java base64不一致的解決方法,需要的朋友可以參考下
    2018-11-11
  • Jeecg-Boot異常處理'jeecg-boot.QRTZ_LOCKS'?doesn't?exist問(wèn)題

    Jeecg-Boot異常處理'jeecg-boot.QRTZ_LOCKS'?doesn'

    這篇文章主要介紹了Jeecg-Boot異常處理'jeecg-boot.QRTZ_LOCKS'?doesn't?exist問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • JVM:晚期(運(yùn)行期)優(yōu)化的深入理解

    JVM:晚期(運(yùn)行期)優(yōu)化的深入理解

    今天小編就為大家分享一篇關(guān)于JVM:晚期(運(yùn)行期)優(yōu)化的深入理解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Java的數(shù)據(jù)類型和參數(shù)傳遞(詳解)

    Java的數(shù)據(jù)類型和參數(shù)傳遞(詳解)

    下面小編就為大家?guī)?lái)一篇Java的數(shù)據(jù)類型和參數(shù)傳遞(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • spring boot 集成dubbo的示例演示

    spring boot 集成dubbo的示例演示

    這篇文章主要介紹了spring boot 集成dubbo的示例演示,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • SpringCloud Alibaba 基本開(kāi)發(fā)框架搭建過(guò)程

    SpringCloud Alibaba 基本開(kāi)發(fā)框架搭建過(guò)程

    這篇文章主要介紹了SpringCloud Alibaba 基本開(kāi)發(fā)框架搭建過(guò)程,開(kāi)發(fā)工具選用的idea,本文通過(guò)圖文實(shí)例相結(jié)合給大家分享搭建全過(guò)程,需要的朋友可以參考下
    2021-06-06
  • 攔截Druid數(shù)據(jù)源自動(dòng)注入帳密解密實(shí)現(xiàn)詳解

    攔截Druid數(shù)據(jù)源自動(dòng)注入帳密解密實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了攔截Druid數(shù)據(jù)源自動(dòng)注入帳密解密實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11

最新評(píng)論