# 前言

这里介绍内容为,在spring boot启动时,排除/不加载某些Bean。

核心思想: 使用@ComponentScan@ComponentScans替换@SpringBootApplication

spring boot启动时,排除/不加载某些模块/AutoConfiguration类,看官方文档。

https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration (opens new window)

# 排除/不加载某些Bean

排除/不加载某些Bean时,需要使用@ComponentScan。具体做法,可以采用下面的方式:

  • 前言
  • 排除/不加载某些Bean
  • 方式1:自定义 @ComponentScan
  • 方式2:自定义 @ComponentScans
  • 方式3:自定义 TypeExcludeFilter
  • 参考
  • Spring中FilterType的说明

# 方式1:自定义 @ComponentScan

假设:我在使用 RuoYi 的时候,想自己的实现 ShiroConfig,而不用 RuoYi 自带的 ShiroConfig ,且,不删除 RuoYi 自带的 ShiroConfig 类。

此种情况下,就需要启动项目时,不加载 ShiroConfig 类。

针对此种情况,可以这样做:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@ComponentScan(excludeFilters = {
			@Filter(type = FilterType.REGEX, pattern = {
				"com.ruoyi.framework.config.ShiroConfig"})
	})
public class RuoYiApplication {
	...
}

说明:

使用自定义的 @ComponentScan 注解替换 @SpringBootApplication 的注解。

定义 excludeFilters 属性。该属性指定排除的Bean/类。

使用正则表达式方式( FilterType.REGEX )排除类 "com.ruoyi.framework.config.ShiroConfig"

# 方式2:自定义 @ComponentScans

@SpringBootApplication 有些特殊,官方的解释是:same as @Configuration @EnableAutoConfiguration @ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	...
}

你看!@SpringBootApplication 带了一个 @ComponentScan。一个 @ComponentScan 会影响 @ComponentScans(为啥?暂不清楚)。

所以,自定义 @ComponentScans 是应该这样:

@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
@ComponentScans({
	@ComponentScan(
			basePackages = {"com.ruoyi"}, 
			excludeFilters = {
					@Filter(type = FilterType.REGEX, pattern = {
							"com.ruoyi.framework.config.ShiroConfig"})}),
	@ComponentScan(basePackages = {"com.third.package"}),
})
public class RuoYiApplication {
	...
}

说明:

使用@Configuration @EnableAutoConfiguration @ComponentScans 替换了 @SpringBootApplication。

使用 @ComponentScans 是为了添加多个 @ComponentScan。只有一个 @ComponentScan 时,可以不用 @ComponentScans。

# 方式3:自定义 TypeExcludeFilter

@SpringBootApplication 有些特殊,它带了一个 @ComponentScan。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	...
}

你看!@SpringBootApplication 自带的 @ComponentScan 中已经有了自定义的 excludeFilters。可以利用此特性实现排除/不加载某些Bean。

实现自己的 TypeExcludeFilter

public class MyTypeExcludeFilter extends TypeExcludeFilter {

	@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		if("com.ruoyi.framework.config.ShiroConfig".equals(metadataReader.getClassMetadata().getClassName())){
			System.out.println("skip >>>>>>> " + metadataReader.getClassMetadata().getClassName());
			return true;
		}
		
		return false;
	}

}

将 MyTypeExcludeFilter 在 ApplicationContext Initializer 时注册Bean(通过注解注册的Bean,进入 Spring 容器太晚了,不起作用)。

public class MyTypeExcludeFilterApplicationContextInitializer 
			implements ApplicationContextInitializer<ConfigurableApplicationContext> {

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addBeanFactoryPostProcessor(new MyTypeExcludeFilterPostProcessor());
	}
	
	private static class MyTypeExcludeFilterPostProcessor
			implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
	
		public static final String BEAN_NAME = "com.ruoyi." + "myTypeExcludeFilter";
		
		@Override
		public int getOrder() {
			return Ordered.HIGHEST_PRECEDENCE;
		}
		
		@Override
		public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		}
		
		@Override
		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
			RootBeanDefinition definition = new RootBeanDefinition(
					MyTypeExcludeFilter.class);
			registry.registerBeanDefinition(BEAN_NAME, definition);
		}	
	}	

}

注册 MyTypeExcludeFilterApplicationContextInitializer

在 src/main/resources 目录下创建文件META-INF/spring.factories(如果该文件已存在,则无须创建)。在文件META-INF/spring.factories中添加内容:

org.springframework.context.ApplicationContextInitializer=\
com.ruoyi.MyTypeExcludeFilterApplicationContextInitializer

说明:操作复杂,实测效果不理想,不推荐使用。

# 参考

https://www.cnblogs.com/yql1986/p/9418419.html

https://stackoverflow.com/questions/41287276/when-should-i-use-typeexcludefilters-in-spring

https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-excluding-config

# Spring中FilterType的说明

在spring中FilterType包括以下几类

public enum FilterType {
    ANNOTATION, //按照注解过滤
    ASSIGNABLE_TYPE, //按照类型过滤
    ASPECTJ,//按照ASPECTJ表达式过滤
    REGEX,//按照正则表达式过滤
    CUSTOM;//按照自定义的过滤规则过滤
    private FilterType() {
    }
}
  • 按照注解过滤: 就是看要注入到容器的类上有哪些注解类似@Controller @Service 这些。
  • 按照类型过滤: 可以指定需要过滤的组件的类型,类似于xxx.class
  • 按照ASPECTJ表达式过滤: 就是用用ASPECTJ定义过滤规则
  • 按照正则表达式过滤: 使用正则表达式定义过滤规则
  • 自定义的过滤规则过滤: spring提供了可自定的过滤规则的方式,按照你自己定义的规则进行过滤。

例如:

@Configuration
@ComponentScans(value = {
        @ComponentScan(basePackages = {"Spring"}, excludeFilters = {
                 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
                 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = PsersonDao.class)
        })
})
public class MyConfig {
    
}