spring mvc框架源码分析(二)-自定义注解以及通过反射获取注解

大多数框架都是通过注解,反射来实现很多框架功能的,在我们这个框架中,我们可以通过注解来标识不同的层,以及每个路径所对应的方法。


如何使用注解:

    参考spring的@Controller和RequestMapping,我们这个框架也可以自定义这两个注解,首先定义一个控制层注解@MyMontroller,这个注解作用于类,主要作用是标识某个类是否为控制层。

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

    之后定义一个方法级注解@MyRequestMapping,这个注解用于作用于方法,主要作用就是标识某个请求路径所对应的处理这个请求的方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String path() default "";
    RequestMethod[] method() default {};
}

    注解中有两个值,path为请求路径,method为请求方式,这里我们会定义一个枚举变量RequestMethod来记录一般的请求方式

public enum RequestMethod {
    GET,POST,PUT,DELETE
}

    最后还定义了一个注解@MyResponseString用于标识方法是返回一个字符串还是一个页面

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyResponseString {
}


容器初始化时通过反射获取注解:    

    至此Controller层基本的注解大概定义完了,接下来的事情就是在容器初始化的时候通过反射来获取每个路径以及每个路径所对应的注解,储存为list类型然后保存到ServletContext中

    首先定义一个AnnotationUtil类来处理注解,之后再定义一个监听器AnnotationListener用于容器启动时执行AnnotationUtil中对应的方法

public class AnnotationListener implements ServletContextListener{

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {

    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

web.xml配置

    <listener>
        <listener-class>com.example.listener.AnnotationListener</listener-class> </listener>

    这里我们开始编写AnnotionUtil,先说一下整体的流程:最开始我们需要定义一个变量stringList,来储存结果集,之后通过反射来读取controller目录下所有的类,最后再扫描这些类获取结果集。

public class AnnotationUtil {
    private  List<String> stringList;
    private List<Class> getClassList(String dir, String srcPath) throws ClassNotFoundException {
        List<Class> classList = new ArrayList<Class>();
        String newDir = dir.replace(".","/");
        String abSolutePath = srcPath + newDir; //构成完整路径
//file:/E:/dev/testServlet/out/artifacts/testServlet_war_exploded/WEB-INF/classes/src/com/example/myController
//去除srcPath 获取到的字符串前缀file:/
        String ss = abSolutePath.substring(6, abSolutePath.length());
        abSolutePath = ss.replace("/",File.separator);
        File dirFile = new File(abSolutePath);
        File[] files = dirFile.listFiles();
        Class<?> cla = null;
        for (File file:files){ //遍历文件夹里面的所有文件遇到文件夹则向下查找
            if (file.isDirectory()){
                String child = dir + "."+file.getName();
                getClassList(child, getSrcPath());
            }else {
                cla = Class.forName(dir+"."+file.getName().split("\\.")[0]);
                classList.add(cla);
            }
        }

        return classList;
    }

    private String getSrcPath(){
        String path="";
        try {
            path =Thread.currentThread().getContextClassLoader().getResource("")+"";  //获取src路径

        } catch (Exception e) {
            e.printStackTrace();
        }
        return path;
    }

    private void setRequestMapping(List<Class> list) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {
        for (Class<?> cla:list){
            if (cla.getAnnotation(MyController.class)!=null){ //是否有MyController注解
                Method[] methods = cla.getMethods();
                for (Method method : methods){
                    if (method.getAnnotation(MyRequestMapping.class)!=null){//判断方法上是否有@MyRequestMapping
                        MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                           if (method.getAnnotation(MyResponseString.class) != null){//判断方法上是否有@MyResponseString
                            stringList.add(annotation.path()+" "+annotation.method()[0].toString()+" "+ SystemConfig.ResponseString+" "+cla.getName()+" "+method.getName());
                        }else {
                            stringList.add(annotation.path()+" "+annotation.method()[0].toString()+" "+SystemConfig.ResponsePage+" "+cla.getName()+" "+method.getName());
                        }
                    }
                }
            }

        }
    }

    public List<String> getRequestMapping(){
        stringList = new ArrayList<String>();
        List<Class> list = null;
        try {
            list = getClassList("com.example.myController", getSrcPath());//获取com.example.myController目录下所有的类
            setRequestMapping(list);//选择符合条件的类,并且把类中所有的路劲,方法储存到list中
        } catch (Exception e) {
            e.printStackTrace();
        }

        return stringList;
    }

}

    AnnotionUtil中获取的List<String>里面有请求路径,请求方式,是否返回字符串,路径对应的类名,路径对应的处理方法,之后我们需要把它存储在ServletContext 中,在之前定义的监听器AnnotationListener 中

public class AnnotationListener implements ServletContextListener{ private ServletContext servletContext; @Override public void contextInitialized(ServletContextEvent servletContextEvent) { List<String> list = new AnnotationUtil().getRequestMapping(); this.servletContext = servletContextEvent.getServletContext(); servletContext.setAttribute("annotationList",list); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }

    之后我们就可以通过getServletContext().getAttribute(“annotationList”);来获取路径以及其对应的方法了。


实际运用注解:

@MyController
public class RequestController {

    @MyResponseString
    @MyRequestMapping(path = "/zhu/test1",method = RequestMethod.GET)
    public String testA(HttpServletRequest httpServletRequest){
        return "xx";
    }

    @MyRequestMapping(path = "/zhu/test",method = RequestMethod.GET)
    public String testOne(){
        return "index.jsp";
    }

    @MyRequestMapping(path = "/zhu/test",method = RequestMethod.POST)
    public String testOnea(){
        return "cao.html";
    }

    @MyRequestMapping(path = "/zhu/html_test",method = RequestMethod.GET)
    public String testOneaa(){
        return "xxx.html";
    }

}
    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/asd1098626303/article/details/60868219
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞