SpringBoot和Spring排除某个Config文件不加载

该博客介绍了如何在SpringBoot测试中排除特定配置文件,特别是SchedulerxConfig,以避免在测试环境中启动不需要的服务。通过使用MockBean注解,可以模拟SchedulerxConfig中的Bean,确保测试时不会尝试初始化这些Bean。示例代码展示了如何在测试类中排除SchedulerxConfig,并在测试用例中使用MockBean进行模拟操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

阿里云的Schedulerx启动时需要阿里的tomcat,需要注册到阿里服务器上,不能使用懒加载方式,但测试时注入不成功,所以测试需要排除Schedulerx的Bean

方法:

1、Schedulerx的Bean单独放一个Config文件(测试不想初始化的Bean单独放一个文件)

一、SpringBoot测试去除某个配置文件不加载。 

package com.suyun.svop.admin;

import com.suyun.svop.SvopAdminApplication;
import com.suyun.svop.cache.CorpStationCache;
import com.suyun.svop.config.SchedulerxConfig;
import org.assertj.core.util.Lists;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Description:
 *
 * @Author: leo.xiong
 * @CreateDate: 2020/12/07 14:21
 * @Email: leo.xiong@suyun360.com
 * @Since:
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SvopAdminApplication.class)
public class TestCache {

    @MockBean
    private CorpStationCache corpStationCache;
    //排除不加载的配置文件,里面的bean不会加载,也可以是某个Bean对象
    @MockBean
    private SchedulerxConfig schedulerxConfig;

    @Test
    public void testMultithreadingCache() {
        int size = 500;
        String cacheKey = "test:key";
        Set<Thread> threadSet = new HashSet<>();
        for (int i = 0; i < size; i++) {
            threadSet.add(new Thread(new TestPutClass(corpStationCache, cacheKey)));
        }
        for (int i = 0; i < size; i++) {
            threadSet.add(new Thread(new TestGetClass(corpStationCache, cacheKey)));
        }
        for (int i = 0; i < size; i++) {
            threadSet.add(new Thread(new TestDeleteClass(corpStationCache, cacheKey)));
        }
        for (Thread thread : threadSet) {
            thread.start();
        }
    }
}

class TestPutClass implements Runnable {
    private CorpStationCache corpStationCache;

    private String cacheKey;

    public TestPutClass(CorpStationCache corpStationCache, String cacheKey) {
        this.corpStationCache = corpStationCache;
        this.cacheKey = cacheKey;
    }

    @Override
    public void run() {
        List<Object> list = Lists.newArrayList("1", "2");
        corpStationCache.put(cacheKey, list);
        System.out.println("插入数据》》》》》》》》》》》》》》》");
    }
}

class TestDeleteClass implements Runnable {
    private CorpStationCache corpStationCache;

    private String cacheKey;

    public TestDeleteClass(CorpStationCache corpStationCache, String cacheKey) {
        this.corpStationCache = corpStationCache;
        this.cacheKey = cacheKey;
    }


    @Override
    public void run() {
        corpStationCache.delete(cacheKey);
        System.out.println("删除数据》》》》》》》》》》》》》》》");
    }
}

class TestGetClass implements Runnable {
    private CorpStationCache corpStationCache;

    private String cacheKey;

    public TestGetClass(CorpStationCache corpStationCache, String cacheKey) {
        this.corpStationCache = corpStationCache;
        this.cacheKey = cacheKey;
    }


    @Override
    public void run() {
        List<Object> list = corpStationCache.get(cacheKey);
        System.out.println("查询数据》》》》》》》》》》》》》》》list" + list);
        if (list != null) {
            try {
                System.out.println("开始读取》》》》》》》》》");
                Thread.sleep(2000);
                System.out.println("读取数据》》》》》》》》" + list.get(0));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//排除不加载的配置文件,里面的bean不会加载,也可以是某个Bean对象
@MockBean
private SchedulerxConfig schedulerxConfig;

二、Spring测试去除某个配置文件不加载。 

package com.suyun.configuration;

import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;

import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.List;

/**
 * Description:
 * AppConfig排除测试无需要加载的config文件
 * SchedulerxConfig引入AppConfig排除的config文件
 * @Author: leo.xiong
 * @CreateDate: 2021/5/8 11:57
 * @Email: leo.xiong@suyun360.com
 * @Since:
 */
@ComponentScan(includeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = AppConfig.class)
        , @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
        , @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = SchedulerxConfig.class)})
@EnableWebMvc
@EnableAsync
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("/");
    }

    @Bean
    public UrlBasedViewResolver setupViewResolver() {
        UrlBasedViewResolver resolver = new UrlBasedViewResolver();
        resolver.setPrefix("/views/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        registry.addInterceptor(localeChangeInterceptor);
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*");
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
                .timeZone("GMT+8")//设置统一时区信息东八区
                .modulesToInstall(new ParameterNamesModule());
        Charset s = Charset.forName("UTF-8");
        converters.add(new StringHttpMessageConverter(s));
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
    }


}

@ComponentScan(includeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = AppConfig.class)
        , @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
        , @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = SchedulerxConfig.class)})

@Configuration("appConfig")
@ComponentScan(basePackages = {"com.suyun", "com.server", "com.utils"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = SchedulerxConfig.class)})
@ImportResource({"classpath:spring-dubbo.xml", "classpath:spring-mvc.xml"})
@PropertySource({"classpath:webConfig.properties", "classpath:kafka.properties"})
@MapperScan("com.suyun.modules.*.dao")
@EnableTransactionManagement
@EnableCaching
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {

}

@ComponentScan(basePackages = {"com.suyun", "com.server", "com.utils"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = SchedulerxConfig.class)})

package com.test;

import com.suyun.configuration.AppConfig;
import com.suyun.configuration.SchedulerxConfig;
import com.suyun.message.listener.DurabilitySummaryListener;
import com.taobao.hsf.lightapi.ServiceFactory;
import dto.VehicleReportDataDto;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.io.IOException;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * Description:
 *
 * @Author: leo.xiong
 * @CreateDate: 2021/02/03 10:52
 * @Email: leo.xiong@suyun360.com
 * @Since:
 */
@RunWith(SpringJUnit4ClassRunner.class)
@org.springframework.context.annotation.Configuration("masterDataServiceConfiguration")
@ContextConfiguration(classes = {AppConfig.class})
public class RedisTaskTest {

    // 这里设置Pandora地址,参数是sar包所在目录,如这里我的sar包地址是D:/tomcat/taobao-tomcat-7.0.59/deploy/taobao-hsf.sar,则只取前面的地址即可
    private static final ServiceFactory FACTORY = ServiceFactory.getInstanceWithPath("D:/tomcat/taobao-tomcat-7.0.59/deploy");

    @Autowired
    private DurabilitySummaryListener durabilitySummaryListener;

    @Test
    public void testRedisChannel() throws IOException {
        VehicleReportDataDto vehicleReportDataDto = new VehicleReportDataDto();
        vehicleReportDataDto.setBusId("b1cd318ee7a64bceb5e2926e4c2a876e");
        vehicleReportDataDto.setDagId(378350104L);
        vehicleReportDataDto.setIsReference("no");
        vehicleReportDataDto.setPalteNo("测试台架2");
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        vehicleReportDataDto.setStatisticsDay(Date.from(LocalDateTime.parse("2021-05-06 00:00:00", dateTimeFormatter).toInstant(ZoneOffset.of("+8"))));
        vehicleReportDataDto.setStatus("success");
        vehicleReportDataDto.setTableName("veh_day_data_distinct_unzip_202105");
        vehicleReportDataDto.setTaskId("f206602851e64631b544c40adca2483a");
        durabilitySummaryListener.buildMessage(vehicleReportDataDto);
    }
}

@ContextConfiguration(classes = {AppConfig.class})

<think>我们正在讨论SpringBoot自动配置中如何排除特定的bean加载。用户的问题很明确:如何在自动配置过程中排除特定的@Bean。根据SpringBoot的文档常见做法,排除特定bean的方法有多种。我们可以参考提供的引用内容并结合SpringBoot的知识来回答。引用[4]提到自动配置的本质是自动配置Spring的各种bean,而引用[5]提到@SpringBootApplication等价于同时添加了@EnableAutoConfiguration、@ComponentScan@Configuration。因此,排除特定bean的方法通常与@EnableAutoConfiguration排除机制有关。常见方法:1.使用@EnableAutoConfiguration的exclude属性:可以在启动类上使用@EnableAutoConfiguration注解的exclude属性来排除特定的自动配置类(这些自动配置类通常含多个bean,排除整个配置类会阻止其中所有bean的加载)。2.使用@SpringBootApplication的exclude属性:因为@SpringBootApplication含了@EnableAutoConfiguration,所以它也有exclude属性,用法相同。3.使用配置文件:在application.properties或application.yml中通过属性排除自动配置类。4.使用条件注解:如果我们有自定义自动配置,可以使用条件注解(如@ConditionalOnMissingBean)来控制bean的加载,但这通常是在编写自动配置时使用,而是在排除时。但是,用户的问题是要排除特定的@Bean,而是整个自动配置类。如果自动配置类中有多个bean,而我们只想排除其中一个,那么上述方法可能过于宽泛(因为排除了整个配置类)。针对排除特定bean,我们可以考虑以下方法:1.在配置类中定义相同类型名称的bean:SpringBoot的自动配置通常使用条件注解,例如@ConditionalOnMissingBean,这意味着如果我们自己定义了一个相同类型的bean,那么自动配置的bean就会被加载。2.使用@ConditionalOnProperty:通过配置属性来控制是否加载该bean(但需要自动配置类中已经使用了该条件注解,或者我们修改自动配置类,这通常可行)。3.使用@Bean注解的name属性:如果我们想替换某个特定名称的bean,可以通过自定义一个相同名称的bean来覆盖。然而,如果我们无法修改自动配置类,且自动配置类没有提供条件属性来控制某个特定bean,那么排除特定bean可能需要更高级的方法,例如:-使用BeanPostProcessor:在bean初始化之后,但在使用之前将其从容器中移除(这种方法比较极端,可能会引起其他问题)。-使用自定义的@Conditional注解:这通常需要我们在自动配置类中预先设计。但是,SpringBoot提供了一种更直接的方式来排除自动配置类中的特定bean:使用@EnableAutoConfiguration的excludeName属性(通过类的全限定名排除自动配置类)或者使用@SpringBootApplication的excludeName属性。但这仍然排除的是整个自动配置类。因此,如果我们的目标仅仅是排除一个特定的bean,而该bean来自一个含多个bean的自动配置类,那么我们可以:1.排除整个自动配置类(如果该类中的其他bean需要,或者我们可以自己提供)。2.覆盖该bean:通过在自己的配置类中定义相同类型的bean(使用相同的bean名称)来覆盖自动配置的bean。由于SpringBoot的自动配置通常使用@ConditionalOnMissingBean,所以自定义的bean会阻止自动配置的bean被加载。另外,从SpringBoot1.4开始,我们可以使用@SpringBootApplication的exclude属性按类排除自动配置,或者使用excludeName按类名排除。在配置文件中,我们可以使用`spring.autoconfigure.exclude`属性。总结:-要排除整个自动配置类(从而排除该类中所有的bean):-使用注解:`@SpringBootApplication(exclude={SomeAutoConfigurationClass.class})`-使用属性:`spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.some.SomeAutoConfiguration`-要排除特定的bean(来自某个自动配置类):-方法1:通过在自己的配置类中定义相同类型的bean(并确保在自动配置类之前加载),利用条件注解(如@ConditionalOnMissingBean)来阻止自动配置的bean加载。-方法2:如果自动配置类中的bean使用了条件注解(如@ConditionalOnProperty),则可以通过设置对应的属性为false来排除该bean。-方法3:如果以上都行,可以考虑使用BeanPostProcessor来移除特定名称的bean(推荐,因为可能破坏自动配置的完整性)。示例:假设我们想排除自动配置中名为`dataSource`的bean,我们可以这样:@ConfigurationpublicclassMyConfiguration{@BeanpublicDataSourcedataSource(){//返回一个我们自定义的DataSource,或者返回null(但返回null需要确保会影响其他依赖,通常我们这样做)//但是,如果我们只是想阻止自动配置的DataSource加载,我们可以返回一个空bean?实际上,如果我们需要数据源,可以排除整个数据源自动配置类。//更好的做法是:如果我们需要数据源,可以排除DataSourceAutoConfiguration}}然而,如果我们想提供自己的bean,而只是单纯想移除自动配置的bean,我们可以使用`@Bean`方法返回null,但这需要满足条件:该方法上使用`@Conditional`注解确保只在特定条件下生效,并且需要确保该bean没有被其他bean依赖。但是,Spring允许在`@Bean`方法中返回null(除非显式地允许),所以这种方法并安全。因此,最安全且推荐的做法是:如果自动配置类中的某个bean我们需要,并且没有该bean也会影响应用运行(或者我们可以通过其他方式提供该bean),那么我们就自定义一个相同类型的bean,并在需要的时候返回一个替代品(或者抛出异常,如果我们想强制使用它)。但通常,排除整个自动配置类更为常见。另外,SpringBoot2.7引入了一个新的机制:通过`@AutoConfiguration`注解`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`文件来定义自动配置类,但排除方式类似。由于用户的问题是如何排除特定的bean,我们可以总结为:如果该bean在自动配置类中,且没有提供单独的条件控制,那么我们可以通过覆盖该bean的方式(自定义一个相同类型的bean)来阻止自动配置的bean加载。下面我们给出具体的代码示例。示例:假设我们想排除自动配置中类型为`MyBean`的bean(假设自动配置类中定义了一个名为`myBean`的bean)。方法1:自定义一个相同类型的bean```java@ConfigurationpublicclassMyConfiguration{@Bean@Primary//如果需要,可以加上@Primary表明是主bean,避免多个同类型bean的冲突publicMyBeanmyBean(){//返回我们自己的实现,或者如果需要这个bean,可以返回一个空对象或者null(但注意,返回null可能会引起问题,所以最好根据情况处理)//例如,如果我们需要这个bean,我们可以返回一个无操作的实例,或者抛异常(推荐),或者使用@Conditional注解控制这个bean只在某些条件下创建returnnewMyDummyBean();//假设MyDummyBean是MyBean的一个实现,用于占位}}```注意:自动配置类中定义`MyBean`的bean方法上通常会有`@ConditionalOnMissingBean`注解,这样当我们自定义了`MyBean`类型的bean后,自动配置的bean就会被创建。方法2:使用`@ConditionalOnProperty`(前提是自动配置类中使用了该条件注解)在application.properties中:```properties#假设自动配置类中定义MyBean的bean方法上有@ConditionalOnProperty(name="mybean.enabled",havingValue="true",matchIfMissing=true)mybean.enabled=false```方法3:排除整个自动配置类(如果我们知道这个bean在哪个自动配置类中)在启动类上:```java@SpringBootApplication(exclude={SomeAutoConfigurationClass.class})publicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}```或者使用配置文件:```propertiesspring.autoconfigure.exclude=com.example.SomeAutoConfigurationClass```如果用户知道自动配置类是什么,可以通过调试启动(添加`--debug`参数)来查看自动配置报告,从而找到定义该bean的自动配置类。因此,针对用户的问题,我们推荐使用覆盖bean的方式(方法1)来排除特定的bean,或者如果该bean所在的自动配置类中其他bean也需要,则排除整个自动配置类(方法3)。最后,请注意,排除特定bean可能会影响其他依赖该bean的组件,因此需要谨慎操作。</think>### 如何排除 Spring Boot 自动配置中的特定 Bean 在 Spring Boot 自动配置中排除特定 Bean 有以下四种主要方法,根据需求选择合适方案: #### 1. **排除整个自动配置类(推荐)** 通过 `@SpringBootApplication` 或 `@EnableAutoConfiguration` 的 `exclude` 属性排除含目标 Bean 的自动配置类: ```java // 方式1:按类排除 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class MyApp { ... } // 方式2:按类名排除(避免类加载问题) @SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration") public class MyApp { ... } ``` **适用场景**:当目标 Bean 属于特定自动配置类(如 `DataSourceAutoConfiguration` 中的 `dataSource` Bean)[^1][^4]。 #### 2. **使用条件注解覆盖** 通过自定义 Bean 并配合 `@ConditionalOnMissingBean` 覆盖自动配置: ```java @Configuration public class CustomConfig { // 此 Bean 会阻止自动配置创建同类型 Bean @Bean @ConditionalOnMissingBean public MyBean myBean() { return new CustomMyBean(); // 自定义实现 } } ``` **原理**:自动配置类通常含 `@ConditionalOnMissingBean` 条件,检测到存在同类型 Bean 时跳过创建[^4][^5]。 #### 3. **配置文件排除** 在 `application.properties` 中全局排除自动配置类: ```properties # 排除多个配置类 spring.autoconfigure.exclude=\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration ``` **优势**:无需修改代码,适合环境隔离(如测试环境排除安全配置)[^1]。 #### 4. **精确排除特定 Bean(高级)** 通过 `BeanPostProcessor` 动态移除已创建的 Bean: ```java @Component public class BeanExcluder implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if ("targetBeanName".equals(beanName)) { return null; // 移除特定 Bean } return bean; } } ``` **注意**:需谨慎使用,可能破坏依赖链[^4]。 --- ### 关键注意事项 1. **定位自动配置类** - 启动时添加 `--debug` 参数查看自动配置报告: ```bash java -jar myapp.jar --debug ``` - 在日志中搜索 `CONDITIONS EVALUATION REPORT` 查看 Bean 来源[^1][^5]。 2. **覆盖 vs 排除** - **排除类**:完全禁用该自动配置的所有 Bean - **覆盖 Bean**:仅替换特定 Bean,保留其他功能 - **示例**:排除 `DataSourceAutoConfiguration` 会禁用所有数据库 Bean;覆盖 `dataSource` Bean 仅替换数据源实现[^4][^5]。 3. **条件注解优先级** Spring Boot 自动配置按以下顺序生效: ```mermaid graph LR A[用户自定义Bean] --> B[自动配置Bean] B -->|@ConditionalOnMissingBean| A ``` 自定义 Bean 优先于自动配置 Bean 加载[^4][^5]。 --- ### 典型应用场景 | 场景 | 推荐方法 | 示例 | |-------------------------------|-----------------------------|----------------------------------------------------------------------| | 禁用整个模块(如数据库) | 排除自动配置类 | `exclude = DataSourceAutoConfiguration.class` | | 替换特定 Bean(如数据源) | 条件注解覆盖 | 自定义 `@Bean DataSource` | | 测试环境排除安全配置 | 配置文件排除 | `spring.autoconfigure.exclude=SecurityAutoConfiguration` | | 动态移除非法 Bean | `BeanPostProcessor` | 返回 `null` 移除 Bean | [^1]: Spring Boot 自动配置通过扫描类路径条件注解动态注册 Bean。 [^4]: 自动配置本质是条件化的 Bean 声明,通过 `@Conditional` 系列注解控制加载。 [^5]: `@SpringBootApplication` 组合了 `@EnableAutoConfiguration` 的核心自动配置功能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值