一、先大致了解一下SpringMVC执行流程:
- 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
- DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
- DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)
- 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 - Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
- 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的 ViewResolver)返回给DispatcherServlet ;
- ViewResolver 结合Model和View,来渲染视图
- 将渲染结果返回给客户端。
二、直接贴代码,代码中有注释
1.首先创建一个maven web工程 添加如下依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lh.core</groupId>
<artifactId>framework</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>framework Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.leoraqlx.club</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!--servlet 依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 工具类-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<!-- json 解析 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.32</version>
</dependency>
<!-- Lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.4</version>
</dependency>
<!-- yaml文件 解析 -->
<dependency>
<groupId>org.jyaml</groupId>
<artifactId>jyaml</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<build>
<finalName>framework</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2.编辑web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--自定义的DispatchServlet-->
<servlet>
<servlet-name>dispatchservlet</servlet-name>
<servlet-class>com.lh.core.framework.servlet.DispatchServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatchservlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
3.几个需要用到的工具解析类
json解析
package com.lh.core.framework.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework.utils
* @ClassName: JsonUtil
* @Description: 将请求参数转换为jsonobject
* @Author: lihao
* @CreateDate: 2018/11/7 3:36 PM
* @UpdateDate: 2018/11/7 3:36 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
public class JsonUtil {
public static JSONObject getBody(HttpServletRequest request){
String contentType = request.getContentType();
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader=null;
try{
InputStream inputStream = request.getInputStream();
if(inputStream!=null){
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
}else{
stringBuilder.append("");
}
}catch (IOException e) {
e.printStackTrace();
}finally {
if(bufferedReader!=null){
try {
bufferedReader.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
if (contentType.equalsIgnoreCase("application/json")) {
String jsonStr = stringBuilder.toString();
JSONObject jsonObject = JSON.parseObject(jsonStr);
return jsonObject;
}
return null;
}
}
获取Java包下所有的类
package com.lh.core.framework.utils;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework.utils
* @ClassName: PackageUtil
* @Description: 获取Java包下所有的类
* @Author: lihao
* @CreateDate: 2018/10/24 5:41 PM
* @UpdateDate: 2018/10/24 5:41 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
public class PackageUtil {
public static List<String> scanPackage(String packageName){
List<String> classz=new ArrayList<>();
URL url = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".","/"));
if(url==null){
return null;
}
String pathFile = url.getFile();
File file = new File(pathFile);
String fileList[] = file.list();
for (String path : fileList) {
File eachFile = new File(pathFile + path);
if (eachFile.isDirectory()) {
scanPackage(packageName + "." + eachFile.getName());
} else {
String name = eachFile.getName();
classz.add(packageName + "." +name.substring(0,name.lastIndexOf(".")));
}
}
return classz;
}
}
yml文件解析
package com.lh.core.framework.utils;
import org.ho.yaml.Yaml;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
public class YmlUtil {
/**
* key:文件名索引
* value:配置文件内容
*/
private static Map<String, LinkedHashMap> ymls = new HashMap<>();
/**
* string:当前线程需要查询的文件名
*/
private static ThreadLocal<String> nowFileName = new ThreadLocal<>();
private static final String PACKAGENAME="spring.application.controller";
private static final String FILENAME="init.yml"; //配置文件名称
/**
* 加载配置文件
* @param fileName
*/
public static void loadYml(String fileName) throws FileNotFoundException {
nowFileName.set(fileName);
if (!ymls.containsKey(fileName)) {
ymls.put(fileName,Yaml.loadType(YmlUtil.class.getResourceAsStream("/" + fileName), LinkedHashMap.class));
}
}
private static Object getValue(String key) throws Exception {
// 首先将key进行拆分
String[] keys = key.split("[.]");
// 将配置文件进行复制
Map ymlInfo = (Map) ymls.get(nowFileName.get()).clone();
for (int i = 0; i < keys.length; i++) {
Object value = ymlInfo.get(keys[i]);
if (i < keys.length - 1) {
ymlInfo = (Map) value;
} else if (value == null) {
throw new Exception("key不存在");
} else {
return value;
}
}
throw new RuntimeException("不可能到这里的...");
}
public static Object getValue() throws Exception {
// 首先加载配置文件
loadYml(FILENAME);
return getValue(PACKAGENAME);
}
}
注解
Controller
package com.lh.core.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework
* @ClassName: ControllerAnnotation
* @Description: java类作用描述
* @Author: lihao
* @CreateDate: 2018/10/24 5:05 PM
* @UpdateDate: 2018/10/24 5:05 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Controller {
String value() default "";
}
RequestMapping
package com.lh.core.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework.annotation
* @ClassName: RequestMapping
* @Description: java类作用描述
* @Author: lihao
* @CreateDate: 2018/10/24 5:12 PM
* @UpdateDate: 2018/10/24 5:12 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
RequestJson
package com.lh.core.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework.annotation
* @ClassName: RequestJson
* @Description: java类作用描述
* @Author: lihao
* @CreateDate: 2018/10/31 5:26 PM
* @UpdateDate: 2018/10/31 5:26 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestJson {
String value() default "";
}
DispatchServlet
package com.lh.core.framework.servlet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lh.core.framework.annotation.Controller;
import com.lh.core.framework.annotation.RequestJson;
import com.lh.core.framework.annotation.RequestMapping;
import com.lh.core.framework.utils.JsonUtil;
import com.lh.core.framework.utils.PackageUtil;
import com.lh.core.framework.utils.YmlUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class DispatchServlet extends HttpServlet {
List<String>classz=new ArrayList<>();//扫描包下所有的类
Map<String,Method> mappingMethodMap=new ConcurrentHashMap<>();//url映射对应的方法
Map<String,Object>mappingClassMap=new ConcurrentHashMap<>();//url映射对应的类
/**
* 初始化,装配接口映射地址
*/
@Override
public void init(){
try {
classz= PackageUtil.scanPackage((String) YmlUtil.getValue());
} catch (Exception e) {
e.printStackTrace();
}
if(classz==null){
return;
}
for (String clazz:classz) {
String mappingUrl="";
try {
Class<?> loadClass =Class.forName(clazz);
Controller controller = loadClass.getAnnotation(Controller.class);
if(controller==null){
continue;
}
RequestMapping controllerUrl = loadClass.getAnnotation(RequestMapping.class);
mappingUrl=mappingUrl+controllerUrl.value();
Method[] methods = loadClass.getMethods();
for (Method method:methods){
RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
if(methodMapping==null){
continue;
}
mappingUrl=mappingUrl+methodMapping.value();
mappingMethodMap.put(mappingUrl,method);
}
Object object = loadClass.newInstance();
mappingClassMap.put(mappingUrl,object);
} catch (ClassNotFoundException e) {
e.printStackTrace();
continue;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
/**
* 执行请求,返回结果
* @param request
* @param response
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
Method method = mappingMethodMap.get(uri);
Object object = mappingClassMap.get(uri);
if(method==null){
return;
}
JSONObject body =JsonUtil.getBody(request);
Parameter[] parameters = method.getParameters();
Object[] o = buildParam(parameters, body);
try {
Object invoke = method.invoke(object,o);
response.getWriter().write(invoke.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 封装接口方法的入参
* @param parameters
* @param body
* @return
*/
private Object[] buildParam( Parameter[] parameters, JSONObject body){
Object[] objects=new Object[parameters.length];
for (int i=0;i< parameters.length ;i++) {
RequestJson requestJson = parameters[i].getAnnotation(RequestJson.class);
if(requestJson==null){
continue;
}
Object object = JSON.toJavaObject(body, parameters[i].getType());
objects[i]=object;
}
return objects;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
public void destroy() {
super.destroy();
}
}
自定义一个controller
package com.lh.core.framework.controller;
import com.lh.core.framework.annotation.Controller;
import com.lh.core.framework.annotation.RequestJson;
import com.lh.core.framework.annotation.RequestMapping;
import com.lh.core.framework.request.IndexRequest;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework.controller
* @ClassName: IndexController
* @Description: java类作用描述
* @Author: lihao
* @CreateDate: 2018/11/9 3:04 PM
* @UpdateDate: 2018/11/9 3:04 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
@RequestMapping("/index")
@Controller
public class IndexController {
@RequestMapping("/test")
public String test(@RequestJson IndexRequest request){
System.out.println("request = [" + request + "]");
return "OK";
}
}
请求参数
package com.lh.core.framework.request;
import lombok.Data;
/**
* @ProjectName: framework
* @Package: com.lh.core.framework.request
* @ClassName: IndexRequest
* @Description: java类作用描述
* @Author: lihao
* @CreateDate: 2018/11/9 3:05 PM
* @UpdateDate: 2018/11/9 3:05 PM
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
@Data
public class IndexRequest {
private String title;
private Integer num;
}
请求示例:
源码地址 :https://github.com/lihao95/framework/tree/master/framework