跟我学Springboot-按需加载Bean

跟我学Springboot-按需加载Bean

在启动SpringBoot应用时,有这样一种需求,不需要加载全部的Bean,按照业务不同,按需加载。

为什么要按需加载

按需加载的概念很简单,按照系统的要求加载不同的模块。在SpringBoot应用中,即加载指定的Bean到Spring容器中。

按需加载有两个好处,首先容器中不需要加载额外的类,造成不必要的资源浪费。其次代码可以按package区分,既高内聚又方便管理。

按需加载的实现

Conditional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
Condition类是一个函数式接口,作为判定一个组件是否应该注入到Spring容器中。
在Bean definition前就执行检查,如果不符合校验规则就不进行注册。
*/
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

public class TravelLoadCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
System.out.println("business=" + context.getEnvironment().getProperty("search.business"));
//获取application.yml配置文件中的search.business属性,并匹配是否等于travel
return context.getEnvironment().getProperty("search.business").equals("travel");
}
}

@Component
//如果TravelLoadCondition返回true,则注入TravelBasicSearch。
@Conditional(TravelLoadCondition.class)
public class TravelBasicSearch {
public TravelBasicSearch() {
System.out.println(this.getClass().getSimpleName() + " loaded....");
}

@PostConstruct
private void init() {
System.out.println(this.getClass().getSimpleName() + " inited....");
}
}

ComponentScan

ComponentScan是用在Configuration上的指令,等同于XML中的<context:component-scan>。它可以自动扫描@Component注解或者被它标记的注解,@Controller@Service@Repository都是被@Component标记的注解。

1
2
3
4
5
6
7
<context:component-scan
base-package="com.yibai.spring.annotation" use-default-filters="false">
<context:include-filter type="custom"
expression="com.yibai.spring.annotation.filter.ColorBeanLoadFilter" />
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Component" />
</context:component-scan>
属性 含义
value 指定要扫描的package
includeFilters 指定只包含的组件,FilterType:指定过滤规则,支持的过滤规则有。ANNOTATION:按照注解规则,过滤被指定注解标记的类;ASSIGNABLE_TYPE:按照给定的类型; ASPECTJ:按照ASPECTJ表达式;REGEX:按照正则表达式 CUSTOM:自定义规则;
excludeFilters 指定需要排除的组件。规则同上
useDefaultFilters True or False。指定是否需要使用Spring默认的扫描规则:被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
1
2
3
4
5
6
7
8
9
10
11
12
1. 扫描指定类文件
@ComponentScan(basePackageClasses = Person.class)
2. 扫描指定包,使用默认扫描规则,即被@Component, @Repository, @Service, @Controller或者已经声明过@Component自定义注解标记的组件;
@ComponentScan(value = "com.yibai")
3. 扫描指定包,加载被@Component注解标记的组件和默认规则的扫描(因为useDefaultFilters默认为true
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
4. 扫描指定包,只加载Person类型的组件
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class) }, useDefaultFilters = false)
5. 扫描指定包,过滤掉被@Component标记的组件
@ComponentScan(value = "com.yibai", excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
6. 扫描指定包,自定义过滤规则
@ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }, useDefaultFilters = true)

自定义扫描过滤规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ColorBeanLoadFilter implements TypeFilter {

@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 当前被扫描类的注解信息
@SuppressWarnings("unused")
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 当前被扫描类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 当前被扫描类资源信息
@SuppressWarnings("unused")
Resource resource = metadataReader.getResource();
try {
String className = classMetadata.getClassName();
Class<?> forName = Class.forName(className);
if (Color.class.isAssignableFrom(forName)) {
// 如果是Color的子类,就加载到IOC容器
return true;
}
} catch (ClassNotFoundException e) {

}
return false;
}
}

参考

1、Spring组件注册注解之@ComponentScan,@ComponentScans

评论