实现:Java 反射 + 注解

  • 创建两个注解:

    • @Bean :创建对象

      1
      2
      3
      4
      @Target(ElementType.TYPE) // 作用在类对象上
      @Retention(RetentionPolicy.RUNTIME) // 运行时生效
      public @interface Bean {
      }
    • @Di:属性注入

      1
      2
      3
      4
      @Target(ElementType.FIELD) // 使用在属性上
      @Retention(RetentionPolicy.RUNTIME) // 运行时生效
      public @interface Di {
      }
  • 创建Bean容器接口 ApplicationContext,定义方法,返回对象;

    1
    2
    3
    4
    public interface ApplicationContext {
    // 返回Bean对象
    Object getBean(Class clazz) throws NoSuchMethodException;
    }
  • 实现Bean容器接口

    • 返回对象
    • 创建有参构造器,传递包路径,设置包扫描规则
    • 根据包规则加载Bean()
    • 属性注入
    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
    33
    34
    35
    36
    37
    public class AnnotationApplicationContext implements ApplicationContext {
    // 创建map集合,存放Bean对象
    private Map<Class,Object> beanFactory = new HashMap<>();
    private String rootPath; // 前路径

    // fa
    @Override
    public Object getBean(Class clazz) throws NoSuchMethodException {
    return beanFactory.get(clazz);
    }

    // 创建有参构造器,传递包路径,设置包扫描规则
    // 当前包及子包中,如果哪个类有@Bean注解,则通过反射将其实例化
    public AnnotationApplicationContext(String basePackage){
    try {
    // 1. 将.替换为\
    String packagePath = basePackage.replaceAll("\\.","\\\\");
    // 2. 获取包的绝对路径
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
    .getResources(packagePath);
    while(urls.hasMoreElements()){
    URL url = urls.nextElement();
    String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);

    // 截取前路径
    rootPath = filePath.substring(0, filePath.length() - packagePath.length());
    // 包扫描
    loadBean(new File(filePath));
    }
    } catch (Exception e) {
    throw new RuntimeException(e);
    }

    // 属性注入
    loadDi();
    }

  • 包扫描加载Bean实现

    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    // 包扫描过程,实例化
    public void loadBean(File file) throws Exception {
    // 判断当前内容是否是一个文件夹
    if(file.isDirectory()) {
    // 获取文件夹中的所有内容
    File[] subFiles = file.listFiles();
    // 判断文件夹是否为空
    if(subFiles==null||subFiles.length==0){
    return;
    }
    // 遍历文件夹所有内容
    for(File subfile:subFiles){
    // 递归遍历文件夹及子文件夹
    if(subfile.isDirectory()){
    // 如果是文件夹,递归遍历
    loadBean(subfile);
    }else{
    // 得到包路径+类名
    String pathWithClass = subfile.getAbsolutePath().substring(rootPath.length()-1);
    // 判断当前文件类型是否为.class
    if(pathWithClass.endsWith(".class")){
    // 把\换成. 并去掉.class后缀
    String allName = pathWithClass.replaceAll("\\\\",".").replace(".class","");
    // 对有注解 @Bean的类进行实例化
    Class<?> clazz = Class.forName(allName);
    // 判断是否为接口
    if(!clazz.isInterface()){
    // 判断有无注解
    Bean annotation = clazz.getAnnotation(Bean.class);
    if(annotation != null){
    // 实例化对象
    Object instance = clazz.getConstructor().newInstance();
    // 实例化后的对象放入到map集合中
    // 如果有接口,接口作为key
    if(clazz.getInterfaces().length>0){
    beanFactory.put(clazz.getInterfaces()[0],instance);
    }else{
    beanFactory.put(clazz,instance);
    }
    }
    }
    }
    }
    }
    }

    }
  • 属性注入实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public void loadDi(){
    // 遍历 map 集合
    for(Map.Entry<Class,Object> entry:beanFactory.entrySet()){
    Object obj = entry.getValue();
    Class<?> clazz = obj.getClass();
    // 获取每个对象的属性
    Field[] fields = clazz.getDeclaredFields();
    // 遍历得到对象的属性数组,得到每个属性
    for(Field field:fields){
    // 判断属性上是否有 @Di 注解,把对象进行注入
    Di annotation = field.getAnnotation(Di.class);
    if(annotation!=null){
    field.setAccessible(true);
    try {
    field.set(obj,beanFactory.get(field.getType()));
    } catch (IllegalAccessException e) {
    throw new RuntimeException(e);
    }
    }
    }
    }
    }