java 手写代码简单模拟SpringMVC

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhuyu19911016520/article/details/82622477

######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) {
    }
}

这里写图片描述
方法接收到了参数

源码下载

猜你喜欢

转载自blog.csdn.net/zhuyu19911016520/article/details/82622477