本文目标:传入指定路径,扫描对应路径下符合要求的类,并添加到指定的集合中。


其原型是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、文件整体目录结构

获取指定路径,扫描路径下指定类(仿Spring)_自定义


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、编写容器类(重点)

具体细节以在代码中给出注释,主要步骤分为以下几步:

  1. 解析传入配置类上@MobianComponentScan注解配置的包路径
  2. 将包路径转化为url路径
  3. 根据url路径定位到具体的文件位置,完成文件夹下文件的遍历
  4. 将具体文件的文件位置,转化为类加载能够识别的包路径
  5. 使用类加载器加载具体的,此时可以完成类的加载
  6. 解析类上的@MobianComponent配置的类的别名信息
  7. 将类的别名信息以及类信息存储到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();
}
}

测试结果:

获取指定路径,扫描路径下指定类(仿Spring)_自定义_02


即我们配置的扫描路径下面包含3个类1个接口,但是符合要求的(@MobianComponent)类只有2个,满足要求的类的别名分别时teacher和是schoolService。