上一篇已經看了,我想你們都明白了mvc的原理,今天咱們來講一下要寫本身mvc框架必需要會的技術。html
mvc的目錄是這樣的java
src目錄是咱們核心的mvc代碼。這個代碼明天講,今天主要講的代碼都在test目錄下。mvc
在第一章我已經說了,寫mvc技術須要用的就是java的反射原理,還有自定義註解。框架
如今咱們先講一下註解的用處,可能這個自定義註解聽着挺高深的,等你看完這個就會明白,其實很簡單,就是xml的另外一種方法。jsp
講解我就都放在代碼的註解裏了,這樣寫比較方便。ide
annotation註解:post
package com.test; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * jdk1.5以上 * @Target 描述註解的使用範圍 * @Retention 註解生命週期 * @Documented 文檔相關 * @Inherited 闡述了某個被標註的類型是被繼承的 * 關於註解的詳細信息能夠看下面這個博客。 * http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html * 這篇文章寫的很好,我就不重複的叨逼叨了。。 * 在mvc裏 咱們就用兩個註解屬性 * @Target 標註使用範圍,這個咱們標註爲method。即這個註解咱們要用在方法級別上。 * @Retention 生命週期,即運行時,Runtime * @author wanglong * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnno { public String ActionName() default ""; public String Result() default ""; }
如今咱們定義註解了,咱們要用在哪呢,就用在Action的方法上,好比咱們寫一個Action學習
package com.test; public class TestAction { @Anno(ActionName="test.action",Result="index.jsp") public void TestAnno(User user) { System.out.println(user.getUsername()); } }
這樣咱們就在TestAction裏的TestAnno方法上加上了咱們自定義的註解。測試
咱們怎麼得到註解裏的值呢,就是經過反射。下面的代碼是Junit4的一個testcase。網站
public void testAnno() throws Exception{ Class clazz = Class.forName("com.test.TestAction"); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if(method.isAnnotationPresent(com.test.Anno.class)) { Anno anno = method.getAnnotation(com.test.Anno.class); System.out.println(anno.ActionName()); //test.action System.out.println(anno.Result()); //index.jsp } } }
恩,這裏咱們已經獲取到了,你們也知道該怎麼用了吧,也知道在mvc裏應該用在哪裏了。註解咱們就講到這裏。下面咱們說反射
java反射
我也不知道你們會不會反射。。我這也不知道說到什麼程度合適,因此我就把要在mvc中用到的反射方法說一下吧。
首頁,咱們有一個登陸頁面-login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form action="test.action" method="post"> <input type="text" value="admin" name="user.userName"> <input type="text" value="adminPassword" name="user.password"> <input type="submit" value="登陸"/> </form> </body> </html>
而後咱們還要有一個user類
package com.test; public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
咱們看到login.jsp請求action是test.action.裏面的參數分別是user.username跟user.password,咱們經過servlet攔截後,能夠經過java的自定義註解也就是咱們上面的Anno這個註解來獲取到test.action註解的這個(TestAnno)方法,可是咱們怎麼把頁面上的這兩個參數傳給action呢,因此咱們就用到反射,經過反射,咱們把頁面上user封裝,而後在經過反射來調用TestAnno方法。
首先,咱們要經過反射獲取到TestAnno這個方法的參數類型。
/* * 反射獲取方法參數類型 */ @org.junit.Test public void testActionParamType() throws Exception{ Class clazz = Class.forName("com.test.TestAction"); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Class[] params = method.getParameterTypes(); for (int i = 0; i < params.length; i++) { System.out.println(method.getName());//獲取的是方法的名稱 也就是 TestAnno System.out.println(params[i]);//方法參數類型 這裏打印 class com.test.User 說明這個方法只有一個參數 } } }
利用jdk的反射,咱們獲取不到方法的參數名稱,也就是public void Test(String s){...}
如今咱們能獲取到String類型,可是若是想獲取這個s,利用jdk的反射是獲取不到的,因此咱們藉助第三方jar包 javassist,這個jar包是對java反射的加強。下載地址百度找吧,若是找不到,能夠去這個地址找 https://jarfiles.pandaidea.com/ 這是個很不錯網站 你們能夠收藏~就不用處處去求包了。下面是利用javassist來獲取方法參數名稱。
/** * 利用javassist獲取方法參數名稱 * @throws Exception */ @org.junit.Test public void testGetParam() throws Exception{ Class clazz = Class.forName("com.test.TestAction"); String methodName = "TestAnno"; ClassPool pool=ClassPool.getDefault(); CtClass ctClass = pool.get("com.test.TestAction"); CtMethod ctMethod = ctClass.getDeclaredMethod(methodName); MethodInfo methodInfo = ctMethod.getMethodInfo(); CodeAttribute codeAttr = methodInfo.getCodeAttribute(); LocalVariableAttribute attr = (LocalVariableAttribute) codeAttr.getAttribute(LocalVariableAttribute.tag); if(attr==null){ System.out.println("沒有屬性"); } String[] paramsName = new String[ctMethod.getParameterTypes().length]; int pos =Modifier.isStatic(ctMethod.getModifiers())?0:1; for (int i = 0; i < paramsName.length; i++) { paramsName[i] = attr.variableName(i+pos); } for (int i = 0; i < paramsName.length; i++) { System.out.println(paramsName[i]); } }
這樣咱們就能獲取到方法所須要的一切了。而後經過反射的invoke來調用方法就能夠了。下面演示一下怎麼使用invoke來調用方法及傳參。
首先咱們新建一個測試類。咱們來調用這個類的say方法
package com.test; public class TestInvoke { public void say(String i ){ System.out.println(i); } }
而後咱們在junit測試裏添加測試方法。
/** * 測試方法調用 * @throws Exception */ @org.junit.Test public void testInvoke() throws Exception{ Class clazz = Class.forName("com.test.TestInvoke"); Object obj = clazz.newInstance(); /** * getMethod(方法名,方法參數類型).invoke(調用類的實例,方法參數); */ clazz.getMethod("say", String.class).invoke(obj, "this is a test"); }
在控制檯會打出this is a test。
在補充一個反射的用法。獲取類的屬性,屬性的類型。
/** * 獲取類的field 和類型 * @throws Exception */ @org.junit.Test public void testFieldType() throws Exception { Class clazz = Class.forName("com.test.User"); Object o = clazz.newInstance(); Field[] fields = clazz.getDeclaredFields(); for (Field f : fields) { System.out.println(f.getName()); System.out.println(f.getType()); /** 控制檯打印: username class java.lang.String password class java.lang.String */ } }
總結一下,經過今天的學習咱們都獲得了什麼。
1.經過annotation,咱們能夠得到註解的名稱,以及其餘屬性,以及註解的方法
2.經過反射:咱們能夠得到一個類都有哪些方法,方法的參數類型,類的屬性,類屬性的類型。不知道這麼說你們能不能明白。下面截個圖給你們看一下。
能夠看到 畫方框的,咱們經過反射(javassist)均可以獲取到,既然咱們都能獲取到,那咱們是否是就能封裝,轉發調用了呢?
一個簡單的mvc就用到這些技術,因此上面這些看懂了的話,你也能寫一個mvc了。
給你們留一個問題:
怎麼經過反射把頁面傳過來的參數 user 封裝起來。
若是你能封裝起來,我想就能經過invoke來調用action參數了吧?
若是不明白沒問題,我後面會教你們怎麼作。若是會的話,就繼續作用invoke來調用action方法吧。
提示:
1.反射
2.request.getParameterMap
寫這麼多實在太累了。。。
明天可能會有一更,週五至週末要去參加婚禮,因此沒時間更了。。。
但願你們把代碼都本身寫一遍。不要copy。能記住纔是本身的。。。
謝謝觀看。。。
ps:有多少人看?