java 手寫程式碼簡單模擬SpringMVC
阿新 • • 發佈:2018-11-09
######1.在Spring MVC中,將一個普通的java類標註上Controller註解之後,再將類中的方法使用RequestMapping註解標註,那麼這個普通的java類就夠處理Web請求
######2.通過一個簡單的java專案來模擬Spring MVC,先說一下整體思路:
- 1.定義@Controller、@RequestMapping註解
- 2.模擬專案啟動時把這些類載入到統一容器中
- 3.模擬web請求,執行請求並返回結果
######3.專案的整體結構
- annotation:註解類
- controller:業務控制器
- test:測試類
- util:把控制器載入到容器,並執行請求
######4.具體實現如下:
@Controller註解
package com.zypcy.annotation; 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 { public String value() default ""; }
@RequestMapping註解
package com.zypcy.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { public String value() default ""; }
專案啟動時,容器只加載@Controller註解類和包含@RequestMapping註解的方法
再定義一個檢視類ModelAndView,控制器可以返回檢視或rest資料
package com.zypcy.annotation;
//模擬SpringMvc的ModelAndView ,但渲染檢視需自行實現
public class ModelAndView {
private String path;//檢視路徑
private Object data;//資料
private ModelAndView(){
}
public ModelAndView(String path){
super();
this.path = path;
}
public ModelAndView(Object data){
super();
this.data = data;
}
public ModelAndView(String path, Object data) {
super();
this.path = path;
this.data = data;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
######5.新增業務控制器
import com.zypcy.annotation.Controller;
import com.zypcy.annotation.ModelAndView;
import com.zypcy.annotation.RequestMapping;
@Controller
public class IndexController {
//返回Rest json 資料
@RequestMapping("/index")
public String index(){
System.out.println("歡迎進入IndexController的index方法");
return "index";
}
//返回檢視,可以自行檢視渲染模版,把資料渲染到模版中,響應到客戶端
@RequestMapping("/home")
public ModelAndView home(){
System.out.println("歡迎進入IndexController的index方法");
return new ModelAndView("/home.html","home");
}
//未加註解的方法,不會被載入到容器中,也就是不會處理web請求
public String getById(int id){
return "zy"+id;
}
}
######6.載入容器
package com.zypcy.util;
import java.lang.reflect.Method;
//容器中存放具體的Controller與Method物件
public class RequestExecuteModel {
private Class<?> clazz;
private Method method;
public RequestExecuteModel(){
}
public RequestExecuteModel(Method method , Class<?> clazz){
this.clazz = clazz;
this.method = method;
}
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
新增請求載入、處理類
package com.zypcy.util;
import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import com.zypcy.annotation.Controller;
import com.zypcy.annotation.ModelAndView;
import com.zypcy.annotation.RequestMapping;
public class RequestExecute {
//容器,用來放class與method
private static HashMap<String, RequestExecuteModel> maps = new HashMap<String, RequestExecuteModel>();
/**
* 掃描含有@Controller註解的類,得到@RequestMapping註解的方法
* 1.先獲取包下面的所有類檔案
* 2.根據類檔案獲取有@Controller註解的class檔案
* 3.再獲取class中有@@RequestMapping註解的方法,並新增到HashMap中
* @param packagePath
*/
public static void scanRequestMapping(String packagePath) {
try {
//獲取包下面的所有類檔案
String filePath = ClassLoader.getSystemResource("").getPath() + packagePath.replace(".", "/");
File file = new File(filePath);
if(file.exists()){
File[] files = file.listFiles();
for(File f : files){
//如果是目錄
if(f.isDirectory()){
//再加上目錄名,繼續掃描
scanRequestMapping(packagePath + "." + f.getName());
}else{
//如果是java類檔案
String className = f.getName().substring(0,f.getName().length() - 6);
//根據架包加類名載入class
Class clazz = Class.forName(packagePath + '.' + className);
//如果當前class使用了@Controller註解
if(clazz != null && clazz.isAnnotationPresent(Controller.class)){
//獲取類下面所有的方法
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods){
//如果方法使用了@RequestMapping註解
if(method.isAnnotationPresent(RequestMapping.class)){
RequestMapping rm = method.getAnnotation(RequestMapping.class);
RequestExecuteModel model = new RequestExecuteModel(method , clazz);
maps.put(rm.value(), model);
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 執行請求,返回結果
* @param uri
*/
public static void executeRequest(String uri){
try {
RequestExecuteModel model = maps.get(uri);
if(model != null){
Method method = model.getMethod();
Class clazz = model.getClazz();
//執行請求,未傳入引數
Object o = method.invoke(clazz.newInstance(), null);
System.out.println("返回值:"+o);
if(o instanceof ModelAndView){
System.out.println("返回頁面檢視");
ModelAndView mv = (ModelAndView)o;
System.out.println("檢視的路徑: " + mv.getPath());
System.out.println("檢視需渲染的資料:" + mv.getData());
}else{
System.out.println("返回json資料");
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
######7.測試
package com.zypcy.test;
import com.zypcy.util.RequestExecute;
public class MvcTest {
//測試
public static void main(String[] args) {
//專案啟動時,把控制器中的@RequestMapping路由請求載入進容器
RequestExecute.scanRequestMapping("com.zypcy.controller");
//模擬請求,獲取容器中的路由,並執行方法,獲取返回值
//index方法返回 Json 資料
RequestExecute.executeRequest("/index");
System.out.println("--------------------------------");
//home方法返回檢視,檢視可自行擴充套件對資料進行渲染
RequestExecute.executeRequest("/home");
}
}
下面是執行測試後結果:
以上是一個簡單的MVC實現,未實現請求傳參,下面是請求加引數,不過需要新增2個jar包,spring-core-4.3.13.RELEASE.jar與commons-logging-1.1.2.jar
/**
* Spring利用類LocalVariableTableParameterNameDiscoverer得到方法引數名,並將相應的引數值注入到RequestMapping的方法中
* @param method
* @param formParams
* @return
*/
public static Object[] getParamsToBePassed(Method method, HashMap<String, Object> formParams) {
LocalVariableTableParameterNameDiscoverer discover = new LocalVariableTableParameterNameDiscoverer();
String[] methodParams = discover.getParameterNames(method);
ArrayList<Object> paramsToBePassed = new ArrayList<Object>();
for (String methodParam : methodParams) {
Set<String> formParamKeys = formParams.keySet();
//和表單裡匹配的引數則相應設定值,不匹配的引數將預設設定為空值
if(formParamKeys.contains(methodParam)) {
paramsToBePassed.add(formParams.get(methodParam));
}else {
paramsToBePassed.add(null);
}
}
return paramsToBePassed.toArray();
}
/**
* 將Form表單的引數注入方法中。根據uri執行對應的處理方法
* @param uri
* @param formParams
*/
public static void executeRequest(String uri, HashMap<String, Object> formParams) {
try {
RequestExecuteModel model = maps.get(uri);
if(model != null){
Method method = model.getMethod();
Class clazz = model.getClazz();
//獲取引數
Object[] paramsToBePassed = getParamsToBePassed(method, formParams);
Object o = method.invoke(clazz.newInstance(), paramsToBePassed);
}
} catch (Exception e) {
}
}
方法接收到了引數