Several ways to overwrite and rewrite the original Spring Bean

Scenes

Under what circumstances should the original Spring Bean be overwritten? For example, there are some problems with a certain class in the imported third-party jar package, but there is no source code provided or it is too troublesome to compile the source code. At this time, you can consider overwriting the original class.

Method 1 Directly build a class with the same package name and replace it in your own project

The method is simple and rude, you can directly override the classes in the jar package, and the spring project will load custom classes first.
The following is to cover a class FlowableCookieFilter in the flowable framework, mainly to modify the business logic of the redirectToLogin method in it. The package path is org.flowable.ui.common.filter, just create the same path and class name FlowableCookieFilter directly in the project.
Insert picture description here

Method 2 uses @Primary annotation

This method is suitable for the interface implementation class. Create an implementation class of the original jar package interface by yourself, and then add the @Primary annotation to the class, and spring will load the Bean instantiated by this class by default.
The following example: An interface RemoteIdmService, originally there is only one implementation class RemoteIdmServiceImpl in the jar package, now I create a CustomRemoteIdmServiceImpl in my own project to inherit the RemoteIdmService interface, and then find that all the methods in the RemoteIdmService interface are actually called by the methods in the CustomRemoteIdmServiceImpl.

Insert picture description here
Insert picture description here

Method 3 Exclude the classes in the jar package that need to be replaced

Use the excludeFilters function in @ComponentScan to exclude the calling class to be replaced, and then write a class to inherit and replace.
The following example is to replace 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);
    }
}

@Service
public class MyPersistentTokenServiceImpl extends PersistentTokenServiceImpl{
    
    
    @Override
    public Token saveAndFlush(Token token) {
    
    
        // 覆写该方法的业务逻辑
        tokenCache.put(token.getId(), token);
        idmIdentityService.saveToken(token);
        return token;
    }

    @Override
    public void delete(Token token) {
    
    
        // 覆写该方法的业务逻辑
        tokenCache.invalidate(token.getId());
        idmIdentityService.deleteToken(token.getId());
    }

    @Override
    public Token getPersistentToken(String tokenId) {
    
    
        // 覆写该方法的业务逻辑
        return getPersistentToken(tokenId, false);
    }
}

Method 4 @Bean override

This scenario is aimed at the framework jar package with @ConditionalOnMissingBean annotation. This annotation means that if you also create the same bean, the framework will not create the bean again by itself, so you can overwrite your own bean.
The configuration class in the original jar package:
Insert picture description here
directly inherit the class to be overridden, rewrite the method inside, and use @Component to inject into spring
Insert picture description here

Method 5 Use BeanDefinitionRegistryPostProcessor

For BeanDefinitionRegistryPostProcessor and BeanFactoryPostProcessor, please refer to the following blog post:
https://blog.csdn.net/ztchun/article/details/90814135

To put it plainly, BeanDefinitionRegistryPostProcessor means that you can modify the properties of the Bean before initializing the Bean, or even replace the bean that was originally prepared to be instantiated.

Practical demonstration:

Assuming that there is a class MyTestService in the jar package, it will be automatically scanned by Spring and injected into the IOC container under normal circumstances.


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;
    }
}

Inherit this class in your own project and override the show method in it


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("------------------------");
    }
}

Then implement the BeanDefinitionRegistryPostProcessor interface, modify the original bean definition, mainly check the implementation of the postProcessBeanDefinitionRegistry method, first clear the original bean definition, and 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
 */
@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);
        // 如果有构造函数参数, 有几个构造函数的参数就设置几个  没有就不用设置
        beanDefinitionBuilder.addConstructorArgValue("构造参数1");
        beanDefinitionBuilder.addConstructorArgValue("构造参数2");
        beanDefinitionBuilder.addConstructorArgValue("构造参数3");
        // 设置 init方法 没有就不用设置
        beanDefinitionBuilder.setInitMethodName("init");
        // 设置 destory方法 没有就不用设置
        beanDefinitionBuilder.setDestroyMethodName("destory");
        // 将Bean 的定义注册到Spring环境
        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);
    }
}

Write a business class BusinessTestService to test, and expect the result: all the places where MyTestService is used actually call the 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
 */
@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();
    }

}

The console prints as follows: It
Insert picture description here
can be found that the results are the same as we expected: all the places where MyTestService is used are actually called methods in MyTestServiceIpml!

OVER …

Guess you like

Origin blog.csdn.net/gzt19881123/article/details/109333230