原来一直是直接用注解,从来没考虑过注解是怎么去实现的,后来自己通过网上学习,并查阅了一些资料,自己动手搭建了一个自定义注解的小框架用来学习,搭建好了一直也没有总结,今天抽空总结一下吧.
先看一个大概的目录结构
有注解类的话才能在别的类中使用注解,所以先创建两个自定义的注解类
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值相同时,这两个方法就都会执行,只要理解了这个流程,就很好解决了!
最后看一下运行结果吧
具体的项目已经放到我的码云仓库了,有需要的可以自行克隆查看