版权声明:本文为博主原创文章,未经博主允许不得转载。 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) {
}
}
方法接收到了参数