做五金生意什么網(wǎng)站做比較好精準營銷的三要素
場景:
因項目需要,一個springcloud微服務工程需要同時部署到A,B兩個項目使用,但A項目使用Eureka注冊中心,B項目使用Nacos注冊中心,現(xiàn)在需要通過部署時修改配置來實現(xiàn)多注冊中心的切換。
解決思路:
如果同時引入nacos和eureka的依賴和配置,不做任何處理,會導致啟動失敗:
***************************
APPLICATION FAILED TO START
***************************Description:Field registration in org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration$ServiceRegistryEndpointConfiguration required a single bean, but 2 were found:- nacosRegistration: defined by method 'nacosRegistration' in class path resource [com/alibaba/cloud/nacos/registry/NacosServiceRegistryAutoConfiguration.class]- eurekaRegistration: defined in BeanDefinition defined in class path resource [org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration.class]Action:Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
不難看出失敗原因是單例bean找到了兩個實例,那么該如何解決這個問題呢?首先想到的肯定是刪除掉暫時不需要使用的實例(如使用eureka注冊中心則刪掉引入pom的nacos依賴),這樣做是沒有問題的,但是維護成本比較高。能不能從springboot自動裝配原理入手,找到更便捷的方法呢?接著看:
我們都知道SpringBoot的啟動類的@SpringBootApplication是一個組合注解,它里面的@EnableAutoConfiguration會引入AutoConfigurationImportSelector.class
從這個類的方法getAutoConfigurationEntry()一層一層點進去看,
SpringFactoriesLoader.loadFactories()會去檢索META-INF/spring.factories文件。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);}
那么思路就比較清晰了,我們可以通過實現(xiàn)AutoConfigurationImportFilter接口,將自己的過濾邏輯寫在實現(xiàn)類中,就可以實現(xiàn)自定義的自動裝配過濾器了。
上代碼:
通過把1、2、3的代碼放到一個starter中,然后在具體的項目中引用這個starter,配置文件中添加4的配置就可以切換了,當然具體nacos和eureka在yml中的配置還是分開寫,只需指定用那個配置就行
1.過濾器
package com.demo.business;import com.demo.business.constants.RegistrationCenterConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;public class EngineAutoConfigurationImportFilter implements AutoConfigurationImportFilter, EnvironmentAware {private Environment environment;@Overridepublic boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {//獲取配置的注冊中心,默認為nacosString registryType = environment.getProperty("registry.type", RegistrationCenterConstants.NACOS);boolean[] match = new boolean[autoConfigurationClasses.length];//當自定義標識為eureka,則排除nacos的自動裝配,反之同理;if (registryType.equals(RegistrationCenterConstants.EUREKA)) {for (int i = 0; i < autoConfigurationClasses.length; i++) {match[i] = !StringUtils.isNotBlank(autoConfigurationClasses[i]) ||!autoConfigurationClasses[i].equals(RegistrationCenterConstants.NACOS_SERVICE_REGISTRY_AUTO_CONFIGURATION);}} else {for (int i = 0; i < autoConfigurationClasses.length; i++) {if (StringUtils.isNotBlank(autoConfigurationClasses[i])){match[i] = !RegistrationCenterConstants.EUREKA_DISCOVERY_CLIENT_CONFIGURATION.equals(autoConfigurationClasses[i])&& !RegistrationCenterConstants.EUREKA_AUTO_CONFIGURATION_CLASSES.equals(autoConfigurationClasses[i]);}}}return match;}@Overridepublic void setEnvironment(Environment environment) {this.environment = environment;}
}或者以下這樣也可以
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;public class EngineAutoConfigurationImportFilter implements AutoConfigurationImportFilter, EnvironmentAware {private Environment environment;public EngineAutoConfigurationImportFilter() {}public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {String registryType = this.environment.getProperty("registry.type", "eureka");boolean[] match = new boolean[autoConfigurationClasses.length];//提取成常量String prefix = registryType.equals("nacos") ? "org.springframework.cloud.netflix.eureka" : "com.alibaba.cloud.nacos";for(int i = 0; i < autoConfigurationClasses.length; ++i) {if (StringUtils.isNotBlank(autoConfigurationClasses[i])) {match[i] = !autoConfigurationClasses[i].startsWith(prefix);}}return match;}public void setEnvironment(Environment environment) {this.environment = environment;}
}
2.常量類
package com.demo.business.constants;/*** 注冊中心相關常量類*/
public class RegistrationCenterConstants {public static final String NACOS = "nacos";public static final String EUREKA = "eureka";public static final String EUREKA_AUTO_CONFIGURATION_CLASSES = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration";public static final String EUREKA_DISCOVERY_CLIENT_CONFIGURATION = "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration";public static final String NACOS_SERVICE_REGISTRY_AUTO_CONFIGURATION = "com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration";或者
public static final String NACOS_PREFIX = "com.alibaba.cloud.nacos";public static final String EUREKA_PREFIX = "org.springframework.cloud.netflix.eureka";}
3.spring.factories文件(注意路徑一定要在META-INF包下)
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\com.example.demo.business.EngineAutoConfigurationImportFilter
4.配置文件添加
registry:type: nacos
效果
通過修改配置項registry.type就可以實現(xiàn)eureka和nacos的切換了