spring 用反射搭建 自定义注解 框架

原来一直是直接用注解,从来没考虑过注解是怎么去实现的,后来自己通过网上学习,并查阅了一些资料,自己动手搭建了一个自定义注解的小框架用来学习,搭建好了一直也没有总结,今天抽空总结一下吧.

先看一个大概的目录结构

有注解类的话才能在别的类中使用注解,所以先创建两个自定义的注解类 

Controller

package annotation;

import java.lang.annotation.*;

@Documented 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
    String value() default "";
}

 RequestMapping

package annotation;

import java.lang.annotation.*;

@Documented 
@Target({ElementType.TYPE,ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
     String value() default "";
}

使用 @interface 声明注解类

@Ducomented : 使用javadoc自动生成文档信息
@Target({ElementType.TYPE,ElementType.METHOD})  : 描述该注解应用于什么地方,如方法上、类上、成员变量上、方法参数上等等

@Retention(RetentionPolicy.RUNTIME) : 定义注解的生命周期RetentionPolicy.RUNTIME .CLASS .SOURCE分别对应运行时 字节码 源码

 然后就可以建一个测试用的controller了,就是我们平常用的controller

TestController

package controller;

import annotation.Controller;
import annotation.RequestMapping;

/**
 * @ClassName TestController
 * @Description : 测试TestController类
*/
@Controller
public class TestController {

    @RequestMapping(value = "partDemo1")
    public Boolean partDemo1(){
        System.out.println("输出TestController第一次");
        return true;
    }

    @RequestMapping(value = "partDemo2")
    public int partDemo2(){
        System.out.println("输出TestController第二次");
        return 100;
    }
}

到了这里,就开始最重要的步骤了,通过反射获取到带有@Controller注解的类->带有@RequestMapping,并且value值与输入值相同,相当于方法路径

Main

import annotation.Controller;
import annotation.RequestMapping;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName Main
 * @Description : 反射练习Main类
 * @Author : R
*/
public class Main {
   public static void testMain(Class aClass,String path) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        Annotation annotation = aClass.getAnnotation(Controller.class);
        //判断这个类是否带有Controller注解
        if (annotation != null){
            final Object o = aClass.newInstance();
            final Method[] declaredMethods1 = o.getClass().getDeclaredMethods();
                for (Method method : declaredMethods1) {
                    //找到带有RequestMapping注解的方法
                    final RequestMapping annotation1 = method.getAnnotation(RequestMapping.class);
                    final String value = annotation1.value();
                    //找到输入的值与 RequestMapping的value值相同的方法并执行
                    if (path.equals(value)){
                        final Object invoke = method.invoke(o);
                        System.out.println("invoke====="+invoke);
                    }
                }
        }
    }
    //通过loader加载所有类
    private static List<Class> loadClassByLoader(ClassLoader load) throws Exception{
        Enumeration<URL> urls = load.getResources("");
        //放所有类型
        List<Class> classes = new ArrayList<Class>();
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            //文件类型(其实是文件夹)
            if (url.getProtocol().equals("file")) {
                loadClassByPath(null, url.getPath(), classes, load);
            }
        }
        return classes;
    }
    //通过文件路径加载所有类 root 主要用来替换path中前缀(除包路径以外的路径)
    private static void loadClassByPath(String root, String path, List<Class> list, ClassLoader load) {
        File f = new File(path);
        if(root==null){
            root = f.getPath();
        }
        //判断是否是class文件
        if (f.isFile() && f.getName().matches("^.*\\.class$")) {
            try {
                String classPath = f.getPath();
                //截取出className 将路径分割符替换为.(windows是\ linux、mac是/)
                String className = classPath.substring(root.length()+1,classPath.length()-6).replace('/','.').replace('\\','.');
                list.add(load.loadClass(className));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } else {
            File[] fs = f.listFiles();
            if (fs == null){
                return;
            }
            for (File file : fs) {
                loadClassByPath(root,file.getPath(), list, load);
            }
        }
    }
    public static void main(String[] args) throws Exception {
        /**
         * java反射 注解问题
         */
        Main main = new Main();
        List<Class> classes = loadClassByLoader(main.getClass().getClassLoader());
        while(true){
            Scanner scan = new Scanner(System.in);
            System.err.println("请输入方法名(输入0结束程序运行):");
            String line = scan.nextLine();
            for (Class aClass : classes) {
                testMain(aClass,line);
                String index = "0";
                while(index.equals(line))
                {
                    return;
                }
            }
        }
    }
}

到这里就大致完成了,输入方法名,就可以执行对应的controller方法,其中还是有些不足的,没有去给@Controller的value赋值,当有两个controller中的@RequestMapping的value值相同时,这两个方法就都会执行,只要理解了这个流程,就很好解决了!

最后看一下运行结果吧

具体的项目已经放到我的码云仓库了,有需要的可以自行克隆查看

项目地址 : https://gitee.com/wei_lu/reflection_annotation.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值