主要思路:
主要是手写了@Controller注解和@RequestMapping注解,然后在测试类上面添加注解,通过自己写好的mvc实现测试类里面的方法。
实现MVC的思路:
- 首先获取类里面所有的java文件,将所有的文件名放入一个List中;
- 然后遍历这个list中的文件名,获取对应的类;
- 判断类中是否有@Controller注解和@RequestMapping注解;
- 如果有就用一个map存储类名以及类对象。
- 获取类中所有的方法,遍历所有的方法,若方法被@RequestMapping标注,则用另一个map存储映射路径以及method方法。
- 最后根据路径,利用反射实现被注解标注的方法。
实现步骤:
-
自定义注解:
选择新建一个java Class,然后选择第四个注解即可生成一个注解类
new-->java class -->Annotation
自定义Controller注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String value() default "";
}
@Target表示注解(被它所注解的注解)可以使用到哪些地方,可以是类,方法,或者是属性上等。@Target(ElementType.TYPE)在这里表示Controller注解被使用到类上。
@Retention是注解的有效时间,RetentionPolicy.RUNTIME是指程序运行的时候。
自定义RequestMapping注解:
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
@Target({ElementType.METHOD, ElementType.TYPE})表示@RequestMapping注解可以加在类上和方法上;
2.扫描路径下面的文件:
该方法通过递归的方法将给定路径下面的文件都扫描出来,添加到一个List中返回。
//找到对应路径下面的文件
private static List<String> scanFileWorker(String path){
List<String> result=new ArrayList<>();
File file=new File(path);
File[] files=file.listFiles();
for(File f:files){
if(f.isFile()){
result.add(f.getAbsolutePath());
}else{
result.addAll(scanFileWorker(f.getAbsolutePath()));
}
}
return result;
}
3. 将给定路径下面的文件里面的java文件过滤出来
//获取该路径下面所有的文件
List<String> result=scanFileWorker(path);
//将后缀为class的文件过滤出来
result=result.stream().filter(item->{
int index=item.lastIndexOf(".");
String ext=item.substring(index+1);
return Objects.equals("class", ext);
}).collect(Collectors.toList());
4.遍历java文件名,获取每个java文件名对应的类,判断类里面是否有@Controller注解和@RequestMapping注解,如果有,获取@RequestMapping中的值和类对象的对应关系,存入一个map中;然后获取类里面所有的方法,若方法被@RequestMapping注解,则将url以及对应的方法实例存入另一个map中。
for(String res:result){
//从包名部分截取到后缀之前,以获取完整的包名+类名的形式
String res1=res.substring(res.indexOf("qcby"),res.indexOf("."));
String res2=res1.replace("\\",".");
//获取对应的类
Class<?> c=Class.forName(res2);
//如果这个类里面有@Controller注解
if(c.isAnnotationPresent(Controller.class)){
System.out.println(c.getName()+"被注册成控制器");
//如果这个类里面有@RequestM注解
if(c.isAnnotationPresent(RequestMapping.class)){
String s=c.getAnnotation(RequestMapping.class).value(); //获取RequestMapping注解中的值
//如果map中已经存在key值了,抛出异常
if(map.containsKey(s)){
throw new RuntimeException("类多注解值:"+s);
}else{
//否则将@RequestMapping中的值作为key放入map中
map.put(s,new HashMap<>());
controllerMap.put(s,c.newInstance());
}
//获取类里面所有的方法
Method[] methods=c.getDeclaredMethods();
for(Method method:methods){
if(method.isAnnotationPresent(RequestMapping.class)){
//获取每个方法上面的RequestMapping注解中的值
String s1=method.getAnnotation(RequestMapping.class).value();
if(map.get(s).containsKey(s1)){
//抛出异常
throw new RuntimeException("方法多注解值:"+s1);
}else{
//将方法名上面的RequestMapping中的值以及对应的方法放入map中
map.get(s).put(s1,method);
}
}
}
System.out.println(map);
System.out.println(controllerMap);
}else {
throw new RuntimeException("类无requestMapping");
}
}
}
5.传入类路径和方法路径,通过反射实现对应的方法:
//调用此方法实现测试类中被注解的方法
public static void exec(String classpath,String methodPath){
//如果controllerMap中没有这个类的名字
if(controllerMap.get(classpath)==null){
System.out.println("没有这个类 404");
}else{
//如果map中没有储存这个方法上面注解的值
if(map.get(classpath).get(methodPath)==null){
System.out.println("没有这个方法 404");
}else{
try {
//通过反射执行方法
map.get(classpath).get(methodPath).invoke(controllerMap.get(classpath));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
6.测试类如下:
import qcby.annotation.Controller;
import qcby.annotation.RequestMapping;
@Controller
@RequestMapping("test")
public class TestController {
@RequestMapping()
public String index(){
System.out.println("test->index");
return "";
}
@RequestMapping("index1")
public String index1(){
System.out.println("test->index1");
return "";
}
}
测试函数:
package qcby.Controller;
import qcby.mvc.Mvc;
import java.io.UnsupportedEncodingException;
import java.util.Objects;
public class Main {
static {
//获取自己的类所在的路径
String path= Objects.requireNonNull(Main.class.getResource("")).getPath();
try {
//路径名中中文,因此用utf-8编码
path=java.net.URLDecoder.decode(path,"utf-8").substring(1);
//执行函数扫描路径下面的文件,并将带有注解的类和方法存入对应的map中
Mvc.scanner(path);
} catch (UnsupportedEncodingException | ClassNotFoundException | InstantiationException |
IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//执行对应的方法
Mvc.exec("","");
Mvc.exec("test","index1");
Mvc.exec("test","");
System.out.println("Hello World!");
}
}
输出:
qcby.Controller.TestController被注册成控制器
{test={=public java.lang.String qcby.Controller.TestController.index(), index1=public java.lang.String qcby.Controller.TestController.index1()}}
{test=qcby.Controller.TestController@10f87f48}
没有这个类 404
test->index1
test->index
Hello World!
第一个Mvc.exec("","")执行后,会在controllermap中寻找key为“”的键值对,因此并不能找到,输出了“没有这个类 404”
第二个Mvc.exec("test","index1"),会在controllermap中找到key为“test”的键值对,在map中找到"index1"对应的方法,利用反射执行对应的方法;第三个会找到没有标注RequestMapping的方法并执行。