In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-03 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces "how to solve Bean name coverage at the same time in springboot1.X and 2.x". In daily operation, I believe many people have doubts about how to solve the problem of Bean name coverage at the same time in springboot1.X and 2.x. Xiaobian consulted all kinds of data and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubt of "how to solve Bean name coverage in springboot1.X and 2.x". Next, please follow the editor to study!
How to solve the problem of overwriting Bean names at the same time
In versions prior to version 2, two bean with the same name in the project could be started successfully, but there would be coverage problems.
But there will be an error in version 2.x:
Could not be registered. A bean with that name has already been defined in class path resource
At this point, the solution can be added to the configuration file:
Whether spring.main.allow-bean-definition-overriding=true/** allows re-registration of different bean implementations with the same name. The default is to allow * / 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. *
Default is "true". [it is clearly stated here that the default is true] * @ see # registerBeanDefinition * / public boolean isAllowBeanDefinitionOverriding () {return this.allowBeanDefinitionOverriding;} @ Overridepublic 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 is loaded into the project of spring and stored in beanDefinitionMap, where key is the name of bean. BeanDefinition existingDefinition = this.beanDefinitionMap.get (beanName); if (existingDefinition! = null) {/ / is not empty, indicating that the bean of the same name already exists. If (! isAllowBeanDefinitionOverriding ()) {/ / if the bean of the same name is not allowed, an exception throw new BeanDefinitionOverrideException (beanName, beanDefinition, existingDefinition) is thrown directly;} 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时,只是记录了一些日志,然后后来发现的这个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 updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set 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对这个参数又进行了二次封装,springboot中的allowBeanDefinitionOverriding是没有初始化默认值的,我们知道,java中的boolean类型不初始化时是false。 springboot中源代码: 在SpringApplication类中 public class SpringApplication { ...//boolean没初始化,所以默认为falseprivate 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 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 ? 例如引入的第三方jar包中的某个类有些问题,然有没有源码提供或者嫌编译源码太费事,这个时间可以考虑覆写原有的类。 方法1 直接在自己工程中建同包同类名的类进行替换 方式简单粗暴,可以直接覆盖掉jar包中的类,spring项目会优先加载自定义的类。 下面是覆盖 flowable框架中的一个类 FlowableCookieFilter,主要是想修改它里面的redirectToLogin方法的业务逻辑。包路径为 org.flowable.ui.common.filter, 直接工程里面新建一样路径一样类名FlowableCookieFilter即可。 方法2 采用@Primary注解 该方法适用于接口实现类,自己创建一个原jar包接口的实现类,然后类上加上@Primary注解,spring则默认加载该类实例化出的Bean。 下面的例子: 一个接口 RemoteIdmService,原先jar包中只有一个实现类 RemoteIdmServiceImpl,现在自己工程里面创建一个 CustomRemoteIdmServiceImpl 继承RemoteIdmService接口,然后发现所有调用RemoteIdmService接口里面的方法实际调用走的是CustomRemoteIdmServiceImpl 里面的方法。Method 3 excludes classes in jar packages that need to be replaced
Use the excludeFilters function in @ ComponentScan to rule out calling the class to be replaced, and then write your own class to inherit the replacement class.
The following example replaces the PersistentTokenServiceImpl class in the jar package
@ 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);}} @ Servicepublic class MyPersistentTokenServiceImpl extends PersistentTokenServiceImpl {@ Override public Token saveAndFlush (Token token) {/ / override the method's business logic tokenCache.put (token.getId (), token) IdmIdentityService.saveToken (token); return token;} @ Override public void delete (Token token) {/ / overwrite the method's business logic tokenCache.invalidate (token.getId ()); idmIdentityService.deleteToken (token.getId ());} @ Override public Token getPersistentToken (String tokenId) {/ / overwrite the method's business logic return getPersistentToken (tokenId, false) }} method 4 @ Bean override
This scenario aims at the @ ConditionalOnMissingBean annotation in the framework jar package, which states that if you create the same Bean, the framework will not create the bean again, so you can overwrite your own bean. Configuration classes in the original jar package:
Directly inherit the class to be overridden, rewrite the method yourself, and inject it into spring using @ Component
Method 5 use BeanDefinitionRegistryPostProcessor
About BeanDefinitionRegistryPostProcessor and BeanFactoryPostProcessor, you can refer to this article:
To put it bluntly, BeanDefinitionRegistryPostProcessor can modify the properties of Bean before initializing Bean, or even replace the bean that was originally intended to be instantiated.
Actual combat demonstration:
Suppose there is a class MyTestService in the jar package, which would normally be automatically scanned into the injected IOC container by spring.
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 ("I was actively added to Spring's IOC through the annotation @ Service in jar"); 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 }}
Inherit this class in your own project and override the methods in 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 ("I was manually registered in the IOC of Spring by BeanDefinitionRegistry"); System.out.println ("-");}}
Then implement the BeanDefinitionRegistryPostProcessor interface, modify the original bean definition, mainly view the implementation of the postProcessBeanDefinitionRegistry method, first empty the original bean definition, register our own bean definition to achieve the purpose of replacement.
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 * / @ Componentpublic class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {private Logger logger = LoggerFactory.getLogger (this.getClass ()); @ Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {logger.info ("bean definition View and modify..."); String beanName = "myTestService"; / / remove the original bean definition beanDefinitionRegistry.removeBeanDefinition (beanName) first / / register our own bean definition BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition (MyTestServiceIpml.class); / / if there are constructor parameters, if there are several constructor parameters, there is no need to set beanDefinitionBuilder.addConstructorArgValue ("constructor parameter 1"); beanDefinitionBuilder.addConstructorArgValue ("constructor parameter 2"); beanDefinitionBuilder.addConstructorArgValue ("constructor parameter 3") / / set init method without beanDefinitionBuilder.setInitMethodName ("init"); / / set destory method without beanDefinitionBuilder.setDestroyMethodName ("destory"); / / register the definition of Bean with Spring environment beanDefinitionRegistry.registerBeanDefinition ("myTestService", beanDefinitionBuilder.getBeanDefinition ()) } @ Override public void postProcessBeanFactory (ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {/ / bean is named key, and the instance of bean is value Map beanMap = configurableListableBeanFactory.getBeansWithAnnotation (RestController.class); logger.info ("bean {}", beanMap) of all RestController;}}
Write a business class BusinessTestService to test, and expect the result: all the places where MyTestService is actually called become methods in 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 * / @ Servicepublic 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 ()); / / see which Bean myTestService.show ();}}
The console prints as follows:
It can be found that, as we expected, all the places where MyTestService is actually called become the methods in MyTestServiceIpml!
At this point, the study on "how to solve the problem of covering Bean names at the same time in springboot1.X and 2.x" is over. I hope I can solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.