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";
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章