欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot1.X和2.X中如何解決Bean名字相同時覆蓋

 更新時間:2022年03月24日 14:41:29   作者:liuhmmjj  
這篇文章主要介紹了springboot1.X和2.X中如何解決Bean名字相同時覆蓋,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

如何解決Bean名字相同時覆蓋

在2版本之前的版本,項目中有兩個相同名字的bean是可以啟動成功的,但是會有覆蓋問題

但是在2.X版本的時候會報錯:

could not be registered. A bean with that name has already been defined in class path resource

這時候解決辦法可以在配置文件中添加:

spring.main.allow-bean-definition-overriding=true
/** 是否允許使用相同名稱重新注冊不同的bean實現(xiàn). 默認是允許*/
private boolean allowBeanDefinitionOverriding = true;
?
/**
?* Set whether it should be allowed to override bean definitions by registering
?* a different definition with the same name, automatically replacing the former.
?* If not, an exception will be thrown. This also applies to overriding aliases.
?* <p>Default is "true".【這里明確說明了默認是true】
?* @see #registerBeanDefinition
?*/
public boolean isAllowBeanDefinitionOverriding() {
?? ?return this.allowBeanDefinitionOverriding;
}
?
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
?? ??? ?throws BeanDefinitionStoreException {
?
?? ?Assert.hasText(beanName, "Bean name must not be empty");
?? ?Assert.notNull(beanDefinition, "BeanDefinition must not be null");
?
?? ?if (beanDefinition instanceof AbstractBeanDefinition) {
?? ??? ?try {
?? ??? ??? ?((AbstractBeanDefinition) beanDefinition).validate();
?? ??? ?}
?? ??? ?catch (BeanDefinitionValidationException ex) {
?? ??? ??? ?throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
?? ??? ??? ??? ??? ?"Validation of bean definition failed", ex);
?? ??? ?}
?? ?}
?
?? ?//bean加載到spring的工程中后,會存儲在beanDefinitionMap中,key是bean的名稱。
?? ?BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
?? ?if (existingDefinition != null) {//不為空,說明相同名稱的bean已經(jīng)存在了
?? ??? ?if (!isAllowBeanDefinitionOverriding()) {//如果不允許相同名稱的bean存在,則直接拋出異常
?? ??? ??? ?throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
?? ??? ?}
?? ??? ?else if (existingDefinition.getRole() < beanDefinition.getRole()) {
?? ??? ??? ?// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
?? ??? ??? ?if (logger.isInfoEnabled()) {
?? ??? ??? ??? ?logger.info("Overriding user-defined bean definition for bean '" + beanName +
?? ??? ??? ??? ??? ??? ?"' with a framework-generated bean definition: replacing [" +
?? ??? ??? ??? ??? ??? ?existingDefinition + "] with [" + beanDefinition + "]");
?? ??? ??? ?}
?? ??? ?}
?? ??? ?else if (!beanDefinition.equals(existingDefinition)) {
?? ??? ??? ?if (logger.isDebugEnabled()) {
?? ??? ??? ??? ?logger.debug("Overriding bean definition for bean '" + beanName +
?? ??? ??? ??? ??? ??? ?"' with a different definition: replacing [" + existingDefinition +
?? ??? ??? ??? ??? ??? ?"] with [" + beanDefinition + "]");
?? ??? ??? ?}
?? ??? ?}
?? ??? ?else {
?? ??? ??? ?if (logger.isTraceEnabled()) {
?? ??? ??? ??? ?logger.trace("Overriding bean definition for bean '" + beanName +
?? ??? ??? ??? ??? ??? ?"' with an equivalent definition: replacing [" + existingDefinition +
?? ??? ??? ??? ??? ??? ?"] with [" + beanDefinition + "]");
?? ??? ??? ?}
?? ??? ?}
?? ??? ?//可見,上面allowBeanDefinitionOverriding =true時,只是記錄了一些日志,然后后來發(fā)現(xiàn)的這個bean,會覆蓋之前老的bean。
?? ??? ?this.beanDefinitionMap.put(beanName, beanDefinition);
?? ?}
?? ?else {
?? ??? ?if (hasBeanCreationStarted()) {
?? ??? ??? ?// Cannot modify startup-time collection elements anymore (for stable iteration)
?? ??? ??? ?synchronized (this.beanDefinitionMap) {
?? ??? ??? ??? ?this.beanDefinitionMap.put(beanName, beanDefinition);
?? ??? ??? ??? ?List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
?? ??? ??? ??? ?updatedDefinitions.addAll(this.beanDefinitionNames);
?? ??? ??? ??? ?updatedDefinitions.add(beanName);
?? ??? ??? ??? ?this.beanDefinitionNames = updatedDefinitions;
?? ??? ??? ??? ?if (this.manualSingletonNames.contains(beanName)) {
?? ??? ??? ??? ??? ?Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
?? ??? ??? ??? ??? ?updatedSingletons.remove(beanName);
?? ??? ??? ??? ??? ?this.manualSingletonNames = updatedSingletons;
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?}
?? ??? ?else {
?? ??? ??? ?// Still in startup registration phase
?? ??? ??? ?this.beanDefinitionMap.put(beanName, beanDefinition);
?? ??? ??? ?this.beanDefinitionNames.add(beanName);
?? ??? ??? ?this.manualSingletonNames.remove(beanName);
?? ??? ?}
?? ??? ?this.frozenBeanDefinitionNames = null;
?? ?}
?
?? ?if (existingDefinition != null || containsSingleton(beanName)) {
?? ??? ?resetBeanDefinition(beanName);
?? ?}
}

上面貼出來的是spring的代碼,而springboot2.X對這個參數(shù)又進行了二次封裝,springboot中的allowBeanDefinitionOverriding是沒有初始化默認值的,我們知道,java中的boolean類型不初始化時是false。

springboot中源代碼:

在SpringApplication類中

public class SpringApplication {
? ? ...
//boolean沒初始化,所以默認為false
private boolean allowBeanDefinitionOverriding;
...?
?private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
? ? ? ? context.setEnvironment(environment);
? ? ? ? this.postProcessApplicationContext(context);
? ? ? ? this.applyInitializers(context);
? ? ? ? listeners.contextPrepared(context);
? ? ? ? if (this.logStartupInfo) {
? ? ? ? ? ? this.logStartupInfo(context.getParent() == null);
? ? ? ? ? ? this.logStartupProfileInfo(context);
? ? ? ? }
?
? ? ? ? ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
? ? ? ? beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
? ? ? ? if (printedBanner != null) {
? ? ? ? ? ? beanFactory.registerSingleton("springBootBanner", printedBanner);
? ? ? ? }
? ? ? ?//將false值傳過去
? ? ? ? if (beanFactory instanceof DefaultListableBeanFactory) {
? ? ? ? ? ? ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
? ? ? ? }
?
? ? ? ? if (this.lazyInitialization) {
? ? ? ? ? ? context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
? ? ? ? }
?
? ? ? ? Set<Object> sources = this.getAllSources();
? ? ? ? Assert.notEmpty(sources, "Sources must not be empty");
? ? ? ? this.load(context, sources.toArray(new Object[0]));
? ? ? ? listeners.contextLoaded(context);
? ? }

而在1.5.8版本中,SpringApplication中,沒有allowBeanDefinitionOverriding屬性,因此在prepareContext方法中也就沒有對allowBeanDefinitionOverriding進行賦值為false,所以在springboot1.5.8版本中默認就是支持名稱相同的bean的覆蓋。

覆蓋重寫 原有Spring Bean幾種方法

什么情況下要覆寫原有的Spring Bean ? 例如引入的第三方j(luò)ar包中的某個類有些問題,然有沒有源碼提供或者嫌編譯源碼太費事,這個時間可以考慮覆寫原有的類。

方法1 直接在自己工程中建同包同類名的類進行替換

方式簡單粗暴,可以直接覆蓋掉jar包中的類,spring項目會優(yōu)先加載自定義的類。

下面是覆蓋 flowable框架中的一個類 FlowableCookieFilter,主要是想修改它里面的redirectToLogin方法的業(yè)務(wù)邏輯。包路徑為 org.flowable.ui.common.filter, 直接工程里面新建一樣路徑一樣類名FlowableCookieFilter即可。

方法2 采用@Primary注解

該方法適用于接口實現(xiàn)類,自己創(chuàng)建一個原jar包接口的實現(xiàn)類,然后類上加上@Primary注解,spring則默認加載該類實例化出的Bean。

下面的例子: 一個接口 RemoteIdmService,原先jar包中只有一個實現(xiàn)類 RemoteIdmServiceImpl,現(xiàn)在自己工程里面創(chuàng)建一個 CustomRemoteIdmServiceImpl 繼承RemoteIdmService接口,然后發(fā)現(xiàn)所有調(diào)用RemoteIdmService接口里面的方法實際調(diào)用走的是CustomRemoteIdmServiceImpl 里面的方法。

方法3 排除需要替換的jar包中的類

使用 @ComponentScan 里面的 excludeFilters 功能排除調(diào)用要替換的類,然后自己寫個類繼承替換的類即可。

下面的例子是替換掉 jar包中的PersistentTokenServiceImpl類

@SpringBootApplication
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = 
FilterType.ASSIGNABLE_TYPE, classes = {PersistentTokenServiceImpl.class})})
public class Application {
    public static void main(String[] args) {
        new SpringApplication(Test.class).run(args);
    }
}
@Service
public class MyPersistentTokenServiceImpl extends PersistentTokenServiceImpl{
    @Override
    public Token saveAndFlush(Token token) {
        // 覆寫該方法的業(yè)務(wù)邏輯
        tokenCache.put(token.getId(), token);
        idmIdentityService.saveToken(token);
        return token;
    }
    @Override
    public void delete(Token token) {
        // 覆寫該方法的業(yè)務(wù)邏輯
        tokenCache.invalidate(token.getId());
        idmIdentityService.deleteToken(token.getId());
    }
    @Override
    public Token getPersistentToken(String tokenId) {
        // 覆寫該方法的業(yè)務(wù)邏輯
        return getPersistentToken(tokenId, false);
    }
}

方法4 @Bean 覆蓋

該場景針對,框架jar包中有@ConditionalOnMissingBean注解,這種注解是說明如果你也創(chuàng)建了一個一樣的Bean則框架就不自己再次創(chuàng)建這個bean了,這樣你可以覆寫自己的bean。原jar包中的配置類:

直接繼承要覆蓋的類,自己重寫里面方法,使用@Component注入到spring中去

方法5 使用BeanDefinitionRegistryPostProcessor

關(guān)于 BeanDefinitionRegistryPostProcessor 、 BeanFactoryPostProcessor可以參考這篇文章

BeanDefinitionRegistryPostProcessor 說白了就是可以在初始化Bean之前修改Bean的屬性,甚至替換原先準(zhǔn)備要實例化的bean。

實戰(zhàn)演示:

假設(shè)jar包中有一個類 MyTestService,正常情況下它會被spring自動掃描到注入IOC容器中去。

package com.middol.mytest.config.beantest.register.jar;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
?* @author guzt
?*/
@Service("myTestService")
public class MyTestService {
? ? private String name1;
? ? private String name2;
? ? private String name3;
? ? public MyTestService() {
? ? ? ? this.name1 = "";
? ? ? ? this.name2 = "";
? ? ? ? this.name3 = "";
? ? }
? ? public MyTestService(String name1, String name2, String name3) {
? ? ? ? this.name1 = name1;
? ? ? ? this.name2 = name2;
? ? ? ? this.name3 = name3;
? ? }
? ? @PostConstruct
? ? public void init() {
? ? ? ? System.out.println("MyTestService init");
? ? }
? ? @PreDestroy
? ? public void destory() {
? ? ? ? System.out.println("MyTestService destroy");
? ? }
? ? public void show() {
? ? ? ? System.out.println("------------------------");
? ? ? ? System.out.println("我是jar中通過注解@Service主動加入Spring的IOC里面的");
? ? ? ? System.out.println("------------------------");
? ? }
? ? public String getName1() {
? ? ? ? return name1;
? ? }
? ? public void setName1(String name1) {
? ? ? ? this.name1 = name1;
? ? }
? ? public String getName2() {
? ? ? ? return name2;
? ? }
? ? public void setName2(String name2) {
? ? ? ? this.name2 = name2;
? ? }
? ? public String getName3() {
? ? ? ? return name3;
? ? }
? ? public void setName3(String name3) {
? ? ? ? this.name3 = name3;
? ? }
}

自己工程中繼承該類,并且覆寫里面的show中的方法

package com.middol.mytest.config.beantest.register;
import com.middol.mytest.config.beantest.register.jar.MyTestService;
/**
?* @author guzt
?*/
public class MyTestServiceIpml extends MyTestService {
? ? public MyTestServiceIpml() {
? ? }
? ? public MyTestServiceIpml(String name1, String name2, String name3) {
? ? ? ? super(name1, name2, name3);
? ? }
? ? @Override
? ? public void show() {
? ? ? ? System.out.println("------------------------");
? ? ? ? System.out.println("我是被BeanDefinitionRegistry手動注冊到Spring的IOC里面的");
? ? ? ? System.out.println("------------------------");
? ? }
}

然后 實現(xiàn) BeanDefinitionRegistryPostProcessor 接口,修改原來bean定義,主要查看postProcessBeanDefinitionRegistry方法的實現(xiàn),先清空原bean定義,注冊我們自己的bean定義來達到替換的目的。

package com.middol.mytest.config.beantest.register;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
?* @author amdin
?*/
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
? ? private Logger logger = LoggerFactory.getLogger(this.getClass());
? ? @Override
? ? public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
? ? ? ? logger.info("bean 定義查看和修改...");
? ? ? ? String beanName = "myTestService";
? ? ? ? // 先移除原來的bean定義
? ? ? ? beanDefinitionRegistry.removeBeanDefinition(beanName);
? ? ? ? // 注冊我們自己的bean定義
? ? ? ? BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(MyTestServiceIpml.class);
? ? ? ? // 如果有構(gòu)造函數(shù)參數(shù), 有幾個構(gòu)造函數(shù)的參數(shù)就設(shè)置幾個 ?沒有就不用設(shè)置
? ? ? ? beanDefinitionBuilder.addConstructorArgValue("構(gòu)造參數(shù)1");
? ? ? ? beanDefinitionBuilder.addConstructorArgValue("構(gòu)造參數(shù)2");
? ? ? ? beanDefinitionBuilder.addConstructorArgValue("構(gòu)造參數(shù)3");
? ? ? ? // 設(shè)置 init方法 沒有就不用設(shè)置
? ? ? ? beanDefinitionBuilder.setInitMethodName("init");
? ? ? ? // 設(shè)置 destory方法 沒有就不用設(shè)置
? ? ? ? beanDefinitionBuilder.setDestroyMethodName("destory");
? ? ? ? // 將Bean 的定義注冊到Spring環(huán)境
? ? ? ? beanDefinitionRegistry.registerBeanDefinition("myTestService", beanDefinitionBuilder.getBeanDefinition());
? ? }
? ? @Override
? ? public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
? ? ? ? // bean的名字為key, bean的實例為value
? ? ? ? Map<String, Object> beanMap = configurableListableBeanFactory.getBeansWithAnnotation(RestController.class);
? ? ? ? logger.info("所有 RestController 的bean {}", beanMap);
? ? }
}

寫一個 業(yè)務(wù)類BusinessTestService測試一下,期望結(jié)果:所有用到 MyTestService的地方實際調(diào)用的變成了MyTestServiceIpml里面的方法。

package com.middol.mytest.config.beantest.register;
import com.middol.mytest.config.beantest.register.jar.MyTestService;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
?* @author guzt
?*/
@Service
public class BusinessTestService {
? ? @Resource
? ? private MyTestService myTestService;
? ? @PostConstruct
? ? public void init() {
? ? ? ? System.out.println(myTestService.getName1());
? ? ? ? System.out.println(myTestService.getName2());
? ? ? ? System.out.println(myTestService.getName3());
? ? ? ? // 看看到底是哪一個Bean
? ? ? ? myTestService.show();
? ? }
}

控制臺打印如下:

可以發(fā)現(xiàn),和我們期望的結(jié)果的一樣:所有用到 MyTestService的地方實際調(diào)用的變成了MyTestServiceIpml里面的方法 !

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot中間件封裝限流器的方案詳解

    SpringBoot中間件封裝限流器的方案詳解

    這篇文章主要介紹了SpringBoot中間件封裝限流器,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • Java多線程中不同條件下編寫生產(chǎn)消費者模型方法介紹

    Java多線程中不同條件下編寫生產(chǎn)消費者模型方法介紹

    這篇文章主要介紹了Java多線程中不同條件下編寫生產(chǎn)消費者模型方法介紹,介紹了生產(chǎn)消費者模型,然后分享了相關(guān)代碼示例,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 使用Idea或Datagrip導(dǎo)入excel數(shù)據(jù)的方法

    使用Idea或Datagrip導(dǎo)入excel數(shù)據(jù)的方法

    這篇文章主要介紹了使用Idea或Datagrip導(dǎo)入excel數(shù)據(jù)的方法,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • 注解@CrossOrigin解決跨域的問題

    注解@CrossOrigin解決跨域的問題

    這篇文章主要介紹了注解@CrossOrigin解決跨域的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Spring MVC+FastJson+hibernate-validator整合的完整實例教程

    Spring MVC+FastJson+hibernate-validator整合的完整實例教程

    這篇文章主要給大家介紹了關(guān)于Spring MVC+FastJson+hibernate-validator整合的完整實例教程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • java swing實現(xiàn)貪吃蛇雙人游戲

    java swing實現(xiàn)貪吃蛇雙人游戲

    這篇文章主要為大家詳細介紹了java swing實現(xiàn)貪吃蛇雙人小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • idea運行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題

    idea運行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題

    這篇文章主要介紹了idea運行tomcat報錯找不到catalina.bat,系統(tǒng)找不到指定的文件問題,具有很好的參考價值,希望對大家有所幫助,
    2023-11-11
  • Java中的Null到底是什么

    Java中的Null到底是什么

    null是沒有地址,""是有地址但是里面的內(nèi)容是空的,好比做飯 null說明連鍋都沒有 而""則是有鍋沒米,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,下面我們來詳細學(xué)習(xí)一下它吧
    2019-06-06
  • Spring的@Autowired加到接口上但獲取的是實現(xiàn)類的問題

    Spring的@Autowired加到接口上但獲取的是實現(xiàn)類的問題

    這篇文章主要介紹了Spring的@Autowired加到接口上但獲取的是實現(xiàn)類的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 配置Spring4.0注解Cache+Redis緩存的用法

    配置Spring4.0注解Cache+Redis緩存的用法

    本篇文章主要介紹了詳解配置Spring4.0注解Cache+Redis緩存的用法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04

最新評論