IoC底层核心原理 扫描组件过滤器
分析底层源码的办法
查看底层源码 的层次结构
进入它的底层源码,按F4,在右边会有显示
先看它的接口,再看它接口的实现类是怎么做的
设定组件扫描加载过滤器
名称: @ComponentScan
类型:类注解
位置:类定义上方
作用:设置spring配置加载类扫描规则
范例
:@componentscan(
value=“com.itheima” //设置基础扫描路径
excludeFilters = //设置过滤规则,当前为排除过滤@componentScan.Filter( //设置过滤器
type= FilterType.ANOTATION //设置过滤方式为按照注解进行过滤
classes=Repository.class ) //设置具体的过滤项,过滤所有@Repository修饰的bean
includeFilters:设置包含性过滤器
excludeFilters:设置排除性过滤器
type:设置过滤器类型
//2.设置排除bean,排除的规则是自定义规则(FilterType.CUSTOM),具体的规则定义为(MyTypeFilter.class)
@ComponentScan(
value = "com.itheima",
excludeFilters = @ComponentScan.Filter(
type= FilterType.CUSTOM,
classes = MyTypeFilter.class
)
)
public class MyTypeFilter implements TypeFilter {
@Override
//加载的类满足要求,匹配成功
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//通过参数获取加载的类的元数据
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//通过类的元数据获取类的名称
String className = classMetadata.getClassName();
//如果加载的类名满足过滤器要求,返回匹配成功
if(className.equals("com.itheima.service.impl.UserServiceImpl")){
//返回true表示匹配成功,返回false表示匹配失败。此处仅确认匹配结果,不会确认是排除还是加入,排除/加入由配置项决定,与此处无关
return true;
}
return false;
}
}
MyTypeFilter 这个类
return true 过滤所有的bean
return false 所有的bean都不过滤
自定义导入器
bean只有通过配置才可以进入spring容器,被spring加载并控制
配置bean的方式如下:
XML文件中使用标签配置
使用@Component及衍生注解配置
自定义导入器牵涉的业务问题
企业开发过程中,通常需要配置大量的bean,需要一种快速高效配置大量bean的方式
导入器
名称: ImportSelector
类型:接口
作用:自定义bean导入器
代码
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 1.编程形式加载一个类
// return new String[]{"com.itheima.dao.impl.BookDaoImpl"};
// 2.加载import.properties文件中的单个类名
// ResourceBundle bundle = ResourceBundle.getBundle("import");
// String className = bundle.getString("className");
// 3.加载import.properties文件中的多个类名
ResourceBundle bundle = ResourceBundle.getBundle("import");
String className = bundle.getString("className");
return className.split(",");
}
}
编写自定义导入器的类 要实现 ImportSelector接口
再将 需要导入的类的全类名封装成字符串数组 返回
return new String[]{“com.itheima.dao.impl.BookDaoImpl”};
在 SpringConfig 核心配置类上 用@Import(MyImportSelector.class),将需要导入的类转换成字节码文件,进行导入。在@Import里是可以写多个类名.class的
@Configuration
@ComponentScan("com.itheima")
//3.自定义导入器类
@Import(MyImportSelector.class)
public class SpringConfig {
}
通过配置文件统配的形式,定义导入器
import.properties 导入器配置文件
path=com.itheima.dao.impl.*
用通配符指定通配的包路径
接下来编写它的 导入器 的实体类
public class CustomerImportSelector implements ImportSelector {
private String expression;
public CustomerImportSelector(){
try {
//初始化时指定加载的properties文件名
Properties loadAllProperties = PropertiesLoaderUtils.loadAllProperties("import.properties");
//设定加载的属性名
expression = loadAllProperties.getProperty("path");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//1.定义扫描包的名称
String[] basePackages = null;
//2.判断有@Import注解的类上是否有@ComponentScan注解
if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
//3.取出@ComponentScan注解的属性
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
//4.取出属性名称为basePackages属性的值
basePackages = (String[]) annotationAttributes.get("basePackages");
}
//5.判断是否有此属性(如果没有ComponentScan注解则属性值为null,如果有ComponentScan注解,则basePackages默认为空数组)
if (basePackages == null || basePackages.length == 0) {
String basePackage = null;
try {
//6.取出包含@Import注解类的包名
basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//7.存入数组中
basePackages = new String[] {basePackage};
}
//8.创建类路径扫描器
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
//9.创建类型过滤器(此处使用切入点表达式类型过滤器)
TypeFilter typeFilter = new AspectJTypeFilter(expression,this.getClass().getClassLoader());
//10.给扫描器加入类型过滤器
scanner.addIncludeFilter(typeFilter);
//11.创建存放全限定类名的集合
Set<String> classes = new HashSet<>();
//12.填充集合数据
for (String basePackage : basePackages) {
scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
}
//13.按照规则返回
return classes.toArray(new String[classes.size()]);
}
}
写完导入器实体类 还要在springConfig核心配置类中的 类注解 @import的括号内 写出这个导入器实体类的字节码文件,使用反射的方式来加载。