写点什么

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

  • 2022 年 7 月 30 日
  • 本文字数:3184 字

    阅读完需:约 10 分钟

重写并自定义依赖的原生的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){       ***********************       *******************    }
复制代码


  1. 使用 @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 中去:


package my.test.gateway.config;@Componentpublic 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; } ..... }
复制代码

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


package my.test.gateway.config;@Componentpublic 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 */@Componentpublic 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

用户头像

不定期更新Java开发工具及Java面试干货技巧 2021.12.12 加入

Java后端工程师,十年大厂经验。具有扎实的Java、JEE基础知识。熟悉Spring、SpringMVC、Struts MyBatisHibernate等JEE常用框架。

评论

发布
暂无评论
重写并自定义依赖的原生的Bean方法_java程序员_了不起的程序猿_InfoQ写作社区