本文目标:传入指定路径,扫描对应路径下符合要求的类,并添加到指定的集合中。
其原型是Spring中初始化容器类的一部分。当然本例仅仅完成基本功能,没有考虑过多功能——即Spring中配置对应的CompontScan路径,并在类上添加@Component注解,即可完成将一个类添加到Spring容器。
文章目录
- 1、文件整体目录结构
- 2、自定义注解
- 3、编写被测试对象类
- 4、编写一个配置类
- 5、编写BeanDefinition类
- 6、编写容器类(重点)
- 7、测试类
自定义两个注解(2个):类比理解Spring中@Component、@ComponentScan注解
自定义MobianContext容器类(1个):类比理解Spring中AnnotationConfigApplicationContext类
自定义MobianBeanDefinition类(1个):类比理解Spring中的BeanDefinition类
自定义MobianConfig配置类(1个):类比理解Spring中的我们自己自定义的配置类
1、文件整体目录结构
2、自定义注解
这两个注解仅有最基础的功能,且必须配置对应的value值
MobianScan的value用于配置扫描路径
MobianComponent的value用来配置类的别名
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MobianScan {
String value();
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MobianComponent {
String value();
}
3、编写被测试对象类
随便写的几个类,部门类添加指定的注解,并且配置别名信息
@MobianComponent("teacher")
public class TeacherService {
}
public class StudentService {
}
public interface SchoolService {
void show();
}
@MobianComponent("schoolService")
public class SchoolServiceImpl implements SchoolService {
@Override
public void show() {
}
}
4、编写一个配置类
初始化context容器时使用
其创建是为了模仿spring的配置类,本例中仅需要使用配置的路径
@MobianScan("pers.imitationspring.service")
public class MobianConfig {
}
5、编写BeanDefinition类
这里的作用和Spring中对应的类作用相同,用来存放所有Bean的初始化信息,只是本例中比较简单
public class MobianBeanDefinition {
private Class clazz;
public Class getClazz() {return clazz;}
public void setClazz(Class clazz) {this.clazz = clazz;}
}
6、编写容器类(重点)
具体细节以在代码中给出注释,主要步骤分为以下几步:
- 解析传入配置类上@MobianComponentScan注解配置的包路径
- 将包路径转化为url路径
- 根据url路径定位到具体的文件位置,完成文件夹下文件的遍历
- 将具体文件的文件位置,转化为类加载能够识别的包路径
- 使用类加载器加载具体的,此时可以完成类的加载
- 解析类上的@MobianComponent配置的类的别名信息
- 将类的别名信息以及类信息存储到beanDefinitionMap中
public class MobianContext {
// 存放所有扫描出来的Bean,等价于Spring中的beanDefinitionMap集合
ConcurrentHashMap<String, MobianBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public MobianContext(Class config) throws ClassNotFoundException {
if (config.isAnnotationPresent(MobianScan.class)) {
MobianScan scanAnno = (MobianScan) config.getAnnotation(MobianScan.class);
// 获取注解中配置的路径
String scanPath = scanAnno.value();
// 将对应的路径转化为路径格式
String scanUrl = scanPath.replace(".", "/");
// 通过类加载器,传入相对于当前项目的路径(target/classes目录下),查找指定的资源
// scanUrl:pers/imitationspring/service
ClassLoader loader = MobianContext.class.getClassLoader();
URL resource = loader.getResource(scanUrl);
// resource : file:/E:/Softwareworkspace/ideaworkspace/writespring/target/classes/pers/imitationspring/service
if (resource != null) {
// 获取对应路径资源下的所有文件
File files = new File(resource.getFile());
// 遍历对应的文件
for (File file : files.listFiles()) {
// 获取当前文件的绝对路径
String path = file.getAbsolutePath();
// 切割路径字符串,拿到相对路径
String classPath = path.substring(path.indexOf("pers"), path.indexOf(".class"));
// 将相对路径转换为包结构
classPath = classPath.replace("\\", ".");
// 根据类的路径去创建一个对象,此时已经能获取到对应的类信息
Class<?> aClass = loader.loadClass(classPath);
System.out.println(aClass);
// 获取类上对应的注解
MobianComponent componentAnno = aClass.getAnnotation(MobianComponent.class);
if (componentAnno != null) {
// 在Component注解不为空的情况下,获取注解内的信息(即类注册的别名)
String beanName = componentAnno.value();
MobianBeanDefinition beanDefinition = new MobianBeanDefinition();
beanDefinition.setClazz(aClass);
// 将类别名和类信息存储到beanDefinitionMap中
beanDefinitionMap.put(beanName, beanDefinition);
}
}
}
}
}
// 获取beanDefinitionMap集合中存储的所用信息
public void printBeanDefinitionMap() {
for (Map.Entry<String, MobianBeanDefinition> bd : beanDefinitionMap.entrySet()) {
System.out.println(bd.getKey() + " : " + bd.getValue().getClazz());
}
}
}
7、测试类
初始化Context容器类,打印beanDefinitionMap集合内容(所有满足要求的类)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
MobianContext context = new MobianContext(MobianConfig.class);
System.out.println("====");
context.printBeanDefinitionMap();
}
}
测试结果:
即我们配置的扫描路径下面包含3个类1个接口,但是符合要求的(@MobianComponent)类只有2个,满足要求的类的别名分别时teacher和是schoolService。