学会使用框架,慢慢的就需要提升自己,学会手写框架。我们先从简单的 MVC 开始。本篇文章
本代码已经上传到Gitee上面:
https://gitee.com/li_kun_zang/do-mvc
技术包含了:
- 自定义注解
- 类加载
- 反射 & 注解扫描
主要实现功能:
- 统一请求控制
- 请求参数数据类型转换
- 统一请求转发、重定向
- 响应JSON数据。
思想
M:Model 模型 代表 @Controller修饰的内容
V:View 视图 图中 返回的结果 404 500 JSON 等等
C:Controller 控制器 图中代表 DispatcherServlet
实操
jdk 8 环境、tomcat 8 环境、maven 环境
1、创建 webapp 项目
自己补全 java、resources、 test 文件夹
2、引入 Maven 依赖
这里是封装的 反射的 操作。
代码语言:javascript复制 <dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
3、创建相关注解
自创建一个 annoation 文件夹 并在 下面创建 @Controller 、@RequestMapping 注解
代码如下
@Controller 注解
代码语言:javascript复制import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author : zanglikun
* @date : 2021/2/23 11:50
* @Version: 1.0
* @Desc : 自定义的 Controller 注解 需要声明 他的运行周期,我们采用元注解 进行 注解 Controller 注解 如果 这里 不理解 ,请去访问 https://www.zanglikun.com/1078.html 学习
*/
@Target(ElementType.TYPE) // 设置 只能修饰在类上
@Retention(RetentionPolicy.RUNTIME) // 设置 在运行期有效
public @interface Controller {
/**
* @Target(ElementType.METHOD)
* @Retention(RetentionPolicy.SOURCE)
*/
// @Override //我们进入 此注解,获取它的 注解格式,我们修改即可
}
@RequeseMapping 注解
代码语言:javascript复制import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author : zanglikun
* @date : 2021/2/23 11:54
* @Version: 1.0
* @Desc : RequestMapping 注解 并加入一个 value 值。
*/
@Target({ElementType.METHOD,ElementType.TYPE}) // 注解在 类和方法上
@Retention(RetentionPolicy.RUNTIME) // 注解此注解 只在运行期
public @interface RequestMapping {
String value() default ""; // 设置注解 可传递的参数,默认值是 ""
}
4、去使用注解
创建 一个 Controller 文件夹 并创建 一个 Java 类
我们在写代码过程,就能看到我们自己的注解了。
写2个 Controller
UserController 其中 NoScanMethod 没有被 @RequestMapping 注解
代码语言:javascript复制import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
/**
* @author : zanglikun
* @date : 2021/2/23 12:27
* @Version: 1.0
* @Desc : 用户控制类
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/add")
public void ADD(){
System.out.println("UserController 执行了add方法");
}
@RequestMapping("/del")
public void DEL(){
System.out.println("UserController 执行了del方法");
}
@RequestMapping("/get")
public void GET(){
System.out.println("UserController 执行了get方法");
}
public void NoScanMethod(){
System.out.println("UserController 执行了NoScanMethod方法");
}
}
NewsController
代码语言:javascript复制import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
/**
* @author : zanglikun
* @date : 2021/2/23 12:27
* @Version: 1.0
* @Desc : 新闻控制类
*/
@Controller
@RequestMapping("/news")
public class NewsController {
@RequestMapping("/add")
public void ADD(){
System.out.println("NewsController 执行了add方法");
}
@RequestMapping("/del")
public void DEL(){
System.out.println("NewsController 执行了del方法");
}
@RequestMapping("/get")
public void GET(){
System.out.println("NewsController 执行了get方法");
}
}
5、创建一个 Model 包 下的UrlMapping 将来集合 收集 类的信息
代码语言:javascript复制import java.lang.reflect.Method;
/**
* @author : zanglikun
* @date : 2021/2/23 14:04
* @Version: 1.0
* @Desc : 费劲,没啥好说的
*/
public class UrlMapping {
private Object obj;
private Method method;
public UrlMapping() {
}
public UrlMapping(Object obj, Method method) {
this.obj = obj;
this.method = method;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
6、扫描注解
创建一个 utils 包,ClassUtils 类
代码语言:javascript复制import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
import com.zanglikun.framework.model.UrlMapping;
import org.reflections.Reflections;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author : zanglikun
* @date : 2021/2/23 13:27
* @Version: 1.0
* @Desc : 费劲,没啥好说的
*/
public class ClassUtils {
// 创建 收集数据的对象
private static Map<String, UrlMapping> map = new HashMap<>();
static {
try {
getUrlMappings("com.zanglikun");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
// 私有静态 代码块的方式,保证了 扫描 所有类的 注解、方法 信息 只执行一次
// 传递 一个 基础的 包名
private static Map<String, UrlMapping> getUrlMappings(String basePackageName) throws IllegalAccessException, InstantiationException, InvocationTargetException {
// 创建反射对象,
Reflections reflections = new Reflections(basePackageName);
// 获取 被 @Controller 修饰的对象的字节码信息
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Controller.class);
// 遍历 这些 类的字节码信息
for (Class<?> aClass : classes) {
// 判断 类上有 @RequestMapping 注解的 类
RequestMapping classAnnotation = aClass.getDeclaredAnnotation(RequestMapping.class);
// 定义 类上的 注解 值
String baseUri = "";
// 判断 是否有这个对象
if (classAnnotation != null) {
// 如果有 获取 类的注解名
String classAnnotationname = classAnnotation.value();
// baseUri 拼接上类的注解名
baseUri = classAnnotationname;
}
Object obj = aClass.newInstance();
// 拿到这些类,我们就获取其中所有的方法信息
Method[] methods = aClass.getMethods();
// 遍历 这些方法
for (Method method : methods) {
//判断 注解 其中被 @RequestMapping 修饰的方法
RequestMapping methodAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
if (methodAnnotation != null) {
// 定义 方法上的 注解值
String value = methodAnnotation.value();
// 获取 最终结果 类名 方法名 RequestMapping的 所有值
String requestUri = aClass.getName() " -- " method.getName() " -- " baseUri value;
map.put(baseUri value, new UrlMapping(obj, method));
}
}
}
return map;
}
public static UrlMapping getURLMapping(String url) {
return map.get(url);
}
// 测试 调用方法
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
// 获取 被RequestMapping 从类 修饰到 方法 匹配到 /news/add 类和方法的信息
UrlMapping urlMapping = ClassUtils.getURLMapping("/news/add");
Method method = urlMapping.getMethod();
// 调用 获取到的类的信息, 准确的 有效的使用反射 进行调用方法!!!
method.invoke(urlMapping.getObj());
}
}
初步测试:
代码语言:javascript复制NewsController 执行了add方法
7、
引入 Servlet
代码语言:javascript复制<!-- 这里 封装了 反射的工具,我们一般 使用其中的 Reflections 对象 进行操作 -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.11</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
8、创建 DispatcherSevlet
让其 继承于 HttpServlet 并重写 doGet、doPost 方法
代码语言:javascript复制import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req,resp);
}
}
9、配置 Servlet
这里 强调 上述代码,使用的Tomcat 版本 是:apache-tomcat-8.5.31 ,经过 残忍的测试,长了教训,Tomcat 不同版本 ,支持的不同协议,以及实现方式 都发生变化。起码经过我测试,Tomcat10 就会报错
代码语言:javascript复制jakarta.servlet.ServletException: 类com.zanglikun.framework.servlet.DispatcherServlet不是Servlet
我们 学过 Java Web 都知道 Servlet 配置 有2 中方式 一个是 注解 @Servlet 另一个 是 webapps 下面的 WEB-INF 的 web.xml
代码语言:javascript复制DispatcherServlet
代码语言:javascript复制import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet 方法 拦截到了:" req.getRequestURI());
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req,resp);
}
}
因为 注解 形式,不好控制 他的初始化顺序,建议,使用 web.xml 进行控制。
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- XML 按照 顺序 加载,优先加载上面的,下面不会覆盖上面的 -->
<!-- 在 servlet-mapping 中,当 servlet-name,相同时,url-pattern 必须不同 -->
<!-- 放行这里 一些前端的文件 为什么这样配置?理由:需要我们到 Tomcat的 源码 conf/web.xml查看一下 一个 叫 DefaultServlet 的 servlet
从命名 我们就能看出 默认的 servlet 同时 上面注释信息 写了:
这是个所有应用的默认servlet,他可以提供静态资源(放行),他处理所有 未映射 到其他服务上的映射
The default servlet for all web applications, that serves static resources.
It processes all requests that are not mapped to other servlets with servlet mappings
-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.img</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.ico</url-pattern>
</servlet-mapping>
<!-- 首页 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jpg</welcome-file>
</welcome-file-list>
<!-- 配置自己的解析器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.zanglikun.framework.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- 拦截所有 资源 -->
<!-- <url-pattern>/*</url-pattern> -->
<!-- 不拦截 .jsp 和 Servlet -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
访问 一些资源看看吧
10、配置 404、500 页面
我们需要修改我们的 DispatcherServlet
删除 doget 方法里的 super.doGet(req, resp);
修改 doget 方法立马的内容
最终结果如下:
代码语言:javascript复制package com.zanglikun.framework.servlet;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 用户请求的 相对路径
String replace = req.getRequestURI().replace(req.getContextPath(), "");
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
System.out.println(replace);
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = ClassUtils.getURLMapping(replace);
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
// 执行方法
method.invoke(obj);
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
11、模拟一个框架不足点
首先 修改 NewsController 中 add()方法,我们要加入参数了
最终 add() 变为:
代码语言:javascript复制@RequestMapping("/add")
public void ADD(HttpServletRequest request, HttpServletResponse response){
System.out.println("request" request);
System.out.println("response" response);
//System.out.println("name" name);
System.out.println("NewsController 执行了add方法");
}
我们重启服务器 再去访问 http://localhost:8080/news/add ,看下是什么结果
500 错误,我们去控制台看一下吧
参数数量 异常,这来自与哪里呢?我们联想,反射的时候,需要传递参数的,不能不传递参数调用。
异常 展示完成,请看接下来如何解决
12、解决 req 与 resp 传参问题
我们 去修改 DispatcherServlet 内容 新增 蓝色部分
代码语言:javascript复制import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 用户请求的 相对路径
String replace = req.getRequestURI().replace(req.getContextPath(), "");
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
System.out.println(replace);
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = ClassUtils.getURLMapping(replace);
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
// 获取方法上参数类型
Class<?>[] paramTypes = method.getParameterTypes();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[paramTypes.length];
// 定义一个 下标
int index = 0;
// 遍历一下 获取方法参数
for (Class<?> parameterType : paramTypes) {
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if(parameterType == HttpServletRequest.class){
argurments[index] = req;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
else if(parameterType == HttpServletResponse.class){
argurments[index] = resp;
}else {
// 这里 是处理 其他参数的
argurments[index] = null;
}
// 相当于 for循环 的i
index ;
}
System.out.println(argurments.length);
// 执行方法
method.invoke(obj,argurments);
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
我们去测试 访问
http://localhost:8080/news/add
http://localhost:8080/news/del
控制台正常输出,通过
其实 这里 还有小问题,我们正常处理的是 req 和 resp 对象传递,其他参数,就无法处理。我们留着下面解决
13、根据 方法返回值进行 请求转发 与 重定向
通过方法的返回值,决定是请求转发,还是重定向,请求到那个路径上
在WEB-INF下面创建jsp页面 创建 news_del.jsp
代码语言:javascript复制<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>news_del</title>
</head>
<body>
我是 news_del 请求转发成功
</body>
</html>
修改 NewsController 的 ADD、DEL 方法 新增返回值 最终结果如下
代码语言:javascript复制 @RequestMapping("/add")
public String ADD(HttpServletRequest request, HttpServletResponse response){
System.out.println("request" request);
System.out.println("response" response);
//System.out.println("name" name);
System.out.println("NewsController 执行了add方法");
return "redirect:/indexA.html";
}
@RequestMapping("/del")
public String DEL(){
System.out.println("NewsController 执行了del方法");
return "news_del";
}
最后还是要修改 DispatcherServlet
代码语言:javascript复制import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 用户请求的 相对路径
String replace = req.getRequestURI().replace(req.getContextPath(), "");
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
System.out.println(replace);
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = ClassUtils.getURLMapping(replace);
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
// 获取方法上参数类型
Class<?>[] paramTypes = method.getParameterTypes();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[paramTypes.length];
// 定义一个 下标
int index = 0;
// 遍历一下 获取方法参数
for (Class<?> parameterType : paramTypes) {
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if (parameterType == HttpServletRequest.class) {
argurments[index] = req;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
else if (parameterType == HttpServletResponse.class) {
argurments[index] = resp;
} else {
// 这里 是处理 其他参数的
argurments[index] = null;
}
// 相当于 for循环 的i
index ;
}
System.out.println(argurments.length);
// 执行方法
Object result = method.invoke(obj, argurments);
// 如果方法有返回值
if (result != null) {
// 判断是不是 String
if (result instanceof String) {
String viewname = (String) result;
// 如果是重定向,走 重定向
if (viewname.startsWith("redirect:")) {
// 重定向 路径拼接 并发送重定向
// 这里有个 小Bug 没有 处理Https 协议,以及内外部资源 我们为了更好的处理,我们直接默认http 以及内部资源
System.out.println(viewname.replace("redirect:", ""));
resp.sendRedirect(viewname.replace("redirect:", ""));
}
// 如果 是重定向 那就走 重定向
else {
// 确保你的文件,在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时,跳转不了,最后发现路径错了
req.getRequestDispatcher("/WEB-INF/jsp/" viewname ".jsp").forward(req,resp);
}
}
// 如果 返回 不是 String 类型的 我们回头在处理
else {
}
}
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
14 返回结果是Json : 响应到 浏览器上
引入阿里巴巴的 fastJson
代码语言:javascript复制<!-- FastJson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.67</version>
</dependency>
继续 看:DispatcherServlet
代码语言:javascript复制import com.alibaba.fastjson.JSON;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 用户请求的 相对路径
String replace = req.getRequestURI().replace(req.getContextPath(), "");
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
System.out.println(replace);
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = ClassUtils.getURLMapping(replace);
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
// 获取方法上参数类型
Class<?>[] paramTypes = method.getParameterTypes();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[paramTypes.length];
// 定义一个 下标
int index = 0;
// 遍历一下 获取方法参数
for (Class<?> parameterType : paramTypes) {
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if (parameterType == HttpServletRequest.class) {
argurments[index] = req;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
else if (parameterType == HttpServletResponse.class) {
argurments[index] = resp;
} else {
// 这里 是处理 其他参数的
argurments[index] = null;
}
// 相当于 for循环 的i
index ;
}
System.out.println(argurments.length);
// 执行方法
Object modelview = method.invoke(obj, argurments);
// 如果方法有返回值
if (modelview != null) {
// 判断是不是 String
if (modelview instanceof String) {
String viewname = (String) modelview;
// 如果是重定向,走 重定向
if (viewname.startsWith("redirect:")) {
// 重定向 路径拼接 并发送重定向
// 这里有个 小Bug 没有 处理Https 协议,以及内外部资源 我们为了更好的处理,我们直接默认http 以及内部资源
System.out.println(viewname.replace("redirect:", ""));
resp.sendRedirect(viewname.replace("redirect:", ""));
}
// 如果 是重定向 那就走 重定向
else {
// 确保你的文件,在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时,跳转不了,最后发现路径错了
req.getRequestDispatcher("/WEB-INF/jsp/" viewname ".jsp").forward(req, resp);
}
}
// 如果 返回 不是 String 类型的 我们现在处理 :按照Json 进行处理
// 1、修改 result 更名未 modelview
else {
resp.setContentType("application/json;charset=UTF-8");
// modelview 调用 阿里的 fastJson 转为 Json 打印在浏览器上
resp.getWriter().write(JSON.toJSONString(modelview).toString());
}
}
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
修改 NewsController 的 Get 方法
代码语言:javascript复制 @RequestMapping("/get")
public Map<String,Object> GET(){
System.out.println("NewsController 执行了get方法");
Map<String,Object> map = new HashMap<>();
map.put("姓名","张三");
map.put("年龄","80");
map.put("爱好",new String[]{"抽烟","喝酒","烫头"});
return map;
}
启动测试:http://localhost:8080/news/get
测试 Json 处理 成功
15、映射其他基本类型参数
首先,原生的JDK 反射包 获取不到参数名称,能获取参数类型
所以 我们 还需要引入 新的java类库 javassist 以获取 参数 名称 方便 将来 参数做映射
时间类型 :只处理 JDK 1.8 新的时间类型,更在的JDK时间类型,不处理。并且,将来框架只支持JDK1.8
代码语言:javascript复制 <!-- 利用反射获取 方法的参数名称 因为原生JDK 无法获取参数的名称 -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
我们 修改 UrlMapping 新增加新的字段
代码语言:javascript复制private Map<String,Class<?>> parameter;
重写 get set 构造方法
ClassUtils 更变为
代码语言:javascript复制import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
import com.zanglikun.framework.model.UrlMapping;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.reflections.Reflections;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* @author : zanglikun
* @date : 2021/2/23 13:27
* @Version: 1.0
* @Desc : 费劲,没啥好说的
*/
public class ClassUtils {
// 创建 收集数据的对象
private static Map<String, UrlMapping> map = new HashMap<>();
static {
try {
getUrlMappings("com.zanglikun");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
}
}
// 私有静态 代码块的方式,保证了 扫描 所有类的 注解、方法 信息 只执行一次
// 传递 一个 基础的 包名
private static Map<String, UrlMapping> getUrlMappings(String basePackageName) throws IllegalAccessException, InstantiationException, InvocationTargetException, NotFoundException {
// 创建反射对象,
Reflections reflections = new Reflections(basePackageName);
// 获取 被 @Controller 修饰的对象的字节码信息
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Controller.class);
// 遍历 这些 类的字节码信息
for (Class<?> aClass : classes) {
// 判断 类上有 @RequestMapping 注解的 类
RequestMapping classAnnotation = aClass.getDeclaredAnnotation(RequestMapping.class);
// 定义 类上的 注解 值
String baseUri = "";
// 判断 是否有这个对象
if (classAnnotation != null) {
// 如果有 获取 类的注解名
String classAnnotationname = classAnnotation.value();
// baseUri 拼接上类的注解名
baseUri = classAnnotationname;
}
Object obj = aClass.newInstance();
// 拿到这些类,我们就获取其中所有的方法信息
Method[] methods = aClass.getMethods();
// 遍历 这些方法
for (Method method : methods) {
//判断 注解 其中被 @RequestMapping 修饰的方法
RequestMapping methodAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
// 如果有被标注的方法
if (methodAnnotation != null) {
// 定义 方法上的 注解值
String value = methodAnnotation.value();
// 获取 最终结果 类名 方法名 RequestMapping的 所有值
String requestUri = aClass.getName() " -- " method.getName() " -- " baseUri value;
// 获取 新对象信息 即 方法参数 名字
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(aClass));
CtMethod cm = classPool.getMethod(aClass.getName(), method.getName());
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attribute =
(LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
// 我们需要定义一个集合,收取 参数名称、类型 等信息因为参数有顺序,我们K 防止 参数名称,Value 防止参数类型
Map<String,Class<?>> parameters = new LinkedHashMap<>();
Class<?>[] classTYPE = method.getParameterTypes();
if (attribute != null) {
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < method.getParameterCount(); i ) {
String argName = attribute.variableName(i pos);
//System.out.println(argName);
parameters.put(argName,classTYPE[i]);
}
}
map.put(baseUri value, new UrlMapping(obj, method,parameters));
}
}
}
return map;
}
public static UrlMapping getURLMapping(String url) {
return map.get(url);
}
// 测试 调用方法
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
// 获取 被RequestMapping 从类 修饰到 方法 匹配到 /news/add 类和方法的信息
UrlMapping urlMapping = ClassUtils.getURLMapping("/news/add");
Method method = urlMapping.getMethod();
// 调用 获取到的类的信息, 准确的 有效的使用反射 进行调用方法!!!
method.invoke(urlMapping.getObj());
}
}
DispatcherServlet 更变为
代码语言:javascript复制import com.alibaba.fastjson.JSON;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 用户请求的 相对路径
String replace = req.getRequestURI().replace(req.getContextPath(), "");
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
System.out.println(replace);
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = ClassUtils.getURLMapping(replace);
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
Map<String, Class<?>> parameters = urlMapping.getParameter();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[method.getParameterTypes().length];
// 定义一个 下标
int index = 0;
for (String key : parameters.keySet()) {
// 请求的参数名称是 key
// 请求参数 的类型
Class<?> parameterType = parameters.get(key);
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if (parameterType == HttpServletRequest.class) {
argurments[index] = req;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
else if (parameterType == HttpServletResponse.class) {
argurments[index] = resp;
} else {
String parameter = req.getParameter(key);
if (parameter != null) {
// 这里 是处理 8大基本类型 与String 利用请求参数类型与 方法参数类型进行匹配 成功就赋值
try {
if (parameterType == java.lang.String.class) {
argurments[index] = parameter;
} else if (parameterType == java.lang.Integer.class) {
argurments[index] = Integer.parseInt(parameter);
} else if (parameterType == java.lang.Double.class) {
argurments[index] = Double.parseDouble(parameter);
} else if (parameterType == java.lang.Float.class) {
argurments[index] = Float.parseFloat(parameter);
} else if (parameterType == java.lang.Short.class) {
argurments[index] = Short.parseShort(parameter);
} else if (parameterType == java.lang.Byte.class) {
argurments[index] = Byte.parseByte(parameter);
} else if (parameterType == java.lang.Long.class) {
argurments[index] = Long.parseLong(parameter);
} else if (parameterType == java.lang.Character.class) {
argurments[index] = parameter.toCharArray()[0];
} else if (parameterType == java.lang.Boolean.class) {
argurments[index] = Boolean.parseBoolean(parameter);
} else if (parameterType == java.time.LocalDate.class) {
argurments[index] = LocalDate.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
} else if (parameterType == java.time.LocalDateTime.class) {
argurments[index] = LocalDateTime.parse(parameter,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
else {
// 非 Java 基本类型 和 String 类型 在这处理
argurments[index] = null;
}
} catch (Exception e) {
// 这里 可能会出现 类型转换异常
e.printStackTrace();
argurments[index] = null;
}
} else {
argurments[index] = null;
}
}
// 相当于 for循环 的i
index ;
}
// 执行方法
Object modelview = method.invoke(obj, argurments);
// 如果方法有返回值
if (modelview != null) {
// 判断是不是 String
if (modelview instanceof String) {
String viewname = (String) modelview;
// 如果是重定向,走 重定向
if (viewname.startsWith("redirect:")) {
// 重定向 路径拼接 并发送重定向
// 这里有个 小Bug 没有 处理Https 协议,以及内外部资源 我们为了更好的处理,我们直接默认http 以及内部资源
resp.sendRedirect(viewname.replace("redirect:", ""));
}
// 如果 是重定向 那就走 重定向
else {
// 确保你的文件,在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时,跳转不了,最后发现路径错了
req.getRequestDispatcher("/WEB-INF/jsp/" viewname ".jsp").forward(req, resp);
}
}
// 如果 返回 不是 String 类型的 我们现在处理 :按照Json 进行处理
// 1、修改 result 更名未 modelview
else {
resp.setContentType("application/json;charset=UTF-8");
// modelview 调用 阿里的 fastJson 转为 Json 打印在浏览器上
resp.getWriter().write(JSON.toJSONString(modelview).toString());
}
}
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
测试
新写一个 DispatcherServlet 下的 put方法
代码语言:javascript复制 @RequestMapping("/put")
public Map<String,Object> PUT(String string, Boolean bo, Short s, Byte by, Long l, Float f, Double d, Character c, Integer i, LocalDate localDate, LocalDateTime localDateTime){
System.out.println("NewsController 执行了PUT方法");
Map<String,Object> map = new HashMap<>();
map.put("String",string);
map.put("Boolean",bo);
map.put("Short",s);
map.put("Byte",by);
map.put("Long",l);
map.put("Float",f);
map.put("Double",d);
map.put("Character",c);
map.put("Integer",i);
map.put("LocalDate",localDate);
map.put("LocalDateTime",localDateTime);
return map;
}
浏览器发送请求,测试:
http://localhost:8080/news/put?string=你好&bo=true&s=1&by=1&l=88888&f=1.1&d=2.34&c=A&i=1&localDate=2020-01-01&localDateTime=2020-01-01 00:00:01
结果:
所有参数均获取
成功
16、处理 普通对象 类型
引入beanUtils
代码语言:javascript复制 <!-- -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
NewsController 新增 HEAD 方法
代码语言:javascript复制 @RequestMapping("/head")
public Map<String,Object> HEAD(News news){
System.out.println("NewsController 执行了head方法");
Map<String,Object> map = new HashMap<>();
if(news != null){
map.put("ID",news.getId());
map.put("Title",news.getTitle());
map.put("LocalDate",news.getLocalDate());
map.put("LocalDateTine",news.getLocalDateTime());
}
return map;
}
在model 创建实体类 News
代码语言:javascript复制import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* @author : zanglikun
* @date : 2021/2/25 15:44
* @Version: 1.0
* @Desc : 新闻测试类
*/
public class News {
private Integer id;
private String title;
private LocalDate localDate;
private LocalDateTime localDateTime;
public News() {
}
public News(Integer id, String title, LocalDate localDate, LocalDateTime localDateTime) {
this.id = id;
this.title = title;
this.localDate = localDate;
this.localDateTime = localDateTime;
}
public LocalDate getLocalDate() {
return localDate;
}
public void setLocalDate(LocalDate localDate) {
this.localDate = localDate;
}
public LocalDateTime getLocalDateTime() {
return localDateTime;
}
public void setLocalDateTime(LocalDateTime localDateTime) {
this.localDateTime = localDateTime;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
创建 时间转换工具类 DateTimeConverter
代码语言:javascript复制import org.apache.commons.beanutils.Converter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author : zanglikun
* @date : 2021/2/25 16:43
* @Version: 1.0
* @Desc : 费劲,没啥好说的
*/
public class DateTimeConverter implements Converter {
// 重写方法
@Override
public Object convert(Class aClass, Object o) {
if (o == null || "".equals(o)){
return null;
}
if (o instanceof java.lang.String){
String value = o.toString().trim();
if(aClass.equals(LocalDate.class)){
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return LocalDate.parse(value,dateTimeFormatter);
}
if(aClass.equals(LocalDateTime.class)){
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(value,dateTimeFormatter);
}
}
return o;
}
}
新增 DispatcherServlet
代码语言:javascript复制package com.zanglikun.framework.servlet;
import com.alibaba.fastjson.JSON;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import com.zanglikun.framework.utils.DateTimeConverter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取 用户请求的 相对路径
String replace = req.getRequestURI().replace(req.getContextPath(), "");
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
System.out.println(replace);
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = ClassUtils.getURLMapping(replace);
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
Map<String, Class<?>> parameters = urlMapping.getParameter();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[method.getParameterTypes().length];
// 定义一个 下标
int index = 0;
for (String key : parameters.keySet()) {
// 请求的参数名称是 key
// 请求参数 的类型
Class<?> parameterType = parameters.get(key);
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if (parameterType == HttpServletRequest.class) {
argurments[index] = req;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
else if (parameterType == HttpServletResponse.class) {
argurments[index] = resp;
} else {
// 用方法名 获取 方法参数 与请求参数进行匹配
String parameter = req.getParameter(key);
if (parameter != null) {
// 这里 是处理 8大基本类型 与String 利用请求参数类型与 方法参数类型进行匹配 成功就赋值
try {
if (parameterType == java.lang.String.class) {
argurments[index] = parameter;
} else if (parameterType == java.lang.Integer.class) {
argurments[index] = Integer.parseInt(parameter);
} else if (parameterType == java.lang.Double.class) {
argurments[index] = Double.parseDouble(parameter);
} else if (parameterType == java.lang.Float.class) {
argurments[index] = Float.parseFloat(parameter);
} else if (parameterType == java.lang.Short.class) {
argurments[index] = Short.parseShort(parameter);
} else if (parameterType == java.lang.Byte.class) {
argurments[index] = Byte.parseByte(parameter);
} else if (parameterType == java.lang.Long.class) {
argurments[index] = Long.parseLong(parameter);
} else if (parameterType == java.lang.Character.class) {
argurments[index] = parameter.toCharArray()[0];
} else if (parameterType == java.lang.Boolean.class) {
argurments[index] = Boolean.parseBoolean(parameter);
} else if (parameterType == java.time.LocalDate.class) {
argurments[index] = LocalDate.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
} else if (parameterType == java.time.LocalDateTime.class) {
argurments[index] = LocalDateTime.parse(parameter,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
else {
argurments[index] = parameter;
}
} catch (Exception e) {
// 这里 可能会出现 类型转换异常
e.printStackTrace();
argurments[index] = null;
}
} else {
// 处理 Java Bean 对象
try {
Object paramObj = parameterType.newInstance();
BeanUtils.populate(paramObj,req.getParameterMap());
argurments[index] = paramObj;
}catch (Exception e){
e.printStackTrace();
argurments[index] = null;
}
}
}
// 相当于 for循环 的i
index ;
}
// 执行方法
Object modelview = method.invoke(obj, argurments);
// 如果方法有返回值
if (modelview != null) {
// 判断是不是 String
if (modelview instanceof String) {
String viewname = (String) modelview;
// 如果是重定向,走 重定向
if (viewname.startsWith("redirect:")) {
// 重定向 路径拼接 并发送重定向
// 这里有个 小Bug 没有 处理Https 协议,以及内外部资源 我们为了更好的处理,我们直接默认http 以及内部资源
resp.sendRedirect(viewname.replace("redirect:", ""));
}
// 如果 是重定向 那就走 重定向
else {
// 确保你的文件,在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时,跳转不了,最后发现路径错了
req.getRequestDispatcher("/WEB-INF/jsp/" viewname ".jsp").forward(req, resp);
}
}
// 如果 返回 不是 String 类型的 我们现在处理 :按照Json 进行处理
// 1、修改 result 更名未 modelview
else {
resp.setContentType("application/json;charset=UTF-8");
// modelview 调用 阿里的 fastJson 转为 Json 打印在浏览器上
resp.getWriter().write(JSON.toJSONString(modelview).toString());
}
}
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
ClassUtils 更变为:
代码语言:javascript复制import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
import com.zanglikun.framework.model.UrlMapping;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.apache.commons.beanutils.ConvertUtils;
import org.reflections.Reflections;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* @author : zanglikun
* @date : 2021/2/23 13:27
* @Version: 1.0
* @Desc : 费劲,没啥好说的
*/
public class ClassUtils {
// 创建 收集数据的对象
private static Map<String, UrlMapping> map = new HashMap<>();
static {
try {
// 注册时间解析
ConvertUtils.register(new DateTimeConverter(), LocalDate.class);
ConvertUtils.register(new DateTimeConverter(), LocalDateTime.class);
getUrlMappings("com.zanglikun");
} catch (Exception e) {
e.printStackTrace();
}
}
// 私有静态 代码块的方式,保证了 扫描 所有类的 注解、方法 信息 只执行一次
// 传递 一个 基础的 包名
private static Map<String, UrlMapping> getUrlMappings(String basePackageName) throws IllegalAccessException, InstantiationException, InvocationTargetException, NotFoundException {
// 创建反射对象,
Reflections reflections = new Reflections(basePackageName);
// 获取 被 @Controller 修饰的对象的字节码信息
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Controller.class);
// 遍历 这些 类的字节码信息
for (Class<?> aClass : classes) {
// 判断 类上有 @RequestMapping 注解的 类
RequestMapping classAnnotation = aClass.getDeclaredAnnotation(RequestMapping.class);
// 定义 类上的 注解 值
String baseUri = "";
// 判断 是否有这个对象
if (classAnnotation != null) {
// 如果有 获取 类的注解名
String classAnnotationname = classAnnotation.value();
// baseUri 拼接上类的注解名
baseUri = classAnnotationname;
}
Object obj = aClass.newInstance();
// 拿到这些类,我们就获取其中所有的方法信息
Method[] methods = aClass.getMethods();
// 遍历 这些方法
for (Method method : methods) {
//判断 注解 其中被 @RequestMapping 修饰的方法
RequestMapping methodAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
// 如果有被标注的方法
if (methodAnnotation != null) {
// 定义 方法上的 注解值
String value = methodAnnotation.value();
// 获取 最终结果 类名 方法名 RequestMapping的 所有值
String requestUri = aClass.getName() " -- " method.getName() " -- " baseUri value;
// 获取 新对象信息 即 方法参数 名字
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(aClass));
CtMethod cm = classPool.getMethod(aClass.getName(), method.getName());
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attribute =
(LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
// 我们需要定义一个集合,收取 参数名称、类型 等信息因为参数有顺序,我们K 防止 参数名称,Value 防止参数类型
Map<String,Class<?>> parameters = new LinkedHashMap<>();
Class<?>[] classTYPE = method.getParameterTypes();
if (attribute != null) {
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < method.getParameterCount(); i ) {
String argName = attribute.variableName(i pos);
//System.out.println(argName);
parameters.put(argName,classTYPE[i]);
}
}
map.put(baseUri value, new UrlMapping(obj, method,parameters));
}
}
}
return map;
}
public static UrlMapping getURLMapping(String url) {
return map.get(url);
}
// 测试 调用方法
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
// 获取 被RequestMapping 从类 修饰到 方法 匹配到 /news/add 类和方法的信息
UrlMapping urlMapping = ClassUtils.getURLMapping("/news/add");
Method method = urlMapping.getMethod();
// 调用 获取到的类的信息, 准确的 有效的使用反射 进行调用方法!!!
method.invoke(urlMapping.getObj());
}
}
测试
http://localhost:8080/news/head?id=1&title=张三&localDate=2020-01-01&localDateTime=2020-01-01 01:01:01
结果:
成功
17、代码优化
因为 serlvet 只有在访问的时候 ClassUtils才会初始化。故 ClassUtils 需要提前加载
重写 DispatcherServlet 重写的 init(ServletConfig config) 方法
同时 我们提升DispatcherServlet加载时间,让其随着类加载而加载 ,
记录能处理的类型,不能处理 统一以 Java类型处理
修改 ClassUtils的代码,删除很多内容
代码语言:javascript复制import com.zanglikun.framework.annoation.Controller;
import com.zanglikun.framework.annoation.RequestMapping;
import com.zanglikun.framework.model.UrlMapping;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.reflections.Reflections;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* @author : zanglikun
* @date : 2021/2/23 13:27
* @Version: 1.0
* @Desc : ClassUtils 扫描我们自定义注解的类 和 方法
*/
public class ClassUtils {
// 创建 收集数据的对象
private static Map<String, UrlMapping> map = new HashMap<>();
/** 此方法 用于扫描被 Controller 和 RequestMapping 类和方法的 集合,将来可以用来判断 */
public static Map<String, UrlMapping> getUrlMappings(String basePackageName) throws IllegalAccessException, InstantiationException, NotFoundException {
// 创建反射对象,
Reflections reflections = new Reflections(basePackageName);
// 获取 被 @Controller 修饰的对象的字节码信息
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Controller.class);
// 遍历 这些 类的字节码信息
for (Class<?> aClass : classes) {
// 判断 类上有 @RequestMapping 注解的 类
RequestMapping classAnnotation = aClass.getDeclaredAnnotation(RequestMapping.class);
// 定义 类上的 注解 值
String baseUri = "";
// 判断 是否有这个对象
if (classAnnotation != null) {
// 如果有 获取 类的注解名
String classAnnotationname = classAnnotation.value();
// baseUri 拼接上类的注解名
baseUri = classAnnotationname;
}
Object obj = aClass.newInstance();
// 拿到这些类,我们就获取其中所有的方法信息
Method[] methods = aClass.getMethods();
// 遍历 这些方法
for (Method method : methods) {
//判断 注解 其中被 @RequestMapping 修饰的方法
RequestMapping methodAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
// 如果有被标注的方法
if (methodAnnotation != null) {
// 定义 方法上的 注解值
String value = methodAnnotation.value();
// 获取 新对象信息 即 方法参数 名字
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(aClass));
CtMethod cm = classPool.getMethod(aClass.getName(), method.getName());
MethodInfo methodInfo = cm.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attribute =
(LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
// 我们需要定义一个集合,收取 参数名称、类型 等信息因为参数有顺序,我们K 防止 参数名称,Value 防止参数类型
Map<String,Class<?>> parameters = new LinkedHashMap<>();
Class<?>[] classTYPE = method.getParameterTypes();
if (attribute != null) {
int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
for (int i = 0; i < method.getParameterCount(); i ) {
String argName = attribute.variableName(i pos);
parameters.put(argName,classTYPE[i]);
}
}
map.put(baseUri value, new UrlMapping(obj, method,parameters));
}
}
}
return map;
}
}
去修改 web.xml的内容
作用是 让DispatcherSerlvet 随着类加载而加载
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- XML 按照 顺序 加载,优先加载上面的,下面不会覆盖上面的 -->
<!-- 在 servlet-mapping 中,当 servlet-name,相同时,url-pattern 必须不同 -->
<!-- 放行这里 一些前端的文件 为什么这样配置?理由:需要我们到 Tomcat的 源码 conf/web.xml查看一下 一个 叫 DefaultServlet 的 servlet
从命名 我们就能看出 默认的 servlet 同时 上面注释信息 写了:
这是个所有应用的默认servlet,他可以提供静态资源(放行),他处理所有 未映射 到其他服务上的映射
The default servlet for all web applications, that serves static resources.
It processes all requests that are not mapped to other servlets with servlet mappings
-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.img</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.ico</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<!-- 首页 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jpg</welcome-file>
</welcome-file-list>
<!-- 配置自己的解析器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.zanglikun.framework.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>prefix</param-name>
<param-value>/WEB-INF/jsp</param-value>
</init-param>
<init-param>
<param-name>suffix</param-name>
<param-value>.jsp</param-value>
</init-param>
<init-param>
<param-name>basePackage</param-name>
<param-value>com.zanglikun</param-value>
</init-param>
<!-- 设为1 让其 随着类加载而加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- 拦截所有 资源 -->
<!-- <url-pattern>/*</url-pattern> -->
<!-- 不拦截 .jsp 和 Servlet -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
DispatcherServlet 变化较大 定义了一些参数值 更多的修复 :减少循环次数 与 if 条件判断
代码语言:javascript复制import com.alibaba.fastjson.JSON;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import com.zanglikun.framework.utils.DateTimeConverter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
// 创建 收集数据的对象
private static Map<String, UrlMapping> map = new HashMap<>();
private static final String REQUEST_PREFIX = "prefix"; // 请求前缀
private static final String REQUEST_SUFFIX = "suffix"; // 请求后缀
private static final String BASKPACKAGE = "basePackage"; //controller 的包路径
private String prefix = "/WEB-INF/jsp";
private String suffix = ".jsp";
private String basePackage = "com.zanglikun";
// 可以处理的Java类型,作用是 如果不可以处理,就让Java对象处理
Set<Class> doCopy = new HashSet<>();
@Override
public void init(ServletConfig config) {
// 注册时间解析
ConvertUtils.register(new DateTimeConverter(), LocalDate.class);
ConvertUtils.register(new DateTimeConverter(), LocalDateTime.class);
try {
// 如果初始化 配置了值,就会使用web.xml 的信息
if (config.getInitParameter(REQUEST_PREFIX) != null) {
prefix = config.getInitParameter(REQUEST_PREFIX);
}
if (config.getInitParameter(REQUEST_SUFFIX) != null) {
suffix = config.getInitParameter(REQUEST_SUFFIX);
}
if (config.getInitParameter(BASKPACKAGE) != null) {
basePackage = config.getInitParameter(BASKPACKAGE);
}
map = ClassUtils.getUrlMappings(basePackage);
// 记录 能处理的类型,作用是 如果方法参数类型 不是记录的值,就以Java 对象处理
doCopy.add(java.lang.String.class);
doCopy.add(java.lang.Integer.class);
doCopy.add(java.lang.Long.class);
doCopy.add(java.lang.Double.class);
doCopy.add(java.lang.Float.class);
doCopy.add(java.lang.Short.class);
doCopy.add(java.lang.Byte.class);
doCopy.add(java.lang.Boolean.class);
doCopy.add(java.lang.Character.class);
doCopy.add(java.time.LocalDateTime.class);
doCopy.add(java.time.LocalDate.class);
} catch (Exception e) {
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
/** 处理请求路径 */
String requestURI = req.getRequestURI();
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = map.get(requestURI.replace(req.getContextPath(), ""));
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
/** 处理请求参数 */
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
Map<String, Class<?>> parameters = urlMapping.getParameter();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[method.getParameterTypes().length];
// 定义一个 下标
int index = 0;
for (String key : parameters.keySet()) {
// 请求的参数名称是 key
// 获取方法参数的Java类型
Class<?> parameterType = parameters.get(key);
// 请求参数值 通过request 获取与 方法参数名称匹配的 参数值
String parameter = req.getParameter(key);
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if (parameterType == HttpServletRequest.class) {
argurments[index ] = req;
continue;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
if (parameterType == HttpServletResponse.class) {
argurments[index ] = resp;
continue;
}
if (parameter != null) {
// 这里 是处理 8大基本类型 与String 利用请求参数类型与 方法参数类型进行匹配 成功就赋值
try {
if (parameterType == java.lang.String.class) {
argurments[index ] = parameter;
continue;
}
/** 基本数据类型转换开始 */
if (parameterType == java.lang.Integer.class) {
argurments[index ] = Integer.parseInt(parameter);
continue;
}
if (parameterType == java.lang.Double.class) {
argurments[index ] = Double.parseDouble(parameter);
continue;
}
if (parameterType == java.lang.Float.class) {
argurments[index ] = Float.parseFloat(parameter);
continue;
}
if (parameterType == java.lang.Short.class) {
argurments[index ] = Short.parseShort(parameter);
continue;
}
if (parameterType == java.lang.Byte.class) {
argurments[index ] = Byte.parseByte(parameter);
continue;
}
if (parameterType == java.lang.Long.class) {
argurments[index ] = Long.parseLong(parameter);
continue;
}
if (parameterType == java.lang.Character.class) {
argurments[index ] = parameter.toCharArray()[0];
continue;
}
if (parameterType == java.lang.Boolean.class) {
argurments[index ] = Boolean.parseBoolean(parameter);
continue;
}
// 基本数据类型转换结束
/** 日期类型转换开始 */
if (parameterType == java.time.LocalDate.class) {
argurments[index ] = LocalDate.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
continue;
}
if (parameterType == java.time.LocalDateTime.class) {
argurments[index ] = LocalDateTime.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
continue;
}
/** 日期类型转换结束 */
// 处理不了的参数 暂时以原来的值处理
else {
argurments[index ] = parameter;
continue;
}
} catch (Exception e) {
// 这里 可能会出现 类型转换异常
e.printStackTrace();
argurments[index ] = null;
continue;
}
} else {
// 如果不是 我们记录的能处理的类型 那就以Java对象转化
if (!doCopy.contains(parameterType)) {
/** Java 对象 转化开始 */
try {
Object paramObj = parameterType.newInstance();
BeanUtils.populate(paramObj, req.getParameterMap());
argurments[index ] = paramObj;
continue;
} catch (Exception e) {
e.printStackTrace();
argurments[index ] = null;
continue;
}
/** Java 对象 转化结束 */
}
}
}
// 执行方法
Object modelview = method.invoke(obj, argurments);
/**
处理方法的返回值
*/
if (modelview != null) {
// 判断是不是 String
if (modelview instanceof String) {
String viewname = (String) modelview;
// 如果是重定向,走 重定向
if (viewname.startsWith("redirect:")) {
// 重定向 路径拼接 并发送重定向
// 这里有个 小Bug 没有 处理Https 协议,以及内外部资源 我们为了更好的处理,我们直接默认http 以及内部资源
resp.sendRedirect(viewname.replace("redirect:", ""));
}
// 如果 是重定向 那就走 重定向
else {
// 确保你的文件,在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时,跳转不了,最后发现路径错了
req.getRequestDispatcher(prefix "/" viewname suffix).forward(req, resp);
}
}
// 如果 返回 不是 String 类型的 我们现在处理 :按照Json 进行处理
// 1、修改 result 更名未 modelview
else {
resp.setContentType("application/json;charset=UTF-8");
// modelview 调用 阿里的 fastJson 转为 Json 打印在浏览器上
resp.getWriter().write(JSON.toJSONString(modelview).toString());
}
}
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
优化完成
19、数组处理
DispatcherServlet 变更为:
代码语言:javascript复制import com.alibaba.fastjson.JSON;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import com.zanglikun.framework.utils.DateTimeConverter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author : zanglikun
* @date : 2021/2/23 14:46
* @Version: 1.0
* @Desc : 配置 DispatcherServlet
*/
public class DispatcherServlet extends HttpServlet {
// 创建 收集数据的对象
private static Map<String, UrlMapping> map = new HashMap<>();
private static final String REQUEST_PREFIX = "prefix"; // 请求前缀
private static final String REQUEST_SUFFIX = "suffix"; // 请求后缀
private static final String BASKPACKAGE = "basePackage"; //controller 的包路径
private String prefix = "/WEB-INF/jsp";
private String suffix = ".jsp";
private String basePackage = "com.zanglikun";
// 可以处理的Java类型,作用是 如果不可以处理,就让Java对象处理
Set<Class> doCopy = new HashSet<>();
@Override
public void init(ServletConfig config) {
// 注册时间解析
ConvertUtils.register(new DateTimeConverter(), LocalDate.class);
ConvertUtils.register(new DateTimeConverter(), LocalDateTime.class);
try {
// 如果初始化 配置了值,就会使用web.xml 的信息
if (config.getInitParameter(REQUEST_PREFIX) != null) {
prefix = config.getInitParameter(REQUEST_PREFIX);
}
if (config.getInitParameter(REQUEST_SUFFIX) != null) {
suffix = config.getInitParameter(REQUEST_SUFFIX);
}
if (config.getInitParameter(BASKPACKAGE) != null) {
basePackage = config.getInitParameter(BASKPACKAGE);
}
map = ClassUtils.getUrlMappings(basePackage);
// 记录 能处理的类型,作用是 如果方法参数类型 不是记录的值,就以Java 对象处理
doCopy.add(java.lang.String.class);
doCopy.add(java.lang.Integer.class);
doCopy.add(java.lang.Long.class);
doCopy.add(java.lang.Double.class);
doCopy.add(java.lang.Float.class);
doCopy.add(java.lang.Short.class);
doCopy.add(java.lang.Byte.class);
doCopy.add(java.lang.Boolean.class);
doCopy.add(java.lang.Character.class);
doCopy.add(java.time.LocalDateTime.class);
doCopy.add(java.time.LocalDate.class);
} catch (Exception e) {
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8");
/** 处理请求路径 */
String requestURI = req.getRequestURI();
// 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
UrlMapping urlMapping = map.get(requestURI.replace(req.getContextPath(), ""));
// 人为 测试 制作服务端错误 500
//int i = 1/0;
// 如果 请求路径不存在
if (urlMapping == null) {
resp.setStatus(404);
resp.getWriter().write("请求路径不存在:<h1>404</h1> 请求路径是" req.getRequestURI());
// 结束 下面代码执行
return;
}
/** 处理请求参数 */
Method method = urlMapping.getMethod();
Object obj = urlMapping.getObj();
Map<String, Class<?>> parameters = urlMapping.getParameter();
// 创建 数组 将来放参数的地方
Object[] argurments = new Object[method.getParameterTypes().length];
// 定义一个 下标
int index = 0;
for (String key : parameters.keySet()) {
// 请求的参数名称是 key
// 获取方法参数的Java类型
Class<?> parameterType = parameters.get(key);
// 请求参数值 通过request 获取与 方法参数名称匹配的 参数值
String parameter = req.getParameter(key);
// 请求参数是数组形式的 请求参数值
String[] parameterValues = req.getParameterValues(key);
// 获取数组的长度
int shuzulength = 0;
if(parameterValues != null){
shuzulength = parameterValues.length;
}
// 请求参数如果是数组形式 获取数组数据类型
Class<?> shuzuType = parameterType.getComponentType();
// 如果是 HttpServletRequest 对象 就放进去 doGet的 req
if (parameterType == HttpServletRequest.class) {
argurments[index ] = req;
continue;
}
// 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
if (parameterType == HttpServletResponse.class) {
argurments[index ] = resp;
continue;
}
if (parameter != null) {
// 这里 是处理 8大基本类型 与String 利用请求参数类型与 方法参数类型进行匹配 成功就赋值
try {
if (parameterType == java.lang.String.class) {
argurments[index ] = parameter;
continue;
}
/** 基本数据类型转换开始 */
if (parameterType == java.lang.Integer.class) {
argurments[index ] = Integer.parseInt(parameter);
continue;
}
if (parameterType == java.lang.Double.class) {
argurments[index ] = Double.parseDouble(parameter);
continue;
}
if (parameterType == java.lang.Float.class) {
argurments[index ] = Float.parseFloat(parameter);
continue;
}
if (parameterType == java.lang.Short.class) {
argurments[index ] = Short.parseShort(parameter);
continue;
}
if (parameterType == java.lang.Byte.class) {
argurments[index ] = Byte.parseByte(parameter);
continue;
}
if (parameterType == java.lang.Long.class) {
argurments[index ] = Long.parseLong(parameter);
continue;
}
if (parameterType == java.lang.Character.class) {
argurments[index ] = parameter.toCharArray()[0];
continue;
}
if (parameterType == java.lang.Boolean.class) {
argurments[index ] = Boolean.parseBoolean(parameter);
continue;
}
// 基本数据类型转换结束
/** 日期类型转换开始 */
if (parameterType == java.time.LocalDate.class) {
argurments[index ] = LocalDate.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
continue;
}
if (parameterType == java.time.LocalDateTime.class) {
argurments[index ] = LocalDateTime.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
continue;
}
/** 日期类型转换结束 */
/** 处理数组 开始*/
if (parameterValues != null && shuzuType != null && shuzulength >0){
if (shuzuType == java.lang.String.class) {
String[] str = new String[shuzulength];
for (int i = 0; i < shuzulength; i ) {
str[i] = parameterValues[i];
}
argurments[index ] = str;
continue;
}
if (shuzuType == java.lang.Integer.class) {
Integer[] arr = new Integer[shuzulength];
for (int i = 0; i < shuzulength; i ) {
arr[i] = Integer.valueOf(parameterValues[i]);
}
argurments[index ] = arr;
continue;
}
if (shuzuType == java.lang.Double.class) {
Double[] dob = new Double[shuzulength];
for (int i = 0; i < shuzulength; i ) {
dob[i] = Double.parseDouble(parameterValues[i]);
}
argurments[index ] = dob;
continue;
}
if (shuzuType == java.lang.Float.class) {
Float[] flo = new Float[shuzulength];
for (int i = 0; i < shuzulength; i ) {
flo[i] = Float.parseFloat(parameterValues[i]);
}
argurments[index ] = flo;
continue;
}
if (shuzuType == java.lang.Short.class) {
Short[] sho = new Short[shuzulength];
for (int i = 0; i < shuzulength; i ) {
sho[i] = Short.valueOf(parameterValues[i]);
}
argurments[index ] = sho;
continue;
}
if (shuzuType == java.lang.Byte.class) {
Byte[] byt = new Byte[shuzulength];
for (int i = 0; i < shuzulength; i ) {
byt[i] = Byte.valueOf(parameterValues[i]);
}
argurments[index ] = byt;
continue;
}
if (shuzuType == java.lang.Long.class) {
Long[] lon = new Long[shuzulength];
for (int i = 0; i < shuzulength; i ) {
lon[i] = Long.valueOf(parameterValues[i]);
}
argurments[index ] = lon;
continue;
}
if (shuzuType == java.lang.Character.class) {
Character[] cha = new Character[shuzulength];
for (int i = 0; i < shuzulength; i ) {
cha[i] = parameterValues[i].toCharArray()[0];
}
argurments[index ] = cha;
continue;
}
if (shuzuType == java.lang.Boolean.class) {
Boolean[] bol = new Boolean[shuzulength];
for (int i = 0; i < shuzulength; i ) {
bol[i] = Boolean.valueOf(parameterValues[i]);
}
argurments[index ] = bol;
continue;
}
if (shuzuType == java.time.LocalDate.class) {
LocalDate[] lol = new LocalDate[shuzulength];
for (int i = 0; i < shuzulength; i ) {
lol[i] = LocalDate.parse(parameterValues[i], DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
argurments[index ] = lol;
continue;
}
if (shuzuType == java.time.LocalDateTime.class) {
LocalDateTime[] lot = new LocalDateTime[shuzulength];
for (int i = 0; i < shuzulength; i ) {
lot[i] = LocalDateTime.parse(parameterValues[i],DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
argurments[index ] = lot;
continue;
}
}
// 处理不了的参数 暂时以原来的值处理
else {
argurments[index ] = parameter;
continue;
}
} catch (Exception e) {
// 这里 可能会出现 类型转换异常
e.printStackTrace();
argurments[index ] = null;
continue;
}
/** 处理数组结束 */
}
else {
// 如果不是 我们记录的能处理的类型 那就以Java对象转化
if (!doCopy.contains(parameterType) && shuzuType ==null) {
/** Java 对象 转化开始 */
try {
Object paramObj = parameterType.newInstance();
BeanUtils.populate(paramObj, req.getParameterMap());
argurments[index ] = paramObj;
continue;
} catch (Exception e) {
e.printStackTrace();
argurments[index ] = null;
continue;
}
/** Java 对象 转化结束 */
}
}
}
for (int i = 0; i < argurments.length; i ) {
System.out.println(argurments[i]);
}
// 执行方法
Object modelview = method.invoke(obj, argurments);
/**
处理方法的返回值
*/
if (modelview != null) {
// 判断是不是 String
if (modelview instanceof String) {
String viewname = (String) modelview;
// 如果是重定向,走 重定向
if (viewname.startsWith("redirect:")) {
// 重定向 路径拼接 并发送重定向
// 这里有个 小Bug 没有 处理Https 协议,以及内外部资源 我们为了更好的处理,我们直接默认http 以及内部资源
resp.sendRedirect(viewname.replace("redirect:", ""));
}
// 如果 是重定向 那就走 重定向
else {
// 确保你的文件,在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时,跳转不了,最后发现路径错了
req.getRequestDispatcher(prefix "/" viewname suffix).forward(req, resp);
}
}
// 如果 返回 不是 String 类型的 我们现在处理 :按照Json 进行处理
// 1、修改 result 更名未 modelview
else {
resp.setContentType("application/json;charset=UTF-8");
// modelview 调用 阿里的 fastJson 转为 Json 打印在浏览器上
resp.getWriter().write(JSON.toJSONString(modelview).toString());
}
}
} catch (Exception e) {
// 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误,请联系系统管理员 Tel :110 ");
/*
sendError 与 getWriter 同时 出现,会执行 sendError() 方法
resp.setStatus(500);
resp.getWriter().println("兄弟 500 错误了 <br>");
e.printStackTrace(resp.getWriter());
*/
// 控制台打印信息
e.printStackTrace();
}
//super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 配置 所有请求都走 doGet 方法
// super.doPost(req, resp);
doGet(req, resp);
}
}
NewsController 新增 post 方法
代码语言:javascript复制 @RequestMapping("/post")
public Map<String,Object> post(String[] string, Boolean[] bo, Short[] s, Byte[] by, Long[] l, Float[] f, Double[] d, Character[] c, Integer[] i, LocalDate[] localDate, LocalDateTime[] localDateTime){
System.out.println("NewsController 执行了post方法");
Map<String,Object> map = new HashMap<>();
map.put("String",string);
map.put("Boolean",bo);
map.put("Short",s);
map.put("Byte",by);
map.put("Long",l);
map.put("Float",f);
map.put("Double",d);
map.put("Character",c);
map.put("Integer",i);
map.put("LocalDate",localDate);
map.put("LocalDateTime",localDateTime);
return map;
}
请求:http://localhost:8080/news/post?string=你好&bo=true&s=1&by=1&l=88888&f=1.1&d=2.34&c=A&i=1&localDate=2020-01-01&localDateTime=2020-01-01 00:00:01&string=滴滴&bo=false&s=2&by=2&l=66666&f=2.3&d=5.67&c=B&i=2&localDate=2020-02-02&localDateTime=2020-02-02 00:00:02
结果如下
成功
结果
我们目前的项目:可有处理String、8大基本数据类型、LocalDate、LocalDateTime 、JavaBean、数组 等等
源代码
源代码:下载
请配合 Tomcat8 使用
特殊说明: 解决问题的光鲜,藏着磕Bug的痛苦。 万物皆入轮回,谁也躲不掉! 以上文章,均是我实际操作,写出来的笔记资料,不会出现全文盗用别人文章!烦请各位,请勿直接盗用!