5

重写并自定义依赖的原生的Bean方法 - 香吧香

 2 years ago
source link: https://www.cnblogs.com/zjdxr-up/p/16533930.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

重写并自定义依赖的原生的Bean方法

转载请注明出处:

  在项目开发过程中,往往是直接应用很多jar包中依赖且声明好的Bean,拿来即用,但很多场景也需要对这些原生的Bean 进行自定义,定制化封装,这样在项目使用的过程中,可以使用自定义的Bean,而不是原生的Bean。下面总结了几种定制化原生Bean 的几种方式:

1. 在项目中创建同包同类名的类

    这种方式使用较少,因为项目中的包路径根据开发规范是根据业务名自定义的包路径

2.使用 @Primary 注解,或  @Qualifier 注解,定义Bean 的优先级或使用时,指定Bean

  • @Primary 优先考虑,优先考虑被注解的对象注入
  • @Qualifier 名字声明,声明后对名字进行使用

  当一个类有多个Bean的实例时,可以在 Bean 的实现类中 使用 @Primary 注解声明Bean 的优先级,在使用过程中,spring则默认加载该类实例化出的Bean。而 @Qualifiler 注解先声明后使用,相当于多个实现起多个不同的名字,注入时候告诉我你要注入哪个;

  @Primary 在源码中使用的示例: spring-cloud-starter-gateway 3.1.1 版本中的  GatewayAutoConfiguration 中的源码

    @Bean
    @Primary
    public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
        return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
    }

  @Qualiflier 注解使用,当一个类有多个Bean 实例时,在使用 Bean 时,通过 @Qualiflier 制定Bean

@Service("employeeserver")
public class EmployeeServiceImpl implements EmployeeService {
    public EmployeeDto getEmployeeById(Long id) {
        return new EmployeeDto();
    }
}
 
@Service("manageserver")
public class ManagServiceImpl implements EmployeeService {
    public ManagerDto getEmployeeById(Long id) {
        return new ManagerDto();
    }
}
@Controller
@RequestMapping("/emplayee")
public class EmployeeInfoControl {
    
    @Autowired
    @Qualifier("employeeserver")
    EmployeeService employeeService;
    
    @RequestMapping(params = "method=showEmplayeeInfo")
    public void showEmplayeeInfo(HttpServletRequest request, HttpServletResponse response){
       ***********************
       *******************
    }

3. 使用 @ComponentScan 里面的 excludeFilters 排除不需要加载的类

  示例如下,排除 MyTestFilter 类

@SpringBootApplication
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})})
public class TestApplication {
  ... }

  这里需要注意的是,可以定义一个与原生相同的Bean,但在 上面引包的时候,需要引入的包路径是要排除的包路径,这样自定义的相同的类就可以加载到容器中,原生的Bean 则不会加载。

  这种方式也是常用的方式;

4.使用 @Bean 注解覆盖原生的Bean

  该场景针对,框架jar包中有@ConditionalOnMissingBean注解,这种注解是说明如果你也创建了一个一样的Bean则框架就不自己再次创建这个bean了,这样你可以覆写自己的bean。

  直接继承要覆盖的类,自己重写里面方法,使用@Component注入到 spring 中去:

  spring-cloud-starter-gateway 3.1.1 版本中的  GatewayAutoConfiguration 中的源码对  HttpClientFactory  的定义

        @Bean
        @ConditionalOnMissingBean({HttpClient.class, HttpClientFactory.class})
        public HttpClientFactory gatewayHttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties, List<HttpClientCustomizer> customizers) {
            return new HttpClientFactory(properties, serverProperties, customizers);
        }

  重新自定义 该类的方法为:直接继承要覆盖的类,自己重写里面方法,使用@Component注入到spring中去

package my.test.gateway.config;

@Component public class HttpClientFactory extends AbstractFactoryBean<HttpClient> { protected final HttpClientProperties properties; protected final ServerProperties serverProperties; protected final List<HttpClientCustomizer> customizers; public HttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties, List<HttpClientCustomizer> customizers) { this.properties = properties; this.serverProperties = serverProperties; this.customizers = customizers; } ..... }

5. 使用BeanDefinitionRegistryPostProcessor 

   使用 Spring 提供的 Bean 后置处理器,进行自定义的Bean 加载;

package com.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);
    }
}

  最常见的重写自定义Bean 的方式为以上的 2,3,4 三种方式,通过以上方式就可以实现重写并自定义原生的Bean;


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK